1 /*	$NetBSD: refclock_chronolog.c,v 1.1.1.1 2009/12/13 16:55:46 kardel Exp $	*/
2 
3 /*
4  * refclock_chronolog - clock driver for Chronolog K-series WWVB receiver.
5  */
6 
7 /*
8  * Must interpolate back to local time.  Very annoying.
9  */
10 #define GET_LOCALTIME
11 
12 #ifdef HAVE_CONFIG_H
13 #include <config.h>
14 #endif
15 
16 #if defined(REFCLOCK) && defined(CLOCK_CHRONOLOG)
17 
18 #include "ntpd.h"
19 #include "ntp_io.h"
20 #include "ntp_refclock.h"
21 #include "ntp_calendar.h"
22 #include "ntp_stdlib.h"
23 
24 #include <stdio.h>
25 #include <ctype.h>
26 
27 /*
28  * This driver supports the Chronolog K-series WWVB receiver.
29  *
30  * Input format:
31  *
32  *	Y YY/MM/DD<cr><lf>
33  *      Z hh:mm:ss<cr><lf>
34  *
35  * YY/MM/DD -- what you'd expect.  This arrives a few seconds before the
36  * timestamp.
37  * hh:mm:ss -- what you'd expect.  We take time on the <cr>.
38  *
39  * Our Chronolog writes time out at 2400 bps 8/N/1, but it can be configured
40  * otherwise.  The clock seems to appear every 60 seconds, which doesn't make
41  * for good statistics collection.
42  *
43  * The original source of this module was the WWVB module.
44  */
45 
46 /*
47  * Interface definitions
48  */
49 #define	DEVICE		"/dev/chronolog%d" /* device name and unit */
50 #define	SPEED232	B2400	/* uart speed (2400 baud) */
51 #define	PRECISION	(-13)	/* precision assumed (about 100 us) */
52 #define	REFID		"chronolog"	/* reference ID */
53 #define	DESCRIPTION	"Chrono-log K" /* WRU */
54 
55 #define MONLIN		15	/* number of monitoring lines */
56 
57 /*
58  * Chrono-log unit control structure
59  */
60 struct chronolog_unit {
61 	u_char	tcswitch;	/* timecode switch */
62 	l_fp	laststamp;	/* last receive timestamp */
63 	u_char	lasthour;	/* last hour (for monitor) */
64 	int   	year;	        /* Y2K-adjusted year */
65 	int   	day;	        /* day-of-month */
66         int   	month;	        /* month-of-year */
67 };
68 
69 /*
70  * Function prototypes
71  */
72 static	int	chronolog_start		(int, struct peer *);
73 static	void	chronolog_shutdown	(int, struct peer *);
74 static	void	chronolog_receive	(struct recvbuf *);
75 static	void	chronolog_poll		(int, struct peer *);
76 
77 /*
78  * Transfer vector
79  */
80 struct	refclock refclock_chronolog = {
81 	chronolog_start,	/* start up driver */
82 	chronolog_shutdown,	/* shut down driver */
83 	chronolog_poll,		/* poll the driver -- a nice fabrication */
84 	noentry,		/* not used */
85 	noentry,		/* not used */
86 	noentry,		/* not used */
87 	NOFLAGS			/* not used */
88 };
89 
90 
91 /*
92  * chronolog_start - open the devices and initialize data for processing
93  */
94 static int
95 chronolog_start(
96 	int unit,
97 	struct peer *peer
98 	)
99 {
100 	register struct chronolog_unit *up;
101 	struct refclockproc *pp;
102 	int fd;
103 	char device[20];
104 
105 	/*
106 	 * Open serial port. Don't bother with CLK line discipline, since
107 	 * it's not available.
108 	 */
109 	(void)sprintf(device, DEVICE, unit);
110 #ifdef DEBUG
111 	if (debug)
112 		printf ("starting Chronolog with device %s\n",device);
113 #endif
114 	if (!(fd = refclock_open(device, SPEED232, 0)))
115 		return (0);
116 
117 	/*
118 	 * Allocate and initialize unit structure
119 	 */
120 	if (!(up = (struct chronolog_unit *)
121 	      emalloc(sizeof(struct chronolog_unit)))) {
122 		(void) close(fd);
123 		return (0);
124 	}
125 	memset((char *)up, 0, sizeof(struct chronolog_unit));
126 	pp = peer->procptr;
127 	pp->unitptr = (caddr_t)up;
128 	pp->io.clock_recv = chronolog_receive;
129 	pp->io.srcclock = (caddr_t)peer;
130 	pp->io.datalen = 0;
131 	pp->io.fd = fd;
132 	if (!io_addclock(&pp->io)) {
133 		(void) close(fd);
134 		free(up);
135 		return (0);
136 	}
137 
138 	/*
139 	 * Initialize miscellaneous variables
140 	 */
141 	peer->precision = PRECISION;
142 	pp->clockdesc = DESCRIPTION;
143 	memcpy((char *)&pp->refid, REFID, 4);
144 	return (1);
145 }
146 
147 
148 /*
149  * chronolog_shutdown - shut down the clock
150  */
151 static void
152 chronolog_shutdown(
153 	int unit,
154 	struct peer *peer
155 	)
156 {
157 	register struct chronolog_unit *up;
158 	struct refclockproc *pp;
159 
160 	pp = peer->procptr;
161 	up = (struct chronolog_unit *)pp->unitptr;
162 	io_closeclock(&pp->io);
163 	free(up);
164 }
165 
166 
167 /*
168  * chronolog_receive - receive data from the serial interface
169  */
170 static void
171 chronolog_receive(
172 	struct recvbuf *rbufp
173 	)
174 {
175 	struct chronolog_unit *up;
176 	struct refclockproc *pp;
177 	struct peer *peer;
178 
179 	l_fp	     trtmp;	/* arrival timestamp */
180 	int          hours;	/* hour-of-day */
181 	int	     minutes;	/* minutes-past-the-hour */
182 	int          seconds;	/* seconds */
183 	int	     temp;	/* int temp */
184 	int          got_good;	/* got a good time flag */
185 
186 	/*
187 	 * Initialize pointers and read the timecode and timestamp
188 	 */
189 	peer = (struct peer *)rbufp->recv_srcclock;
190 	pp = peer->procptr;
191 	up = (struct chronolog_unit *)pp->unitptr;
192 	temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
193 
194 	if (temp == 0) {
195 		if (up->tcswitch == 0) {
196 			up->tcswitch = 1;
197 			up->laststamp = trtmp;
198 		} else
199 		    up->tcswitch = 0;
200 		return;
201 	}
202 	pp->lencode = temp;
203 	pp->lastrec = up->laststamp;
204 	up->laststamp = trtmp;
205 	up->tcswitch = 1;
206 
207 #ifdef DEBUG
208 	if (debug)
209 		printf("chronolog: timecode %d %s\n", pp->lencode,
210 		    pp->a_lastcode);
211 #endif
212 
213 	/*
214 	 * We get down to business. Check the timecode format and decode
215 	 * its contents. This code uses the first character to see whether
216 	 * we're looking at a date or a time.  We store data data across
217 	 * calls since it is transmitted a few seconds ahead of the
218 	 * timestamp.
219 	 */
220 	got_good=0;
221 	if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day))
222 	{
223 	    /*
224 	     * Y2K convert the 2-digit year
225 	     */
226 	    up->year = up->year >= 69 ? up->year : up->year + 100;
227 	    return;
228 	}
229 	if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d",
230 		   &hours,&minutes,&seconds) == 3)
231 	{
232 #ifdef GET_LOCALTIME
233 	    struct tm  local;
234 	    struct tm *gmtp;
235 	    time_t     unixtime;
236 	    int        adjyear;
237 	    int        adjmon;
238 
239 	    /*
240 	     * Convert to GMT for sites that distribute localtime.  This
241              * means we have to do Y2K conversion on the 2-digit year;
242 	     * otherwise, we get the time wrong.
243 	     */
244 
245 	    memset(&local, 0, sizeof(local));
246 
247 	    local.tm_year  = up->year;
248 	    local.tm_mon   = up->month-1;
249 	    local.tm_mday  = up->day;
250 	    local.tm_hour  = hours;
251 	    local.tm_min   = minutes;
252 	    local.tm_sec   = seconds;
253 	    local.tm_isdst = -1;
254 
255 	    unixtime = mktime (&local);
256 	    if ((gmtp = gmtime (&unixtime)) == NULL)
257 	    {
258 		refclock_report (peer, CEVNT_FAULT);
259 		return;
260 	    }
261 	    adjyear = gmtp->tm_year+1900;
262 	    adjmon  = gmtp->tm_mon+1;
263 	    pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
264 	    pp->hour   = gmtp->tm_hour;
265 	    pp->minute = gmtp->tm_min;
266 	    pp->second = gmtp->tm_sec;
267 #ifdef DEBUG
268 	    if (debug)
269 		printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
270 			adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
271 			pp->second);
272 #endif
273 
274 #else
275 	    /*
276 	     * For more rational sites distributing UTC
277 	     */
278 	    pp->day    = ymd2yd(year+1900,month,day);
279 	    pp->hour   = hours;
280 	    pp->minute = minutes;
281 	    pp->second = seconds;
282 
283 #endif
284 	    got_good=1;
285 	}
286 
287 	if (!got_good)
288 	    return;
289 
290 
291 	/*
292 	 * Process the new sample in the median filter and determine the
293 	 * timecode timestamp.
294 	 */
295 	if (!refclock_process(pp)) {
296 		refclock_report(peer, CEVNT_BADTIME);
297 		return;
298 	}
299 	pp->lastref = pp->lastrec;
300 	refclock_receive(peer);
301 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
302 	up->lasthour = pp->hour;
303 }
304 
305 
306 /*
307  * chronolog_poll - called by the transmit procedure
308  */
309 static void
310 chronolog_poll(
311 	int unit,
312 	struct peer *peer
313 	)
314 {
315 	/*
316 	 * Time to poll the clock. The Chrono-log clock is supposed to
317 	 * respond to a 'T' by returning a timecode in the format(s)
318 	 * specified above.  Ours does (can?) not, but this seems to be
319 	 * an installation-specific problem.  This code is dyked out,
320 	 * but may be re-enabled if anyone ever finds a Chrono-log that
321 	 * actually listens to this command.
322 	 */
323 #if 0
324 	register struct chronolog_unit *up;
325 	struct refclockproc *pp;
326 	char pollchar;
327 
328 	pp = peer->procptr;
329 	up = (struct chronolog_unit *)pp->unitptr;
330 	if (peer->burst == 0 && peer->reach == 0)
331 		refclock_report(peer, CEVNT_TIMEOUT);
332 	if (up->linect > 0)
333 		pollchar = 'R';
334 	else
335 		pollchar = 'T';
336 	if (write(pp->io.fd, &pollchar, 1) != 1)
337 		refclock_report(peer, CEVNT_FAULT);
338 	else
339 		pp->polls++;
340 #endif
341 }
342 
343 #else
344 int refclock_chronolog_bs;
345 #endif /* REFCLOCK */
346