1232950Stheraven/*
2232950Stheraven * refclock_arbiter - clock driver for Arbiter 1088A/B Satellite
3232950Stheraven *	Controlled Clock
4232950Stheraven */
5232950Stheraven
6232950Stheraven#ifdef HAVE_CONFIG_H
7232950Stheraven#include <config.h>
8232950Stheraven#endif
9232950Stheraven
10232950Stheraven#if defined(REFCLOCK) && defined(CLOCK_ARBITER)
11232950Stheraven
12232950Stheraven#include "ntpd.h"
13232950Stheraven#include "ntp_io.h"
14232950Stheraven#include "ntp_refclock.h"
15232950Stheraven#include "ntp_stdlib.h"
16232950Stheraven
17232950Stheraven#include <stdio.h>
18232950Stheraven#include <ctype.h>
19232950Stheraven
20232950Stheraven#ifdef SYS_WINNT
21232950Stheravenextern int async_write(int, const void *, unsigned int);
22232950Stheraven#undef write
23232950Stheraven#define write(fd, data, octets)	async_write(fd, data, octets)
24232950Stheraven#endif
25232950Stheraven
26227825Stheraven/*
27227825Stheraven * This driver supports the Arbiter 1088A/B Satellite Controlled Clock.
28227825Stheraven * The claimed accuracy of this clock is 100 ns relative to the PPS
29227825Stheraven * output when receiving four or more satellites.
30227825Stheraven *
31227825Stheraven * The receiver should be configured before starting the NTP daemon, in
32227825Stheraven * order to establish reliable position and operating conditions. It
33227825Stheraven * does not initiate surveying or hold mode. For use with NTP, the
34227825Stheraven * daylight savings time feature should be disables (D0 command) and the
35227825Stheraven * broadcast mode set to operate in UTC (BU command).
36227825Stheraven *
37227825Stheraven * The timecode format supported by this driver is selected by the poll
38227825Stheraven * sequence "B5", which initiates a line in the following format to be
39227825Stheraven * repeated once per second until turned off by the "B0" poll sequence.
40227825Stheraven *
41227825Stheraven * Format B5 (24 ASCII printing characters):
42227825Stheraven *
43227825Stheraven * <cr><lf>i yy ddd hh:mm:ss.000bbb
44227972Stheraven *
45227825Stheraven *	on-time = <cr>
46227825Stheraven *	i = synchronization flag (' ' = locked, '?' = unlocked)
47227972Stheraven *	yy = year of century
48227825Stheraven *	ddd = day of year
49227825Stheraven *	hh:mm:ss = hours, minutes, seconds
50227825Stheraven *	.000 = fraction of second (not used)
51227825Stheraven *	bbb = tailing spaces for fill
52227825Stheraven *
53227825Stheraven * The alarm condition is indicated by a '?' at i, which indicates the
54227825Stheraven * receiver is not synchronized. In normal operation, a line consisting
55227825Stheraven * of the timecode followed by the time quality character (TQ) followed
56227825Stheraven * by the receiver status string (SR) is written to the clockstats file.
57227825Stheraven * The time quality character is encoded in IEEE P1344 standard:
58227825Stheraven *
59227825Stheraven * Format TQ (IEEE P1344 estimated worst-case time quality)
60253159Stheraven *
61253159Stheraven *	0	clock locked, maximum accuracy
62227825Stheraven *	F	clock failure, time not reliable
63227825Stheraven *	4	clock unlocked, accuracy < 1 us
64227825Stheraven *	5	clock unlocked, accuracy < 10 us
65227825Stheraven *	6	clock unlocked, accuracy < 100 us
66227825Stheraven *	7	clock unlocked, accuracy < 1 ms
67227825Stheraven *	8	clock unlocked, accuracy < 10 ms
68227825Stheraven *	9	clock unlocked, accuracy < 100 ms
69227825Stheraven *	A	clock unlocked, accuracy < 1 s
70227825Stheraven *	B	clock unlocked, accuracy < 10 s
71227825Stheraven *
72227825Stheraven * The status string is encoded as follows:
73227825Stheraven *
74227825Stheraven * Format SR (25 ASCII printing characters)
75227825Stheraven *
76227825Stheraven *	V=vv S=ss T=t P=pdop E=ee
77227825Stheraven *
78227825Stheraven *	vv = satellites visible
79227825Stheraven *	ss = relative signal strength
80227825Stheraven *	t = satellites tracked
81227825Stheraven *	pdop = position dilution of precision (meters)
82227825Stheraven *	ee = hardware errors
83227825Stheraven *
84227825Stheraven * If flag4 is set, an additional line consisting of the receiver
85227825Stheraven * latitude (LA), longitude (LO), elevation (LH) (meters), and data
86278724Sdim * buffer (DB) is written to this file. If channel B is enabled for
87227825Stheraven * deviation mode and connected to a 1-PPS signal, the last two numbers
88227825Stheraven * on the line are the deviation and standard deviation averaged over
89227825Stheraven * the last 15 seconds.
90227825Stheraven *
91227825Stheraven * PPS calibration fudge time1 .001240
92227825Stheraven */
93227825Stheraven
94227825Stheraven/*
95227825Stheraven * Interface definitions
96227825Stheraven */
97227825Stheraven#define	DEVICE		"/dev/gps%d" /* device name and unit */
98227825Stheraven#define	SPEED232	B9600	/* uart speed (9600 baud) */
99227825Stheraven#define	PRECISION	(-20)	/* precision assumed (about 1 us) */
100227825Stheraven#define	REFID		"GPS "	/* reference ID */
101227825Stheraven#define	DESCRIPTION	"Arbiter 1088A/B GPS Receiver" /* WRU */
102227825Stheraven#define	LENARB		24	/* format B5 timecode length */
103227825Stheraven#define MAXSTA		40	/* max length of status string */
104227825Stheraven#define MAXPOS		80	/* max length of position string */
105227825Stheraven
106227825Stheraven#ifdef PRE_NTP420
107227825Stheraven#define MODE ttlmax
108227825Stheraven#else
109227825Stheraven#define MODE ttl
110227825Stheraven#endif
111227825Stheraven
112227825Stheraven#define COMMAND_HALT_BCAST ( (peer->MODE % 2) ? "O0" : "B0" )
113227825Stheraven#define COMMAND_START_BCAST ( (peer->MODE % 2) ? "O5" : "B5" )
114227825Stheraven
115227825Stheraven/*
116227825Stheraven * ARB unit control structure
117227825Stheraven */
118278724Sdimstruct arbunit {
119227825Stheraven	l_fp	laststamp;	/* last receive timestamp */
120227825Stheraven	int	tcswitch;	/* timecode switch/counter */
121227825Stheraven	char	qualchar;	/* IEEE P1344 quality (TQ command) */
122227825Stheraven	char	status[MAXSTA];	/* receiver status (SR command) */
123227825Stheraven	char	latlon[MAXPOS];	/* receiver position (lat/lon/alt) */
124227825Stheraven};
125227825Stheraven
126227825Stheraven/*
127227825Stheraven * Function prototypes
128227825Stheraven */
129227825Stheravenstatic	int	arb_start	(int, struct peer *);
130227825Stheravenstatic	void	arb_shutdown	(int, struct peer *);
131227825Stheravenstatic	void	arb_receive	(struct recvbuf *);
132227825Stheravenstatic	void	arb_poll	(int, struct peer *);
133227825Stheraven
134227825Stheraven/*
135227825Stheraven * Transfer vector
136227825Stheraven */
137227825Stheravenstruct	refclock refclock_arbiter = {
138227825Stheraven	arb_start,		/* start up driver */
139227825Stheraven	arb_shutdown,		/* shut down driver */
140227825Stheraven	arb_poll,		/* transmit poll message */
141227825Stheraven	noentry,		/* not used (old arb_control) */
142227825Stheraven	noentry,		/* initialize driver (not used) */
143227825Stheraven	noentry,		/* not used (old arb_buginfo) */
144227825Stheraven	NOFLAGS			/* not used */
145227825Stheraven};
146227825Stheraven
147227825Stheraven
148227825Stheraven/*
149227825Stheraven * arb_start - open the devices and initialize data for processing
150227825Stheraven */
151227825Stheravenstatic int
152227825Stheravenarb_start(
153227825Stheraven	int unit,
154227825Stheraven	struct peer *peer
155227825Stheraven	)
156227825Stheraven{
157227825Stheraven	register struct arbunit *up;
158227825Stheraven	struct refclockproc *pp;
159227825Stheraven	int fd;
160227825Stheraven	char device[20];
161227825Stheraven
162227825Stheraven	/*
163227825Stheraven	 * Open serial port. Use CLK line discipline, if available.
164227825Stheraven	 */
165227825Stheraven	snprintf(device, sizeof(device), DEVICE, unit);
166227825Stheraven	fd = refclock_open(device, SPEED232, LDISC_CLK);
167227825Stheraven	if (fd <= 0)
168227825Stheraven		return (0);
169227825Stheraven
170227825Stheraven	/*
171227825Stheraven	 * Allocate and initialize unit structure
172227825Stheraven	 */
173227825Stheraven	up = emalloc_zero(sizeof(*up));
174227825Stheraven	pp = peer->procptr;
175227825Stheraven	pp->io.clock_recv = arb_receive;
176227825Stheraven	pp->io.srcclock = peer;
177227825Stheraven	pp->io.datalen = 0;
178227825Stheraven	pp->io.fd = fd;
179227825Stheraven	if (!io_addclock(&pp->io)) {
180227825Stheraven		close(fd);
181227825Stheraven		pp->io.fd = -1;
182227825Stheraven		free(up);
183227825Stheraven		return (0);
184227825Stheraven	}
185227825Stheraven	pp->unitptr = up;
186227825Stheraven
187227825Stheraven	/*
188227825Stheraven	 * Initialize miscellaneous variables
189227825Stheraven	 */
190227825Stheraven	peer->precision = PRECISION;
191227825Stheraven	pp->clockdesc = DESCRIPTION;
192227825Stheraven	memcpy((char *)&pp->refid, REFID, 4);
193227825Stheraven	if (peer->MODE > 1) {
194227825Stheraven		msyslog(LOG_NOTICE, "ARBITER: Invalid mode %d", peer->MODE);
195227825Stheraven		close(fd);
196227825Stheraven		pp->io.fd = -1;
197227825Stheraven		free(up);
198227825Stheraven		return (0);
199227825Stheraven	}
200227825Stheraven#ifdef DEBUG
201227825Stheraven	if(debug) { printf("arbiter: mode = %d.\n", peer->MODE); }
202227825Stheraven#endif
203227825Stheraven	write(pp->io.fd, COMMAND_HALT_BCAST, 2);
204227825Stheraven	return (1);
205227825Stheraven}
206227825Stheraven
207227825Stheraven
208227825Stheraven/*
209278724Sdim * arb_shutdown - shut down the clock
210227825Stheraven */
211278724Sdimstatic void
212227825Stheravenarb_shutdown(
213227825Stheraven	int unit,
214227825Stheraven	struct peer *peer
215227825Stheraven	)
216227825Stheraven{
217227825Stheraven	register struct arbunit *up;
218227825Stheraven	struct refclockproc *pp;
219227825Stheraven
220227825Stheraven	pp = peer->procptr;
221227825Stheraven	up = pp->unitptr;
222227825Stheraven	if (-1 != pp->io.fd)
223227825Stheraven		io_closeclock(&pp->io);
224227825Stheraven	if (NULL != up)
225227825Stheraven		free(up);
226279456Sdim}
227279456Sdim
228279456Sdim
229279456Sdim/*
230279456Sdim * arb_receive - receive data from the serial interface
231279456Sdim */
232227825Stheravenstatic void
233227825Stheravenarb_receive(
234227825Stheraven	struct recvbuf *rbufp
235227825Stheraven	)
236227825Stheraven{
237227825Stheraven	register struct arbunit *up;
238227825Stheraven	struct refclockproc *pp;
239227825Stheraven	struct peer *peer;
240227825Stheraven	l_fp trtmp;
241227825Stheraven	int temp;
242279456Sdim	u_char	syncchar;		/* synch indicator */
243227825Stheraven	char	tbuf[BMAX];		/* temp buffer */
244279456Sdim
245227825Stheraven	/*
246227825Stheraven	 * Initialize pointers and read the timecode and timestamp
247227825Stheraven	 */
248227825Stheraven	peer = rbufp->recv_peer;
249227825Stheraven	pp = peer->procptr;
250227825Stheraven	up = pp->unitptr;
251227825Stheraven	temp = refclock_gtlin(rbufp, tbuf, sizeof(tbuf), &trtmp);
252227825Stheraven
253227825Stheraven	/*
254227825Stheraven	 * Note we get a buffer and timestamp for both a <cr> and <lf>,
255227825Stheraven	 * but only the <cr> timestamp is retained. The program first
256227825Stheraven	 * sends a TQ and expects the echo followed by the time quality
257227825Stheraven	 * character. It then sends a B5 starting the timecode broadcast
258227825Stheraven	 * and expects the echo followed some time later by the on-time
259227825Stheraven	 * character <cr> and then the <lf> beginning the timecode
260227825Stheraven	 * itself. Finally, at the <cr> beginning the next timecode at
261227825Stheraven	 * the next second, the program sends a B0 shutting down the
262227825Stheraven	 * timecode broadcast.
263227825Stheraven	 *
264278724Sdim	 * If flag4 is set, the program snatches the latitude, longitude
265227825Stheraven	 * and elevation and writes it to the clockstats file.
266227825Stheraven	 */
267278724Sdim	if (temp == 0)
268227825Stheraven		return;
269227825Stheraven
270278724Sdim	pp->lastrec = up->laststamp;
271227825Stheraven	up->laststamp = trtmp;
272227825Stheraven	if (temp < 3)
273278724Sdim		return;
274227825Stheraven
275227825Stheraven	if (up->tcswitch == 0) {
276227825Stheraven
277227825Stheraven		/*
278227825Stheraven		 * Collect statistics. If nothing is recogized, just
279227825Stheraven		 * ignore; sometimes the clock doesn't stop spewing
280227825Stheraven		 * timecodes for awhile after the B0 command.
281227825Stheraven		 *
282227825Stheraven		 * If flag4 is not set, send TQ, SR, B5. If flag4 is
283278724Sdim		 * sset, send TQ, SR, LA, LO, LH, DB, B5. When the
284227825Stheraven		 * median filter is full, send B0.
285227825Stheraven		 */
286227825Stheraven		if (!strncmp(tbuf, "TQ", 2)) {
287227825Stheraven			up->qualchar = tbuf[2];
288227825Stheraven			write(pp->io.fd, "SR", 2);
289227825Stheraven			return;
290227825Stheraven
291227825Stheraven		} else if (!strncmp(tbuf, "SR", 2)) {
292227825Stheraven			strlcpy(up->status, tbuf + 2,
293227825Stheraven				sizeof(up->status));
294227825Stheraven			if (pp->sloppyclockflag & CLK_FLAG4)
295227825Stheraven				write(pp->io.fd, "LA", 2);
296227825Stheraven			else
297227825Stheraven				write(pp->io.fd, COMMAND_START_BCAST, 2);
298227825Stheraven			return;
299227825Stheraven
300227825Stheraven		} else if (!strncmp(tbuf, "LA", 2)) {
301227825Stheraven			strlcpy(up->latlon, tbuf + 2, sizeof(up->latlon));
302227825Stheraven			write(pp->io.fd, "LO", 2);
303227825Stheraven			return;
304227825Stheraven
305227825Stheraven		} else if (!strncmp(tbuf, "LO", 2)) {
306227825Stheraven			strlcat(up->latlon, " ", sizeof(up->latlon));
307227825Stheraven			strlcat(up->latlon, tbuf + 2, sizeof(up->latlon));
308227825Stheraven			write(pp->io.fd, "LH", 2);
309227825Stheraven			return;
310227825Stheraven
311227825Stheraven		} else if (!strncmp(tbuf, "LH", 2)) {
312227825Stheraven			strlcat(up->latlon, " ", sizeof(up->latlon));
313227825Stheraven			strlcat(up->latlon, tbuf + 2, sizeof(up->latlon));
314227825Stheraven			write(pp->io.fd, "DB", 2);
315227825Stheraven			return;
316227825Stheraven
317227825Stheraven		} else if (!strncmp(tbuf, "DB", 2)) {
318227825Stheraven			strlcat(up->latlon, " ", sizeof(up->latlon));
319227825Stheraven			strlcat(up->latlon, tbuf + 2, sizeof(up->latlon));
320227825Stheraven			record_clock_stats(&peer->srcadr, up->latlon);
321227825Stheraven#ifdef DEBUG
322227825Stheraven			if (debug)
323227825Stheraven				printf("arbiter: %s\n", up->latlon);
324227825Stheraven#endif
325227825Stheraven			write(pp->io.fd, COMMAND_START_BCAST, 2);
326227825Stheraven		}
327227825Stheraven	}
328227825Stheraven
329227825Stheraven	/*
330227825Stheraven	 * We get down to business, check the timecode format and decode
331227825Stheraven	 * its contents. If the timecode has valid length, but not in
332227825Stheraven	 * proper format, we declare bad format and exit. If the
333227825Stheraven	 * timecode has invalid length, which sometimes occurs when the
334227825Stheraven	 * B0 amputates the broadcast, we just quietly steal away. Note
335227825Stheraven	 * that the time quality character and receiver status string is
336227825Stheraven	 * tacked on the end for clockstats display.
337227825Stheraven	 */
338227825Stheraven	up->tcswitch++;
339227825Stheraven	if (up->tcswitch <= 1 || temp < LENARB)
340227825Stheraven		return;
341227825Stheraven
342227825Stheraven	/*
343278724Sdim	 * Timecode format B5: "i yy ddd hh:mm:ss.000   "
344227825Stheraven	 */
345227825Stheraven	strlcpy(pp->a_lastcode, tbuf, sizeof(pp->a_lastcode));
346227825Stheraven	pp->a_lastcode[LENARB - 2] = up->qualchar;
347227825Stheraven	strlcat(pp->a_lastcode, up->status, sizeof(pp->a_lastcode));
348278724Sdim	pp->lencode = strlen(pp->a_lastcode);
349227825Stheraven	syncchar = ' ';
350278724Sdim	if (sscanf(pp->a_lastcode, "%c%2d %3d %2d:%2d:%2d",
351227825Stheraven	    &syncchar, &pp->year, &pp->day, &pp->hour,
352227825Stheraven	    &pp->minute, &pp->second) != 6) {
353227825Stheraven		refclock_report(peer, CEVNT_BADREPLY);
354227825Stheraven		write(pp->io.fd, COMMAND_HALT_BCAST, 2);
355227825Stheraven		return;
356227825Stheraven	}
357227825Stheraven
358227825Stheraven	/*
359227825Stheraven	 * We decode the clock dispersion from the time quality
360227825Stheraven	 * character.
361227825Stheraven	 */
362227825Stheraven	switch (up->qualchar) {
363227825Stheraven
364227825Stheraven	    case '0':		/* locked, max accuracy */
365227825Stheraven		pp->disp = 1e-7;
366278724Sdim		pp->lastref = pp->lastrec;
367227972Stheraven		break;
368227972Stheraven
369227825Stheraven	    case '4':		/* unlock accuracy < 1 us */
370278724Sdim		pp->disp = 1e-6;
371227825Stheraven		break;
372227825Stheraven
373227825Stheraven	    case '5':		/* unlock accuracy < 10 us */
374278724Sdim		pp->disp = 1e-5;
375227825Stheraven		break;
376227825Stheraven
377278724Sdim	    case '6':		/* unlock accuracy < 100 us */
378227825Stheraven		pp->disp = 1e-4;
379227825Stheraven		break;
380227825Stheraven
381227825Stheraven	    case '7':		/* unlock accuracy < 1 ms */
382227825Stheraven		pp->disp = .001;
383227825Stheraven		break;
384227825Stheraven
385227825Stheraven	    case '8':		/* unlock accuracy < 10 ms */
386227825Stheraven		pp->disp = .01;
387227825Stheraven		break;
388227825Stheraven
389227825Stheraven	    case '9':		/* unlock accuracy < 100 ms */
390227825Stheraven		pp->disp = .1;
391227825Stheraven		break;
392227825Stheraven
393227825Stheraven	    case 'A':		/* unlock accuracy < 1 s */
394227825Stheraven		pp->disp = 1;
395227825Stheraven		break;
396227825Stheraven
397227825Stheraven	    case 'B':		/* unlock accuracy < 10 s */
398227825Stheraven		pp->disp = 10;
399227825Stheraven		break;
400227825Stheraven
401227825Stheraven	    case 'F':		/* clock failure */
402227825Stheraven		pp->disp = MAXDISPERSE;
403227825Stheraven		refclock_report(peer, CEVNT_FAULT);
404227825Stheraven		write(pp->io.fd, COMMAND_HALT_BCAST, 2);
405227825Stheraven		return;
406227825Stheraven
407227825Stheraven	    default:
408227825Stheraven		pp->disp = MAXDISPERSE;
409227825Stheraven		refclock_report(peer, CEVNT_BADREPLY);
410227825Stheraven		write(pp->io.fd, COMMAND_HALT_BCAST, 2);
411227825Stheraven		return;
412227825Stheraven	}
413227825Stheraven	if (syncchar != ' ')
414278724Sdim		pp->leap = LEAP_NOTINSYNC;
415227825Stheraven	else
416227825Stheraven		pp->leap = LEAP_NOWARNING;
417227825Stheraven
418227825Stheraven	/*
419227825Stheraven	 * Process the new sample in the median filter and determine the
420227825Stheraven	 * timecode timestamp.
421227825Stheraven	 */
422227825Stheraven	if (!refclock_process(pp))
423227825Stheraven		refclock_report(peer, CEVNT_BADTIME);
424227825Stheraven	else if (peer->disp > MAXDISTANCE)
425227825Stheraven		refclock_receive(peer);
426227825Stheraven
427227825Stheraven	/* if (up->tcswitch >= MAXSTAGE) { */
428227825Stheraven	write(pp->io.fd, COMMAND_HALT_BCAST, 2);
429227825Stheraven	/* } */
430227825Stheraven}
431227825Stheraven
432227825Stheraven
433227825Stheraven/*
434227825Stheraven * arb_poll - called by the transmit procedure
435227825Stheraven */
436227825Stheravenstatic void
437227825Stheravenarb_poll(
438227825Stheraven	int unit,
439227825Stheraven	struct peer *peer
440227825Stheraven	)
441227825Stheraven{
442227825Stheraven	register struct arbunit *up;
443227825Stheraven	struct refclockproc *pp;
444227825Stheraven
445227825Stheraven	/*
446227825Stheraven	 * Time to poll the clock. The Arbiter clock responds to a "B5"
447227825Stheraven	 * by returning a timecode in the format specified above.
448227825Stheraven	 * Transmission occurs once per second, unless turned off by a
449227825Stheraven	 * "B0". Note there is no checking on state, since this may not
450227825Stheraven	 * be the only customer reading the clock. Only one customer
451227825Stheraven	 * need poll the clock; all others just listen in.
452227825Stheraven	 */
453227825Stheraven	pp = peer->procptr;
454227825Stheraven	up = pp->unitptr;
455227825Stheraven	pp->polls++;
456227825Stheraven	up->tcswitch = 0;
457227825Stheraven	if (write(pp->io.fd, "TQ", 2) != 2)
458227825Stheraven		refclock_report(peer, CEVNT_FAULT);
459227825Stheraven
460227825Stheraven	/*
461227825Stheraven	 * Process median filter samples. If none received, declare a
462227825Stheraven	 * timeout and keep going.
463227825Stheraven	 */
464278724Sdim	if (pp->coderecv == pp->codeproc) {
465278724Sdim		refclock_report(peer, CEVNT_TIMEOUT);
466278724Sdim		return;
467278724Sdim	}
468278724Sdim	refclock_receive(peer);
469278724Sdim	record_clock_stats(&peer->srcadr, pp->a_lastcode);
470278724Sdim#ifdef DEBUG
471278724Sdim	if (debug)
472227825Stheraven		printf("arbiter: timecode %d %s\n",
473227825Stheraven		   pp->lencode, pp->a_lastcode);
474278724Sdim#endif
475278724Sdim}
476278724Sdim
477278724Sdim#else
478int refclock_arbiter_bs;
479#endif /* REFCLOCK */
480