154359Sroberto/* 254359Sroberto * refclock_chronolog - clock driver for Chronolog K-series WWVB receiver. 354359Sroberto */ 454359Sroberto 554359Sroberto/* 654359Sroberto * Must interpolate back to local time. Very annoying. 754359Sroberto */ 854359Sroberto#define GET_LOCALTIME 954359Sroberto 1054359Sroberto#ifdef HAVE_CONFIG_H 1154359Sroberto#include <config.h> 1254359Sroberto#endif 1354359Sroberto 1454359Sroberto#if defined(REFCLOCK) && defined(CLOCK_CHRONOLOG) 1554359Sroberto 1654359Sroberto#include "ntpd.h" 1754359Sroberto#include "ntp_io.h" 1854359Sroberto#include "ntp_refclock.h" 1954359Sroberto#include "ntp_calendar.h" 2054359Sroberto#include "ntp_stdlib.h" 2154359Sroberto 2282498Sroberto#include <stdio.h> 2382498Sroberto#include <ctype.h> 2482498Sroberto 2554359Sroberto/* 2654359Sroberto * This driver supports the Chronolog K-series WWVB receiver. 2754359Sroberto * 2854359Sroberto * Input format: 2954359Sroberto * 3054359Sroberto * Y YY/MM/DD<cr><lf> 3154359Sroberto * Z hh:mm:ss<cr><lf> 3254359Sroberto * 3354359Sroberto * YY/MM/DD -- what you'd expect. This arrives a few seconds before the 3454359Sroberto * timestamp. 3554359Sroberto * hh:mm:ss -- what you'd expect. We take time on the <cr>. 3654359Sroberto * 3754359Sroberto * Our Chronolog writes time out at 2400 bps 8/N/1, but it can be configured 3854359Sroberto * otherwise. The clock seems to appear every 60 seconds, which doesn't make 3954359Sroberto * for good statistics collection. 4054359Sroberto * 4154359Sroberto * The original source of this module was the WWVB module. 4254359Sroberto */ 4354359Sroberto 4454359Sroberto/* 4554359Sroberto * Interface definitions 4654359Sroberto */ 4754359Sroberto#define DEVICE "/dev/chronolog%d" /* device name and unit */ 4854359Sroberto#define SPEED232 B2400 /* uart speed (2400 baud) */ 4954359Sroberto#define PRECISION (-13) /* precision assumed (about 100 us) */ 5054359Sroberto#define REFID "chronolog" /* reference ID */ 5154359Sroberto#define DESCRIPTION "Chrono-log K" /* WRU */ 5254359Sroberto 5354359Sroberto#define MONLIN 15 /* number of monitoring lines */ 5454359Sroberto 5554359Sroberto/* 5654359Sroberto * Chrono-log unit control structure 5754359Sroberto */ 5854359Srobertostruct chronolog_unit { 5954359Sroberto u_char tcswitch; /* timecode switch */ 6054359Sroberto l_fp laststamp; /* last receive timestamp */ 6154359Sroberto u_char lasthour; /* last hour (for monitor) */ 6254359Sroberto int year; /* Y2K-adjusted year */ 6354359Sroberto int day; /* day-of-month */ 6454359Sroberto int month; /* month-of-year */ 6554359Sroberto}; 6654359Sroberto 6754359Sroberto/* 6854359Sroberto * Function prototypes 6954359Sroberto */ 70280849Scystatic int chronolog_start (int, struct peer *); 71280849Scystatic void chronolog_shutdown (int, struct peer *); 72280849Scystatic void chronolog_receive (struct recvbuf *); 73280849Scystatic void chronolog_poll (int, struct peer *); 7454359Sroberto 7554359Sroberto/* 7654359Sroberto * Transfer vector 7754359Sroberto */ 7854359Srobertostruct refclock refclock_chronolog = { 7954359Sroberto chronolog_start, /* start up driver */ 8054359Sroberto chronolog_shutdown, /* shut down driver */ 8154359Sroberto chronolog_poll, /* poll the driver -- a nice fabrication */ 8254359Sroberto noentry, /* not used */ 8354359Sroberto noentry, /* not used */ 8454359Sroberto noentry, /* not used */ 8554359Sroberto NOFLAGS /* not used */ 8654359Sroberto}; 8754359Sroberto 8854359Sroberto 8954359Sroberto/* 9054359Sroberto * chronolog_start - open the devices and initialize data for processing 9154359Sroberto */ 9254359Srobertostatic int 9354359Srobertochronolog_start( 9454359Sroberto int unit, 9554359Sroberto struct peer *peer 9654359Sroberto ) 9754359Sroberto{ 9854359Sroberto register struct chronolog_unit *up; 9954359Sroberto struct refclockproc *pp; 10054359Sroberto int fd; 10154359Sroberto char device[20]; 10254359Sroberto 10354359Sroberto /* 10454359Sroberto * Open serial port. Don't bother with CLK line discipline, since 10554359Sroberto * it's not available. 10654359Sroberto */ 107280849Scy snprintf(device, sizeof(device), DEVICE, unit); 10854359Sroberto#ifdef DEBUG 10954359Sroberto if (debug) 11054359Sroberto printf ("starting Chronolog with device %s\n",device); 11154359Sroberto#endif 112280849Scy fd = refclock_open(device, SPEED232, 0); 113280849Scy if (fd <= 0) 11454359Sroberto return (0); 11554359Sroberto 11654359Sroberto /* 11754359Sroberto * Allocate and initialize unit structure 11854359Sroberto */ 119280849Scy up = emalloc_zero(sizeof(*up)); 12054359Sroberto pp = peer->procptr; 121280849Scy pp->unitptr = up; 12254359Sroberto pp->io.clock_recv = chronolog_receive; 123280849Scy pp->io.srcclock = peer; 12454359Sroberto pp->io.datalen = 0; 12554359Sroberto pp->io.fd = fd; 12654359Sroberto if (!io_addclock(&pp->io)) { 127280849Scy close(fd); 128280849Scy pp->io.fd = -1; 12954359Sroberto free(up); 130280849Scy pp->unitptr = NULL; 13154359Sroberto return (0); 13254359Sroberto } 13354359Sroberto 13454359Sroberto /* 13554359Sroberto * Initialize miscellaneous variables 13654359Sroberto */ 13754359Sroberto peer->precision = PRECISION; 13854359Sroberto pp->clockdesc = DESCRIPTION; 13954359Sroberto memcpy((char *)&pp->refid, REFID, 4); 14054359Sroberto return (1); 14154359Sroberto} 14254359Sroberto 14354359Sroberto 14454359Sroberto/* 14554359Sroberto * chronolog_shutdown - shut down the clock 14654359Sroberto */ 14754359Srobertostatic void 14854359Srobertochronolog_shutdown( 14954359Sroberto int unit, 15054359Sroberto struct peer *peer 15154359Sroberto ) 15254359Sroberto{ 15354359Sroberto register struct chronolog_unit *up; 15454359Sroberto struct refclockproc *pp; 15554359Sroberto 15654359Sroberto pp = peer->procptr; 157280849Scy up = pp->unitptr; 158280849Scy if (-1 != pp->io.fd) 159280849Scy io_closeclock(&pp->io); 160280849Scy if (NULL != up) 161280849Scy free(up); 16254359Sroberto} 16354359Sroberto 16454359Sroberto 16554359Sroberto/* 16654359Sroberto * chronolog_receive - receive data from the serial interface 16754359Sroberto */ 16854359Srobertostatic void 16954359Srobertochronolog_receive( 17054359Sroberto struct recvbuf *rbufp 17154359Sroberto ) 17254359Sroberto{ 17354359Sroberto struct chronolog_unit *up; 17454359Sroberto struct refclockproc *pp; 17554359Sroberto struct peer *peer; 17654359Sroberto 17754359Sroberto l_fp trtmp; /* arrival timestamp */ 17854359Sroberto int hours; /* hour-of-day */ 17954359Sroberto int minutes; /* minutes-past-the-hour */ 18054359Sroberto int seconds; /* seconds */ 18154359Sroberto int temp; /* int temp */ 18254359Sroberto int got_good; /* got a good time flag */ 18354359Sroberto 18454359Sroberto /* 18554359Sroberto * Initialize pointers and read the timecode and timestamp 18654359Sroberto */ 187280849Scy peer = rbufp->recv_peer; 18854359Sroberto pp = peer->procptr; 189280849Scy up = pp->unitptr; 19054359Sroberto temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); 19154359Sroberto 19254359Sroberto if (temp == 0) { 19354359Sroberto if (up->tcswitch == 0) { 19454359Sroberto up->tcswitch = 1; 19554359Sroberto up->laststamp = trtmp; 19654359Sroberto } else 19754359Sroberto up->tcswitch = 0; 19854359Sroberto return; 19954359Sroberto } 20054359Sroberto pp->lencode = temp; 20154359Sroberto pp->lastrec = up->laststamp; 20254359Sroberto up->laststamp = trtmp; 20354359Sroberto up->tcswitch = 1; 20454359Sroberto 20554359Sroberto#ifdef DEBUG 20654359Sroberto if (debug) 20754359Sroberto printf("chronolog: timecode %d %s\n", pp->lencode, 20854359Sroberto pp->a_lastcode); 20954359Sroberto#endif 21054359Sroberto 21154359Sroberto /* 21254359Sroberto * We get down to business. Check the timecode format and decode 21354359Sroberto * its contents. This code uses the first character to see whether 21454359Sroberto * we're looking at a date or a time. We store data data across 21554359Sroberto * calls since it is transmitted a few seconds ahead of the 21654359Sroberto * timestamp. 21754359Sroberto */ 21854359Sroberto got_good=0; 21954359Sroberto if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day)) 22054359Sroberto { 22154359Sroberto /* 22254359Sroberto * Y2K convert the 2-digit year 22354359Sroberto */ 22454359Sroberto up->year = up->year >= 69 ? up->year : up->year + 100; 22554359Sroberto return; 22654359Sroberto } 22754359Sroberto if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d", 22854359Sroberto &hours,&minutes,&seconds) == 3) 22954359Sroberto { 23054359Sroberto#ifdef GET_LOCALTIME 23154359Sroberto struct tm local; 23254359Sroberto struct tm *gmtp; 23354359Sroberto time_t unixtime; 23454359Sroberto int adjyear; 23554359Sroberto int adjmon; 23654359Sroberto 23754359Sroberto /* 23854359Sroberto * Convert to GMT for sites that distribute localtime. This 23954359Sroberto * means we have to do Y2K conversion on the 2-digit year; 24054359Sroberto * otherwise, we get the time wrong. 24154359Sroberto */ 24254359Sroberto 243280849Scy memset(&local, 0, sizeof(local)); 244280849Scy 24554359Sroberto local.tm_year = up->year; 24654359Sroberto local.tm_mon = up->month-1; 24754359Sroberto local.tm_mday = up->day; 24854359Sroberto local.tm_hour = hours; 24954359Sroberto local.tm_min = minutes; 25054359Sroberto local.tm_sec = seconds; 25154359Sroberto local.tm_isdst = -1; 25254359Sroberto 25354359Sroberto unixtime = mktime (&local); 25454359Sroberto if ((gmtp = gmtime (&unixtime)) == NULL) 25554359Sroberto { 25654359Sroberto refclock_report (peer, CEVNT_FAULT); 25754359Sroberto return; 25854359Sroberto } 25954359Sroberto adjyear = gmtp->tm_year+1900; 26054359Sroberto adjmon = gmtp->tm_mon+1; 26154359Sroberto pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday); 26254359Sroberto pp->hour = gmtp->tm_hour; 26354359Sroberto pp->minute = gmtp->tm_min; 26454359Sroberto pp->second = gmtp->tm_sec; 26554359Sroberto#ifdef DEBUG 26654359Sroberto if (debug) 26754359Sroberto printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 26854359Sroberto adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute, 26954359Sroberto pp->second); 27054359Sroberto#endif 27154359Sroberto 27254359Sroberto#else 27354359Sroberto /* 27454359Sroberto * For more rational sites distributing UTC 27554359Sroberto */ 27654359Sroberto pp->day = ymd2yd(year+1900,month,day); 27754359Sroberto pp->hour = hours; 27854359Sroberto pp->minute = minutes; 27954359Sroberto pp->second = seconds; 28054359Sroberto 28154359Sroberto#endif 28254359Sroberto got_good=1; 28354359Sroberto } 28454359Sroberto 28554359Sroberto if (!got_good) 28654359Sroberto return; 28754359Sroberto 28854359Sroberto 28954359Sroberto /* 29054359Sroberto * Process the new sample in the median filter and determine the 29154359Sroberto * timecode timestamp. 29254359Sroberto */ 29354359Sroberto if (!refclock_process(pp)) { 29454359Sroberto refclock_report(peer, CEVNT_BADTIME); 29554359Sroberto return; 29654359Sroberto } 297132451Sroberto pp->lastref = pp->lastrec; 298132451Sroberto refclock_receive(peer); 29954359Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 300280849Scy up->lasthour = (u_char)pp->hour; 30154359Sroberto} 30254359Sroberto 30354359Sroberto 30454359Sroberto/* 30554359Sroberto * chronolog_poll - called by the transmit procedure 30654359Sroberto */ 30754359Srobertostatic void 30854359Srobertochronolog_poll( 30954359Sroberto int unit, 31054359Sroberto struct peer *peer 31154359Sroberto ) 31254359Sroberto{ 31354359Sroberto /* 31454359Sroberto * Time to poll the clock. The Chrono-log clock is supposed to 31554359Sroberto * respond to a 'T' by returning a timecode in the format(s) 31654359Sroberto * specified above. Ours does (can?) not, but this seems to be 31754359Sroberto * an installation-specific problem. This code is dyked out, 31854359Sroberto * but may be re-enabled if anyone ever finds a Chrono-log that 31954359Sroberto * actually listens to this command. 32054359Sroberto */ 32154359Sroberto#if 0 32254359Sroberto register struct chronolog_unit *up; 32354359Sroberto struct refclockproc *pp; 32454359Sroberto char pollchar; 32554359Sroberto 32654359Sroberto pp = peer->procptr; 327280849Scy up = pp->unitptr; 32854359Sroberto if (peer->burst == 0 && peer->reach == 0) 32954359Sroberto refclock_report(peer, CEVNT_TIMEOUT); 33054359Sroberto if (up->linect > 0) 33154359Sroberto pollchar = 'R'; 33254359Sroberto else 33354359Sroberto pollchar = 'T'; 33454359Sroberto if (write(pp->io.fd, &pollchar, 1) != 1) 33554359Sroberto refclock_report(peer, CEVNT_FAULT); 33654359Sroberto else 33754359Sroberto pp->polls++; 33854359Sroberto#endif 33954359Sroberto} 34054359Sroberto 34154359Sroberto#else 34254359Srobertoint refclock_chronolog_bs; 34354359Sroberto#endif /* REFCLOCK */ 344