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