diff options
Diffstat (limited to 'ntpd/refclock_dumbclock.c')
-rw-r--r-- | ntpd/refclock_dumbclock.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/ntpd/refclock_dumbclock.c b/ntpd/refclock_dumbclock.c new file mode 100644 index 0000000..2788649 --- /dev/null +++ b/ntpd/refclock_dumbclock.c @@ -0,0 +1,385 @@ +/* + * refclock_dumbclock - clock driver for a unknown time distribution system + * that only provides hh:mm:ss (in local time, yet!). + */ + +/* + * Must interpolate back to local time. Very annoying. + */ +#define GET_LOCALTIME + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(SYS_WINNT) +#undef close +#define close closesocket +#endif + +#if defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK) + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_calendar.h" +#include "ntp_stdlib.h" + +#include <stdio.h> +#include <ctype.h> + +/* + * This driver supports a generic dumb clock that only outputs hh:mm:ss, + * in local time, no less. + * + * Input format: + * + * hh:mm:ss <cr> + * + * hh:mm:ss -- what you'd expect, with a 24 hour clock. (Heck, that's the only + * way it could get stupider.) We take time on the <cr>. + * + * The original source of this module was the WWVB module. + */ + +/* + * Interface definitions + */ +#define DEVICE "/dev/dumbclock%d" /* device name and unit */ +#define SPEED232 B9600 /* uart speed (9600 baud) */ +#define PRECISION (-13) /* precision assumed (about 100 us) */ +#define REFID "dumbclock" /* reference ID */ +#define DESCRIPTION "Dumb clock" /* WRU */ + + +/* + * Insanity check. Since the time is local, we need to make sure that during midnight + * transitions, we can convert back to Unix time. If the conversion results in some number + * worse than this number of seconds away, assume the next day and retry. + */ +#define INSANE_SECONDS 3600 + +/* + * Dumb clock control structure + */ +struct dumbclock_unit { + u_char tcswitch; /* timecode switch */ + l_fp laststamp; /* last receive timestamp */ + u_char lasthour; /* last hour (for monitor) */ + u_char linect; /* count ignored lines (for monitor */ + struct tm ymd; /* struct tm for y/m/d only */ +}; + +/* + * Function prototypes + */ +static int dumbclock_start P((int, struct peer *)); +static void dumbclock_shutdown P((int, struct peer *)); +static void dumbclock_receive P((struct recvbuf *)); +#if 0 +static void dumbclock_poll P((int, struct peer *)); +#endif + +/* + * Transfer vector + */ +struct refclock refclock_dumbclock = { + dumbclock_start, /* start up driver */ + dumbclock_shutdown, /* shut down driver */ + noentry, /* poll the driver -- a nice fabrication */ + noentry, /* not used */ + noentry, /* not used */ + noentry, /* not used */ + NOFLAGS /* not used */ +}; + + +/* + * dumbclock_start - open the devices and initialize data for processing + */ +static int +dumbclock_start( + int unit, + struct peer *peer + ) +{ + register struct dumbclock_unit *up; + struct refclockproc *pp; + int fd; + char device[20]; + struct tm *tm_time_p; + time_t now; + + /* + * Open serial port. Don't bother with CLK line discipline, since + * it's not available. + */ + (void)sprintf(device, DEVICE, unit); +#ifdef DEBUG + if (debug) + printf ("starting Dumbclock with device %s\n",device); +#endif + fd = refclock_open(device, SPEED232, 0); + if (fd < 0) + return (0); + + /* + * Allocate and initialize unit structure + */ + up = (struct dumbclock_unit *)emalloc(sizeof(struct dumbclock_unit)); + if (up == NULL) { + (void) close(fd); + return (0); + } + memset((char *)up, 0, sizeof(struct dumbclock_unit)); + pp = peer->procptr; + pp->unitptr = (caddr_t)up; + pp->io.clock_recv = dumbclock_receive; + pp->io.srcclock = (caddr_t)peer; + pp->io.datalen = 0; + pp->io.fd = fd; + if (!io_addclock(&pp->io)) { + (void) close(fd); + free(up); + return (0); + } + + + time(&now); +#ifdef GET_LOCALTIME + tm_time_p = localtime(&now); +#else + tm_time_p = gmtime(&now); +#endif + if (tm_time_p) + { + up->ymd = *tm_time_p; + } + else + { + return 0; + } + + /* + * Initialize miscellaneous variables + */ + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + return (1); +} + + +/* + * dumbclock_shutdown - shut down the clock + */ +static void +dumbclock_shutdown( + int unit, + struct peer *peer + ) +{ + register struct dumbclock_unit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct dumbclock_unit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); +} + + +/* + * dumbclock_receive - receive data from the serial interface + */ +static void +dumbclock_receive( + struct recvbuf *rbufp + ) +{ + struct dumbclock_unit *up; + struct refclockproc *pp; + struct peer *peer; + + l_fp trtmp; /* arrival timestamp */ + int hours; /* hour-of-day */ + int minutes; /* minutes-past-the-hour */ + int seconds; /* seconds */ + int temp; /* int temp */ + int got_good; /* got a good time flag */ + + /* + * Initialize pointers and read the timecode and timestamp + */ + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct dumbclock_unit *)pp->unitptr; + temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); + + if (temp == 0) { + if (up->tcswitch == 0) { + up->tcswitch = 1; + up->laststamp = trtmp; + } else + up->tcswitch = 0; + return; + } + pp->lencode = (u_short)temp; + pp->lastrec = up->laststamp; + up->laststamp = trtmp; + up->tcswitch = 1; + +#ifdef DEBUG + if (debug) + printf("dumbclock: timecode %d %s\n", + pp->lencode, pp->a_lastcode); +#endif + + /* + * We get down to business. Check the timecode format... + */ + got_good=0; + if (sscanf(pp->a_lastcode,"%02d:%02d:%02d", + &hours,&minutes,&seconds) == 3) + { + struct tm *gmtp; + struct tm *lt_p; + time_t asserted_time; /* the SPM time based on the composite time+date */ + struct tm asserted_tm; /* the struct tm of the same */ + int adjyear; + int adjmon; + int reality_delta; + time_t now; + + + /* + * Convert to GMT for sites that distribute localtime. This + * means we have to figure out what day it is. Easier said + * than done... + */ + + asserted_tm.tm_year = up->ymd.tm_year; + asserted_tm.tm_mon = up->ymd.tm_mon; + asserted_tm.tm_mday = up->ymd.tm_mday; + asserted_tm.tm_hour = hours; + asserted_tm.tm_min = minutes; + asserted_tm.tm_sec = seconds; + asserted_tm.tm_isdst = -1; + +#ifdef GET_LOCALTIME + asserted_time = mktime (&asserted_tm); + time(&now); +#else +#include "GMT unsupported for dumbclock!" +#endif + reality_delta = asserted_time - now; + + /* + * We assume that if the time is grossly wrong, it's because we got the + * year/month/day wrong. + */ + if (reality_delta > INSANE_SECONDS) + { + asserted_time -= SECSPERDAY; /* local clock behind real time */ + } + else if (-reality_delta > INSANE_SECONDS) + { + asserted_time += SECSPERDAY; /* local clock ahead of real time */ + } + lt_p = localtime(&asserted_time); + if (lt_p) + { + up->ymd = *lt_p; + } + else + { + refclock_report (peer, CEVNT_FAULT); + return; + } + + if ((gmtp = gmtime (&asserted_time)) == NULL) + { + refclock_report (peer, CEVNT_FAULT); + return; + } + adjyear = gmtp->tm_year+1900; + adjmon = gmtp->tm_mon+1; + pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday); + pp->hour = gmtp->tm_hour; + pp->minute = gmtp->tm_min; + pp->second = gmtp->tm_sec; +#ifdef DEBUG + if (debug) + printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", + adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute, + pp->second); +#endif + + got_good=1; + } + + if (!got_good) + { + if (up->linect > 0) + up->linect--; + else + refclock_report(peer, CEVNT_BADREPLY); + return; + } + + /* + * Process the new sample in the median filter and determine the + * timecode timestamp. + */ + if (!refclock_process(pp)) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + pp->lastref = pp->lastrec; + refclock_receive(peer); + record_clock_stats(&peer->srcadr, pp->a_lastcode); + up->lasthour = (u_char)pp->hour; +} + +#if 0 +/* + * dumbclock_poll - called by the transmit procedure + */ +static void +dumbclock_poll( + int unit, + struct peer *peer + ) +{ + register struct dumbclock_unit *up; + struct refclockproc *pp; + char pollchar; + + /* + * Time to poll the clock. The Chrono-log clock is supposed to + * respond to a 'T' by returning a timecode in the format(s) + * specified above. Ours does (can?) not, but this seems to be + * an installation-specific problem. This code is dyked out, + * but may be re-enabled if anyone ever finds a Chrono-log that + * actually listens to this command. + */ +#if 0 + pp = peer->procptr; + up = (struct dumbclock_unit *)pp->unitptr; + if (peer->reach == 0) + refclock_report(peer, CEVNT_TIMEOUT); + if (up->linect > 0) + pollchar = 'R'; + else + pollchar = 'T'; + if (write(pp->io.fd, &pollchar, 1) != 1) + refclock_report(peer, CEVNT_FAULT); + else + pp->polls++; +#endif +} +#endif + +#else +int refclock_dumbclock_bs; +#endif /* REFCLOCK */ |