1c0b746e5SOllivier Robert /* 2ea906c41SOllivier Robert * refclock_wwvb - clock driver for Spectracom WWVB and GPS receivers 3c0b746e5SOllivier Robert */ 4c0b746e5SOllivier Robert 5c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 6c0b746e5SOllivier Robert #include <config.h> 7c0b746e5SOllivier Robert #endif 8c0b746e5SOllivier Robert 9a151a66cSOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_SPECTRACOM) 10c0b746e5SOllivier Robert 11c0b746e5SOllivier Robert #include "ntpd.h" 12c0b746e5SOllivier Robert #include "ntp_io.h" 13c0b746e5SOllivier Robert #include "ntp_refclock.h" 14c0b746e5SOllivier Robert #include "ntp_calendar.h" 15c0b746e5SOllivier Robert #include "ntp_stdlib.h" 16c0b746e5SOllivier Robert 17224ba2bdSOllivier Robert #include <stdio.h> 18224ba2bdSOllivier Robert #include <ctype.h> 19224ba2bdSOllivier Robert 202b15cb3dSCy Schubert #ifdef HAVE_PPSAPI 212b15cb3dSCy Schubert #include "ppsapi_timepps.h" 222b15cb3dSCy Schubert #include "refclock_atom.h" 232b15cb3dSCy Schubert #endif /* HAVE_PPSAPI */ 242b15cb3dSCy Schubert 25c0b746e5SOllivier Robert /* 26c0b746e5SOllivier Robert * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB 27c0b746e5SOllivier Robert * Synchronized Clocks and the Netclock/GPS Master Clock. Both the WWVB 28c0b746e5SOllivier Robert * and GPS clocks have proven reliable sources of time; however, the 29c0b746e5SOllivier Robert * WWVB clocks have proven vulnerable to high ambient conductive RF 30c0b746e5SOllivier Robert * interference. The claimed accuracy of the WWVB clocks is 100 us 31c0b746e5SOllivier Robert * relative to the broadcast signal, while the claimed accuracy of the 32c0b746e5SOllivier Robert * GPS clock is 50 ns; however, in most cases the actual accuracy is 33c0b746e5SOllivier Robert * limited by the resolution of the timecode and the latencies of the 34c0b746e5SOllivier Robert * serial interface and operating system. 35c0b746e5SOllivier Robert * 36c0b746e5SOllivier Robert * The WWVB and GPS clocks should be configured for 24-hour display, 37c0b746e5SOllivier Robert * AUTO DST off, time zone 0 (UTC), data format 0 or 2 (see below) and 38c0b746e5SOllivier Robert * baud rate 9600. If the clock is to used as the source for the IRIG 39c0b746e5SOllivier Robert * Audio Decoder (refclock_irig.c in this distribution), it should be 40c0b746e5SOllivier Robert * configured for AM IRIG output and IRIG format 1 (IRIG B with 41c0b746e5SOllivier Robert * signature control). The GPS clock can be configured either to respond 42c0b746e5SOllivier Robert * to a 'T' poll character or left running continuously. 43c0b746e5SOllivier Robert * 44c0b746e5SOllivier Robert * There are two timecode formats used by these clocks. Format 0, which 45c0b746e5SOllivier Robert * is available with both the Netclock/2 and 8170, and format 2, which 46c0b746e5SOllivier Robert * is available only with the Netclock/2, specially modified 8170 and 47c0b746e5SOllivier Robert * GPS. 48c0b746e5SOllivier Robert * 49c0b746e5SOllivier Robert * Format 0 (22 ASCII printing characters): 50c0b746e5SOllivier Robert * 51c0b746e5SOllivier Robert * <cr><lf>i ddd hh:mm:ss TZ=zz<cr><lf> 52c0b746e5SOllivier Robert * 53c0b746e5SOllivier Robert * on-time = first <cr> 54c0b746e5SOllivier Robert * hh:mm:ss = hours, minutes, seconds 55c0b746e5SOllivier Robert * i = synchronization flag (' ' = in synch, '?' = out of synch) 56c0b746e5SOllivier Robert * 572b15cb3dSCy Schubert * The alarm condition is indicated by other than ' ' at i, which occurs 58c0b746e5SOllivier Robert * during initial synchronization and when received signal is lost for 59c0b746e5SOllivier Robert * about ten hours. 60c0b746e5SOllivier Robert * 61c0b746e5SOllivier Robert * Format 2 (24 ASCII printing characters): 62c0b746e5SOllivier Robert * 63c0b746e5SOllivier Robert * <cr><lf>iqyy ddd hh:mm:ss.fff ld 64c0b746e5SOllivier Robert * 65c0b746e5SOllivier Robert * on-time = <cr> 66c0b746e5SOllivier Robert * i = synchronization flag (' ' = in synch, '?' = out of synch) 67c0b746e5SOllivier Robert * q = quality indicator (' ' = locked, 'A'...'D' = unlocked) 68c0b746e5SOllivier Robert * yy = year (as broadcast) 69c0b746e5SOllivier Robert * ddd = day of year 70c0b746e5SOllivier Robert * hh:mm:ss.fff = hours, minutes, seconds, milliseconds 71c0b746e5SOllivier Robert * 722b15cb3dSCy Schubert * The alarm condition is indicated by other than ' ' at i, which occurs 73c0b746e5SOllivier Robert * during initial synchronization and when received signal is lost for 74c0b746e5SOllivier Robert * about ten hours. The unlock condition is indicated by other than ' ' 75c0b746e5SOllivier Robert * at q. 76c0b746e5SOllivier Robert * 77c0b746e5SOllivier Robert * The q is normally ' ' when the time error is less than 1 ms and a 78c0b746e5SOllivier Robert * character in the set 'A'...'D' when the time error is less than 10, 79c0b746e5SOllivier Robert * 100, 500 and greater than 500 ms respectively. The l is normally ' ', 80c0b746e5SOllivier Robert * but is set to 'L' early in the month of an upcoming UTC leap second 81c0b746e5SOllivier Robert * and reset to ' ' on the first day of the following month. The d is 82c0b746e5SOllivier Robert * set to 'S' for standard time 'I' on the day preceding a switch to 83c0b746e5SOllivier Robert * daylight time, 'D' for daylight time and 'O' on the day preceding a 84c0b746e5SOllivier Robert * switch to standard time. The start bit of the first <cr> is 85c0b746e5SOllivier Robert * synchronized to the indicated time as returned. 86c0b746e5SOllivier Robert * 87c0b746e5SOllivier Robert * This driver does not need to be told which format is in use - it 88ea906c41SOllivier Robert * figures out which one from the length of the message. The driver 89ea906c41SOllivier Robert * makes no attempt to correct for the intrinsic jitter of the radio 90ea906c41SOllivier Robert * itself, which is a known problem with the older radios. 91c0b746e5SOllivier Robert * 922b15cb3dSCy Schubert * PPS Signal Processing 932b15cb3dSCy Schubert * 942b15cb3dSCy Schubert * When PPS signal processing is enabled, and when the system clock has 952b15cb3dSCy Schubert * been set by this or another driver and the PPS signal offset is 962b15cb3dSCy Schubert * within 0.4 s of the system clock offset, the PPS signal replaces the 972b15cb3dSCy Schubert * timecode for as long as the PPS signal is active. If for some reason 982b15cb3dSCy Schubert * the PPS signal fails for one or more poll intervals, the driver 992b15cb3dSCy Schubert * reverts to the timecode. If the timecode fails for one or more poll 1002b15cb3dSCy Schubert * intervals, the PPS signal is disconnected. 1012b15cb3dSCy Schubert * 102c0b746e5SOllivier Robert * Fudge Factors 103c0b746e5SOllivier Robert * 104c0b746e5SOllivier Robert * This driver can retrieve a table of quality data maintained 105c0b746e5SOllivier Robert * internally by the Netclock/2 clock. If flag4 of the fudge 106c0b746e5SOllivier Robert * configuration command is set to 1, the driver will retrieve this 107ea906c41SOllivier Robert * table and write it to the clockstats file when the first timecode 108c0b746e5SOllivier Robert * message of a new day is received. 109ea906c41SOllivier Robert * 110ea906c41SOllivier Robert * PPS calibration fudge time 1: format 0 .003134, format 2 .004034 111c0b746e5SOllivier Robert */ 112c0b746e5SOllivier Robert /* 113c0b746e5SOllivier Robert * Interface definitions 114c0b746e5SOllivier Robert */ 115c0b746e5SOllivier Robert #define DEVICE "/dev/wwvb%d" /* device name and unit */ 116c0b746e5SOllivier Robert #define SPEED232 B9600 /* uart speed (9600 baud) */ 117c0b746e5SOllivier Robert #define PRECISION (-13) /* precision assumed (about 100 us) */ 1182b15cb3dSCy Schubert #define PPS_PRECISION (-13) /* precision assumed (about 100 us) */ 119c0b746e5SOllivier Robert #define REFID "WWVB" /* reference ID */ 120ea906c41SOllivier Robert #define DESCRIPTION "Spectracom WWVB/GPS Receiver" /* WRU */ 121c0b746e5SOllivier Robert 122c0b746e5SOllivier Robert #define LENWWVB0 22 /* format 0 timecode length */ 123c0b746e5SOllivier Robert #define LENWWVB2 24 /* format 2 timecode length */ 124c0b746e5SOllivier Robert #define LENWWVB3 29 /* format 3 timecode length */ 125c0b746e5SOllivier Robert #define MONLIN 15 /* number of monitoring lines */ 126c0b746e5SOllivier Robert 127c0b746e5SOllivier Robert /* 128c0b746e5SOllivier Robert * WWVB unit control structure 129c0b746e5SOllivier Robert */ 130c0b746e5SOllivier Robert struct wwvbunit { 1312b15cb3dSCy Schubert #ifdef HAVE_PPSAPI 1322b15cb3dSCy Schubert struct refclock_atom atom; /* PPSAPI structure */ 1332b15cb3dSCy Schubert int ppsapi_tried; /* attempt PPSAPI once */ 1342b15cb3dSCy Schubert int ppsapi_lit; /* time_pps_create() worked */ 1352b15cb3dSCy Schubert int tcount; /* timecode sample counter */ 1362b15cb3dSCy Schubert int pcount; /* PPS sample counter */ 1372b15cb3dSCy Schubert #endif /* HAVE_PPSAPI */ 1382b15cb3dSCy Schubert l_fp laststamp; /* last <CR> timestamp */ 1392b15cb3dSCy Schubert int prev_eol_cr; /* was last EOL <CR> (not <LF>)? */ 140c0b746e5SOllivier Robert u_char lasthour; /* last hour (for monitor) */ 141c0b746e5SOllivier Robert u_char linect; /* count ignored lines (for monitor */ 142c0b746e5SOllivier Robert }; 143c0b746e5SOllivier Robert 144c0b746e5SOllivier Robert /* 145c0b746e5SOllivier Robert * Function prototypes 146c0b746e5SOllivier Robert */ 1472b15cb3dSCy Schubert static int wwvb_start (int, struct peer *); 1482b15cb3dSCy Schubert static void wwvb_shutdown (int, struct peer *); 1492b15cb3dSCy Schubert static void wwvb_receive (struct recvbuf *); 1502b15cb3dSCy Schubert static void wwvb_poll (int, struct peer *); 1512b15cb3dSCy Schubert static void wwvb_timer (int, struct peer *); 1522b15cb3dSCy Schubert #ifdef HAVE_PPSAPI 1532b15cb3dSCy Schubert static void wwvb_control (int, const struct refclockstat *, 1542b15cb3dSCy Schubert struct refclockstat *, struct peer *); 1552b15cb3dSCy Schubert #define WWVB_CONTROL wwvb_control 1562b15cb3dSCy Schubert #else 1572b15cb3dSCy Schubert #define WWVB_CONTROL noentry 1582b15cb3dSCy Schubert #endif /* HAVE_PPSAPI */ 159c0b746e5SOllivier Robert 160c0b746e5SOllivier Robert /* 161c0b746e5SOllivier Robert * Transfer vector 162c0b746e5SOllivier Robert */ 163c0b746e5SOllivier Robert struct refclock refclock_wwvb = { 164c0b746e5SOllivier Robert wwvb_start, /* start up driver */ 165c0b746e5SOllivier Robert wwvb_shutdown, /* shut down driver */ 166c0b746e5SOllivier Robert wwvb_poll, /* transmit poll message */ 1672b15cb3dSCy Schubert WWVB_CONTROL, /* fudge set/change notification */ 168c0b746e5SOllivier Robert noentry, /* initialize driver (not used) */ 169c0b746e5SOllivier Robert noentry, /* not used (old wwvb_buginfo) */ 170ea906c41SOllivier Robert wwvb_timer /* called once per second */ 171c0b746e5SOllivier Robert }; 172c0b746e5SOllivier Robert 173c0b746e5SOllivier Robert 174c0b746e5SOllivier Robert /* 175c0b746e5SOllivier Robert * wwvb_start - open the devices and initialize data for processing 176c0b746e5SOllivier Robert */ 177c0b746e5SOllivier Robert static int 178c0b746e5SOllivier Robert wwvb_start( 179c0b746e5SOllivier Robert int unit, 180c0b746e5SOllivier Robert struct peer *peer 181c0b746e5SOllivier Robert ) 182c0b746e5SOllivier Robert { 183c0b746e5SOllivier Robert register struct wwvbunit *up; 184c0b746e5SOllivier Robert struct refclockproc *pp; 185c0b746e5SOllivier Robert int fd; 186c0b746e5SOllivier Robert char device[20]; 187c0b746e5SOllivier Robert 188c0b746e5SOllivier Robert /* 189c0b746e5SOllivier Robert * Open serial port. Use CLK line discipline, if available. 190c0b746e5SOllivier Robert */ 1912b15cb3dSCy Schubert snprintf(device, sizeof(device), DEVICE, unit); 192a466cc55SCy Schubert fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_CLK); 1932b15cb3dSCy Schubert if (fd <= 0) 194c0b746e5SOllivier Robert return (0); 195c0b746e5SOllivier Robert 196c0b746e5SOllivier Robert /* 197c0b746e5SOllivier Robert * Allocate and initialize unit structure 198c0b746e5SOllivier Robert */ 1992b15cb3dSCy Schubert up = emalloc_zero(sizeof(*up)); 200c0b746e5SOllivier Robert pp = peer->procptr; 201c0b746e5SOllivier Robert pp->io.clock_recv = wwvb_receive; 2022b15cb3dSCy Schubert pp->io.srcclock = peer; 203c0b746e5SOllivier Robert pp->io.datalen = 0; 204c0b746e5SOllivier Robert pp->io.fd = fd; 205c0b746e5SOllivier Robert if (!io_addclock(&pp->io)) { 206ea906c41SOllivier Robert close(fd); 2072b15cb3dSCy Schubert pp->io.fd = -1; 208c0b746e5SOllivier Robert free(up); 209c0b746e5SOllivier Robert return (0); 210c0b746e5SOllivier Robert } 2112b15cb3dSCy Schubert pp->unitptr = up; 212c0b746e5SOllivier Robert 213c0b746e5SOllivier Robert /* 214c0b746e5SOllivier Robert * Initialize miscellaneous variables 215c0b746e5SOllivier Robert */ 216c0b746e5SOllivier Robert peer->precision = PRECISION; 217c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 2182b15cb3dSCy Schubert memcpy(&pp->refid, REFID, 4); 219c0b746e5SOllivier Robert return (1); 220c0b746e5SOllivier Robert } 221c0b746e5SOllivier Robert 222c0b746e5SOllivier Robert 223c0b746e5SOllivier Robert /* 224c0b746e5SOllivier Robert * wwvb_shutdown - shut down the clock 225c0b746e5SOllivier Robert */ 226c0b746e5SOllivier Robert static void 227c0b746e5SOllivier Robert wwvb_shutdown( 228c0b746e5SOllivier Robert int unit, 229c0b746e5SOllivier Robert struct peer *peer 230c0b746e5SOllivier Robert ) 231c0b746e5SOllivier Robert { 232c0b746e5SOllivier Robert struct refclockproc * pp; 2332b15cb3dSCy Schubert struct wwvbunit * up; 234c0b746e5SOllivier Robert 235c0b746e5SOllivier Robert pp = peer->procptr; 2362b15cb3dSCy Schubert up = pp->unitptr; 2372b15cb3dSCy Schubert if (-1 != pp->io.fd) 238c0b746e5SOllivier Robert io_closeclock(&pp->io); 2392b15cb3dSCy Schubert if (NULL != up) 240c0b746e5SOllivier Robert free(up); 241c0b746e5SOllivier Robert } 242c0b746e5SOllivier Robert 243c0b746e5SOllivier Robert 244c0b746e5SOllivier Robert /* 245c0b746e5SOllivier Robert * wwvb_receive - receive data from the serial interface 246c0b746e5SOllivier Robert */ 247c0b746e5SOllivier Robert static void 248c0b746e5SOllivier Robert wwvb_receive( 249c0b746e5SOllivier Robert struct recvbuf *rbufp 250c0b746e5SOllivier Robert ) 251c0b746e5SOllivier Robert { 252c0b746e5SOllivier Robert struct wwvbunit *up; 253c0b746e5SOllivier Robert struct refclockproc *pp; 254c0b746e5SOllivier Robert struct peer *peer; 255c0b746e5SOllivier Robert 256c0b746e5SOllivier Robert l_fp trtmp; /* arrival timestamp */ 257c0b746e5SOllivier Robert int tz; /* time zone */ 258c0b746e5SOllivier Robert int day, month; /* ddd conversion */ 259c0b746e5SOllivier Robert int temp; /* int temp */ 260c0b746e5SOllivier Robert char syncchar; /* synchronization indicator */ 261c0b746e5SOllivier Robert char qualchar; /* quality indicator */ 262c0b746e5SOllivier Robert char leapchar; /* leap indicator */ 263c0b746e5SOllivier Robert char dstchar; /* daylight/standard indicator */ 264224ba2bdSOllivier Robert char tmpchar; /* trashbin */ 265c0b746e5SOllivier Robert 266c0b746e5SOllivier Robert /* 267c0b746e5SOllivier Robert * Initialize pointers and read the timecode and timestamp 268c0b746e5SOllivier Robert */ 2692b15cb3dSCy Schubert peer = rbufp->recv_peer; 270c0b746e5SOllivier Robert pp = peer->procptr; 2712b15cb3dSCy Schubert up = pp->unitptr; 272c0b746e5SOllivier Robert temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); 273c0b746e5SOllivier Robert 274c0b746e5SOllivier Robert /* 275c0b746e5SOllivier Robert * Note we get a buffer and timestamp for both a <cr> and <lf>, 276c0b746e5SOllivier Robert * but only the <cr> timestamp is retained. Note: in format 0 on 277c0b746e5SOllivier Robert * a Netclock/2 or upgraded 8170 the start bit is delayed 100 278c0b746e5SOllivier Robert * +-50 us relative to the pps; however, on an unmodified 8170 279c0b746e5SOllivier Robert * the start bit can be delayed up to 10 ms. In format 2 the 280c0b746e5SOllivier Robert * reading precision is only to the millisecond. Thus, unless 281ea906c41SOllivier Robert * you have a PPS gadget and don't have to have the year, format 282c0b746e5SOllivier Robert * 0 provides the lowest jitter. 2832b15cb3dSCy Schubert * Save the timestamp of each <CR> in up->laststamp. Lines with 2842b15cb3dSCy Schubert * no characters occur for every <LF>, and for some <CR>s when 2852b15cb3dSCy Schubert * format 0 is used. Format 0 starts and ends each cycle with a 2862b15cb3dSCy Schubert * <CR><LF> pair, format 2 starts each cycle with its only pair. 2872b15cb3dSCy Schubert * The preceding <CR> is the on-time character for both formats. 2882b15cb3dSCy Schubert * The timestamp provided with non-empty lines corresponds to 2892b15cb3dSCy Schubert * the <CR> following the timecode, which is ultimately not used 2902b15cb3dSCy Schubert * with format 0 and is used for the following timecode for 2912b15cb3dSCy Schubert * format 2. 292c0b746e5SOllivier Robert */ 293c0b746e5SOllivier Robert if (temp == 0) { 2942b15cb3dSCy Schubert if (up->prev_eol_cr) { 2952b15cb3dSCy Schubert DPRINTF(2, ("wwvb: <LF> @ %s\n", 2962b15cb3dSCy Schubert prettydate(&trtmp))); 2972b15cb3dSCy Schubert } else { 298c0b746e5SOllivier Robert up->laststamp = trtmp; 2992b15cb3dSCy Schubert DPRINTF(2, ("wwvb: <CR> @ %s\n", 3002b15cb3dSCy Schubert prettydate(&trtmp))); 3012b15cb3dSCy Schubert } 3022b15cb3dSCy Schubert up->prev_eol_cr = !up->prev_eol_cr; 303c0b746e5SOllivier Robert return; 304c0b746e5SOllivier Robert } 305c0b746e5SOllivier Robert pp->lencode = temp; 306c0b746e5SOllivier Robert pp->lastrec = up->laststamp; 3072b15cb3dSCy Schubert up->laststamp = trtmp; 3082b15cb3dSCy Schubert up->prev_eol_cr = TRUE; 3092b15cb3dSCy Schubert DPRINTF(2, ("wwvb: code @ %s\n" 3102b15cb3dSCy Schubert " using %s minus one char\n", 3112b15cb3dSCy Schubert prettydate(&trtmp), prettydate(&pp->lastrec))); 3122b15cb3dSCy Schubert if (L_ISZERO(&pp->lastrec)) 3132b15cb3dSCy Schubert return; 314c0b746e5SOllivier Robert 315c0b746e5SOllivier Robert /* 316c0b746e5SOllivier Robert * We get down to business, check the timecode format and decode 317c0b746e5SOllivier Robert * its contents. This code uses the timecode length to determine 318c0b746e5SOllivier Robert * format 0, 2 or 3. If the timecode has invalid length or is 319c0b746e5SOllivier Robert * not in proper format, we declare bad format and exit. 320c0b746e5SOllivier Robert */ 321c0b746e5SOllivier Robert syncchar = qualchar = leapchar = dstchar = ' '; 322c0b746e5SOllivier Robert tz = 0; 323c0b746e5SOllivier Robert switch (pp->lencode) { 324c0b746e5SOllivier Robert 325c0b746e5SOllivier Robert case LENWWVB0: 326c0b746e5SOllivier Robert 327c0b746e5SOllivier Robert /* 328c0b746e5SOllivier Robert * Timecode format 0: "I ddd hh:mm:ss DTZ=nn" 329c0b746e5SOllivier Robert */ 330c0b746e5SOllivier Robert if (sscanf(pp->a_lastcode, 331224ba2bdSOllivier Robert "%c %3d %2d:%2d:%2d%c%cTZ=%2d", 332c0b746e5SOllivier Robert &syncchar, &pp->day, &pp->hour, &pp->minute, 3332b15cb3dSCy Schubert &pp->second, &tmpchar, &dstchar, &tz) == 8) { 3349c2daa00SOllivier Robert pp->nsec = 0; 335c0b746e5SOllivier Robert break; 3362b15cb3dSCy Schubert } 3372b15cb3dSCy Schubert goto bad_format; 338c0b746e5SOllivier Robert 339c0b746e5SOllivier Robert case LENWWVB2: 340c0b746e5SOllivier Robert 341c0b746e5SOllivier Robert /* 342224ba2bdSOllivier Robert * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */ 343c0b746e5SOllivier Robert if (sscanf(pp->a_lastcode, 3449c2daa00SOllivier Robert "%c%c %2d %3d %2d:%2d:%2d.%3ld %c", 345c0b746e5SOllivier Robert &syncchar, &qualchar, &pp->year, &pp->day, 3469c2daa00SOllivier Robert &pp->hour, &pp->minute, &pp->second, &pp->nsec, 3472b15cb3dSCy Schubert &leapchar) == 9) { 3489c2daa00SOllivier Robert pp->nsec *= 1000000; 349c0b746e5SOllivier Robert break; 3502b15cb3dSCy Schubert } 3512b15cb3dSCy Schubert goto bad_format; 352c0b746e5SOllivier Robert 353c0b746e5SOllivier Robert case LENWWVB3: 354c0b746e5SOllivier Robert 355c0b746e5SOllivier Robert /* 356c0b746e5SOllivier Robert * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#" 3572b15cb3dSCy Schubert * WARNING: Undocumented, and the on-time character # is 3582b15cb3dSCy Schubert * not yet handled correctly by this driver. It may be 3592b15cb3dSCy Schubert * as simple as compensating for an additional 1/960 s. 360c0b746e5SOllivier Robert */ 361c0b746e5SOllivier Robert if (sscanf(pp->a_lastcode, 362c0b746e5SOllivier Robert "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c", 363c0b746e5SOllivier Robert &syncchar, &pp->year, &month, &day, &pp->hour, 364c0b746e5SOllivier Robert &pp->minute, &pp->second, &dstchar, &leapchar) == 8) 365c0b746e5SOllivier Robert { 366c0b746e5SOllivier Robert pp->day = ymd2yd(pp->year, month, day); 3679c2daa00SOllivier Robert pp->nsec = 0; 368c0b746e5SOllivier Robert break; 369c0b746e5SOllivier Robert } 3702b15cb3dSCy Schubert goto bad_format; 371c0b746e5SOllivier Robert 372c0b746e5SOllivier Robert default: 3732b15cb3dSCy Schubert bad_format: 374c0b746e5SOllivier Robert 375c0b746e5SOllivier Robert /* 376c0b746e5SOllivier Robert * Unknown format: If dumping internal table, record 377c0b746e5SOllivier Robert * stats; otherwise, declare bad format. 378c0b746e5SOllivier Robert */ 379c0b746e5SOllivier Robert if (up->linect > 0) { 380c0b746e5SOllivier Robert up->linect--; 381c0b746e5SOllivier Robert record_clock_stats(&peer->srcadr, 382c0b746e5SOllivier Robert pp->a_lastcode); 383c0b746e5SOllivier Robert } else { 384c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 385c0b746e5SOllivier Robert } 386c0b746e5SOllivier Robert return; 387c0b746e5SOllivier Robert } 388c0b746e5SOllivier Robert 389c0b746e5SOllivier Robert /* 390c0b746e5SOllivier Robert * Decode synchronization, quality and leap characters. If 391c0b746e5SOllivier Robert * unsynchronized, set the leap bits accordingly and exit. 392c0b746e5SOllivier Robert * Otherwise, set the leap bits according to the leap character. 393c0b746e5SOllivier Robert * Once synchronized, the dispersion depends only on the 394c0b746e5SOllivier Robert * quality character. 395c0b746e5SOllivier Robert */ 396c0b746e5SOllivier Robert switch (qualchar) { 397c0b746e5SOllivier Robert 398c0b746e5SOllivier Robert case ' ': 399c0b746e5SOllivier Robert pp->disp = .001; 4009c2daa00SOllivier Robert pp->lastref = pp->lastrec; 401c0b746e5SOllivier Robert break; 402c0b746e5SOllivier Robert 403c0b746e5SOllivier Robert case 'A': 404c0b746e5SOllivier Robert pp->disp = .01; 405c0b746e5SOllivier Robert break; 406c0b746e5SOllivier Robert 407c0b746e5SOllivier Robert case 'B': 408c0b746e5SOllivier Robert pp->disp = .1; 409c0b746e5SOllivier Robert break; 410c0b746e5SOllivier Robert 411c0b746e5SOllivier Robert case 'C': 412c0b746e5SOllivier Robert pp->disp = .5; 413c0b746e5SOllivier Robert break; 414c0b746e5SOllivier Robert 415c0b746e5SOllivier Robert case 'D': 416c0b746e5SOllivier Robert pp->disp = MAXDISPERSE; 417c0b746e5SOllivier Robert break; 418c0b746e5SOllivier Robert 419c0b746e5SOllivier Robert default: 420c0b746e5SOllivier Robert pp->disp = MAXDISPERSE; 421c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 422c0b746e5SOllivier Robert break; 423c0b746e5SOllivier Robert } 424c0b746e5SOllivier Robert if (syncchar != ' ') 425c0b746e5SOllivier Robert pp->leap = LEAP_NOTINSYNC; 426c0b746e5SOllivier Robert else if (leapchar == 'L') 427c0b746e5SOllivier Robert pp->leap = LEAP_ADDSECOND; 428c0b746e5SOllivier Robert else 429c0b746e5SOllivier Robert pp->leap = LEAP_NOWARNING; 430c0b746e5SOllivier Robert 431c0b746e5SOllivier Robert /* 432c0b746e5SOllivier Robert * Process the new sample in the median filter and determine the 4332b15cb3dSCy Schubert * timecode timestamp, but only if the PPS is not in control. 434c0b746e5SOllivier Robert */ 4352b15cb3dSCy Schubert #ifdef HAVE_PPSAPI 4362b15cb3dSCy Schubert up->tcount++; 4372b15cb3dSCy Schubert if (peer->flags & FLAG_PPS) 4382b15cb3dSCy Schubert return; 4392b15cb3dSCy Schubert 4402b15cb3dSCy Schubert #endif /* HAVE_PPSAPI */ 4412b15cb3dSCy Schubert if (!refclock_process_f(pp, pp->fudgetime2)) 442c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 443ea906c41SOllivier Robert } 444ea906c41SOllivier Robert 445ea906c41SOllivier Robert 446ea906c41SOllivier Robert /* 447ea906c41SOllivier Robert * wwvb_timer - called once per second by the transmit procedure 448ea906c41SOllivier Robert */ 449ea906c41SOllivier Robert static void 450ea906c41SOllivier Robert wwvb_timer( 451ea906c41SOllivier Robert int unit, 452ea906c41SOllivier Robert struct peer *peer 453ea906c41SOllivier Robert ) 454ea906c41SOllivier Robert { 455ea906c41SOllivier Robert register struct wwvbunit *up; 456ea906c41SOllivier Robert struct refclockproc *pp; 457ea906c41SOllivier Robert char pollchar; /* character sent to clock */ 4582b15cb3dSCy Schubert #ifdef DEBUG 4592b15cb3dSCy Schubert l_fp now; 4602b15cb3dSCy Schubert #endif 461ea906c41SOllivier Robert 462ea906c41SOllivier Robert /* 463ea906c41SOllivier Robert * Time to poll the clock. The Spectracom clock responds to a 464ea906c41SOllivier Robert * 'T' by returning a timecode in the format(s) specified above. 465ea906c41SOllivier Robert * Note there is no checking on state, since this may not be the 466ea906c41SOllivier Robert * only customer reading the clock. Only one customer need poll 467ea906c41SOllivier Robert * the clock; all others just listen in. 468ea906c41SOllivier Robert */ 469ea906c41SOllivier Robert pp = peer->procptr; 4702b15cb3dSCy Schubert up = pp->unitptr; 471ea906c41SOllivier Robert if (up->linect > 0) 472ea906c41SOllivier Robert pollchar = 'R'; 473ea906c41SOllivier Robert else 474ea906c41SOllivier Robert pollchar = 'T'; 475ea906c41SOllivier Robert if (write(pp->io.fd, &pollchar, 1) != 1) 476ea906c41SOllivier Robert refclock_report(peer, CEVNT_FAULT); 4772b15cb3dSCy Schubert #ifdef DEBUG 4782b15cb3dSCy Schubert get_systime(&now); 4792b15cb3dSCy Schubert if (debug) 4802b15cb3dSCy Schubert printf("%c poll at %s\n", pollchar, prettydate(&now)); 4812b15cb3dSCy Schubert #endif 4822b15cb3dSCy Schubert #ifdef HAVE_PPSAPI 4832b15cb3dSCy Schubert if (up->ppsapi_lit && 4842b15cb3dSCy Schubert refclock_pps(peer, &up->atom, pp->sloppyclockflag) > 0) { 4852b15cb3dSCy Schubert up->pcount++, 4862b15cb3dSCy Schubert peer->flags |= FLAG_PPS; 4872b15cb3dSCy Schubert peer->precision = PPS_PRECISION; 4882b15cb3dSCy Schubert } 4892b15cb3dSCy Schubert #endif /* HAVE_PPSAPI */ 490c0b746e5SOllivier Robert } 491c0b746e5SOllivier Robert 492c0b746e5SOllivier Robert 493c0b746e5SOllivier Robert /* 494c0b746e5SOllivier Robert * wwvb_poll - called by the transmit procedure 495c0b746e5SOllivier Robert */ 496c0b746e5SOllivier Robert static void 497c0b746e5SOllivier Robert wwvb_poll( 498c0b746e5SOllivier Robert int unit, 499c0b746e5SOllivier Robert struct peer *peer 500c0b746e5SOllivier Robert ) 501c0b746e5SOllivier Robert { 502c0b746e5SOllivier Robert register struct wwvbunit *up; 503c0b746e5SOllivier Robert struct refclockproc *pp; 504c0b746e5SOllivier Robert 505c0b746e5SOllivier Robert /* 506ea906c41SOllivier Robert * Sweep up the samples received since the last poll. If none 507ea906c41SOllivier Robert * are received, declare a timeout and keep going. 508c0b746e5SOllivier Robert */ 509c0b746e5SOllivier Robert pp = peer->procptr; 5102b15cb3dSCy Schubert up = pp->unitptr; 511ea906c41SOllivier Robert pp->polls++; 512ea906c41SOllivier Robert 513ea906c41SOllivier Robert /* 514ea906c41SOllivier Robert * If the monitor flag is set (flag4), we dump the internal 515ea906c41SOllivier Robert * quality table at the first timecode beginning the day. 516ea906c41SOllivier Robert */ 517ea906c41SOllivier Robert if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour < 518ea906c41SOllivier Robert (int)up->lasthour) 519ea906c41SOllivier Robert up->linect = MONLIN; 5202b15cb3dSCy Schubert up->lasthour = (u_char)pp->hour; 521ea906c41SOllivier Robert 522ea906c41SOllivier Robert /* 523ea906c41SOllivier Robert * Process median filter samples. If none received, declare a 524ea906c41SOllivier Robert * timeout and keep going. 525ea906c41SOllivier Robert */ 5262b15cb3dSCy Schubert #ifdef HAVE_PPSAPI 5272b15cb3dSCy Schubert if (up->pcount == 0) { 5282b15cb3dSCy Schubert peer->flags &= ~FLAG_PPS; 5292b15cb3dSCy Schubert peer->precision = PRECISION; 5302b15cb3dSCy Schubert } 5312b15cb3dSCy Schubert if (up->tcount == 0) { 5322b15cb3dSCy Schubert pp->coderecv = pp->codeproc; 5332b15cb3dSCy Schubert refclock_report(peer, CEVNT_TIMEOUT); 5342b15cb3dSCy Schubert return; 5352b15cb3dSCy Schubert } 5362b15cb3dSCy Schubert up->pcount = up->tcount = 0; 5372b15cb3dSCy Schubert #else /* HAVE_PPSAPI */ 538c0b746e5SOllivier Robert if (pp->coderecv == pp->codeproc) { 539c0b746e5SOllivier Robert refclock_report(peer, CEVNT_TIMEOUT); 540c0b746e5SOllivier Robert return; 541c0b746e5SOllivier Robert } 5422b15cb3dSCy Schubert #endif /* HAVE_PPSAPI */ 543c0b746e5SOllivier Robert refclock_receive(peer); 5449c2daa00SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 5459c2daa00SOllivier Robert #ifdef DEBUG 5469c2daa00SOllivier Robert if (debug) 5479c2daa00SOllivier Robert printf("wwvb: timecode %d %s\n", pp->lencode, 5489c2daa00SOllivier Robert pp->a_lastcode); 5499c2daa00SOllivier Robert #endif 550c0b746e5SOllivier Robert } 551c0b746e5SOllivier Robert 5522b15cb3dSCy Schubert 5532b15cb3dSCy Schubert /* 5542b15cb3dSCy Schubert * wwvb_control - fudge parameters have been set or changed 5552b15cb3dSCy Schubert */ 5562b15cb3dSCy Schubert #ifdef HAVE_PPSAPI 5572b15cb3dSCy Schubert static void 5582b15cb3dSCy Schubert wwvb_control( 5592b15cb3dSCy Schubert int unit, 5602b15cb3dSCy Schubert const struct refclockstat *in_st, 5612b15cb3dSCy Schubert struct refclockstat *out_st, 5622b15cb3dSCy Schubert struct peer *peer 5632b15cb3dSCy Schubert ) 5642b15cb3dSCy Schubert { 5652b15cb3dSCy Schubert register struct wwvbunit *up; 5662b15cb3dSCy Schubert struct refclockproc *pp; 5672b15cb3dSCy Schubert 5682b15cb3dSCy Schubert pp = peer->procptr; 5692b15cb3dSCy Schubert up = pp->unitptr; 5702b15cb3dSCy Schubert 5712b15cb3dSCy Schubert if (!(pp->sloppyclockflag & CLK_FLAG1)) { 5722b15cb3dSCy Schubert if (!up->ppsapi_tried) 5732b15cb3dSCy Schubert return; 5742b15cb3dSCy Schubert up->ppsapi_tried = 0; 5752b15cb3dSCy Schubert if (!up->ppsapi_lit) 5762b15cb3dSCy Schubert return; 5772b15cb3dSCy Schubert peer->flags &= ~FLAG_PPS; 5782b15cb3dSCy Schubert peer->precision = PRECISION; 5792b15cb3dSCy Schubert time_pps_destroy(up->atom.handle); 5802b15cb3dSCy Schubert up->atom.handle = 0; 5812b15cb3dSCy Schubert up->ppsapi_lit = 0; 5822b15cb3dSCy Schubert return; 5832b15cb3dSCy Schubert } 5842b15cb3dSCy Schubert 5852b15cb3dSCy Schubert if (up->ppsapi_tried) 5862b15cb3dSCy Schubert return; 5872b15cb3dSCy Schubert /* 5882b15cb3dSCy Schubert * Light up the PPSAPI interface. 5892b15cb3dSCy Schubert */ 5902b15cb3dSCy Schubert up->ppsapi_tried = 1; 5912b15cb3dSCy Schubert if (refclock_ppsapi(pp->io.fd, &up->atom)) { 5922b15cb3dSCy Schubert up->ppsapi_lit = 1; 5932b15cb3dSCy Schubert return; 5942b15cb3dSCy Schubert } 5952b15cb3dSCy Schubert 5962b15cb3dSCy Schubert msyslog(LOG_WARNING, "%s flag1 1 but PPSAPI fails", 5972b15cb3dSCy Schubert refnumtoa(&peer->srcadr)); 5982b15cb3dSCy Schubert } 5992b15cb3dSCy Schubert #endif /* HAVE_PPSAPI */ 6002b15cb3dSCy Schubert 601c0b746e5SOllivier Robert #else 602*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT 603c0b746e5SOllivier Robert #endif /* REFCLOCK */ 604