2 * refclock_chronolog - clock driver for Chronolog K-series WWVB receiver.
6 * Must interpolate back to local time. Very annoying.
14 #if defined(REFCLOCK) && defined(CLOCK_CHRONOLOG)
18 #include "ntp_refclock.h"
19 #include "ntp_calendar.h"
20 #include "ntp_stdlib.h"
26 * This driver supports the Chronolog K-series WWVB receiver.
33 * YY/MM/DD -- what you'd expect. This arrives a few seconds before the
35 * hh:mm:ss -- what you'd expect. We take time on the <cr>.
37 * Our Chronolog writes time out at 2400 bps 8/N/1, but it can be configured
38 * otherwise. The clock seems to appear every 60 seconds, which doesn't make
39 * for good statistics collection.
41 * The original source of this module was the WWVB module.
45 * Interface definitions
47 #define DEVICE "/dev/chronolog%d" /* device name and unit */
48 #define SPEED232 B2400 /* uart speed (2400 baud) */
49 #define PRECISION (-13) /* precision assumed (about 100 us) */
50 #define REFID "chronolog" /* reference ID */
51 #define DESCRIPTION "Chrono-log K" /* WRU */
53 #define MONLIN 15 /* number of monitoring lines */
56 * Chrono-log unit control structure
58 struct chronolog_unit {
59 u_char tcswitch; /* timecode switch */
60 l_fp laststamp; /* last receive timestamp */
61 u_char lasthour; /* last hour (for monitor) */
62 int year; /* Y2K-adjusted year */
63 int day; /* day-of-month */
64 int month; /* month-of-year */
70 static int chronolog_start P((int, struct peer *));
71 static void chronolog_shutdown P((int, struct peer *));
72 static void chronolog_receive P((struct recvbuf *));
73 static void chronolog_poll P((int, struct peer *));
78 struct refclock refclock_chronolog = {
79 chronolog_start, /* start up driver */
80 chronolog_shutdown, /* shut down driver */
81 chronolog_poll, /* poll the driver -- a nice fabrication */
82 noentry, /* not used */
83 noentry, /* not used */
84 noentry, /* not used */
85 NOFLAGS /* not used */
90 * chronolog_start - open the devices and initialize data for processing
98 register struct chronolog_unit *up;
99 struct refclockproc *pp;
104 * Open serial port. Don't bother with CLK line discipline, since
105 * it's not available.
107 (void)sprintf(device, DEVICE, unit);
110 printf ("starting Chronolog with device %s\n",device);
112 if (!(fd = refclock_open(device, SPEED232, 0)))
116 * Allocate and initialize unit structure
118 if (!(up = (struct chronolog_unit *)
119 emalloc(sizeof(struct chronolog_unit)))) {
123 memset((char *)up, 0, sizeof(struct chronolog_unit));
125 pp->unitptr = (caddr_t)up;
126 pp->io.clock_recv = chronolog_receive;
127 pp->io.srcclock = (caddr_t)peer;
130 if (!io_addclock(&pp->io)) {
137 * Initialize miscellaneous variables
139 peer->precision = PRECISION;
140 pp->clockdesc = DESCRIPTION;
141 memcpy((char *)&pp->refid, REFID, 4);
147 * chronolog_shutdown - shut down the clock
155 register struct chronolog_unit *up;
156 struct refclockproc *pp;
159 up = (struct chronolog_unit *)pp->unitptr;
160 io_closeclock(&pp->io);
166 * chronolog_receive - receive data from the serial interface
170 struct recvbuf *rbufp
173 struct chronolog_unit *up;
174 struct refclockproc *pp;
177 l_fp trtmp; /* arrival timestamp */
178 int hours; /* hour-of-day */
179 int minutes; /* minutes-past-the-hour */
180 int seconds; /* seconds */
181 int temp; /* int temp */
182 int got_good; /* got a good time flag */
185 * Initialize pointers and read the timecode and timestamp
187 peer = (struct peer *)rbufp->recv_srcclock;
189 up = (struct chronolog_unit *)pp->unitptr;
190 temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
193 if (up->tcswitch == 0) {
195 up->laststamp = trtmp;
201 pp->lastrec = up->laststamp;
202 up->laststamp = trtmp;
207 printf("chronolog: timecode %d %s\n", pp->lencode,
212 * We get down to business. Check the timecode format and decode
213 * its contents. This code uses the first character to see whether
214 * we're looking at a date or a time. We store data data across
215 * calls since it is transmitted a few seconds ahead of the
219 if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day))
222 * Y2K convert the 2-digit year
224 up->year = up->year >= 69 ? up->year : up->year + 100;
227 if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d",
228 &hours,&minutes,&seconds) == 3)
238 * Convert to GMT for sites that distribute localtime. This
239 * means we have to do Y2K conversion on the 2-digit year;
240 * otherwise, we get the time wrong.
243 local.tm_year = up->year;
244 local.tm_mon = up->month-1;
245 local.tm_mday = up->day;
246 local.tm_hour = hours;
247 local.tm_min = minutes;
248 local.tm_sec = seconds;
251 unixtime = mktime (&local);
252 if ((gmtp = gmtime (&unixtime)) == NULL)
254 refclock_report (peer, CEVNT_FAULT);
257 adjyear = gmtp->tm_year+1900;
258 adjmon = gmtp->tm_mon+1;
259 pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
260 pp->hour = gmtp->tm_hour;
261 pp->minute = gmtp->tm_min;
262 pp->second = gmtp->tm_sec;
265 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
266 adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
272 * For more rational sites distributing UTC
274 pp->day = ymd2yd(year+1900,month,day);
276 pp->minute = minutes;
277 pp->second = seconds;
288 * Process the new sample in the median filter and determine the
289 * timecode timestamp.
291 if (!refclock_process(pp)) {
292 refclock_report(peer, CEVNT_BADTIME);
295 pp->lastref = pp->lastrec;
296 refclock_receive(peer);
297 record_clock_stats(&peer->srcadr, pp->a_lastcode);
298 up->lasthour = pp->hour;
303 * chronolog_poll - called by the transmit procedure
312 * Time to poll the clock. The Chrono-log clock is supposed to
313 * respond to a 'T' by returning a timecode in the format(s)
314 * specified above. Ours does (can?) not, but this seems to be
315 * an installation-specific problem. This code is dyked out,
316 * but may be re-enabled if anyone ever finds a Chrono-log that
317 * actually listens to this command.
320 register struct chronolog_unit *up;
321 struct refclockproc *pp;
325 up = (struct chronolog_unit *)pp->unitptr;
326 if (peer->burst == 0 && peer->reach == 0)
327 refclock_report(peer, CEVNT_TIMEOUT);
332 if (write(pp->io.fd, &pollchar, 1) != 1)
333 refclock_report(peer, CEVNT_FAULT);
340 int refclock_chronolog_bs;
341 #endif /* REFCLOCK */