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