154359Sroberto/*
2182007Sroberto * refclock_wwvb - clock driver for Spectracom WWVB and GPS receivers
354359Sroberto */
454359Sroberto
554359Sroberto#ifdef HAVE_CONFIG_H
654359Sroberto#include <config.h>
754359Sroberto#endif
854359Sroberto
956746Sroberto#if defined(REFCLOCK) && defined(CLOCK_SPECTRACOM)
1054359Sroberto
1154359Sroberto#include "ntpd.h"
1254359Sroberto#include "ntp_io.h"
1354359Sroberto#include "ntp_refclock.h"
1454359Sroberto#include "ntp_calendar.h"
1554359Sroberto#include "ntp_stdlib.h"
1654359Sroberto
1782498Sroberto#include <stdio.h>
1882498Sroberto#include <ctype.h>
1982498Sroberto
20285612Sdelphij#ifdef HAVE_PPSAPI
21285612Sdelphij#include "ppsapi_timepps.h"
22285612Sdelphij#include "refclock_atom.h"
23285612Sdelphij#endif /* HAVE_PPSAPI */
24285612Sdelphij
2554359Sroberto/*
2654359Sroberto * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
2754359Sroberto * Synchronized Clocks and the Netclock/GPS Master Clock. Both the WWVB
2854359Sroberto * and GPS clocks have proven reliable sources of time; however, the
2954359Sroberto * WWVB clocks have proven vulnerable to high ambient conductive RF
3054359Sroberto * interference. The claimed accuracy of the WWVB clocks is 100 us
3154359Sroberto * relative to the broadcast signal, while the claimed accuracy of the
3254359Sroberto * GPS clock is 50 ns; however, in most cases the actual accuracy is
3354359Sroberto * limited by the resolution of the timecode and the latencies of the
3454359Sroberto * serial interface and operating system.
3554359Sroberto *
3654359Sroberto * The WWVB and GPS clocks should be configured for 24-hour display,
3754359Sroberto * AUTO DST off, time zone 0 (UTC), data format 0 or 2 (see below) and
3854359Sroberto * baud rate 9600. If the clock is to used as the source for the IRIG
3954359Sroberto * Audio Decoder (refclock_irig.c in this distribution), it should be
4054359Sroberto * configured for AM IRIG output and IRIG format 1 (IRIG B with
4154359Sroberto * signature control). The GPS clock can be configured either to respond
4254359Sroberto * to a 'T' poll character or left running continuously.
4354359Sroberto *
4454359Sroberto * There are two timecode formats used by these clocks. Format 0, which
4554359Sroberto * is available with both the Netclock/2 and 8170, and format 2, which
4654359Sroberto * is available only with the Netclock/2, specially modified 8170 and
4754359Sroberto * GPS.
4854359Sroberto *
4954359Sroberto * Format 0 (22 ASCII printing characters):
5054359Sroberto *
51182007Sroberto * <cr><lf>i  ddd hh:mm:ss TZ=zz<cr><lf>
5254359Sroberto *
5354359Sroberto *	on-time = first <cr>
5454359Sroberto *	hh:mm:ss = hours, minutes, seconds
5554359Sroberto *	i = synchronization flag (' ' = in synch, '?' = out of synch)
5654359Sroberto *
57285612Sdelphij * The alarm condition is indicated by other than ' ' at i, which occurs
5854359Sroberto * during initial synchronization and when received signal is lost for
5954359Sroberto * about ten hours.
6054359Sroberto *
6154359Sroberto * Format 2 (24 ASCII printing characters):
6254359Sroberto *
6354359Sroberto * <cr><lf>iqyy ddd hh:mm:ss.fff ld
6454359Sroberto *
6554359Sroberto *	on-time = <cr>
6654359Sroberto *	i = synchronization flag (' ' = in synch, '?' = out of synch)
6754359Sroberto *	q = quality indicator (' ' = locked, 'A'...'D' = unlocked)
6854359Sroberto *	yy = year (as broadcast)
6954359Sroberto *	ddd = day of year
7054359Sroberto *	hh:mm:ss.fff = hours, minutes, seconds, milliseconds
7154359Sroberto *
72285612Sdelphij * The alarm condition is indicated by other than ' ' at i, which occurs
7354359Sroberto * during initial synchronization and when received signal is lost for
7454359Sroberto * about ten hours. The unlock condition is indicated by other than ' '
7554359Sroberto * at q.
7654359Sroberto *
7754359Sroberto * The q is normally ' ' when the time error is less than 1 ms and a
7854359Sroberto * character in the set 'A'...'D' when the time error is less than 10,
7954359Sroberto * 100, 500 and greater than 500 ms respectively. The l is normally ' ',
8054359Sroberto * but is set to 'L' early in the month of an upcoming UTC leap second
8154359Sroberto * and reset to ' ' on the first day of the following month. The d is
8254359Sroberto * set to 'S' for standard time 'I' on the day preceding a switch to
8354359Sroberto * daylight time, 'D' for daylight time and 'O' on the day preceding a
8454359Sroberto * switch to standard time. The start bit of the first <cr> is
8554359Sroberto * synchronized to the indicated time as returned.
8654359Sroberto *
8754359Sroberto * This driver does not need to be told which format is in use - it
88182007Sroberto * figures out which one from the length of the message. The driver
89182007Sroberto * makes no attempt to correct for the intrinsic jitter of the radio
90182007Sroberto * itself, which is a known problem with the older radios.
9154359Sroberto *
92285612Sdelphij * PPS Signal Processing
93285612Sdelphij *
94285612Sdelphij * When PPS signal processing is enabled, and when the system clock has
95285612Sdelphij * been set by this or another driver and the PPS signal offset is
96285612Sdelphij * within 0.4 s of the system clock offset, the PPS signal replaces the
97285612Sdelphij * timecode for as long as the PPS signal is active. If for some reason
98285612Sdelphij * the PPS signal fails for one or more poll intervals, the driver
99285612Sdelphij * reverts to the timecode. If the timecode fails for one or more poll
100285612Sdelphij * intervals, the PPS signal is disconnected.
101285612Sdelphij *
10254359Sroberto * Fudge Factors
10354359Sroberto *
10454359Sroberto * This driver can retrieve a table of quality data maintained
10554359Sroberto * internally by the Netclock/2 clock. If flag4 of the fudge
10654359Sroberto * configuration command is set to 1, the driver will retrieve this
107182007Sroberto * table and write it to the clockstats file when the first timecode
10854359Sroberto * message of a new day is received.
109182007Sroberto *
110182007Sroberto * PPS calibration fudge time 1: format 0 .003134, format 2 .004034
11154359Sroberto */
11254359Sroberto/*
11354359Sroberto * Interface definitions
11454359Sroberto */
11554359Sroberto#define	DEVICE		"/dev/wwvb%d" /* device name and unit */
11654359Sroberto#define	SPEED232	B9600	/* uart speed (9600 baud) */
11754359Sroberto#define	PRECISION	(-13)	/* precision assumed (about 100 us) */
118285612Sdelphij#define	PPS_PRECISION	(-13)	/* precision assumed (about 100 us) */
11954359Sroberto#define	REFID		"WWVB"	/* reference ID */
120182007Sroberto#define	DESCRIPTION	"Spectracom WWVB/GPS Receiver" /* WRU */
12154359Sroberto
12254359Sroberto#define	LENWWVB0	22	/* format 0 timecode length */
12354359Sroberto#define	LENWWVB2	24	/* format 2 timecode length */
124285612Sdelphij#define LENWWVB3	29	/* format 3 timecode length */
12554359Sroberto#define MONLIN		15	/* number of monitoring lines */
12654359Sroberto
12754359Sroberto/*
12854359Sroberto * WWVB unit control structure
12954359Sroberto */
13054359Srobertostruct wwvbunit {
131285612Sdelphij#ifdef HAVE_PPSAPI
132285612Sdelphij	struct refclock_atom atom; /* PPSAPI structure */
133285612Sdelphij	int	ppsapi_tried;	/* attempt PPSAPI once */
134285612Sdelphij	int	ppsapi_lit;	/* time_pps_create() worked */
135285612Sdelphij	int	tcount;		/* timecode sample counter */
136285612Sdelphij	int	pcount;		/* PPS sample counter */
137285612Sdelphij#endif /* HAVE_PPSAPI */
138285612Sdelphij	l_fp	laststamp;	/* last <CR> timestamp */
139285612Sdelphij	int	prev_eol_cr;	/* was last EOL <CR> (not <LF>)? */
14054359Sroberto	u_char	lasthour;	/* last hour (for monitor) */
14154359Sroberto	u_char	linect;		/* count ignored lines (for monitor */
14254359Sroberto};
14354359Sroberto
14454359Sroberto/*
14554359Sroberto * Function prototypes
14654359Sroberto */
147285612Sdelphijstatic	int	wwvb_start	(int, struct peer *);
148285612Sdelphijstatic	void	wwvb_shutdown	(int, struct peer *);
149285612Sdelphijstatic	void	wwvb_receive	(struct recvbuf *);
150285612Sdelphijstatic	void	wwvb_poll	(int, struct peer *);
151285612Sdelphijstatic	void	wwvb_timer	(int, struct peer *);
152285612Sdelphij#ifdef HAVE_PPSAPI
153285612Sdelphijstatic	void	wwvb_control	(int, const struct refclockstat *,
154285612Sdelphij				 struct refclockstat *, struct peer *);
155285612Sdelphij#define		WWVB_CONTROL	wwvb_control
156285612Sdelphij#else
157285612Sdelphij#define		WWVB_CONTROL	noentry
158285612Sdelphij#endif /* HAVE_PPSAPI */
15954359Sroberto
16054359Sroberto/*
16154359Sroberto * Transfer vector
16254359Sroberto */
16354359Srobertostruct	refclock refclock_wwvb = {
16454359Sroberto	wwvb_start,		/* start up driver */
16554359Sroberto	wwvb_shutdown,		/* shut down driver */
16654359Sroberto	wwvb_poll,		/* transmit poll message */
167285612Sdelphij	WWVB_CONTROL,		/* fudge set/change notification */
16854359Sroberto	noentry,		/* initialize driver (not used) */
16954359Sroberto	noentry,		/* not used (old wwvb_buginfo) */
170182007Sroberto	wwvb_timer		/* called once per second */
17154359Sroberto};
17254359Sroberto
17354359Sroberto
17454359Sroberto/*
17554359Sroberto * wwvb_start - open the devices and initialize data for processing
17654359Sroberto */
17754359Srobertostatic int
17854359Srobertowwvb_start(
17954359Sroberto	int unit,
18054359Sroberto	struct peer *peer
18154359Sroberto	)
18254359Sroberto{
18354359Sroberto	register struct wwvbunit *up;
18454359Sroberto	struct refclockproc *pp;
18554359Sroberto	int fd;
18654359Sroberto	char device[20];
18754359Sroberto
18854359Sroberto	/*
18954359Sroberto	 * Open serial port. Use CLK line discipline, if available.
19054359Sroberto	 */
191285612Sdelphij	snprintf(device, sizeof(device), DEVICE, unit);
192285612Sdelphij	fd = refclock_open(device, SPEED232, LDISC_CLK);
193285612Sdelphij	if (fd <= 0)
19454359Sroberto		return (0);
19554359Sroberto
19654359Sroberto	/*
19754359Sroberto	 * Allocate and initialize unit structure
19854359Sroberto	 */
199285612Sdelphij	up = emalloc_zero(sizeof(*up));
20054359Sroberto	pp = peer->procptr;
20154359Sroberto	pp->io.clock_recv = wwvb_receive;
202285612Sdelphij	pp->io.srcclock = peer;
20354359Sroberto	pp->io.datalen = 0;
20454359Sroberto	pp->io.fd = fd;
20554359Sroberto	if (!io_addclock(&pp->io)) {
206182007Sroberto		close(fd);
207285612Sdelphij		pp->io.fd = -1;
20854359Sroberto		free(up);
20954359Sroberto		return (0);
21054359Sroberto	}
211285612Sdelphij	pp->unitptr = up;
21254359Sroberto
21354359Sroberto	/*
21454359Sroberto	 * Initialize miscellaneous variables
21554359Sroberto	 */
21654359Sroberto	peer->precision = PRECISION;
21754359Sroberto	pp->clockdesc = DESCRIPTION;
218285612Sdelphij	memcpy(&pp->refid, REFID, 4);
21954359Sroberto	return (1);
22054359Sroberto}
22154359Sroberto
22254359Sroberto
22354359Sroberto/*
22454359Sroberto * wwvb_shutdown - shut down the clock
22554359Sroberto */
22654359Srobertostatic void
22754359Srobertowwvb_shutdown(
22854359Sroberto	int unit,
22954359Sroberto	struct peer *peer
23054359Sroberto	)
23154359Sroberto{
232285612Sdelphij	struct refclockproc *	pp;
233285612Sdelphij	struct wwvbunit *	up;
23454359Sroberto
23554359Sroberto	pp = peer->procptr;
236285612Sdelphij	up = pp->unitptr;
237285612Sdelphij	if (-1 != pp->io.fd)
238285612Sdelphij		io_closeclock(&pp->io);
239285612Sdelphij	if (NULL != up)
240285612Sdelphij		free(up);
24154359Sroberto}
24254359Sroberto
24354359Sroberto
24454359Sroberto/*
24554359Sroberto * wwvb_receive - receive data from the serial interface
24654359Sroberto */
24754359Srobertostatic void
24854359Srobertowwvb_receive(
24954359Sroberto	struct recvbuf *rbufp
25054359Sroberto	)
25154359Sroberto{
25254359Sroberto	struct wwvbunit *up;
25354359Sroberto	struct refclockproc *pp;
25454359Sroberto	struct peer *peer;
25554359Sroberto
25654359Sroberto	l_fp	trtmp;		/* arrival timestamp */
25754359Sroberto	int	tz;		/* time zone */
25854359Sroberto	int	day, month;	/* ddd conversion */
25954359Sroberto	int	temp;		/* int temp */
26054359Sroberto	char	syncchar;	/* synchronization indicator */
26154359Sroberto	char	qualchar;	/* quality indicator */
26254359Sroberto	char	leapchar;	/* leap indicator */
26354359Sroberto	char	dstchar;	/* daylight/standard indicator */
26482498Sroberto	char	tmpchar;	/* trashbin */
26554359Sroberto
26654359Sroberto	/*
26754359Sroberto	 * Initialize pointers and read the timecode and timestamp
26854359Sroberto	 */
269285612Sdelphij	peer = rbufp->recv_peer;
27054359Sroberto	pp = peer->procptr;
271285612Sdelphij	up = pp->unitptr;
27254359Sroberto	temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
27354359Sroberto
27454359Sroberto	/*
27554359Sroberto	 * Note we get a buffer and timestamp for both a <cr> and <lf>,
27654359Sroberto	 * but only the <cr> timestamp is retained. Note: in format 0 on
27754359Sroberto	 * a Netclock/2 or upgraded 8170 the start bit is delayed 100
27854359Sroberto	 * +-50 us relative to the pps; however, on an unmodified 8170
27954359Sroberto	 * the start bit can be delayed up to 10 ms. In format 2 the
28054359Sroberto	 * reading precision is only to the millisecond. Thus, unless
281182007Sroberto	 * you have a PPS gadget and don't have to have the year, format
28254359Sroberto	 * 0 provides the lowest jitter.
283285612Sdelphij	 * Save the timestamp of each <CR> in up->laststamp.  Lines with
284285612Sdelphij	 * no characters occur for every <LF>, and for some <CR>s when
285285612Sdelphij	 * format 0 is used. Format 0 starts and ends each cycle with a
286285612Sdelphij	 * <CR><LF> pair, format 2 starts each cycle with its only pair.
287285612Sdelphij	 * The preceding <CR> is the on-time character for both formats.
288285612Sdelphij	 * The timestamp provided with non-empty lines corresponds to
289285612Sdelphij	 * the <CR> following the timecode, which is ultimately not used
290285612Sdelphij	 * with format 0 and is used for the following timecode for
291285612Sdelphij	 * format 2.
29254359Sroberto	 */
29354359Sroberto	if (temp == 0) {
294285612Sdelphij		if (up->prev_eol_cr) {
295285612Sdelphij			DPRINTF(2, ("wwvb: <LF> @ %s\n",
296285612Sdelphij				    prettydate(&trtmp)));
297285612Sdelphij		} else {
298285612Sdelphij			up->laststamp = trtmp;
299285612Sdelphij			DPRINTF(2, ("wwvb: <CR> @ %s\n",
300285612Sdelphij				    prettydate(&trtmp)));
301285612Sdelphij		}
302285612Sdelphij		up->prev_eol_cr = !up->prev_eol_cr;
30354359Sroberto		return;
30454359Sroberto	}
30554359Sroberto	pp->lencode = temp;
30654359Sroberto	pp->lastrec = up->laststamp;
307285612Sdelphij	up->laststamp = trtmp;
308285612Sdelphij	up->prev_eol_cr = TRUE;
309285612Sdelphij	DPRINTF(2, ("wwvb: code @ %s\n"
310285612Sdelphij		    "       using %s minus one char\n",
311285612Sdelphij		    prettydate(&trtmp), prettydate(&pp->lastrec)));
312285612Sdelphij	if (L_ISZERO(&pp->lastrec))
313285612Sdelphij		return;
31454359Sroberto
31554359Sroberto	/*
31654359Sroberto	 * We get down to business, check the timecode format and decode
31754359Sroberto	 * its contents. This code uses the timecode length to determine
31854359Sroberto	 * format 0, 2 or 3. If the timecode has invalid length or is
31954359Sroberto	 * not in proper format, we declare bad format and exit.
32054359Sroberto	 */
32154359Sroberto	syncchar = qualchar = leapchar = dstchar = ' ';
32254359Sroberto	tz = 0;
32354359Sroberto	switch (pp->lencode) {
32454359Sroberto
325132451Sroberto	case LENWWVB0:
32654359Sroberto
32754359Sroberto		/*
32854359Sroberto		 * Timecode format 0: "I  ddd hh:mm:ss DTZ=nn"
32954359Sroberto		 */
33054359Sroberto		if (sscanf(pp->a_lastcode,
33182498Sroberto		    "%c %3d %2d:%2d:%2d%c%cTZ=%2d",
33254359Sroberto		    &syncchar, &pp->day, &pp->hour, &pp->minute,
333285612Sdelphij		    &pp->second, &tmpchar, &dstchar, &tz) == 8) {
334132451Sroberto			pp->nsec = 0;
33554359Sroberto			break;
336285612Sdelphij		}
337285612Sdelphij		goto bad_format;
33854359Sroberto
339132451Sroberto	case LENWWVB2:
34054359Sroberto
34154359Sroberto		/*
34282498Sroberto		 * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */
34354359Sroberto		if (sscanf(pp->a_lastcode,
344132451Sroberto		    "%c%c %2d %3d %2d:%2d:%2d.%3ld %c",
34554359Sroberto		    &syncchar, &qualchar, &pp->year, &pp->day,
346132451Sroberto		    &pp->hour, &pp->minute, &pp->second, &pp->nsec,
347285612Sdelphij		    &leapchar) == 9) {
348132451Sroberto			pp->nsec *= 1000000;
34954359Sroberto			break;
350285612Sdelphij		}
351285612Sdelphij		goto bad_format;
35254359Sroberto
353132451Sroberto	case LENWWVB3:
35454359Sroberto
355285612Sdelphij		/*
35654359Sroberto		 * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#"
357285612Sdelphij		 * WARNING: Undocumented, and the on-time character # is
358285612Sdelphij		 * not yet handled correctly by this driver.  It may be
359285612Sdelphij		 * as simple as compensating for an additional 1/960 s.
36054359Sroberto		 */
36154359Sroberto		if (sscanf(pp->a_lastcode,
36254359Sroberto		    "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c",
36354359Sroberto		    &syncchar, &pp->year, &month, &day, &pp->hour,
36454359Sroberto		    &pp->minute, &pp->second, &dstchar, &leapchar) == 8)
36554359Sroberto		    {
36654359Sroberto			pp->day = ymd2yd(pp->year, month, day);
367132451Sroberto			pp->nsec = 0;
36854359Sroberto			break;
36954359Sroberto		}
370285612Sdelphij		goto bad_format;
37154359Sroberto
372132451Sroberto	default:
373285612Sdelphij	bad_format:
37454359Sroberto
37554359Sroberto		/*
37654359Sroberto		 * Unknown format: If dumping internal table, record
37754359Sroberto		 * stats; otherwise, declare bad format.
37854359Sroberto		 */
37954359Sroberto		if (up->linect > 0) {
38054359Sroberto			up->linect--;
38154359Sroberto			record_clock_stats(&peer->srcadr,
38254359Sroberto			    pp->a_lastcode);
38354359Sroberto		} else {
38454359Sroberto			refclock_report(peer, CEVNT_BADREPLY);
38554359Sroberto		}
38654359Sroberto		return;
38754359Sroberto	}
38854359Sroberto
38954359Sroberto	/*
39054359Sroberto	 * Decode synchronization, quality and leap characters. If
39154359Sroberto	 * unsynchronized, set the leap bits accordingly and exit.
39254359Sroberto	 * Otherwise, set the leap bits according to the leap character.
39354359Sroberto	 * Once synchronized, the dispersion depends only on the
39454359Sroberto	 * quality character.
39554359Sroberto	 */
39654359Sroberto	switch (qualchar) {
39754359Sroberto
398285612Sdelphij	case ' ':
39954359Sroberto		pp->disp = .001;
400132451Sroberto		pp->lastref = pp->lastrec;
40154359Sroberto		break;
40254359Sroberto
403285612Sdelphij	case 'A':
40454359Sroberto		pp->disp = .01;
40554359Sroberto		break;
40654359Sroberto
407285612Sdelphij	case 'B':
40854359Sroberto		pp->disp = .1;
40954359Sroberto		break;
41054359Sroberto
411285612Sdelphij	case 'C':
41254359Sroberto		pp->disp = .5;
41354359Sroberto		break;
41454359Sroberto
415285612Sdelphij	case 'D':
41654359Sroberto		pp->disp = MAXDISPERSE;
41754359Sroberto		break;
41854359Sroberto
419285612Sdelphij	default:
42054359Sroberto		pp->disp = MAXDISPERSE;
42154359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
42254359Sroberto		break;
42354359Sroberto	}
42454359Sroberto	if (syncchar != ' ')
42554359Sroberto		pp->leap = LEAP_NOTINSYNC;
42654359Sroberto	else if (leapchar == 'L')
42754359Sroberto		pp->leap = LEAP_ADDSECOND;
42854359Sroberto	else
42954359Sroberto		pp->leap = LEAP_NOWARNING;
43054359Sroberto
43154359Sroberto	/*
43254359Sroberto	 * Process the new sample in the median filter and determine the
433285612Sdelphij	 * timecode timestamp, but only if the PPS is not in control.
43454359Sroberto	 */
435285612Sdelphij#ifdef HAVE_PPSAPI
436285612Sdelphij	up->tcount++;
437285612Sdelphij	if (peer->flags & FLAG_PPS)
438285612Sdelphij		return;
439285612Sdelphij
440285612Sdelphij#endif /* HAVE_PPSAPI */
441285612Sdelphij	if (!refclock_process_f(pp, pp->fudgetime2))
44254359Sroberto		refclock_report(peer, CEVNT_BADTIME);
44354359Sroberto}
44454359Sroberto
44554359Sroberto
44654359Sroberto/*
447182007Sroberto * wwvb_timer - called once per second by the transmit procedure
44854359Sroberto */
44954359Srobertostatic void
450182007Srobertowwvb_timer(
45154359Sroberto	int unit,
45254359Sroberto	struct peer *peer
45354359Sroberto	)
45454359Sroberto{
45554359Sroberto	register struct wwvbunit *up;
45654359Sroberto	struct refclockproc *pp;
45754359Sroberto	char	pollchar;	/* character sent to clock */
458285612Sdelphij#ifdef DEBUG
459285612Sdelphij	l_fp	now;
460285612Sdelphij#endif
46154359Sroberto
46254359Sroberto	/*
46354359Sroberto	 * Time to poll the clock. The Spectracom clock responds to a
46454359Sroberto	 * 'T' by returning a timecode in the format(s) specified above.
46554359Sroberto	 * Note there is no checking on state, since this may not be the
46654359Sroberto	 * only customer reading the clock. Only one customer need poll
467182007Sroberto	 * the clock; all others just listen in.
46854359Sroberto	 */
46954359Sroberto	pp = peer->procptr;
470285612Sdelphij	up = pp->unitptr;
47154359Sroberto	if (up->linect > 0)
47254359Sroberto		pollchar = 'R';
47354359Sroberto	else
47454359Sroberto		pollchar = 'T';
47554359Sroberto	if (write(pp->io.fd, &pollchar, 1) != 1)
47654359Sroberto		refclock_report(peer, CEVNT_FAULT);
477285612Sdelphij#ifdef DEBUG
478285612Sdelphij	get_systime(&now);
479285612Sdelphij	if (debug)
480285612Sdelphij		printf("%c poll at %s\n", pollchar, prettydate(&now));
481285612Sdelphij#endif
482285612Sdelphij#ifdef HAVE_PPSAPI
483285612Sdelphij	if (up->ppsapi_lit &&
484285612Sdelphij	    refclock_pps(peer, &up->atom, pp->sloppyclockflag) > 0) {
485285612Sdelphij		up->pcount++,
486285612Sdelphij		peer->flags |= FLAG_PPS;
487285612Sdelphij		peer->precision = PPS_PRECISION;
488285612Sdelphij	}
489285612Sdelphij#endif /* HAVE_PPSAPI */
490182007Sroberto}
491182007Sroberto
492182007Sroberto
493182007Sroberto/*
494182007Sroberto * wwvb_poll - called by the transmit procedure
495182007Sroberto */
496182007Srobertostatic void
497182007Srobertowwvb_poll(
498182007Sroberto	int unit,
499182007Sroberto	struct peer *peer
500182007Sroberto	)
501182007Sroberto{
502182007Sroberto	register struct wwvbunit *up;
503182007Sroberto	struct refclockproc *pp;
504182007Sroberto
505182007Sroberto	/*
506182007Sroberto	 * Sweep up the samples received since the last poll. If none
507182007Sroberto	 * are received, declare a timeout and keep going.
508182007Sroberto	 */
509182007Sroberto	pp = peer->procptr;
510285612Sdelphij	up = pp->unitptr;
511182007Sroberto	pp->polls++;
512182007Sroberto
513182007Sroberto	/*
514182007Sroberto	 * If the monitor flag is set (flag4), we dump the internal
515182007Sroberto	 * quality table at the first timecode beginning the day.
516182007Sroberto	 */
517182007Sroberto	if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour <
518182007Sroberto	    (int)up->lasthour)
519182007Sroberto		up->linect = MONLIN;
520285612Sdelphij	up->lasthour = (u_char)pp->hour;
521182007Sroberto
522182007Sroberto	/*
523182007Sroberto	 * Process median filter samples. If none received, declare a
524182007Sroberto	 * timeout and keep going.
525182007Sroberto	 */
526285612Sdelphij#ifdef HAVE_PPSAPI
527285612Sdelphij	if (up->pcount == 0) {
528285612Sdelphij		peer->flags &= ~FLAG_PPS;
529285612Sdelphij		peer->precision = PRECISION;
530285612Sdelphij	}
531285612Sdelphij	if (up->tcount == 0) {
532285612Sdelphij		pp->coderecv = pp->codeproc;
533285612Sdelphij		refclock_report(peer, CEVNT_TIMEOUT);
534285612Sdelphij		return;
535285612Sdelphij	}
536285612Sdelphij	up->pcount = up->tcount = 0;
537285612Sdelphij#else /* HAVE_PPSAPI */
53854359Sroberto	if (pp->coderecv == pp->codeproc) {
53954359Sroberto		refclock_report(peer, CEVNT_TIMEOUT);
54054359Sroberto		return;
54154359Sroberto	}
542285612Sdelphij#endif /* HAVE_PPSAPI */
543132451Sroberto	refclock_receive(peer);
54454359Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
545132451Sroberto#ifdef DEBUG
546132451Sroberto	if (debug)
547132451Sroberto		printf("wwvb: timecode %d %s\n", pp->lencode,
548132451Sroberto		    pp->a_lastcode);
549132451Sroberto#endif
55054359Sroberto}
55154359Sroberto
552285612Sdelphij
553285612Sdelphij/*
554285612Sdelphij * wwvb_control - fudge parameters have been set or changed
555285612Sdelphij */
556285612Sdelphij#ifdef HAVE_PPSAPI
557285612Sdelphijstatic void
558285612Sdelphijwwvb_control(
559285612Sdelphij	int unit,
560285612Sdelphij	const struct refclockstat *in_st,
561285612Sdelphij	struct refclockstat *out_st,
562285612Sdelphij	struct peer *peer
563285612Sdelphij	)
564285612Sdelphij{
565285612Sdelphij	register struct wwvbunit *up;
566285612Sdelphij	struct refclockproc *pp;
567285612Sdelphij
568285612Sdelphij	pp = peer->procptr;
569285612Sdelphij	up = pp->unitptr;
570285612Sdelphij
571285612Sdelphij	if (!(pp->sloppyclockflag & CLK_FLAG1)) {
572285612Sdelphij		if (!up->ppsapi_tried)
573285612Sdelphij			return;
574285612Sdelphij		up->ppsapi_tried = 0;
575285612Sdelphij		if (!up->ppsapi_lit)
576285612Sdelphij			return;
577285612Sdelphij		peer->flags &= ~FLAG_PPS;
578285612Sdelphij		peer->precision = PRECISION;
579285612Sdelphij		time_pps_destroy(up->atom.handle);
580285612Sdelphij		up->atom.handle = 0;
581285612Sdelphij		up->ppsapi_lit = 0;
582285612Sdelphij		return;
583285612Sdelphij	}
584285612Sdelphij
585285612Sdelphij	if (up->ppsapi_tried)
586285612Sdelphij		return;
587285612Sdelphij	/*
588285612Sdelphij	 * Light up the PPSAPI interface.
589285612Sdelphij	 */
590285612Sdelphij	up->ppsapi_tried = 1;
591285612Sdelphij	if (refclock_ppsapi(pp->io.fd, &up->atom)) {
592285612Sdelphij		up->ppsapi_lit = 1;
593285612Sdelphij		return;
594285612Sdelphij	}
595285612Sdelphij
596285612Sdelphij	msyslog(LOG_WARNING, "%s flag1 1 but PPSAPI fails",
597285612Sdelphij		refnumtoa(&peer->srcadr));
598285612Sdelphij}
599285612Sdelphij#endif	/* HAVE_PPSAPI */
600285612Sdelphij
60154359Sroberto#else
60254359Srobertoint refclock_wwvb_bs;
60354359Sroberto#endif /* REFCLOCK */
604