154359Sroberto/*
2182007Sroberto * refclock_heath - clock driver for Heath GC-1000
3182007Sroberto * (but no longer the GC-1001 Model II, which apparently never worked)
454359Sroberto */
5182007Sroberto
654359Sroberto#ifdef HAVE_CONFIG_H
7182007Sroberto# include <config.h>
854359Sroberto#endif
954359Sroberto
1054359Sroberto#if defined(REFCLOCK) && defined(CLOCK_HEATH)
1154359Sroberto
1282498Sroberto#include "ntpd.h"
1382498Sroberto#include "ntp_io.h"
1482498Sroberto#include "ntp_refclock.h"
1582498Sroberto#include "ntp_stdlib.h"
1682498Sroberto
1754359Sroberto#include <stdio.h>
1854359Sroberto#include <ctype.h>
1982498Sroberto
2054359Sroberto#ifdef HAVE_SYS_IOCTL_H
2154359Sroberto# include <sys/ioctl.h>
2254359Sroberto#endif /* not HAVE_SYS_IOCTL_H */
2354359Sroberto
2454359Sroberto/*
2554359Sroberto * This driver supports the Heath GC-1000 Most Accurate Clock, with
2654359Sroberto * RS232C Output Accessory. This is a WWV/WWVH receiver somewhat less
2754359Sroberto * robust than other supported receivers. Its claimed accuracy is 100 ms
2854359Sroberto * when actually synchronized to the broadcast signal, but this doesn't
2954359Sroberto * happen even most of the time, due to propagation conditions, ambient
3054359Sroberto * noise sources, etc. When not synchronized, the accuracy is at the
3154359Sroberto * whim of the internal clock oscillator, which can wander into the
3254359Sroberto * sunset without warning. Since the indicated precision is 100 ms,
3354359Sroberto * expect a host synchronized only to this thing to wander to and fro,
3454359Sroberto * occasionally being rudely stepped when the offset exceeds the default
3554359Sroberto * clock_max of 128 ms.
3654359Sroberto *
37182007Sroberto * There were two GC-1000 versions supported by this driver. The original
3882498Sroberto * GC-1000 with RS-232 output first appeared in 1983, but dissapeared
39182007Sroberto * from the market a few years later. The GC-1001 II with RS-232 output
4082498Sroberto * first appeared circa 1990, but apparently is no longer manufactured.
4182498Sroberto * The two models differ considerably, both in interface and commands.
4282498Sroberto * The GC-1000 has a pseudo-bipolar timecode output triggered by a RTS
4382498Sroberto * transition. The timecode includes both the day of year and time of
44182007Sroberto * day. The GC-1001 II has a true bipolar output and a complement of
4582498Sroberto * single character commands. The timecode includes only the time of
4682498Sroberto * day.
4754359Sroberto *
48182007Sroberto * The GC-1001 II was apparently never tested and, based on a Coverity
49182007Sroberto * scan, apparently never worked [Bug 689].  Related code has been disabled.
50182007Sroberto *
5182498Sroberto * GC-1000
5282498Sroberto *
5382498Sroberto * The internal DIPswitches should be set to operate in MANUAL mode. The
5482498Sroberto * external DIPswitches should be set to GMT and 24-hour format.
5582498Sroberto *
5654359Sroberto * In MANUAL mode the clock responds to a rising edge of the request to
5754359Sroberto * send (RTS) modem control line by sending the timecode. Therefore, it
5854359Sroberto * is necessary that the operating system implement the TIOCMBIC and
5954359Sroberto * TIOCMBIS ioctl system calls and TIOCM_RTS control bit. Present
6054359Sroberto * restrictions require the use of a POSIX-compatible programming
6154359Sroberto * interface, although other interfaces may work as well.
6254359Sroberto *
6354359Sroberto * A simple hardware modification to the clock can be made which
6454359Sroberto * prevents the clock hearing the request to send (RTS) if the HI SPEC
6554359Sroberto * lamp is out. Route the HISPEC signal to the tone decoder board pin
6654359Sroberto * 19, from the display, pin 19. Isolate pin 19 of the decoder board
6754359Sroberto * first, but maintain connection with pin 10. Also isolate pin 38 of
6854359Sroberto * the CPU on the tone board, and use half an added 7400 to gate the
6954359Sroberto * original signal to pin 38 with that from pin 19.
7054359Sroberto *
7154359Sroberto * The clock message consists of 23 ASCII printing characters in the
7254359Sroberto * following format:
7354359Sroberto *
7454359Sroberto * hh:mm:ss.f AM  dd/mm/yr<cr>
7554359Sroberto *
7654359Sroberto *	hh:mm:ss.f = hours, minutes, seconds
7754359Sroberto *	f = deciseconds ('?' when out of spec)
7854359Sroberto *	AM/PM/bb = blank in 24-hour mode
7954359Sroberto *	dd/mm/yr = day, month, year
8054359Sroberto *
8154359Sroberto * The alarm condition is indicated by '?', rather than a digit, at f.
8254359Sroberto * Note that 0?:??:??.? is displayed before synchronization is first
8354359Sroberto * established and hh:mm:ss.? once synchronization is established and
8454359Sroberto * then lost again for about a day.
8554359Sroberto *
86182007Sroberto * GC-1001 II
8782498Sroberto *
8882498Sroberto * Commands consist of a single letter and are case sensitive. When
8982498Sroberto * enterred in lower case, a description of the action performed is
9082498Sroberto * displayed. When enterred in upper case the action is performed.
9182498Sroberto * Following is a summary of descriptions as displayed by the clock:
9282498Sroberto *
9382498Sroberto * The clock responds with a command The 'A' command returns an ASCII
9482498Sroberto * local time string:  HH:MM:SS.T xx<CR>, where
9582498Sroberto *
9682498Sroberto *	HH = hours
9782498Sroberto *	MM = minutes
9882498Sroberto *	SS = seconds
9982498Sroberto *	T = tenths-of-seconds
10082498Sroberto *	xx = 'AM', 'PM', or '  '
10182498Sroberto *	<CR> = carriage return
10282498Sroberto *
10382498Sroberto * The 'D' command returns 24 pairs of bytes containing the variable
10482498Sroberto * divisor value at the end of each of the previous 24 hours. This
10582498Sroberto * allows the timebase trimming process to be observed.  UTC hour 00 is
10682498Sroberto * always returned first. The first byte of each pair is the high byte
10782498Sroberto * of (variable divisor * 16); the second byte is the low byte of
10882498Sroberto * (variable divisor * 16). For example, the byte pair 3C 10 would be
10982498Sroberto * returned for a divisor of 03C1 hex (961 decimal).
11082498Sroberto *
11182498Sroberto * The 'I' command returns:  | TH | TL | ER | DH | DL | U1 | I1 | I2 | ,
11282498Sroberto * where
11382498Sroberto *
11482498Sroberto *	TH = minutes since timebase last trimmed (high byte)
11582498Sroberto *	TL = minutes since timebase last trimmed (low byte)
11682498Sroberto *	ER = last accumulated error in 1.25 ms increments
11782498Sroberto *	DH = high byte of (current variable divisor * 16)
11882498Sroberto *	DL = low byte of (current variable divisor * 16)
11982498Sroberto *	U1 = UT1 offset (/.1 s):  | + | 4 | 2 | 1 | 0 | 0 | 0 | 0 |
12082498Sroberto *	I1 = information byte 1:  | W | C | D | I | U | T | Z | 1 | ,
12182498Sroberto *	     where
12282498Sroberto *
12382498Sroberto *		W = set by WWV(H)
12482498Sroberto *		C = CAPTURE LED on
12582498Sroberto *		D = TRIM DN LED on
12682498Sroberto *		I = HI SPEC LED on
12782498Sroberto *		U = TRIM UP LED on
12882498Sroberto *		T = DST switch on
12982498Sroberto *		Z = UTC switch on
13082498Sroberto *		1 = UT1 switch on
13182498Sroberto *
13282498Sroberto *	I2 = information byte 2:  | 8 | 8 | 4 | 2 | 1 | D | d | S | ,
13382498Sroberto *	     where
13482498Sroberto *
13582498Sroberto *		8, 8, 4, 2, 1 = TIME ZONE switch settings
13682498Sroberto *		D = DST bit (#55) in last-received frame
13782498Sroberto *		d = DST bit (#2) in last-received frame
13882498Sroberto *		S = clock is in simulation mode
13982498Sroberto *
14082498Sroberto * The 'P' command returns 24 bytes containing the number of frames
14182498Sroberto * received without error during UTC hours 00 through 23, providing an
14282498Sroberto * indication of hourly propagation.  These bytes are updated each hour
14382498Sroberto * to reflect the previous 24 hour period.  UTC hour 00 is always
14482498Sroberto * returned first.
14582498Sroberto *
14682498Sroberto * The 'T' command returns the UTC time:  | HH | MM | SS | T0 | , where
14782498Sroberto *	HH = tens-of-hours and hours (packed BCD)
14882498Sroberto *	MM = tens-of-minutes and minutes (packed BCD)
14982498Sroberto *	SS = tens-of-seconds and seconds (packed BCD)
15082498Sroberto *	T = tenths-of-seconds (BCD)
15182498Sroberto *
15254359Sroberto * Fudge Factors
15354359Sroberto *
15454359Sroberto * A fudge time1 value of .04 s appears to center the clock offset
15554359Sroberto * residuals. The fudge time2 parameter is the local time offset east of
15654359Sroberto * Greenwich, which depends on DST. Sorry about that, but the clock
15754359Sroberto * gives no hint on what the DIPswitches say.
15854359Sroberto */
15954359Sroberto
16054359Sroberto/*
16154359Sroberto * Interface definitions
16254359Sroberto */
16354359Sroberto#define	DEVICE		"/dev/heath%d" /* device name and unit */
16454359Sroberto#define	PRECISION	(-4)	/* precision assumed (about 100 ms) */
16554359Sroberto#define	REFID		"WWV\0"	/* reference ID */
16654359Sroberto#define	DESCRIPTION	"Heath GC-1000 Most Accurate Clock" /* WRU */
16754359Sroberto
16882498Sroberto#define LENHEATH1	23	/* min timecode length */
169182007Sroberto#if 0	/* BUG 689 */
17082498Sroberto#define LENHEATH2	13	/* min timecode length */
171182007Sroberto#endif
17254359Sroberto
17354359Sroberto/*
17454359Sroberto * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
17554359Sroberto * leap.
17654359Sroberto */
17754359Srobertostatic int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
17854359Srobertostatic int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
17954359Sroberto
18054359Sroberto/*
18182498Sroberto * Baud rate table. The GC-1000 supports 1200, 2400 and 4800; the
182182007Sroberto * GC-1001 II supports only 9600.
18382498Sroberto */
18482498Srobertostatic int speed[] = {B1200, B2400, B4800, B9600};
18582498Sroberto
18682498Sroberto/*
18754359Sroberto * Function prototypes
18854359Sroberto */
189290001Sglebiusstatic	int	heath_start	(int, struct peer *);
190290001Sglebiusstatic	void	heath_shutdown	(int, struct peer *);
191290001Sglebiusstatic	void	heath_receive	(struct recvbuf *);
192290001Sglebiusstatic	void	heath_poll	(int, struct peer *);
19354359Sroberto
19454359Sroberto/*
19554359Sroberto * Transfer vector
19654359Sroberto */
19754359Srobertostruct	refclock refclock_heath = {
19854359Sroberto	heath_start,		/* start up driver */
19954359Sroberto	heath_shutdown,		/* shut down driver */
20054359Sroberto	heath_poll,		/* transmit poll message */
20154359Sroberto	noentry,		/* not used (old heath_control) */
20254359Sroberto	noentry,		/* initialize driver */
20354359Sroberto	noentry,		/* not used (old heath_buginfo) */
20454359Sroberto	NOFLAGS			/* not used */
20554359Sroberto};
20654359Sroberto
20754359Sroberto
20854359Sroberto/*
20954359Sroberto * heath_start - open the devices and initialize data for processing
21054359Sroberto */
21154359Srobertostatic int
21254359Srobertoheath_start(
21354359Sroberto	int unit,
21454359Sroberto	struct peer *peer
21554359Sroberto	)
21654359Sroberto{
21754359Sroberto	struct refclockproc *pp;
21854359Sroberto	int fd;
21954359Sroberto	char device[20];
22054359Sroberto
22154359Sroberto	/*
22254359Sroberto	 * Open serial port
22354359Sroberto	 */
224290001Sglebius	snprintf(device, sizeof(device), DEVICE, unit);
225290001Sglebius	fd = refclock_open(device, speed[peer->ttl & 0x3],
226290001Sglebius			   LDISC_REMOTE);
227290001Sglebius	if (fd <= 0)
228182007Sroberto		return (0);
22954359Sroberto	pp = peer->procptr;
23054359Sroberto	pp->io.clock_recv = heath_receive;
231290001Sglebius	pp->io.srcclock = peer;
23254359Sroberto	pp->io.datalen = 0;
23354359Sroberto	pp->io.fd = fd;
23454359Sroberto	if (!io_addclock(&pp->io)) {
235290001Sglebius		close(fd);
236290001Sglebius		pp->io.fd = -1;
23754359Sroberto		return (0);
23854359Sroberto	}
23954359Sroberto
24054359Sroberto	/*
24154359Sroberto	 * Initialize miscellaneous variables
24254359Sroberto	 */
24354359Sroberto	peer->precision = PRECISION;
24454359Sroberto	pp->clockdesc = DESCRIPTION;
245290001Sglebius	memcpy(&pp->refid, REFID, 4);
24654359Sroberto	return (1);
24754359Sroberto}
24854359Sroberto
24954359Sroberto
25054359Sroberto/*
25154359Sroberto * heath_shutdown - shut down the clock
25254359Sroberto */
25354359Srobertostatic void
25454359Srobertoheath_shutdown(
25554359Sroberto	int unit,
25654359Sroberto	struct peer *peer
25754359Sroberto	)
25854359Sroberto{
25954359Sroberto	struct refclockproc *pp;
26054359Sroberto
26154359Sroberto	pp = peer->procptr;
262290001Sglebius	if (-1 != pp->io.fd)
263290001Sglebius		io_closeclock(&pp->io);
26454359Sroberto}
26554359Sroberto
26654359Sroberto
26754359Sroberto/*
26854359Sroberto * heath_receive - receive data from the serial interface
26954359Sroberto */
27054359Srobertostatic void
27154359Srobertoheath_receive(
27254359Sroberto	struct recvbuf *rbufp
27354359Sroberto	)
27454359Sroberto{
27554359Sroberto	struct refclockproc *pp;
27654359Sroberto	struct peer *peer;
27754359Sroberto	l_fp trtmp;
27854359Sroberto	int month, day;
27954359Sroberto	int i;
28054359Sroberto	char dsec, a[5];
28154359Sroberto
28254359Sroberto	/*
28354359Sroberto	 * Initialize pointers and read the timecode and timestamp
28454359Sroberto	 */
285290001Sglebius	peer = rbufp->recv_peer;
28654359Sroberto	pp = peer->procptr;
28782498Sroberto	pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
28882498Sroberto	    &trtmp);
28954359Sroberto
29054359Sroberto	/*
29154359Sroberto	 * We get down to business, check the timecode format and decode
29254359Sroberto	 * its contents. If the timecode has invalid length or is not in
29354359Sroberto	 * proper format, we declare bad format and exit.
29454359Sroberto	 */
29582498Sroberto	switch (pp->lencode) {
29654359Sroberto
29754359Sroberto	/*
29882498Sroberto	 * GC-1000 timecode format: "hh:mm:ss.f AM  mm/dd/yy"
299182007Sroberto	 * GC-1001 II timecode format: "hh:mm:ss.f   "
30054359Sroberto	 */
30182498Sroberto	case LENHEATH1:
30282498Sroberto		if (sscanf(pp->a_lastcode,
30382498Sroberto		    "%2d:%2d:%2d.%c%5c%2d/%2d/%2d", &pp->hour,
30482498Sroberto		    &pp->minute, &pp->second, &dsec, a, &month, &day,
30582498Sroberto		    &pp->year) != 8) {
30682498Sroberto			refclock_report(peer, CEVNT_BADREPLY);
30782498Sroberto			return;
30882498Sroberto		}
30954359Sroberto		break;
31054359Sroberto
311182007Sroberto#if 0	/* BUG 689 */
31254359Sroberto	/*
313182007Sroberto	 * GC-1001 II timecode format: "hh:mm:ss.f   "
31454359Sroberto	 */
31582498Sroberto	case LENHEATH2:
31682498Sroberto		if (sscanf(pp->a_lastcode, "%2d:%2d:%2d.%c", &pp->hour,
31782498Sroberto		    &pp->minute, &pp->second, &dsec) != 4) {
31882498Sroberto			refclock_report(peer, CEVNT_BADREPLY);
31954359Sroberto			return;
320182007Sroberto		} else {
321182007Sroberto			struct tm *tm_time_p;
322182007Sroberto			time_t     now;
323182007Sroberto
324182007Sroberto			time(&now);	/* we should grab 'now' earlier */
325182007Sroberto			tm_time_p = gmtime(&now);
326182007Sroberto			/*
327182007Sroberto			 * There is a window of time around midnight
328182007Sroberto			 * where this will Do The Wrong Thing.
329182007Sroberto			 */
330182007Sroberto			if (tm_time_p) {
331182007Sroberto				month = tm_time_p->tm_mon + 1;
332182007Sroberto				day = tm_time_p->tm_mday;
333182007Sroberto			} else {
334182007Sroberto				refclock_report(peer, CEVNT_FAULT);
335182007Sroberto				return;
336182007Sroberto			}
33754359Sroberto		}
33882498Sroberto		break;
339182007Sroberto#endif
34054359Sroberto
34182498Sroberto	default:
34282498Sroberto		refclock_report(peer, CEVNT_BADREPLY);
34382498Sroberto		return;
34454359Sroberto	}
34554359Sroberto
34654359Sroberto	/*
34754359Sroberto	 * We determine the day of the year from the DIPswitches. This
34854359Sroberto	 * should be fixed, since somebody might forget to set them.
34954359Sroberto	 * Someday this hazard will be fixed by a fiendish scheme that
35054359Sroberto	 * looks at the timecode and year the radio shows, then computes
35154359Sroberto	 * the residue of the seconds mod the seconds in a leap cycle.
35254359Sroberto	 * If in the third year of that cycle and the third and later
35354359Sroberto	 * months of that year, add one to the day. Then, correct the
35454359Sroberto	 * timecode accordingly. Icky pooh. This bit of nonsense could
35554359Sroberto	 * be avoided if the engineers had been required to write a
35654359Sroberto	 * device driver before finalizing the timecode format.
35754359Sroberto	 */
35854359Sroberto	if (month < 1 || month > 12 || day < 1) {
35954359Sroberto		refclock_report(peer, CEVNT_BADTIME);
36054359Sroberto		return;
36154359Sroberto	}
36254359Sroberto	if (pp->year % 4) {
36354359Sroberto		if (day > day1tab[month - 1]) {
36454359Sroberto			refclock_report(peer, CEVNT_BADTIME);
36554359Sroberto			return;
36654359Sroberto		}
36754359Sroberto		for (i = 0; i < month - 1; i++)
36854359Sroberto		    day += day1tab[i];
36954359Sroberto	} else {
37054359Sroberto		if (day > day2tab[month - 1]) {
37154359Sroberto			refclock_report(peer, CEVNT_BADTIME);
37254359Sroberto			return;
37354359Sroberto		}
37454359Sroberto		for (i = 0; i < month - 1; i++)
37554359Sroberto		    day += day2tab[i];
37654359Sroberto	}
37754359Sroberto	pp->day = day;
37854359Sroberto
37954359Sroberto	/*
38054359Sroberto	 * Determine synchronization and last update
38154359Sroberto	 */
382290001Sglebius	if (!isdigit((unsigned char)dsec))
38354359Sroberto		pp->leap = LEAP_NOTINSYNC;
38454359Sroberto	else {
385132451Sroberto		pp->nsec = (dsec - '0') * 100000000;
38654359Sroberto		pp->leap = LEAP_NOWARNING;
38754359Sroberto	}
38854359Sroberto	if (!refclock_process(pp))
38954359Sroberto		refclock_report(peer, CEVNT_BADTIME);
39054359Sroberto}
39154359Sroberto
39254359Sroberto
39354359Sroberto/*
39454359Sroberto * heath_poll - called by the transmit procedure
39554359Sroberto */
39654359Srobertostatic void
39754359Srobertoheath_poll(
39854359Sroberto	int unit,
39954359Sroberto	struct peer *peer
40054359Sroberto	)
40154359Sroberto{
40254359Sroberto	struct refclockproc *pp;
40354359Sroberto	int bits = TIOCM_RTS;
40454359Sroberto
40554359Sroberto	/*
40654359Sroberto	 * At each poll we check for timeout and toggle the RTS modem
40754359Sroberto	 * control line, then take a timestamp. Presumably, this is the
40854359Sroberto	 * event the radio captures to generate the timecode.
409132451Sroberto	 * Apparently, the radio takes about a second to make up its
410132451Sroberto	 * mind to send a timecode, so the receive timestamp is
411132451Sroberto	 * worthless.
41254359Sroberto	 */
41354359Sroberto	pp = peer->procptr;
41454359Sroberto
41554359Sroberto	/*
41682498Sroberto	 * We toggle the RTS modem control lead (GC-1000) and sent a T
417182007Sroberto	 * (GC-1001 II) to kick a timecode loose from the radio. This
41882498Sroberto	 * code works only for POSIX and SYSV interfaces. With bsd you
41982498Sroberto	 * are on your own. We take a timestamp between the up and down
42082498Sroberto	 * edges to lengthen the pulse, which should be about 50 usec on
42182498Sroberto	 * a Sun IPC. With hotshot CPUs, the pulse might get too short.
42282498Sroberto	 * Later.
423182007Sroberto	 *
424182007Sroberto	 * Bug 689: Even though we no longer support the GC-1001 II,
425182007Sroberto	 * I'm leaving the 'T' write in for timing purposes.
42654359Sroberto	 */
42754359Sroberto	if (ioctl(pp->io.fd, TIOCMBIC, (char *)&bits) < 0)
42854359Sroberto		refclock_report(peer, CEVNT_FAULT);
429132451Sroberto	get_systime(&pp->lastrec);
43082498Sroberto	if (write(pp->io.fd, "T", 1) != 1)
43182498Sroberto		refclock_report(peer, CEVNT_FAULT);
432132451Sroberto	ioctl(pp->io.fd, TIOCMBIS, (char *)&bits);
43354359Sroberto	if (pp->coderecv == pp->codeproc) {
43454359Sroberto		refclock_report(peer, CEVNT_TIMEOUT);
43554359Sroberto		return;
43654359Sroberto	}
437132451Sroberto	pp->lastref = pp->lastrec;
438132451Sroberto	refclock_receive(peer);
43954359Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
440132451Sroberto#ifdef DEBUG
441132451Sroberto	if (debug)
442132451Sroberto	    printf("heath: timecode %d %s\n", pp->lencode,
443132451Sroberto		   pp->a_lastcode);
444132451Sroberto#endif
445132451Sroberto	pp->polls++;
44654359Sroberto}
44754359Sroberto
44854359Sroberto#else
44954359Srobertoint refclock_heath_bs;
45054359Sroberto#endif /* REFCLOCK */
451