/contrib/ntp/ntpd/refclock_chronolog.c
https://bitbucket.org/freebsd/freebsd-head/ · C · 341 lines · 212 code · 38 blank · 91 comment · 25 complexity · 82808f8bca25dc4c5686799b1e656ec1 MD5 · raw file
- /*
- * refclock_chronolog - clock driver for Chronolog K-series WWVB receiver.
- */
- /*
- * Must interpolate back to local time. Very annoying.
- */
- #define GET_LOCALTIME
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #if defined(REFCLOCK) && defined(CLOCK_CHRONOLOG)
- #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 the Chronolog K-series WWVB receiver.
- *
- * Input format:
- *
- * Y YY/MM/DD<cr><lf>
- * Z hh:mm:ss<cr><lf>
- *
- * YY/MM/DD -- what you'd expect. This arrives a few seconds before the
- * timestamp.
- * hh:mm:ss -- what you'd expect. We take time on the <cr>.
- *
- * Our Chronolog writes time out at 2400 bps 8/N/1, but it can be configured
- * otherwise. The clock seems to appear every 60 seconds, which doesn't make
- * for good statistics collection.
- *
- * The original source of this module was the WWVB module.
- */
- /*
- * Interface definitions
- */
- #define DEVICE "/dev/chronolog%d" /* device name and unit */
- #define SPEED232 B2400 /* uart speed (2400 baud) */
- #define PRECISION (-13) /* precision assumed (about 100 us) */
- #define REFID "chronolog" /* reference ID */
- #define DESCRIPTION "Chrono-log K" /* WRU */
- #define MONLIN 15 /* number of monitoring lines */
- /*
- * Chrono-log unit control structure
- */
- struct chronolog_unit {
- u_char tcswitch; /* timecode switch */
- l_fp laststamp; /* last receive timestamp */
- u_char lasthour; /* last hour (for monitor) */
- int year; /* Y2K-adjusted year */
- int day; /* day-of-month */
- int month; /* month-of-year */
- };
- /*
- * Function prototypes
- */
- static int chronolog_start P((int, struct peer *));
- static void chronolog_shutdown P((int, struct peer *));
- static void chronolog_receive P((struct recvbuf *));
- static void chronolog_poll P((int, struct peer *));
- /*
- * Transfer vector
- */
- struct refclock refclock_chronolog = {
- chronolog_start, /* start up driver */
- chronolog_shutdown, /* shut down driver */
- chronolog_poll, /* poll the driver -- a nice fabrication */
- noentry, /* not used */
- noentry, /* not used */
- noentry, /* not used */
- NOFLAGS /* not used */
- };
- /*
- * chronolog_start - open the devices and initialize data for processing
- */
- static int
- chronolog_start(
- int unit,
- struct peer *peer
- )
- {
- register struct chronolog_unit *up;
- struct refclockproc *pp;
- int fd;
- char device[20];
- /*
- * 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 Chronolog with device %s\n",device);
- #endif
- if (!(fd = refclock_open(device, SPEED232, 0)))
- return (0);
- /*
- * Allocate and initialize unit structure
- */
- if (!(up = (struct chronolog_unit *)
- emalloc(sizeof(struct chronolog_unit)))) {
- (void) close(fd);
- return (0);
- }
- memset((char *)up, 0, sizeof(struct chronolog_unit));
- pp = peer->procptr;
- pp->unitptr = (caddr_t)up;
- pp->io.clock_recv = chronolog_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);
- }
- /*
- * Initialize miscellaneous variables
- */
- peer->precision = PRECISION;
- pp->clockdesc = DESCRIPTION;
- memcpy((char *)&pp->refid, REFID, 4);
- return (1);
- }
- /*
- * chronolog_shutdown - shut down the clock
- */
- static void
- chronolog_shutdown(
- int unit,
- struct peer *peer
- )
- {
- register struct chronolog_unit *up;
- struct refclockproc *pp;
- pp = peer->procptr;
- up = (struct chronolog_unit *)pp->unitptr;
- io_closeclock(&pp->io);
- free(up);
- }
- /*
- * chronolog_receive - receive data from the serial interface
- */
- static void
- chronolog_receive(
- struct recvbuf *rbufp
- )
- {
- struct chronolog_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 chronolog_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 = temp;
- pp->lastrec = up->laststamp;
- up->laststamp = trtmp;
- up->tcswitch = 1;
- #ifdef DEBUG
- if (debug)
- printf("chronolog: timecode %d %s\n", pp->lencode,
- pp->a_lastcode);
- #endif
- /*
- * We get down to business. Check the timecode format and decode
- * its contents. This code uses the first character to see whether
- * we're looking at a date or a time. We store data data across
- * calls since it is transmitted a few seconds ahead of the
- * timestamp.
- */
- got_good=0;
- if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day))
- {
- /*
- * Y2K convert the 2-digit year
- */
- up->year = up->year >= 69 ? up->year : up->year + 100;
- return;
- }
- if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d",
- &hours,&minutes,&seconds) == 3)
- {
- #ifdef GET_LOCALTIME
- struct tm local;
- struct tm *gmtp;
- time_t unixtime;
- int adjyear;
- int adjmon;
- /*
- * Convert to GMT for sites that distribute localtime. This
- * means we have to do Y2K conversion on the 2-digit year;
- * otherwise, we get the time wrong.
- */
-
- local.tm_year = up->year;
- local.tm_mon = up->month-1;
- local.tm_mday = up->day;
- local.tm_hour = hours;
- local.tm_min = minutes;
- local.tm_sec = seconds;
- local.tm_isdst = -1;
- unixtime = mktime (&local);
- if ((gmtp = gmtime (&unixtime)) == 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
-
- #else
- /*
- * For more rational sites distributing UTC
- */
- pp->day = ymd2yd(year+1900,month,day);
- pp->hour = hours;
- pp->minute = minutes;
- pp->second = seconds;
- #endif
- got_good=1;
- }
- if (!got_good)
- 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 = pp->hour;
- }
- /*
- * chronolog_poll - called by the transmit procedure
- */
- static void
- chronolog_poll(
- int unit,
- struct peer *peer
- )
- {
- /*
- * 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
- register struct chronolog_unit *up;
- struct refclockproc *pp;
- char pollchar;
- pp = peer->procptr;
- up = (struct chronolog_unit *)pp->unitptr;
- if (peer->burst == 0 && 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
- }
- #else
- int refclock_chronolog_bs;
- #endif /* REFCLOCK */