1c0b746e5SOllivier Robert /* 2c0b746e5SOllivier Robert * refclock_hpgps - clock driver for HP 58503A GPS receiver 3c0b746e5SOllivier Robert */ 4224ba2bdSOllivier Robert 5c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 6c0b746e5SOllivier Robert # include <config.h> 7c0b746e5SOllivier Robert #endif 8c0b746e5SOllivier Robert 9c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_HPGPS) 10c0b746e5SOllivier Robert 11c0b746e5SOllivier Robert #include "ntpd.h" 12c0b746e5SOllivier Robert #include "ntp_io.h" 13c0b746e5SOllivier Robert #include "ntp_refclock.h" 14c0b746e5SOllivier Robert #include "ntp_stdlib.h" 15c0b746e5SOllivier Robert 16224ba2bdSOllivier Robert #include <stdio.h> 17224ba2bdSOllivier Robert #include <ctype.h> 18224ba2bdSOllivier Robert 19c0b746e5SOllivier Robert /* Version 0.1 April 1, 1995 20c0b746e5SOllivier Robert * 0.2 April 25, 1995 21c0b746e5SOllivier Robert * tolerant of missing timecode response prompt and sends 22c0b746e5SOllivier Robert * clear status if prompt indicates error; 23c0b746e5SOllivier Robert * can use either local time or UTC from receiver; 24c0b746e5SOllivier Robert * can get receiver status screen via flag4 25c0b746e5SOllivier Robert * 26c0b746e5SOllivier Robert * WARNING!: This driver is UNDER CONSTRUCTION 27c0b746e5SOllivier Robert * Everything in here should be treated with suspicion. 28c0b746e5SOllivier Robert * If it looks wrong, it probably is. 29c0b746e5SOllivier Robert * 30c0b746e5SOllivier Robert * Comments and/or questions to: Dave Vitanye 31c0b746e5SOllivier Robert * Hewlett Packard Company 32c0b746e5SOllivier Robert * dave@scd.hp.com 33c0b746e5SOllivier Robert * (408) 553-2856 34c0b746e5SOllivier Robert * 35c0b746e5SOllivier Robert * Thanks to the author of the PST driver, which was the starting point for 36c0b746e5SOllivier Robert * this one. 37c0b746e5SOllivier Robert * 38c0b746e5SOllivier Robert * This driver supports the HP 58503A Time and Frequency Reference Receiver. 39c0b746e5SOllivier Robert * This receiver uses HP SmartClock (TM) to implement an Enhanced GPS receiver. 40c0b746e5SOllivier Robert * The receiver accuracy when locked to GPS in normal operation is better 41c0b746e5SOllivier Robert * than 1 usec. The accuracy when operating in holdover is typically better 42c0b746e5SOllivier Robert * than 10 usec. per day. 43c0b746e5SOllivier Robert * 44ea906c41SOllivier Robert * The same driver also handles the HP Z3801A which is available surplus 45ea906c41SOllivier Robert * from the cell phone industry. It's popular with hams. 46ea906c41SOllivier Robert * It needs a different line setup: 19200 baud, 7 data bits, odd parity 47ea906c41SOllivier Robert * That is selected by adding "mode 1" to the server line in ntp.conf 48ea906c41SOllivier Robert * HP Z3801A code from Jeff Mock added by Hal Murray, Sep 2005 49ea906c41SOllivier Robert * 50ea906c41SOllivier Robert * 51c0b746e5SOllivier Robert * The receiver should be operated with factory default settings. 52c0b746e5SOllivier Robert * Initial driver operation: expects the receiver to be already locked 53c0b746e5SOllivier Robert * to GPS, configured and able to output timecode format 2 messages. 54c0b746e5SOllivier Robert * 55c0b746e5SOllivier Robert * The driver uses the poll sequence :PTIME:TCODE? to get a response from 56c0b746e5SOllivier Robert * the receiver. The receiver responds with a timecode string of ASCII 57c0b746e5SOllivier Robert * printing characters, followed by a <cr><lf>, followed by a prompt string 58c0b746e5SOllivier Robert * issued by the receiver, in the following format: 59c0b746e5SOllivier Robert * T#yyyymmddhhmmssMFLRVcc<cr><lf>scpi > 60c0b746e5SOllivier Robert * 61c0b746e5SOllivier Robert * The driver processes the response at the <cr> and <lf>, so what the 62c0b746e5SOllivier Robert * driver sees is the prompt from the previous poll, followed by this 63c0b746e5SOllivier Robert * timecode. The prompt from the current poll is (usually) left unread until 64c0b746e5SOllivier Robert * the next poll. So (except on the very first poll) the driver sees this: 65c0b746e5SOllivier Robert * 66c0b746e5SOllivier Robert * scpi > T#yyyymmddhhmmssMFLRVcc<cr><lf> 67c0b746e5SOllivier Robert * 68c0b746e5SOllivier Robert * The T is the on-time character, at 980 msec. before the next 1PPS edge. 69c0b746e5SOllivier Robert * The # is the timecode format type. We look for format 2. 70c0b746e5SOllivier Robert * Without any of the CLK or PPS stuff, then, the receiver buffer timestamp 71c0b746e5SOllivier Robert * at the <cr> is 24 characters later, which is about 25 msec. at 9600 bps, 72c0b746e5SOllivier Robert * so the first approximation for fudge time1 is nominally -0.955 seconds. 73c0b746e5SOllivier Robert * This number probably needs adjusting for each machine / OS type, so far: 74c0b746e5SOllivier Robert * -0.955000 on an HP 9000 Model 712/80 HP-UX 9.05 75c0b746e5SOllivier Robert * -0.953175 on an HP 9000 Model 370 HP-UX 9.10 76c0b746e5SOllivier Robert * 77c0b746e5SOllivier Robert * This receiver also provides a 1PPS signal, but I haven't figured out 78c0b746e5SOllivier Robert * how to deal with any of the CLK or PPS stuff yet. Stay tuned. 79c0b746e5SOllivier Robert * 80c0b746e5SOllivier Robert */ 81c0b746e5SOllivier Robert 82c0b746e5SOllivier Robert /* 83c0b746e5SOllivier Robert * Fudge Factors 84c0b746e5SOllivier Robert * 85c0b746e5SOllivier Robert * Fudge time1 is used to accomodate the timecode serial interface adjustment. 86c0b746e5SOllivier Robert * Fudge flag4 can be set to request a receiver status screen summary, which 87c0b746e5SOllivier Robert * is recorded in the clockstats file. 88c0b746e5SOllivier Robert */ 89c0b746e5SOllivier Robert 90c0b746e5SOllivier Robert /* 91c0b746e5SOllivier Robert * Interface definitions 92c0b746e5SOllivier Robert */ 93c0b746e5SOllivier Robert #define DEVICE "/dev/hpgps%d" /* device name and unit */ 94c0b746e5SOllivier Robert #define SPEED232 B9600 /* uart speed (9600 baud) */ 95ea906c41SOllivier Robert #define SPEED232Z B19200 /* uart speed (19200 baud) */ 96c0b746e5SOllivier Robert #define PRECISION (-10) /* precision assumed (about 1 ms) */ 97c0b746e5SOllivier Robert #define REFID "GPS\0" /* reference ID */ 98c0b746e5SOllivier Robert #define DESCRIPTION "HP 58503A GPS Time and Frequency Reference Receiver" 99c0b746e5SOllivier Robert 100c0b746e5SOllivier Robert #define SMAX 23*80+1 /* for :SYSTEM:PRINT? status screen response */ 101c0b746e5SOllivier Robert 102c0b746e5SOllivier Robert #define MTZONE 2 /* number of fields in timezone reply */ 103c0b746e5SOllivier Robert #define MTCODET2 12 /* number of fields in timecode format T2 */ 104c0b746e5SOllivier Robert #define NTCODET2 21 /* number of chars to checksum in format T2 */ 105c0b746e5SOllivier Robert 106c0b746e5SOllivier Robert /* 107c0b746e5SOllivier Robert * Tables to compute the day of year from yyyymmdd timecode. 108c0b746e5SOllivier Robert * Viva la leap. 109c0b746e5SOllivier Robert */ 110c0b746e5SOllivier Robert static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 111c0b746e5SOllivier Robert static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 112c0b746e5SOllivier Robert 113c0b746e5SOllivier Robert /* 114c0b746e5SOllivier Robert * Unit control structure 115c0b746e5SOllivier Robert */ 116c0b746e5SOllivier Robert struct hpgpsunit { 117c0b746e5SOllivier Robert int pollcnt; /* poll message counter */ 118c0b746e5SOllivier Robert int tzhour; /* timezone offset, hours */ 119c0b746e5SOllivier Robert int tzminute; /* timezone offset, minutes */ 120c0b746e5SOllivier Robert int linecnt; /* set for expected multiple line responses */ 121c0b746e5SOllivier Robert char *lastptr; /* pointer to receiver response data */ 122c0b746e5SOllivier Robert char statscrn[SMAX]; /* receiver status screen buffer */ 123c0b746e5SOllivier Robert }; 124c0b746e5SOllivier Robert 125c0b746e5SOllivier Robert /* 126c0b746e5SOllivier Robert * Function prototypes 127c0b746e5SOllivier Robert */ 1282b15cb3dSCy Schubert static int hpgps_start (int, struct peer *); 1292b15cb3dSCy Schubert static void hpgps_shutdown (int, struct peer *); 1302b15cb3dSCy Schubert static void hpgps_receive (struct recvbuf *); 1312b15cb3dSCy Schubert static void hpgps_poll (int, struct peer *); 132c0b746e5SOllivier Robert 133c0b746e5SOllivier Robert /* 134c0b746e5SOllivier Robert * Transfer vector 135c0b746e5SOllivier Robert */ 136c0b746e5SOllivier Robert struct refclock refclock_hpgps = { 137c0b746e5SOllivier Robert hpgps_start, /* start up driver */ 138c0b746e5SOllivier Robert hpgps_shutdown, /* shut down driver */ 139c0b746e5SOllivier Robert hpgps_poll, /* transmit poll message */ 140c0b746e5SOllivier Robert noentry, /* not used (old hpgps_control) */ 141c0b746e5SOllivier Robert noentry, /* initialize driver */ 142c0b746e5SOllivier Robert noentry, /* not used (old hpgps_buginfo) */ 143c0b746e5SOllivier Robert NOFLAGS /* not used */ 144c0b746e5SOllivier Robert }; 145c0b746e5SOllivier Robert 146c0b746e5SOllivier Robert 147c0b746e5SOllivier Robert /* 148c0b746e5SOllivier Robert * hpgps_start - open the devices and initialize data for processing 149c0b746e5SOllivier Robert */ 150c0b746e5SOllivier Robert static int 151c0b746e5SOllivier Robert hpgps_start( 152c0b746e5SOllivier Robert int unit, 153c0b746e5SOllivier Robert struct peer *peer 154c0b746e5SOllivier Robert ) 155c0b746e5SOllivier Robert { 156c0b746e5SOllivier Robert register struct hpgpsunit *up; 157c0b746e5SOllivier Robert struct refclockproc *pp; 158c0b746e5SOllivier Robert int fd; 1592b15cb3dSCy Schubert int speed, ldisc; 160c0b746e5SOllivier Robert char device[20]; 161c0b746e5SOllivier Robert 162c0b746e5SOllivier Robert /* 163c0b746e5SOllivier Robert * Open serial port. Use CLK line discipline, if available. 164ea906c41SOllivier Robert * Default is HP 58503A, mode arg selects HP Z3801A 165c0b746e5SOllivier Robert */ 1662b15cb3dSCy Schubert snprintf(device, sizeof(device), DEVICE, unit); 1672b15cb3dSCy Schubert ldisc = LDISC_CLK; 1682b15cb3dSCy Schubert speed = SPEED232; 169ea906c41SOllivier Robert /* mode parameter to server config line shares ttl slot */ 1702b15cb3dSCy Schubert if (1 == peer->ttl) { 1712b15cb3dSCy Schubert ldisc |= LDISC_7O1; 1722b15cb3dSCy Schubert speed = SPEED232Z; 173ea906c41SOllivier Robert } 174a466cc55SCy Schubert fd = refclock_open(&peer->srcadr, device, speed, ldisc); 1752b15cb3dSCy Schubert if (fd <= 0) 1762b15cb3dSCy Schubert return (0); 177c0b746e5SOllivier Robert /* 178c0b746e5SOllivier Robert * Allocate and initialize unit structure 179c0b746e5SOllivier Robert */ 1802b15cb3dSCy Schubert up = emalloc_zero(sizeof(*up)); 181c0b746e5SOllivier Robert pp = peer->procptr; 182c0b746e5SOllivier Robert pp->io.clock_recv = hpgps_receive; 1832b15cb3dSCy Schubert pp->io.srcclock = peer; 184c0b746e5SOllivier Robert pp->io.datalen = 0; 185c0b746e5SOllivier Robert pp->io.fd = fd; 186c0b746e5SOllivier Robert if (!io_addclock(&pp->io)) { 1872b15cb3dSCy Schubert close(fd); 1882b15cb3dSCy Schubert pp->io.fd = -1; 189c0b746e5SOllivier Robert free(up); 190c0b746e5SOllivier Robert return (0); 191c0b746e5SOllivier Robert } 1922b15cb3dSCy Schubert pp->unitptr = up; 193c0b746e5SOllivier Robert 194c0b746e5SOllivier Robert /* 195c0b746e5SOllivier Robert * Initialize miscellaneous variables 196c0b746e5SOllivier Robert */ 197c0b746e5SOllivier Robert peer->precision = PRECISION; 198c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 199c0b746e5SOllivier Robert memcpy((char *)&pp->refid, REFID, 4); 200c0b746e5SOllivier Robert up->tzhour = 0; 201c0b746e5SOllivier Robert up->tzminute = 0; 202c0b746e5SOllivier Robert 203c0b746e5SOllivier Robert *up->statscrn = '\0'; 204c0b746e5SOllivier Robert up->lastptr = up->statscrn; 205c0b746e5SOllivier Robert up->pollcnt = 2; 206c0b746e5SOllivier Robert 207c0b746e5SOllivier Robert /* 208c0b746e5SOllivier Robert * Get the identifier string, which is logged but otherwise ignored, 209c0b746e5SOllivier Robert * and get the local timezone information 210c0b746e5SOllivier Robert */ 211c0b746e5SOllivier Robert up->linecnt = 1; 212a466cc55SCy Schubert if (refclock_write(peer, "*IDN?\r:PTIME:TZONE?\r", 20, NULL) != 20) 213c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 214c0b746e5SOllivier Robert 215c0b746e5SOllivier Robert return (1); 216c0b746e5SOllivier Robert } 217c0b746e5SOllivier Robert 218c0b746e5SOllivier Robert 219c0b746e5SOllivier Robert /* 220c0b746e5SOllivier Robert * hpgps_shutdown - shut down the clock 221c0b746e5SOllivier Robert */ 222c0b746e5SOllivier Robert static void 223c0b746e5SOllivier Robert hpgps_shutdown( 224c0b746e5SOllivier Robert int unit, 225c0b746e5SOllivier Robert struct peer *peer 226c0b746e5SOllivier Robert ) 227c0b746e5SOllivier Robert { 228c0b746e5SOllivier Robert register struct hpgpsunit *up; 229c0b746e5SOllivier Robert struct refclockproc *pp; 230c0b746e5SOllivier Robert 231c0b746e5SOllivier Robert pp = peer->procptr; 2322b15cb3dSCy Schubert up = pp->unitptr; 2332b15cb3dSCy Schubert if (-1 != pp->io.fd) 234c0b746e5SOllivier Robert io_closeclock(&pp->io); 2352b15cb3dSCy Schubert if (NULL != up) 236c0b746e5SOllivier Robert free(up); 237c0b746e5SOllivier Robert } 238c0b746e5SOllivier Robert 239c0b746e5SOllivier Robert 240c0b746e5SOllivier Robert /* 241c0b746e5SOllivier Robert * hpgps_receive - receive data from the serial interface 242c0b746e5SOllivier Robert */ 243c0b746e5SOllivier Robert static void 244c0b746e5SOllivier Robert hpgps_receive( 245c0b746e5SOllivier Robert struct recvbuf *rbufp 246c0b746e5SOllivier Robert ) 247c0b746e5SOllivier Robert { 248c0b746e5SOllivier Robert register struct hpgpsunit *up; 249c0b746e5SOllivier Robert struct refclockproc *pp; 250c0b746e5SOllivier Robert struct peer *peer; 251c0b746e5SOllivier Robert l_fp trtmp; 252c0b746e5SOllivier Robert char tcodechar1; /* identifies timecode format */ 253c0b746e5SOllivier Robert char tcodechar2; /* identifies timecode format */ 254c0b746e5SOllivier Robert char timequal; /* time figure of merit: 0-9 */ 255c0b746e5SOllivier Robert char freqqual; /* frequency figure of merit: 0-3 */ 256c0b746e5SOllivier Robert char leapchar; /* leapsecond: + or 0 or - */ 257c0b746e5SOllivier Robert char servchar; /* request for service: 0 = no, 1 = yes */ 258c0b746e5SOllivier Robert char syncchar; /* time info is invalid: 0 = no, 1 = yes */ 259c0b746e5SOllivier Robert short expectedsm; /* expected timecode byte checksum */ 260c0b746e5SOllivier Robert short tcodechksm; /* computed timecode byte checksum */ 261c0b746e5SOllivier Robert int i,m,n; 262c0b746e5SOllivier Robert int month, day, lastday; 263c0b746e5SOllivier Robert char *tcp; /* timecode pointer (skips over the prompt) */ 264c0b746e5SOllivier Robert char prompt[BMAX]; /* prompt in response from receiver */ 265c0b746e5SOllivier Robert 266c0b746e5SOllivier Robert /* 267c0b746e5SOllivier Robert * Initialize pointers and read the receiver response 268c0b746e5SOllivier Robert */ 2692b15cb3dSCy Schubert peer = rbufp->recv_peer; 270c0b746e5SOllivier Robert pp = peer->procptr; 2712b15cb3dSCy Schubert up = pp->unitptr; 272c0b746e5SOllivier Robert *pp->a_lastcode = '\0'; 273c0b746e5SOllivier Robert pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); 274c0b746e5SOllivier Robert 275c0b746e5SOllivier Robert #ifdef DEBUG 276c0b746e5SOllivier Robert if (debug) 277c0b746e5SOllivier Robert printf("hpgps: lencode: %d timecode:%s\n", 278c0b746e5SOllivier Robert pp->lencode, pp->a_lastcode); 279c0b746e5SOllivier Robert #endif 280c0b746e5SOllivier Robert 281c0b746e5SOllivier Robert /* 282c0b746e5SOllivier Robert * If there's no characters in the reply, we can quit now 283c0b746e5SOllivier Robert */ 284c0b746e5SOllivier Robert if (pp->lencode == 0) 285c0b746e5SOllivier Robert return; 286c0b746e5SOllivier Robert 287c0b746e5SOllivier Robert /* 288c0b746e5SOllivier Robert * If linecnt is greater than zero, we are getting information only, 289c0b746e5SOllivier Robert * such as the receiver identification string or the receiver status 290c0b746e5SOllivier Robert * screen, so put the receiver response at the end of the status 291c0b746e5SOllivier Robert * screen buffer. When we have the last line, write the buffer to 292c0b746e5SOllivier Robert * the clockstats file and return without further processing. 293c0b746e5SOllivier Robert * 294c0b746e5SOllivier Robert * If linecnt is zero, we are expecting either the timezone 295c0b746e5SOllivier Robert * or a timecode. At this point, also write the response 296c0b746e5SOllivier Robert * to the clockstats file, and go on to process the prompt (if any), 297c0b746e5SOllivier Robert * timezone, or timecode and timestamp. 298c0b746e5SOllivier Robert */ 299c0b746e5SOllivier Robert 300c0b746e5SOllivier Robert 301c0b746e5SOllivier Robert if (up->linecnt-- > 0) { 302c0b746e5SOllivier Robert if ((int)(pp->lencode + 2) <= (SMAX - (up->lastptr - up->statscrn))) { 303c0b746e5SOllivier Robert *up->lastptr++ = '\n'; 3042b15cb3dSCy Schubert memcpy(up->lastptr, pp->a_lastcode, pp->lencode); 305c0b746e5SOllivier Robert up->lastptr += pp->lencode; 306c0b746e5SOllivier Robert } 307c0b746e5SOllivier Robert if (up->linecnt == 0) 308c0b746e5SOllivier Robert record_clock_stats(&peer->srcadr, up->statscrn); 309c0b746e5SOllivier Robert 310c0b746e5SOllivier Robert return; 311c0b746e5SOllivier Robert } 312c0b746e5SOllivier Robert 313c0b746e5SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 314c0b746e5SOllivier Robert pp->lastrec = trtmp; 315c0b746e5SOllivier Robert 316c0b746e5SOllivier Robert up->lastptr = up->statscrn; 317c0b746e5SOllivier Robert *up->lastptr = '\0'; 318c0b746e5SOllivier Robert up->pollcnt = 2; 319c0b746e5SOllivier Robert 320c0b746e5SOllivier Robert /* 321c0b746e5SOllivier Robert * We get down to business: get a prompt if one is there, issue 322c0b746e5SOllivier Robert * a clear status command if it contains an error indication. 323c0b746e5SOllivier Robert * Next, check for either the timezone reply or the timecode reply 324c0b746e5SOllivier Robert * and decode it. If we don't recognize the reply, or don't get the 325c0b746e5SOllivier Robert * proper number of decoded fields, or get an out of range timezone, 326c0b746e5SOllivier Robert * or if the timecode checksum is bad, then we declare bad format 327c0b746e5SOllivier Robert * and exit. 328c0b746e5SOllivier Robert * 329c0b746e5SOllivier Robert * Timezone format (including nominal prompt): 330c0b746e5SOllivier Robert * scpi > -H,-M<cr><lf> 331c0b746e5SOllivier Robert * 332c0b746e5SOllivier Robert * Timecode format (including nominal prompt): 333c0b746e5SOllivier Robert * scpi > T2yyyymmddhhmmssMFLRVcc<cr><lf> 334c0b746e5SOllivier Robert * 335c0b746e5SOllivier Robert */ 336c0b746e5SOllivier Robert 3372b15cb3dSCy Schubert strlcpy(prompt, pp->a_lastcode, sizeof(prompt)); 338c0b746e5SOllivier Robert tcp = strrchr(pp->a_lastcode,'>'); 339c0b746e5SOllivier Robert if (tcp == NULL) 340c0b746e5SOllivier Robert tcp = pp->a_lastcode; 341c0b746e5SOllivier Robert else 342c0b746e5SOllivier Robert tcp++; 343c0b746e5SOllivier Robert prompt[tcp - pp->a_lastcode] = '\0'; 344c0b746e5SOllivier Robert while ((*tcp == ' ') || (*tcp == '\t')) tcp++; 345c0b746e5SOllivier Robert 346c0b746e5SOllivier Robert /* 347c0b746e5SOllivier Robert * deal with an error indication in the prompt here 348c0b746e5SOllivier Robert */ 349c0b746e5SOllivier Robert if (strrchr(prompt,'E') > strrchr(prompt,'s')){ 350c0b746e5SOllivier Robert #ifdef DEBUG 351c0b746e5SOllivier Robert if (debug) 352c0b746e5SOllivier Robert printf("hpgps: error indicated in prompt: %s\n", prompt); 353c0b746e5SOllivier Robert #endif 354a466cc55SCy Schubert if (refclock_write(peer, "*CLS\r\r", 6, NULL) != 6) 355c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 356c0b746e5SOllivier Robert } 357c0b746e5SOllivier Robert 358c0b746e5SOllivier Robert /* 359c0b746e5SOllivier Robert * make sure we got a timezone or timecode format and 360c0b746e5SOllivier Robert * then process accordingly 361c0b746e5SOllivier Robert */ 362c0b746e5SOllivier Robert m = sscanf(tcp,"%c%c", &tcodechar1, &tcodechar2); 363c0b746e5SOllivier Robert 364c0b746e5SOllivier Robert if (m != 2){ 365c0b746e5SOllivier Robert #ifdef DEBUG 366c0b746e5SOllivier Robert if (debug) 367c0b746e5SOllivier Robert printf("hpgps: no format indicator\n"); 368c0b746e5SOllivier Robert #endif 369c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 370c0b746e5SOllivier Robert return; 371c0b746e5SOllivier Robert } 372c0b746e5SOllivier Robert 373c0b746e5SOllivier Robert switch (tcodechar1) { 374c0b746e5SOllivier Robert 375c0b746e5SOllivier Robert case '+': 376c0b746e5SOllivier Robert case '-': 377c0b746e5SOllivier Robert m = sscanf(tcp,"%d,%d", &up->tzhour, &up->tzminute); 378c0b746e5SOllivier Robert if (m != MTZONE) { 379c0b746e5SOllivier Robert #ifdef DEBUG 380c0b746e5SOllivier Robert if (debug) 381c0b746e5SOllivier Robert printf("hpgps: only %d fields recognized in timezone\n", m); 382c0b746e5SOllivier Robert #endif 383c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 384c0b746e5SOllivier Robert return; 385c0b746e5SOllivier Robert } 386c0b746e5SOllivier Robert if ((up->tzhour < -12) || (up->tzhour > 13) || 387c0b746e5SOllivier Robert (up->tzminute < -59) || (up->tzminute > 59)){ 388c0b746e5SOllivier Robert #ifdef DEBUG 389c0b746e5SOllivier Robert if (debug) 390c0b746e5SOllivier Robert printf("hpgps: timezone %d, %d out of range\n", 391c0b746e5SOllivier Robert up->tzhour, up->tzminute); 392c0b746e5SOllivier Robert #endif 393c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 394c0b746e5SOllivier Robert return; 395c0b746e5SOllivier Robert } 396c0b746e5SOllivier Robert return; 397c0b746e5SOllivier Robert 398c0b746e5SOllivier Robert case 'T': 399c0b746e5SOllivier Robert break; 400c0b746e5SOllivier Robert 401c0b746e5SOllivier Robert default: 402c0b746e5SOllivier Robert #ifdef DEBUG 403c0b746e5SOllivier Robert if (debug) 404c0b746e5SOllivier Robert printf("hpgps: unrecognized reply format %c%c\n", 405c0b746e5SOllivier Robert tcodechar1, tcodechar2); 406c0b746e5SOllivier Robert #endif 407c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 408c0b746e5SOllivier Robert return; 409c0b746e5SOllivier Robert } /* end of tcodechar1 switch */ 410c0b746e5SOllivier Robert 411c0b746e5SOllivier Robert 412c0b746e5SOllivier Robert switch (tcodechar2) { 413c0b746e5SOllivier Robert 414c0b746e5SOllivier Robert case '2': 415c0b746e5SOllivier Robert m = sscanf(tcp,"%*c%*c%4d%2d%2d%2d%2d%2d%c%c%c%c%c%2hx", 416c0b746e5SOllivier Robert &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second, 417c0b746e5SOllivier Robert &timequal, &freqqual, &leapchar, &servchar, &syncchar, 418c0b746e5SOllivier Robert &expectedsm); 419c0b746e5SOllivier Robert n = NTCODET2; 420c0b746e5SOllivier Robert 421c0b746e5SOllivier Robert if (m != MTCODET2){ 422c0b746e5SOllivier Robert #ifdef DEBUG 423c0b746e5SOllivier Robert if (debug) 424c0b746e5SOllivier Robert printf("hpgps: only %d fields recognized in timecode\n", m); 425c0b746e5SOllivier Robert #endif 426c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 427c0b746e5SOllivier Robert return; 428c0b746e5SOllivier Robert } 429c0b746e5SOllivier Robert break; 430c0b746e5SOllivier Robert 431c0b746e5SOllivier Robert default: 432c0b746e5SOllivier Robert #ifdef DEBUG 433c0b746e5SOllivier Robert if (debug) 434c0b746e5SOllivier Robert printf("hpgps: unrecognized timecode format %c%c\n", 435c0b746e5SOllivier Robert tcodechar1, tcodechar2); 436c0b746e5SOllivier Robert #endif 437c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 438c0b746e5SOllivier Robert return; 439c0b746e5SOllivier Robert } /* end of tcodechar2 format switch */ 440c0b746e5SOllivier Robert 441c0b746e5SOllivier Robert /* 442c0b746e5SOllivier Robert * Compute and verify the checksum. 443c0b746e5SOllivier Robert * Characters are summed starting at tcodechar1, ending at just 444c0b746e5SOllivier Robert * before the expected checksum. Bail out if incorrect. 445c0b746e5SOllivier Robert */ 446c0b746e5SOllivier Robert tcodechksm = 0; 447c0b746e5SOllivier Robert while (n-- > 0) tcodechksm += *tcp++; 448c0b746e5SOllivier Robert tcodechksm &= 0x00ff; 449c0b746e5SOllivier Robert 450c0b746e5SOllivier Robert if (tcodechksm != expectedsm) { 451c0b746e5SOllivier Robert #ifdef DEBUG 452c0b746e5SOllivier Robert if (debug) 453c0b746e5SOllivier Robert printf("hpgps: checksum %2hX doesn't match %2hX expected\n", 454c0b746e5SOllivier Robert tcodechksm, expectedsm); 455c0b746e5SOllivier Robert #endif 456c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 457c0b746e5SOllivier Robert return; 458c0b746e5SOllivier Robert } 459c0b746e5SOllivier Robert 460c0b746e5SOllivier Robert /* 461c0b746e5SOllivier Robert * Compute the day of year from the yyyymmdd format. 462c0b746e5SOllivier Robert */ 463c0b746e5SOllivier Robert if (month < 1 || month > 12 || day < 1) { 464c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 465c0b746e5SOllivier Robert return; 466c0b746e5SOllivier Robert } 467c0b746e5SOllivier Robert 468c0b746e5SOllivier Robert if ( ! isleap_4(pp->year) ) { /* Y2KFixes */ 469c0b746e5SOllivier Robert /* not a leap year */ 470c0b746e5SOllivier Robert if (day > day1tab[month - 1]) { 471c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 472c0b746e5SOllivier Robert return; 473c0b746e5SOllivier Robert } 474c0b746e5SOllivier Robert for (i = 0; i < month - 1; i++) day += day1tab[i]; 475c0b746e5SOllivier Robert lastday = 365; 476c0b746e5SOllivier Robert } else { 477c0b746e5SOllivier Robert /* a leap year */ 478c0b746e5SOllivier Robert if (day > day2tab[month - 1]) { 479c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 480c0b746e5SOllivier Robert return; 481c0b746e5SOllivier Robert } 482c0b746e5SOllivier Robert for (i = 0; i < month - 1; i++) day += day2tab[i]; 483c0b746e5SOllivier Robert lastday = 366; 484c0b746e5SOllivier Robert } 485c0b746e5SOllivier Robert 486c0b746e5SOllivier Robert /* 487c0b746e5SOllivier Robert * Deal with the timezone offset here. The receiver timecode is in 488c0b746e5SOllivier Robert * local time = UTC + :PTIME:TZONE, so SUBTRACT the timezone values. 489c0b746e5SOllivier Robert * For example, Pacific Standard Time is -8 hours , 0 minutes. 490c0b746e5SOllivier Robert * Deal with the underflows and overflows. 491c0b746e5SOllivier Robert */ 492c0b746e5SOllivier Robert pp->minute -= up->tzminute; 493c0b746e5SOllivier Robert pp->hour -= up->tzhour; 494c0b746e5SOllivier Robert 495c0b746e5SOllivier Robert if (pp->minute < 0) { 496c0b746e5SOllivier Robert pp->minute += 60; 497c0b746e5SOllivier Robert pp->hour--; 498c0b746e5SOllivier Robert } 499c0b746e5SOllivier Robert if (pp->minute > 59) { 500c0b746e5SOllivier Robert pp->minute -= 60; 501c0b746e5SOllivier Robert pp->hour++; 502c0b746e5SOllivier Robert } 503c0b746e5SOllivier Robert if (pp->hour < 0) { 504c0b746e5SOllivier Robert pp->hour += 24; 505c0b746e5SOllivier Robert day--; 506c0b746e5SOllivier Robert if (day < 1) { 507c0b746e5SOllivier Robert pp->year--; 508c0b746e5SOllivier Robert if ( isleap_4(pp->year) ) /* Y2KFixes */ 509c0b746e5SOllivier Robert day = 366; 510c0b746e5SOllivier Robert else 511c0b746e5SOllivier Robert day = 365; 512c0b746e5SOllivier Robert } 513c0b746e5SOllivier Robert } 514c0b746e5SOllivier Robert 515c0b746e5SOllivier Robert if (pp->hour > 23) { 516c0b746e5SOllivier Robert pp->hour -= 24; 517c0b746e5SOllivier Robert day++; 518c0b746e5SOllivier Robert if (day > lastday) { 519c0b746e5SOllivier Robert pp->year++; 520c0b746e5SOllivier Robert day = 1; 521c0b746e5SOllivier Robert } 522c0b746e5SOllivier Robert } 523c0b746e5SOllivier Robert 524c0b746e5SOllivier Robert pp->day = day; 525c0b746e5SOllivier Robert 526c0b746e5SOllivier Robert /* 527c0b746e5SOllivier Robert * Decode the MFLRV indicators. 528c0b746e5SOllivier Robert * NEED TO FIGURE OUT how to deal with the request for service, 529c0b746e5SOllivier Robert * time quality, and frequency quality indicators some day. 530c0b746e5SOllivier Robert */ 531c0b746e5SOllivier Robert if (syncchar != '0') { 532c0b746e5SOllivier Robert pp->leap = LEAP_NOTINSYNC; 533c0b746e5SOllivier Robert } 534c0b746e5SOllivier Robert else { 5352b15cb3dSCy Schubert pp->leap = LEAP_NOWARNING; 536c0b746e5SOllivier Robert switch (leapchar) { 537c0b746e5SOllivier Robert 5382b15cb3dSCy Schubert case '0': 5392b15cb3dSCy Schubert break; 5402b15cb3dSCy Schubert 5412b15cb3dSCy Schubert /* See http://bugs.ntp.org/1090 5422b15cb3dSCy Schubert * Ignore leap announcements unless June or December. 5432b15cb3dSCy Schubert * Better would be to use :GPSTime? to find the month, 5442b15cb3dSCy Schubert * but that seems too likely to introduce other bugs. 5452b15cb3dSCy Schubert */ 546c0b746e5SOllivier Robert case '+': 5472b15cb3dSCy Schubert if ((month==6) || (month==12)) 548c0b746e5SOllivier Robert pp->leap = LEAP_ADDSECOND; 549c0b746e5SOllivier Robert break; 550c0b746e5SOllivier Robert 551c0b746e5SOllivier Robert case '-': 5522b15cb3dSCy Schubert if ((month==6) || (month==12)) 553c0b746e5SOllivier Robert pp->leap = LEAP_DELSECOND; 554c0b746e5SOllivier Robert break; 555c0b746e5SOllivier Robert 556c0b746e5SOllivier Robert default: 557c0b746e5SOllivier Robert #ifdef DEBUG 558c0b746e5SOllivier Robert if (debug) 559c0b746e5SOllivier Robert printf("hpgps: unrecognized leap indicator: %c\n", 560c0b746e5SOllivier Robert leapchar); 561c0b746e5SOllivier Robert #endif 562c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 563c0b746e5SOllivier Robert return; 564c0b746e5SOllivier Robert } /* end of leapchar switch */ 565c0b746e5SOllivier Robert } 566c0b746e5SOllivier Robert 567c0b746e5SOllivier Robert /* 568c0b746e5SOllivier Robert * Process the new sample in the median filter and determine the 569c0b746e5SOllivier Robert * reference clock offset and dispersion. We use lastrec as both 570c0b746e5SOllivier Robert * the reference time and receive time in order to avoid being 571c0b746e5SOllivier Robert * cute, like setting the reference time later than the receive 572c0b746e5SOllivier Robert * time, which may cause a paranoid protocol module to chuck out 573c0b746e5SOllivier Robert * the data. 574c0b746e5SOllivier Robert */ 575c0b746e5SOllivier Robert if (!refclock_process(pp)) { 576c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 577c0b746e5SOllivier Robert return; 578c0b746e5SOllivier Robert } 5799c2daa00SOllivier Robert pp->lastref = pp->lastrec; 580c0b746e5SOllivier Robert refclock_receive(peer); 581c0b746e5SOllivier Robert 582c0b746e5SOllivier Robert /* 583c0b746e5SOllivier Robert * If CLK_FLAG4 is set, ask for the status screen response. 584c0b746e5SOllivier Robert */ 585c0b746e5SOllivier Robert if (pp->sloppyclockflag & CLK_FLAG4){ 586c0b746e5SOllivier Robert up->linecnt = 22; 587a466cc55SCy Schubert if (refclock_write(peer, ":SYSTEM:PRINT?\r", 15, NULL) != 15) 588c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 589c0b746e5SOllivier Robert } 590c0b746e5SOllivier Robert } 591c0b746e5SOllivier Robert 592c0b746e5SOllivier Robert 593c0b746e5SOllivier Robert /* 594c0b746e5SOllivier Robert * hpgps_poll - called by the transmit procedure 595c0b746e5SOllivier Robert */ 596c0b746e5SOllivier Robert static void 597c0b746e5SOllivier Robert hpgps_poll( 598c0b746e5SOllivier Robert int unit, 599c0b746e5SOllivier Robert struct peer *peer 600c0b746e5SOllivier Robert ) 601c0b746e5SOllivier Robert { 602c0b746e5SOllivier Robert register struct hpgpsunit *up; 603c0b746e5SOllivier Robert struct refclockproc *pp; 604c0b746e5SOllivier Robert 605c0b746e5SOllivier Robert /* 606c0b746e5SOllivier Robert * Time to poll the clock. The HP 58503A responds to a 607c0b746e5SOllivier Robert * ":PTIME:TCODE?" by returning a timecode in the format specified 608c0b746e5SOllivier Robert * above. If nothing is heard from the clock for two polls, 609c0b746e5SOllivier Robert * declare a timeout and keep going. 610c0b746e5SOllivier Robert */ 611c0b746e5SOllivier Robert pp = peer->procptr; 6122b15cb3dSCy Schubert up = pp->unitptr; 613c0b746e5SOllivier Robert if (up->pollcnt == 0) 614c0b746e5SOllivier Robert refclock_report(peer, CEVNT_TIMEOUT); 615c0b746e5SOllivier Robert else 616c0b746e5SOllivier Robert up->pollcnt--; 617a466cc55SCy Schubert if (refclock_write(peer, ":PTIME:TCODE?\r", 14, NULL) != 14) { 618c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 619c0b746e5SOllivier Robert } 620c0b746e5SOllivier Robert else 621c0b746e5SOllivier Robert pp->polls++; 622c0b746e5SOllivier Robert } 623c0b746e5SOllivier Robert 624c0b746e5SOllivier Robert #else 625*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT 626c0b746e5SOllivier Robert #endif /* REFCLOCK */ 627