2 * refclock_dumbclock - clock driver for a unknown time distribution system
3 * that only provides hh:mm:ss (in local time, yet!).
7 * Must interpolate back to local time. Very annoying.
15 #if defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK)
19 #include "ntp_refclock.h"
20 #include "ntp_calendar.h"
21 #include "ntp_stdlib.h"
27 * This driver supports a generic dumb clock that only outputs hh:mm:ss,
28 * in local time, no less.
34 * hh:mm:ss -- what you'd expect, with a 24 hour clock. (Heck, that's the only
35 * way it could get stupider.) We take time on the <cr>.
37 * The original source of this module was the WWVB module.
41 * Interface definitions
43 #define DEVICE "/dev/dumbclock%d" /* device name and unit */
44 #define SPEED232 B9600 /* uart speed (9600 baud) */
45 #define PRECISION (-13) /* precision assumed (about 100 us) */
46 #define REFID "dumbclock" /* reference ID */
47 #define DESCRIPTION "Dumb clock" /* WRU */
51 * Insanity check. Since the time is local, we need to make sure that during midnight
52 * transitions, we can convert back to Unix time. If the conversion results in some number
53 * worse than this number of seconds away, assume the next day and retry.
55 #define INSANE_SECONDS 3600
58 * Dumb clock control structure
60 struct dumbclock_unit {
61 u_char tcswitch; /* timecode switch */
62 l_fp laststamp; /* last receive timestamp */
63 u_char lasthour; /* last hour (for monitor) */
64 u_char linect; /* count ignored lines (for monitor */
65 struct tm ymd; /* struct tm for y/m/d only */
71 static int dumbclock_start P((int, struct peer *));
72 static void dumbclock_shutdown P((int, struct peer *));
73 static void dumbclock_receive P((struct recvbuf *));
75 static void dumbclock_poll P((int, struct peer *));
81 struct refclock refclock_dumbclock = {
82 dumbclock_start, /* start up driver */
83 dumbclock_shutdown, /* shut down driver */
84 noentry, /* poll the driver -- a nice fabrication */
85 noentry, /* not used */
86 noentry, /* not used */
87 noentry, /* not used */
88 NOFLAGS /* not used */
93 * dumbclock_start - open the devices and initialize data for processing
101 register struct dumbclock_unit *up;
102 struct refclockproc *pp;
105 struct tm *tm_time_p;
109 * Open serial port. Don't bother with CLK line discipline, since
110 * it's not available.
112 (void)sprintf(device, DEVICE, unit);
115 printf ("starting Dumbclock with device %s\n",device);
117 if (!(fd = refclock_open(device, SPEED232, 0)))
121 * Allocate and initialize unit structure
123 if (!(up = (struct dumbclock_unit *)
124 emalloc(sizeof(struct dumbclock_unit)))) {
128 memset((char *)up, 0, sizeof(struct dumbclock_unit));
130 pp->unitptr = (caddr_t)up;
131 pp->io.clock_recv = dumbclock_receive;
132 pp->io.srcclock = (caddr_t)peer;
135 if (!io_addclock(&pp->io)) {
144 tm_time_p = localtime(&now);
146 tm_time_p = gmtime(&now);
150 up->ymd = *tm_time_p;
158 * Initialize miscellaneous variables
160 peer->precision = PRECISION;
161 pp->clockdesc = DESCRIPTION;
162 memcpy((char *)&pp->refid, REFID, 4);
168 * dumbclock_shutdown - shut down the clock
176 register struct dumbclock_unit *up;
177 struct refclockproc *pp;
180 up = (struct dumbclock_unit *)pp->unitptr;
181 io_closeclock(&pp->io);
187 * dumbclock_receive - receive data from the serial interface
191 struct recvbuf *rbufp
194 struct dumbclock_unit *up;
195 struct refclockproc *pp;
198 l_fp trtmp; /* arrival timestamp */
199 int hours; /* hour-of-day */
200 int minutes; /* minutes-past-the-hour */
201 int seconds; /* seconds */
202 int temp; /* int temp */
203 int got_good; /* got a good time flag */
206 * Initialize pointers and read the timecode and timestamp
208 peer = (struct peer *)rbufp->recv_srcclock;
210 up = (struct dumbclock_unit *)pp->unitptr;
211 temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
214 if (up->tcswitch == 0) {
216 up->laststamp = trtmp;
222 pp->lastrec = up->laststamp;
223 up->laststamp = trtmp;
228 printf("dumbclock: timecode %d %s\n",
229 pp->lencode, pp->a_lastcode);
233 * We get down to business. Check the timecode format...
237 if (sscanf(pp->a_lastcode,"%02d:%02d:%02d",
238 &hours,&minutes,&seconds) == 3)
242 time_t asserted_time; /* the SPM time based on the composite time+date */
243 struct tm asserted_tm; /* the struct tm of the same */
251 * Convert to GMT for sites that distribute localtime. This
252 * means we have to figure out what day it is. Easier said
256 asserted_tm.tm_year = up->ymd.tm_year;
257 asserted_tm.tm_mon = up->ymd.tm_mon;
258 asserted_tm.tm_mday = up->ymd.tm_mday;
259 asserted_tm.tm_hour = hours;
260 asserted_tm.tm_min = minutes;
261 asserted_tm.tm_sec = seconds;
262 asserted_tm.tm_isdst = -1;
265 asserted_time = mktime (&asserted_tm);
268 #include "GMT unsupported for dumbclock!"
270 reality_delta = asserted_time - now;
273 * We assume that if the time is grossly wrong, it's because we got the
274 * year/month/day wrong.
276 if (reality_delta > INSANE_SECONDS)
278 asserted_time -= SECSPERDAY; /* local clock behind real time */
280 else if (-reality_delta > INSANE_SECONDS)
282 asserted_time += SECSPERDAY; /* local clock ahead of real time */
284 lt_p = localtime(&asserted_time);
291 refclock_report (peer, CEVNT_FAULT);
295 if ((gmtp = gmtime (&asserted_time)) == NULL)
297 refclock_report (peer, CEVNT_FAULT);
300 adjyear = gmtp->tm_year+1900;
301 adjmon = gmtp->tm_mon+1;
302 pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
303 pp->hour = gmtp->tm_hour;
304 pp->minute = gmtp->tm_min;
305 pp->second = gmtp->tm_sec;
308 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
309 adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
321 refclock_report(peer, CEVNT_BADREPLY);
326 * Process the new sample in the median filter and determine the
327 * timecode timestamp.
329 if (!refclock_process(pp)) {
330 refclock_report(peer, CEVNT_BADTIME);
333 record_clock_stats(&peer->srcadr, pp->a_lastcode);
334 refclock_receive(peer);
335 up->lasthour = pp->hour;
340 * dumbclock_poll - called by the transmit procedure
348 register struct dumbclock_unit *up;
349 struct refclockproc *pp;
353 * Time to poll the clock. The Chrono-log clock is supposed to
354 * respond to a 'T' by returning a timecode in the format(s)
355 * specified above. Ours does (can?) not, but this seems to be
356 * an installation-specific problem. This code is dyked out,
357 * but may be re-enabled if anyone ever finds a Chrono-log that
358 * actually listens to this command.
362 up = (struct dumbclock_unit *)pp->unitptr;
363 if (peer->reach == 0)
364 refclock_report(peer, CEVNT_TIMEOUT);
369 if (write(pp->io.fd, &pollchar, 1) != 1)
370 refclock_report(peer, CEVNT_FAULT);
378 int refclock_dumbclock_bs;
379 #endif /* REFCLOCK */