154359Sroberto/*
254359Sroberto * refclock_hpgps - clock driver for HP 58503A GPS receiver
354359Sroberto */
482498Sroberto
554359Sroberto#ifdef HAVE_CONFIG_H
682498Sroberto# include <config.h>
754359Sroberto#endif
854359Sroberto
954359Sroberto#if defined(REFCLOCK) && defined(CLOCK_HPGPS)
1054359Sroberto
1154359Sroberto#include "ntpd.h"
1254359Sroberto#include "ntp_io.h"
1354359Sroberto#include "ntp_refclock.h"
1454359Sroberto#include "ntp_stdlib.h"
1554359Sroberto
1682498Sroberto#include <stdio.h>
1782498Sroberto#include <ctype.h>
1882498Sroberto
1954359Sroberto/* Version 0.1 April  1, 1995
2054359Sroberto *         0.2 April 25, 1995
2154359Sroberto *             tolerant of missing timecode response prompt and sends
2254359Sroberto *             clear status if prompt indicates error;
2354359Sroberto *             can use either local time or UTC from receiver;
2454359Sroberto *             can get receiver status screen via flag4
2554359Sroberto *
2654359Sroberto * WARNING!: This driver is UNDER CONSTRUCTION
2754359Sroberto * Everything in here should be treated with suspicion.
2854359Sroberto * If it looks wrong, it probably is.
2954359Sroberto *
3054359Sroberto * Comments and/or questions to: Dave Vitanye
3154359Sroberto *                               Hewlett Packard Company
3254359Sroberto *                               dave@scd.hp.com
3354359Sroberto *                               (408) 553-2856
3454359Sroberto *
3554359Sroberto * Thanks to the author of the PST driver, which was the starting point for
3654359Sroberto * this one.
3754359Sroberto *
3854359Sroberto * This driver supports the HP 58503A Time and Frequency Reference Receiver.
3954359Sroberto * This receiver uses HP SmartClock (TM) to implement an Enhanced GPS receiver.
4054359Sroberto * The receiver accuracy when locked to GPS in normal operation is better
4154359Sroberto * than 1 usec. The accuracy when operating in holdover is typically better
4254359Sroberto * than 10 usec. per day.
4354359Sroberto *
44182007Sroberto * The same driver also handles the HP Z3801A which is available surplus
45182007Sroberto * from the cell phone industry.  It's popular with hams.
46182007Sroberto * It needs a different line setup: 19200 baud, 7 data bits, odd parity
47182007Sroberto * That is selected by adding "mode 1" to the server line in ntp.conf
48182007Sroberto * HP Z3801A code from Jeff Mock added by Hal Murray, Sep 2005
49182007Sroberto *
50182007Sroberto *
5154359Sroberto * The receiver should be operated with factory default settings.
5254359Sroberto * Initial driver operation: expects the receiver to be already locked
5354359Sroberto * to GPS, configured and able to output timecode format 2 messages.
5454359Sroberto *
5554359Sroberto * The driver uses the poll sequence :PTIME:TCODE? to get a response from
5654359Sroberto * the receiver. The receiver responds with a timecode string of ASCII
5754359Sroberto * printing characters, followed by a <cr><lf>, followed by a prompt string
5854359Sroberto * issued by the receiver, in the following format:
5954359Sroberto * T#yyyymmddhhmmssMFLRVcc<cr><lf>scpi >
6054359Sroberto *
6154359Sroberto * The driver processes the response at the <cr> and <lf>, so what the
6254359Sroberto * driver sees is the prompt from the previous poll, followed by this
6354359Sroberto * timecode. The prompt from the current poll is (usually) left unread until
6454359Sroberto * the next poll. So (except on the very first poll) the driver sees this:
6554359Sroberto *
6654359Sroberto * scpi > T#yyyymmddhhmmssMFLRVcc<cr><lf>
6754359Sroberto *
6854359Sroberto * The T is the on-time character, at 980 msec. before the next 1PPS edge.
6954359Sroberto * The # is the timecode format type. We look for format 2.
7054359Sroberto * Without any of the CLK or PPS stuff, then, the receiver buffer timestamp
7154359Sroberto * at the <cr> is 24 characters later, which is about 25 msec. at 9600 bps,
7254359Sroberto * so the first approximation for fudge time1 is nominally -0.955 seconds.
7354359Sroberto * This number probably needs adjusting for each machine / OS type, so far:
7454359Sroberto *  -0.955000 on an HP 9000 Model 712/80 HP-UX 9.05
7554359Sroberto *  -0.953175 on an HP 9000 Model 370    HP-UX 9.10
7654359Sroberto *
7754359Sroberto * This receiver also provides a 1PPS signal, but I haven't figured out
7854359Sroberto * how to deal with any of the CLK or PPS stuff yet. Stay tuned.
7954359Sroberto *
8054359Sroberto */
8154359Sroberto
8254359Sroberto/*
8354359Sroberto * Fudge Factors
8454359Sroberto *
8554359Sroberto * Fudge time1 is used to accomodate the timecode serial interface adjustment.
8654359Sroberto * Fudge flag4 can be set to request a receiver status screen summary, which
8754359Sroberto * is recorded in the clockstats file.
8854359Sroberto */
8954359Sroberto
9054359Sroberto/*
9154359Sroberto * Interface definitions
9254359Sroberto */
9354359Sroberto#define	DEVICE		"/dev/hpgps%d" /* device name and unit */
9454359Sroberto#define	SPEED232	B9600	/* uart speed (9600 baud) */
95182007Sroberto#define	SPEED232Z	B19200	/* uart speed (19200 baud) */
9654359Sroberto#define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
9754359Sroberto#define	REFID		"GPS\0"	/*  reference ID */
9854359Sroberto#define	DESCRIPTION	"HP 58503A GPS Time and Frequency Reference Receiver"
9954359Sroberto
10054359Sroberto#define SMAX            23*80+1 /* for :SYSTEM:PRINT? status screen response */
10154359Sroberto
10254359Sroberto#define MTZONE          2       /* number of fields in timezone reply */
10354359Sroberto#define MTCODET2        12      /* number of fields in timecode format T2 */
10454359Sroberto#define NTCODET2        21      /* number of chars to checksum in format T2 */
10554359Sroberto
10654359Sroberto/*
10754359Sroberto * Tables to compute the day of year from yyyymmdd timecode.
10854359Sroberto * Viva la leap.
10954359Sroberto */
11054359Srobertostatic int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
11154359Srobertostatic int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
11254359Sroberto
11354359Sroberto/*
11454359Sroberto * Unit control structure
11554359Sroberto */
11654359Srobertostruct hpgpsunit {
11754359Sroberto	int	pollcnt;	/* poll message counter */
11854359Sroberto	int     tzhour;         /* timezone offset, hours */
11954359Sroberto	int     tzminute;       /* timezone offset, minutes */
12054359Sroberto	int     linecnt;        /* set for expected multiple line responses */
12154359Sroberto	char	*lastptr;	/* pointer to receiver response data */
12254359Sroberto	char    statscrn[SMAX]; /* receiver status screen buffer */
12354359Sroberto};
12454359Sroberto
12554359Sroberto/*
12654359Sroberto * Function prototypes
12754359Sroberto */
128280849Scystatic	int	hpgps_start	(int, struct peer *);
129280849Scystatic	void	hpgps_shutdown	(int, struct peer *);
130280849Scystatic	void	hpgps_receive	(struct recvbuf *);
131280849Scystatic	void	hpgps_poll	(int, struct peer *);
13254359Sroberto
13354359Sroberto/*
13454359Sroberto * Transfer vector
13554359Sroberto */
13654359Srobertostruct	refclock refclock_hpgps = {
13754359Sroberto	hpgps_start,		/* start up driver */
13854359Sroberto	hpgps_shutdown,		/* shut down driver */
13954359Sroberto	hpgps_poll,		/* transmit poll message */
14054359Sroberto	noentry,		/* not used (old hpgps_control) */
14154359Sroberto	noentry,		/* initialize driver */
14254359Sroberto	noentry,		/* not used (old hpgps_buginfo) */
14354359Sroberto	NOFLAGS			/* not used */
14454359Sroberto};
14554359Sroberto
14654359Sroberto
14754359Sroberto/*
14854359Sroberto * hpgps_start - open the devices and initialize data for processing
14954359Sroberto */
15054359Srobertostatic int
15154359Srobertohpgps_start(
15254359Sroberto	int unit,
15354359Sroberto	struct peer *peer
15454359Sroberto	)
15554359Sroberto{
15654359Sroberto	register struct hpgpsunit *up;
15754359Sroberto	struct refclockproc *pp;
15854359Sroberto	int fd;
159280849Scy	int speed, ldisc;
16054359Sroberto	char device[20];
16154359Sroberto
16254359Sroberto	/*
16354359Sroberto	 * Open serial port. Use CLK line discipline, if available.
164182007Sroberto	 * Default is HP 58503A, mode arg selects HP Z3801A
16554359Sroberto	 */
166280849Scy	snprintf(device, sizeof(device), DEVICE, unit);
167280849Scy	ldisc = LDISC_CLK;
168280849Scy	speed = SPEED232;
169182007Sroberto	/* mode parameter to server config line shares ttl slot */
170280849Scy	if (1 == peer->ttl) {
171280849Scy		ldisc |= LDISC_7O1;
172280849Scy		speed = SPEED232Z;
173182007Sroberto	}
174280849Scy	fd = refclock_open(device, speed, ldisc);
175280849Scy	if (fd <= 0)
176280849Scy		return (0);
17754359Sroberto	/*
17854359Sroberto	 * Allocate and initialize unit structure
17954359Sroberto	 */
180280849Scy	up = emalloc_zero(sizeof(*up));
18154359Sroberto	pp = peer->procptr;
18254359Sroberto	pp->io.clock_recv = hpgps_receive;
183280849Scy	pp->io.srcclock = peer;
18454359Sroberto	pp->io.datalen = 0;
18554359Sroberto	pp->io.fd = fd;
18654359Sroberto	if (!io_addclock(&pp->io)) {
187280849Scy		close(fd);
188280849Scy		pp->io.fd = -1;
18954359Sroberto		free(up);
19054359Sroberto		return (0);
19154359Sroberto	}
192280849Scy	pp->unitptr = up;
19354359Sroberto
19454359Sroberto	/*
19554359Sroberto	 * Initialize miscellaneous variables
19654359Sroberto	 */
19754359Sroberto	peer->precision = PRECISION;
19854359Sroberto	pp->clockdesc = DESCRIPTION;
19954359Sroberto	memcpy((char *)&pp->refid, REFID, 4);
20054359Sroberto	up->tzhour = 0;
20154359Sroberto	up->tzminute = 0;
20254359Sroberto
20354359Sroberto	*up->statscrn = '\0';
20454359Sroberto	up->lastptr = up->statscrn;
20554359Sroberto	up->pollcnt = 2;
20654359Sroberto
20754359Sroberto	/*
20854359Sroberto	 * Get the identifier string, which is logged but otherwise ignored,
20954359Sroberto	 * and get the local timezone information
21054359Sroberto	 */
21154359Sroberto	up->linecnt = 1;
21254359Sroberto	if (write(pp->io.fd, "*IDN?\r:PTIME:TZONE?\r", 20) != 20)
21354359Sroberto	    refclock_report(peer, CEVNT_FAULT);
21454359Sroberto
21554359Sroberto	return (1);
21654359Sroberto}
21754359Sroberto
21854359Sroberto
21954359Sroberto/*
22054359Sroberto * hpgps_shutdown - shut down the clock
22154359Sroberto */
22254359Srobertostatic void
22354359Srobertohpgps_shutdown(
22454359Sroberto	int unit,
22554359Sroberto	struct peer *peer
22654359Sroberto	)
22754359Sroberto{
22854359Sroberto	register struct hpgpsunit *up;
22954359Sroberto	struct refclockproc *pp;
23054359Sroberto
23154359Sroberto	pp = peer->procptr;
232280849Scy	up = pp->unitptr;
233280849Scy	if (-1 != pp->io.fd)
234280849Scy		io_closeclock(&pp->io);
235280849Scy	if (NULL != up)
236280849Scy		free(up);
23754359Sroberto}
23854359Sroberto
23954359Sroberto
24054359Sroberto/*
24154359Sroberto * hpgps_receive - receive data from the serial interface
24254359Sroberto */
24354359Srobertostatic void
24454359Srobertohpgps_receive(
24554359Sroberto	struct recvbuf *rbufp
24654359Sroberto	)
24754359Sroberto{
24854359Sroberto	register struct hpgpsunit *up;
24954359Sroberto	struct refclockproc *pp;
25054359Sroberto	struct peer *peer;
25154359Sroberto	l_fp trtmp;
25254359Sroberto	char tcodechar1;        /* identifies timecode format */
25354359Sroberto	char tcodechar2;        /* identifies timecode format */
25454359Sroberto	char timequal;          /* time figure of merit: 0-9 */
25554359Sroberto	char freqqual;          /* frequency figure of merit: 0-3 */
25654359Sroberto	char leapchar;          /* leapsecond: + or 0 or - */
25754359Sroberto	char servchar;          /* request for service: 0 = no, 1 = yes */
25854359Sroberto	char syncchar;          /* time info is invalid: 0 = no, 1 = yes */
25954359Sroberto	short expectedsm;       /* expected timecode byte checksum */
26054359Sroberto	short tcodechksm;       /* computed timecode byte checksum */
26154359Sroberto	int i,m,n;
26254359Sroberto	int month, day, lastday;
26354359Sroberto	char *tcp;              /* timecode pointer (skips over the prompt) */
26454359Sroberto	char prompt[BMAX];      /* prompt in response from receiver */
26554359Sroberto
26654359Sroberto	/*
26754359Sroberto	 * Initialize pointers and read the receiver response
26854359Sroberto	 */
269280849Scy	peer = rbufp->recv_peer;
27054359Sroberto	pp = peer->procptr;
271280849Scy	up = pp->unitptr;
27254359Sroberto	*pp->a_lastcode = '\0';
27354359Sroberto	pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
27454359Sroberto
27554359Sroberto#ifdef DEBUG
27654359Sroberto	if (debug)
27754359Sroberto	    printf("hpgps: lencode: %d timecode:%s\n",
27854359Sroberto		   pp->lencode, pp->a_lastcode);
27954359Sroberto#endif
28054359Sroberto
28154359Sroberto	/*
28254359Sroberto	 * If there's no characters in the reply, we can quit now
28354359Sroberto	 */
28454359Sroberto	if (pp->lencode == 0)
28554359Sroberto	    return;
28654359Sroberto
28754359Sroberto	/*
28854359Sroberto	 * If linecnt is greater than zero, we are getting information only,
28954359Sroberto	 * such as the receiver identification string or the receiver status
29054359Sroberto	 * screen, so put the receiver response at the end of the status
29154359Sroberto	 * screen buffer. When we have the last line, write the buffer to
29254359Sroberto	 * the clockstats file and return without further processing.
29354359Sroberto	 *
29454359Sroberto	 * If linecnt is zero, we are expecting either the timezone
29554359Sroberto	 * or a timecode. At this point, also write the response
29654359Sroberto	 * to the clockstats file, and go on to process the prompt (if any),
29754359Sroberto	 * timezone, or timecode and timestamp.
29854359Sroberto	 */
29954359Sroberto
30054359Sroberto
30154359Sroberto	if (up->linecnt-- > 0) {
30254359Sroberto		if ((int)(pp->lencode + 2) <= (SMAX - (up->lastptr - up->statscrn))) {
30354359Sroberto			*up->lastptr++ = '\n';
304280849Scy			memcpy(up->lastptr, pp->a_lastcode, pp->lencode);
30554359Sroberto			up->lastptr += pp->lencode;
30654359Sroberto		}
30754359Sroberto		if (up->linecnt == 0)
30854359Sroberto		    record_clock_stats(&peer->srcadr, up->statscrn);
30954359Sroberto
31054359Sroberto		return;
31154359Sroberto	}
31254359Sroberto
31354359Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
31454359Sroberto	pp->lastrec = trtmp;
31554359Sroberto
31654359Sroberto	up->lastptr = up->statscrn;
31754359Sroberto	*up->lastptr = '\0';
31854359Sroberto	up->pollcnt = 2;
31954359Sroberto
32054359Sroberto	/*
32154359Sroberto	 * We get down to business: get a prompt if one is there, issue
32254359Sroberto	 * a clear status command if it contains an error indication.
32354359Sroberto	 * Next, check for either the timezone reply or the timecode reply
32454359Sroberto	 * and decode it.  If we don't recognize the reply, or don't get the
32554359Sroberto	 * proper number of decoded fields, or get an out of range timezone,
32654359Sroberto	 * or if the timecode checksum is bad, then we declare bad format
32754359Sroberto	 * and exit.
32854359Sroberto	 *
32954359Sroberto	 * Timezone format (including nominal prompt):
33054359Sroberto	 * scpi > -H,-M<cr><lf>
33154359Sroberto	 *
33254359Sroberto	 * Timecode format (including nominal prompt):
33354359Sroberto	 * scpi > T2yyyymmddhhmmssMFLRVcc<cr><lf>
33454359Sroberto	 *
33554359Sroberto	 */
33654359Sroberto
337280849Scy	strlcpy(prompt, pp->a_lastcode, sizeof(prompt));
33854359Sroberto	tcp = strrchr(pp->a_lastcode,'>');
33954359Sroberto	if (tcp == NULL)
34054359Sroberto	    tcp = pp->a_lastcode;
34154359Sroberto	else
34254359Sroberto	    tcp++;
34354359Sroberto	prompt[tcp - pp->a_lastcode] = '\0';
34454359Sroberto	while ((*tcp == ' ') || (*tcp == '\t')) tcp++;
34554359Sroberto
34654359Sroberto	/*
34754359Sroberto	 * deal with an error indication in the prompt here
34854359Sroberto	 */
34954359Sroberto	if (strrchr(prompt,'E') > strrchr(prompt,'s')){
35054359Sroberto#ifdef DEBUG
35154359Sroberto		if (debug)
35254359Sroberto		    printf("hpgps: error indicated in prompt: %s\n", prompt);
35354359Sroberto#endif
35454359Sroberto		if (write(pp->io.fd, "*CLS\r\r", 6) != 6)
35554359Sroberto		    refclock_report(peer, CEVNT_FAULT);
35654359Sroberto	}
35754359Sroberto
35854359Sroberto	/*
35954359Sroberto	 * make sure we got a timezone or timecode format and
36054359Sroberto	 * then process accordingly
36154359Sroberto	 */
36254359Sroberto	m = sscanf(tcp,"%c%c", &tcodechar1, &tcodechar2);
36354359Sroberto
36454359Sroberto	if (m != 2){
36554359Sroberto#ifdef DEBUG
36654359Sroberto		if (debug)
36754359Sroberto		    printf("hpgps: no format indicator\n");
36854359Sroberto#endif
36954359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
37054359Sroberto		return;
37154359Sroberto	}
37254359Sroberto
37354359Sroberto	switch (tcodechar1) {
37454359Sroberto
37554359Sroberto	    case '+':
37654359Sroberto	    case '-':
37754359Sroberto		m = sscanf(tcp,"%d,%d", &up->tzhour, &up->tzminute);
37854359Sroberto		if (m != MTZONE) {
37954359Sroberto#ifdef DEBUG
38054359Sroberto			if (debug)
38154359Sroberto			    printf("hpgps: only %d fields recognized in timezone\n", m);
38254359Sroberto#endif
38354359Sroberto			refclock_report(peer, CEVNT_BADREPLY);
38454359Sroberto			return;
38554359Sroberto		}
38654359Sroberto		if ((up->tzhour < -12) || (up->tzhour > 13) ||
38754359Sroberto		    (up->tzminute < -59) || (up->tzminute > 59)){
38854359Sroberto#ifdef DEBUG
38954359Sroberto			if (debug)
39054359Sroberto			    printf("hpgps: timezone %d, %d out of range\n",
39154359Sroberto				   up->tzhour, up->tzminute);
39254359Sroberto#endif
39354359Sroberto			refclock_report(peer, CEVNT_BADREPLY);
39454359Sroberto			return;
39554359Sroberto		}
39654359Sroberto		return;
39754359Sroberto
39854359Sroberto	    case 'T':
39954359Sroberto		break;
40054359Sroberto
40154359Sroberto	    default:
40254359Sroberto#ifdef DEBUG
40354359Sroberto		if (debug)
40454359Sroberto		    printf("hpgps: unrecognized reply format %c%c\n",
40554359Sroberto			   tcodechar1, tcodechar2);
40654359Sroberto#endif
40754359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
40854359Sroberto		return;
40954359Sroberto	} /* end of tcodechar1 switch */
41054359Sroberto
41154359Sroberto
41254359Sroberto	switch (tcodechar2) {
41354359Sroberto
41454359Sroberto	    case '2':
41554359Sroberto		m = sscanf(tcp,"%*c%*c%4d%2d%2d%2d%2d%2d%c%c%c%c%c%2hx",
41654359Sroberto			   &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second,
41754359Sroberto			   &timequal, &freqqual, &leapchar, &servchar, &syncchar,
41854359Sroberto			   &expectedsm);
41954359Sroberto		n = NTCODET2;
42054359Sroberto
42154359Sroberto		if (m != MTCODET2){
42254359Sroberto#ifdef DEBUG
42354359Sroberto			if (debug)
42454359Sroberto			    printf("hpgps: only %d fields recognized in timecode\n", m);
42554359Sroberto#endif
42654359Sroberto			refclock_report(peer, CEVNT_BADREPLY);
42754359Sroberto			return;
42854359Sroberto		}
42954359Sroberto		break;
43054359Sroberto
43154359Sroberto	    default:
43254359Sroberto#ifdef DEBUG
43354359Sroberto		if (debug)
43454359Sroberto		    printf("hpgps: unrecognized timecode format %c%c\n",
43554359Sroberto			   tcodechar1, tcodechar2);
43654359Sroberto#endif
43754359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
43854359Sroberto		return;
43954359Sroberto	} /* end of tcodechar2 format switch */
44054359Sroberto
44154359Sroberto	/*
44254359Sroberto	 * Compute and verify the checksum.
44354359Sroberto	 * Characters are summed starting at tcodechar1, ending at just
44454359Sroberto	 * before the expected checksum.  Bail out if incorrect.
44554359Sroberto	 */
44654359Sroberto	tcodechksm = 0;
44754359Sroberto	while (n-- > 0) tcodechksm += *tcp++;
44854359Sroberto	tcodechksm &= 0x00ff;
44954359Sroberto
45054359Sroberto	if (tcodechksm != expectedsm) {
45154359Sroberto#ifdef DEBUG
45254359Sroberto		if (debug)
45354359Sroberto		    printf("hpgps: checksum %2hX doesn't match %2hX expected\n",
45454359Sroberto			   tcodechksm, expectedsm);
45554359Sroberto#endif
45654359Sroberto		refclock_report(peer, CEVNT_BADREPLY);
45754359Sroberto		return;
45854359Sroberto	}
45954359Sroberto
46054359Sroberto	/*
46154359Sroberto	 * Compute the day of year from the yyyymmdd format.
46254359Sroberto	 */
46354359Sroberto	if (month < 1 || month > 12 || day < 1) {
46454359Sroberto		refclock_report(peer, CEVNT_BADTIME);
46554359Sroberto		return;
46654359Sroberto	}
46754359Sroberto
46854359Sroberto	if ( ! isleap_4(pp->year) ) {				/* Y2KFixes */
46954359Sroberto		/* not a leap year */
47054359Sroberto		if (day > day1tab[month - 1]) {
47154359Sroberto			refclock_report(peer, CEVNT_BADTIME);
47254359Sroberto			return;
47354359Sroberto		}
47454359Sroberto		for (i = 0; i < month - 1; i++) day += day1tab[i];
47554359Sroberto		lastday = 365;
47654359Sroberto	} else {
47754359Sroberto		/* a leap year */
47854359Sroberto		if (day > day2tab[month - 1]) {
47954359Sroberto			refclock_report(peer, CEVNT_BADTIME);
48054359Sroberto			return;
48154359Sroberto		}
48254359Sroberto		for (i = 0; i < month - 1; i++) day += day2tab[i];
48354359Sroberto		lastday = 366;
48454359Sroberto	}
48554359Sroberto
48654359Sroberto	/*
48754359Sroberto	 * Deal with the timezone offset here. The receiver timecode is in
48854359Sroberto	 * local time = UTC + :PTIME:TZONE, so SUBTRACT the timezone values.
48954359Sroberto	 * For example, Pacific Standard Time is -8 hours , 0 minutes.
49054359Sroberto	 * Deal with the underflows and overflows.
49154359Sroberto	 */
49254359Sroberto	pp->minute -= up->tzminute;
49354359Sroberto	pp->hour -= up->tzhour;
49454359Sroberto
49554359Sroberto	if (pp->minute < 0) {
49654359Sroberto		pp->minute += 60;
49754359Sroberto		pp->hour--;
49854359Sroberto	}
49954359Sroberto	if (pp->minute > 59) {
50054359Sroberto		pp->minute -= 60;
50154359Sroberto		pp->hour++;
50254359Sroberto	}
50354359Sroberto	if (pp->hour < 0)  {
50454359Sroberto		pp->hour += 24;
50554359Sroberto		day--;
50654359Sroberto		if (day < 1) {
50754359Sroberto			pp->year--;
50854359Sroberto			if ( isleap_4(pp->year) )		/* Y2KFixes */
50954359Sroberto			    day = 366;
51054359Sroberto			else
51154359Sroberto			    day = 365;
51254359Sroberto		}
51354359Sroberto	}
51454359Sroberto
51554359Sroberto	if (pp->hour > 23) {
51654359Sroberto		pp->hour -= 24;
51754359Sroberto		day++;
51854359Sroberto		if (day > lastday) {
51954359Sroberto			pp->year++;
52054359Sroberto			day = 1;
52154359Sroberto		}
52254359Sroberto	}
52354359Sroberto
52454359Sroberto	pp->day = day;
52554359Sroberto
52654359Sroberto	/*
52754359Sroberto	 * Decode the MFLRV indicators.
52854359Sroberto	 * NEED TO FIGURE OUT how to deal with the request for service,
52954359Sroberto	 * time quality, and frequency quality indicators some day.
53054359Sroberto	 */
53154359Sroberto	if (syncchar != '0') {
53254359Sroberto		pp->leap = LEAP_NOTINSYNC;
53354359Sroberto	}
53454359Sroberto	else {
535280849Scy		pp->leap = LEAP_NOWARNING;
53654359Sroberto		switch (leapchar) {
53754359Sroberto
538280849Scy		    case '0':
53954359Sroberto			break;
54054359Sroberto
541280849Scy		    /* See http://bugs.ntp.org/1090
542280849Scy		     * Ignore leap announcements unless June or December.
543280849Scy		     * Better would be to use :GPSTime? to find the month,
544280849Scy		     * but that seems too likely to introduce other bugs.
545280849Scy		     */
546280849Scy		    case '+':
547280849Scy			if ((month==6) || (month==12))
548280849Scy			    pp->leap = LEAP_ADDSECOND;
54954359Sroberto			break;
55054359Sroberto
55154359Sroberto		    case '-':
552280849Scy			if ((month==6) || (month==12))
553280849Scy			    pp->leap = LEAP_DELSECOND;
55454359Sroberto			break;
55554359Sroberto
55654359Sroberto		    default:
55754359Sroberto#ifdef DEBUG
55854359Sroberto			if (debug)
55954359Sroberto			    printf("hpgps: unrecognized leap indicator: %c\n",
56054359Sroberto				   leapchar);
56154359Sroberto#endif
56254359Sroberto			refclock_report(peer, CEVNT_BADTIME);
56354359Sroberto			return;
56454359Sroberto		} /* end of leapchar switch */
56554359Sroberto	}
56654359Sroberto
56754359Sroberto	/*
56854359Sroberto	 * Process the new sample in the median filter and determine the
56954359Sroberto	 * reference clock offset and dispersion. We use lastrec as both
57054359Sroberto	 * the reference time and receive time in order to avoid being
57154359Sroberto	 * cute, like setting the reference time later than the receive
57254359Sroberto	 * time, which may cause a paranoid protocol module to chuck out
57354359Sroberto	 * the data.
57454359Sroberto	 */
57554359Sroberto	if (!refclock_process(pp)) {
57654359Sroberto		refclock_report(peer, CEVNT_BADTIME);
57754359Sroberto		return;
57854359Sroberto	}
579132451Sroberto	pp->lastref = pp->lastrec;
58054359Sroberto	refclock_receive(peer);
58154359Sroberto
58254359Sroberto	/*
58354359Sroberto	 * If CLK_FLAG4 is set, ask for the status screen response.
58454359Sroberto	 */
58554359Sroberto	if (pp->sloppyclockflag & CLK_FLAG4){
58654359Sroberto		up->linecnt = 22;
58754359Sroberto		if (write(pp->io.fd, ":SYSTEM:PRINT?\r", 15) != 15)
58854359Sroberto		    refclock_report(peer, CEVNT_FAULT);
58954359Sroberto	}
59054359Sroberto}
59154359Sroberto
59254359Sroberto
59354359Sroberto/*
59454359Sroberto * hpgps_poll - called by the transmit procedure
59554359Sroberto */
59654359Srobertostatic void
59754359Srobertohpgps_poll(
59854359Sroberto	int unit,
59954359Sroberto	struct peer *peer
60054359Sroberto	)
60154359Sroberto{
60254359Sroberto	register struct hpgpsunit *up;
60354359Sroberto	struct refclockproc *pp;
60454359Sroberto
60554359Sroberto	/*
60654359Sroberto	 * Time to poll the clock. The HP 58503A responds to a
60754359Sroberto	 * ":PTIME:TCODE?" by returning a timecode in the format specified
60854359Sroberto	 * above. If nothing is heard from the clock for two polls,
60954359Sroberto	 * declare a timeout and keep going.
61054359Sroberto	 */
61154359Sroberto	pp = peer->procptr;
612280849Scy	up = pp->unitptr;
61354359Sroberto	if (up->pollcnt == 0)
61454359Sroberto	    refclock_report(peer, CEVNT_TIMEOUT);
61554359Sroberto	else
61654359Sroberto	    up->pollcnt--;
61754359Sroberto	if (write(pp->io.fd, ":PTIME:TCODE?\r", 14) != 14) {
61854359Sroberto		refclock_report(peer, CEVNT_FAULT);
61954359Sroberto	}
62054359Sroberto	else
62154359Sroberto	    pp->polls++;
62254359Sroberto}
62354359Sroberto
62454359Sroberto#else
62554359Srobertoint refclock_hpgps_bs;
62654359Sroberto#endif /* REFCLOCK */
627