4 * refclock_chronolog - clock driver for Chronolog K-series WWVB receiver.
8 * Must interpolate back to local time. Very annoying.
16 #if defined(REFCLOCK) && defined(CLOCK_CHRONOLOG)
20 #include "ntp_refclock.h"
21 #include "ntp_calendar.h"
22 #include "ntp_stdlib.h"
28 * This driver supports the Chronolog K-series WWVB receiver.
35 * YY/MM/DD -- what you'd expect. This arrives a few seconds before the
37 * hh:mm:ss -- what you'd expect. We take time on the <cr>.
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.
43 * The original source of this module was the WWVB module.
47 * Interface definitions
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 */
55 #define MONLIN 15 /* number of monitoring lines */
58 * Chrono-log unit control structure
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 */
72 static int chronolog_start
P((int, struct peer
*));
73 static void chronolog_shutdown
P((int, struct peer
*));
74 static void chronolog_receive
P((struct recvbuf
*));
75 static void chronolog_poll
P((int, struct peer
*));
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 */
92 * chronolog_start - open the devices and initialize data for processing
100 register struct chronolog_unit
*up
;
101 struct refclockproc
*pp
;
106 * Open serial port. Don't bother with CLK line discipline, since
107 * it's not available.
109 (void)sprintf(device
, DEVICE
, unit
);
112 printf ("starting Chronolog with device %s\n",device
);
114 if (!(fd
= refclock_open(device
, SPEED232
, 0)))
118 * Allocate and initialize unit structure
120 if (!(up
= (struct chronolog_unit
*)
121 emalloc(sizeof(struct chronolog_unit
)))) {
125 memset((char *)up
, 0, sizeof(struct chronolog_unit
));
127 pp
->unitptr
= (caddr_t
)up
;
128 pp
->io
.clock_recv
= chronolog_receive
;
129 pp
->io
.srcclock
= (caddr_t
)peer
;
132 if (!io_addclock(&pp
->io
)) {
139 * Initialize miscellaneous variables
141 peer
->precision
= PRECISION
;
142 pp
->clockdesc
= DESCRIPTION
;
143 memcpy((char *)&pp
->refid
, REFID
, 4);
149 * chronolog_shutdown - shut down the clock
157 register struct chronolog_unit
*up
;
158 struct refclockproc
*pp
;
161 up
= (struct chronolog_unit
*)pp
->unitptr
;
162 io_closeclock(&pp
->io
);
168 * chronolog_receive - receive data from the serial interface
172 struct recvbuf
*rbufp
175 struct chronolog_unit
*up
;
176 struct refclockproc
*pp
;
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 */
187 * Initialize pointers and read the timecode and timestamp
189 peer
= (struct peer
*)rbufp
->recv_srcclock
;
191 up
= (struct chronolog_unit
*)pp
->unitptr
;
192 temp
= refclock_gtlin(rbufp
, pp
->a_lastcode
, BMAX
, &trtmp
);
195 if (up
->tcswitch
== 0) {
197 up
->laststamp
= trtmp
;
203 pp
->lastrec
= up
->laststamp
;
204 up
->laststamp
= trtmp
;
209 printf("chronolog: timecode %d %s\n", pp
->lencode
,
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
221 if (sscanf(pp
->a_lastcode
, "Y %d/%d/%d", &up
->year
,&up
->month
,&up
->day
))
224 * Y2K convert the 2-digit year
226 up
->year
= up
->year
>= 69 ? up
->year
: up
->year
+ 100;
229 if (sscanf(pp
->a_lastcode
,"Z %02d:%02d:%02d",
230 &hours
,&minutes
,&seconds
) == 3)
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.
245 local
.tm_year
= up
->year
;
246 local
.tm_mon
= up
->month
-1;
247 local
.tm_mday
= up
->day
;
248 local
.tm_hour
= hours
;
249 local
.tm_min
= minutes
;
250 local
.tm_sec
= seconds
;
253 unixtime
= mktime (&local
);
254 if ((gmtp
= gmtime (&unixtime
)) == NULL
)
256 refclock_report (peer
, CEVNT_FAULT
);
259 adjyear
= gmtp
->tm_year
+1900;
260 adjmon
= gmtp
->tm_mon
+1;
261 pp
->day
= ymd2yd (adjyear
, adjmon
, gmtp
->tm_mday
);
262 pp
->hour
= gmtp
->tm_hour
;
263 pp
->minute
= gmtp
->tm_min
;
264 pp
->second
= gmtp
->tm_sec
;
267 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
268 adjyear
,adjmon
,gmtp
->tm_mday
,pp
->hour
,pp
->minute
,
274 * For more rational sites distributing UTC
276 pp
->day
= ymd2yd(year
+1900,month
,day
);
278 pp
->minute
= minutes
;
279 pp
->second
= seconds
;
290 * Process the new sample in the median filter and determine the
291 * timecode timestamp.
293 if (!refclock_process(pp
)) {
294 refclock_report(peer
, CEVNT_BADTIME
);
297 pp
->lastref
= pp
->lastrec
;
298 refclock_receive(peer
);
299 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
300 up
->lasthour
= pp
->hour
;
305 * chronolog_poll - called by the transmit procedure
314 * Time to poll the clock. The Chrono-log clock is supposed to
315 * respond to a 'T' by returning a timecode in the format(s)
316 * specified above. Ours does (can?) not, but this seems to be
317 * an installation-specific problem. This code is dyked out,
318 * but may be re-enabled if anyone ever finds a Chrono-log that
319 * actually listens to this command.
322 register struct chronolog_unit
*up
;
323 struct refclockproc
*pp
;
327 up
= (struct chronolog_unit
*)pp
->unitptr
;
328 if (peer
->burst
== 0 && peer
->reach
== 0)
329 refclock_report(peer
, CEVNT_TIMEOUT
);
334 if (write(pp
->io
.fd
, &pollchar
, 1) != 1)
335 refclock_report(peer
, CEVNT_FAULT
);
342 int refclock_chronolog_bs
;
343 #endif /* REFCLOCK */