1 /* $NetBSD: refclock_dumbclock.c,v 1.2 2003/12/04 16:23:37 drochner Exp $ */
4 * refclock_dumbclock - clock driver for a unknown time distribution system
5 * that only provides hh:mm:ss (in local time, yet!).
9 * Must interpolate back to local time. Very annoying.
17 #if defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK)
21 #include "ntp_refclock.h"
22 #include "ntp_calendar.h"
23 #include "ntp_stdlib.h"
29 extern int async_write(int, const void *, unsigned int);
31 #define write(fd, data, octets) async_write(fd, data, octets)
35 * This driver supports a generic dumb clock that only outputs hh:mm:ss,
36 * in local time, no less.
42 * hh:mm:ss -- what you'd expect, with a 24 hour clock. (Heck, that's the only
43 * way it could get stupider.) We take time on the <cr>.
45 * The original source of this module was the WWVB module.
49 * Interface definitions
51 #define DEVICE "/dev/dumbclock%d" /* device name and unit */
52 #define SPEED232 B9600 /* uart speed (9600 baud) */
53 #define PRECISION (-13) /* precision assumed (about 100 us) */
54 #define REFID "dumbclock" /* reference ID */
55 #define DESCRIPTION "Dumb clock" /* WRU */
59 * Insanity check. Since the time is local, we need to make sure that during midnight
60 * transitions, we can convert back to Unix time. If the conversion results in some number
61 * worse than this number of seconds away, assume the next day and retry.
63 #define INSANE_SECONDS 3600
66 * Dumb clock control structure
68 struct dumbclock_unit
{
69 u_char tcswitch
; /* timecode switch */
70 l_fp laststamp
; /* last receive timestamp */
71 u_char lasthour
; /* last hour (for monitor) */
72 u_char linect
; /* count ignored lines (for monitor */
73 struct tm ymd
; /* struct tm for y/m/d only */
79 static int dumbclock_start
P((int, struct peer
*));
80 static void dumbclock_shutdown
P((int, struct peer
*));
81 static void dumbclock_receive
P((struct recvbuf
*));
83 static void dumbclock_poll
P((int, struct peer
*));
89 struct refclock refclock_dumbclock
= {
90 dumbclock_start
, /* start up driver */
91 dumbclock_shutdown
, /* shut down driver */
92 noentry
, /* poll the driver -- a nice fabrication */
93 noentry
, /* not used */
94 noentry
, /* not used */
95 noentry
, /* not used */
96 NOFLAGS
/* not used */
101 * dumbclock_start - open the devices and initialize data for processing
109 register struct dumbclock_unit
*up
;
110 struct refclockproc
*pp
;
113 struct tm
*tm_time_p
;
117 * Open serial port. Don't bother with CLK line discipline, since
118 * it's not available.
120 (void)sprintf(device
, DEVICE
, unit
);
123 printf ("starting Dumbclock with device %s\n",device
);
125 fd
= refclock_open(device
, SPEED232
, 0);
130 * Allocate and initialize unit structure
132 up
= (struct dumbclock_unit
*)emalloc(sizeof(struct dumbclock_unit
));
137 memset((char *)up
, 0, sizeof(struct dumbclock_unit
));
139 pp
->unitptr
= (caddr_t
)up
;
140 pp
->io
.clock_recv
= dumbclock_receive
;
141 pp
->io
.srcclock
= (caddr_t
)peer
;
144 if (!io_addclock(&pp
->io
)) {
153 tm_time_p
= localtime(&now
);
155 tm_time_p
= gmtime(&now
);
159 up
->ymd
= *tm_time_p
;
167 * Initialize miscellaneous variables
169 peer
->precision
= PRECISION
;
170 pp
->clockdesc
= DESCRIPTION
;
171 memcpy((char *)&pp
->refid
, REFID
, 4);
177 * dumbclock_shutdown - shut down the clock
185 register struct dumbclock_unit
*up
;
186 struct refclockproc
*pp
;
189 up
= (struct dumbclock_unit
*)pp
->unitptr
;
190 io_closeclock(&pp
->io
);
196 * dumbclock_receive - receive data from the serial interface
200 struct recvbuf
*rbufp
203 struct dumbclock_unit
*up
;
204 struct refclockproc
*pp
;
207 l_fp trtmp
; /* arrival timestamp */
208 int hours
; /* hour-of-day */
209 int minutes
; /* minutes-past-the-hour */
210 int seconds
; /* seconds */
211 int temp
; /* int temp */
212 int got_good
; /* got a good time flag */
215 * Initialize pointers and read the timecode and timestamp
217 peer
= (struct peer
*)rbufp
->recv_srcclock
;
219 up
= (struct dumbclock_unit
*)pp
->unitptr
;
220 temp
= refclock_gtlin(rbufp
, pp
->a_lastcode
, BMAX
, &trtmp
);
223 if (up
->tcswitch
== 0) {
225 up
->laststamp
= trtmp
;
230 pp
->lencode
= (u_short
)temp
;
231 pp
->lastrec
= up
->laststamp
;
232 up
->laststamp
= trtmp
;
237 printf("dumbclock: timecode %d %s\n",
238 pp
->lencode
, pp
->a_lastcode
);
242 * We get down to business. Check the timecode format...
245 if (sscanf(pp
->a_lastcode
,"%02d:%02d:%02d",
246 &hours
,&minutes
,&seconds
) == 3)
250 time_t asserted_time
; /* the SPM time based on the composite time+date */
251 struct tm asserted_tm
; /* the struct tm of the same */
259 * Convert to GMT for sites that distribute localtime. This
260 * means we have to figure out what day it is. Easier said
264 asserted_tm
.tm_year
= up
->ymd
.tm_year
;
265 asserted_tm
.tm_mon
= up
->ymd
.tm_mon
;
266 asserted_tm
.tm_mday
= up
->ymd
.tm_mday
;
267 asserted_tm
.tm_hour
= hours
;
268 asserted_tm
.tm_min
= minutes
;
269 asserted_tm
.tm_sec
= seconds
;
270 asserted_tm
.tm_isdst
= -1;
273 asserted_time
= mktime (&asserted_tm
);
276 #include "GMT unsupported for dumbclock!"
278 reality_delta
= asserted_time
- now
;
281 * We assume that if the time is grossly wrong, it's because we got the
282 * year/month/day wrong.
284 if (reality_delta
> INSANE_SECONDS
)
286 asserted_time
-= SECSPERDAY
; /* local clock behind real time */
288 else if (-reality_delta
> INSANE_SECONDS
)
290 asserted_time
+= SECSPERDAY
; /* local clock ahead of real time */
292 lt_p
= localtime(&asserted_time
);
299 refclock_report (peer
, CEVNT_FAULT
);
303 if ((gmtp
= gmtime (&asserted_time
)) == NULL
)
305 refclock_report (peer
, CEVNT_FAULT
);
308 adjyear
= gmtp
->tm_year
+1900;
309 adjmon
= gmtp
->tm_mon
+1;
310 pp
->day
= ymd2yd (adjyear
, adjmon
, gmtp
->tm_mday
);
311 pp
->hour
= gmtp
->tm_hour
;
312 pp
->minute
= gmtp
->tm_min
;
313 pp
->second
= gmtp
->tm_sec
;
316 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
317 adjyear
,adjmon
,gmtp
->tm_mday
,pp
->hour
,pp
->minute
,
329 refclock_report(peer
, CEVNT_BADREPLY
);
334 * Process the new sample in the median filter and determine the
335 * timecode timestamp.
337 if (!refclock_process(pp
)) {
338 refclock_report(peer
, CEVNT_BADTIME
);
341 pp
->lastref
= pp
->lastrec
;
342 refclock_receive(peer
);
343 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
344 up
->lasthour
= (u_char
)pp
->hour
;
349 * dumbclock_poll - called by the transmit procedure
357 register struct dumbclock_unit
*up
;
358 struct refclockproc
*pp
;
362 * Time to poll the clock. The Chrono-log clock is supposed to
363 * respond to a 'T' by returning a timecode in the format(s)
364 * specified above. Ours does (can?) not, but this seems to be
365 * an installation-specific problem. This code is dyked out,
366 * but may be re-enabled if anyone ever finds a Chrono-log that
367 * actually listens to this command.
371 up
= (struct dumbclock_unit
*)pp
->unitptr
;
372 if (peer
->reach
== 0)
373 refclock_report(peer
, CEVNT_TIMEOUT
);
378 if (write(pp
->io
.fd
, &pollchar
, 1) != 1)
379 refclock_report(peer
, CEVNT_FAULT
);
387 int refclock_dumbclock_bs
;
388 #endif /* REFCLOCK */