154359Sroberto/* 2182007Sroberto * refclock_wwvb - clock driver for Spectracom WWVB and GPS receivers 354359Sroberto */ 454359Sroberto 554359Sroberto#ifdef HAVE_CONFIG_H 654359Sroberto#include <config.h> 754359Sroberto#endif 854359Sroberto 956746Sroberto#if defined(REFCLOCK) && defined(CLOCK_SPECTRACOM) 1054359Sroberto 1154359Sroberto#include "ntpd.h" 1254359Sroberto#include "ntp_io.h" 1354359Sroberto#include "ntp_refclock.h" 1454359Sroberto#include "ntp_calendar.h" 1554359Sroberto#include "ntp_stdlib.h" 1654359Sroberto 1782498Sroberto#include <stdio.h> 1882498Sroberto#include <ctype.h> 1982498Sroberto 20290001Sglebius#ifdef HAVE_PPSAPI 21290001Sglebius#include "ppsapi_timepps.h" 22290001Sglebius#include "refclock_atom.h" 23290001Sglebius#endif /* HAVE_PPSAPI */ 24290001Sglebius 2554359Sroberto/* 2654359Sroberto * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB 2754359Sroberto * Synchronized Clocks and the Netclock/GPS Master Clock. Both the WWVB 2854359Sroberto * and GPS clocks have proven reliable sources of time; however, the 2954359Sroberto * WWVB clocks have proven vulnerable to high ambient conductive RF 3054359Sroberto * interference. The claimed accuracy of the WWVB clocks is 100 us 3154359Sroberto * relative to the broadcast signal, while the claimed accuracy of the 3254359Sroberto * GPS clock is 50 ns; however, in most cases the actual accuracy is 3354359Sroberto * limited by the resolution of the timecode and the latencies of the 3454359Sroberto * serial interface and operating system. 3554359Sroberto * 3654359Sroberto * The WWVB and GPS clocks should be configured for 24-hour display, 3754359Sroberto * AUTO DST off, time zone 0 (UTC), data format 0 or 2 (see below) and 3854359Sroberto * baud rate 9600. If the clock is to used as the source for the IRIG 3954359Sroberto * Audio Decoder (refclock_irig.c in this distribution), it should be 4054359Sroberto * configured for AM IRIG output and IRIG format 1 (IRIG B with 4154359Sroberto * signature control). The GPS clock can be configured either to respond 4254359Sroberto * to a 'T' poll character or left running continuously. 4354359Sroberto * 4454359Sroberto * There are two timecode formats used by these clocks. Format 0, which 4554359Sroberto * is available with both the Netclock/2 and 8170, and format 2, which 4654359Sroberto * is available only with the Netclock/2, specially modified 8170 and 4754359Sroberto * GPS. 4854359Sroberto * 4954359Sroberto * Format 0 (22 ASCII printing characters): 5054359Sroberto * 51182007Sroberto * <cr><lf>i ddd hh:mm:ss TZ=zz<cr><lf> 5254359Sroberto * 5354359Sroberto * on-time = first <cr> 5454359Sroberto * hh:mm:ss = hours, minutes, seconds 5554359Sroberto * i = synchronization flag (' ' = in synch, '?' = out of synch) 5654359Sroberto * 57290001Sglebius * The alarm condition is indicated by other than ' ' at i, which occurs 5854359Sroberto * during initial synchronization and when received signal is lost for 5954359Sroberto * about ten hours. 6054359Sroberto * 6154359Sroberto * Format 2 (24 ASCII printing characters): 6254359Sroberto * 6354359Sroberto * <cr><lf>iqyy ddd hh:mm:ss.fff ld 6454359Sroberto * 6554359Sroberto * on-time = <cr> 6654359Sroberto * i = synchronization flag (' ' = in synch, '?' = out of synch) 6754359Sroberto * q = quality indicator (' ' = locked, 'A'...'D' = unlocked) 6854359Sroberto * yy = year (as broadcast) 6954359Sroberto * ddd = day of year 7054359Sroberto * hh:mm:ss.fff = hours, minutes, seconds, milliseconds 7154359Sroberto * 72290001Sglebius * The alarm condition is indicated by other than ' ' at i, which occurs 7354359Sroberto * during initial synchronization and when received signal is lost for 7454359Sroberto * about ten hours. The unlock condition is indicated by other than ' ' 7554359Sroberto * at q. 7654359Sroberto * 7754359Sroberto * The q is normally ' ' when the time error is less than 1 ms and a 7854359Sroberto * character in the set 'A'...'D' when the time error is less than 10, 7954359Sroberto * 100, 500 and greater than 500 ms respectively. The l is normally ' ', 8054359Sroberto * but is set to 'L' early in the month of an upcoming UTC leap second 8154359Sroberto * and reset to ' ' on the first day of the following month. The d is 8254359Sroberto * set to 'S' for standard time 'I' on the day preceding a switch to 8354359Sroberto * daylight time, 'D' for daylight time and 'O' on the day preceding a 8454359Sroberto * switch to standard time. The start bit of the first <cr> is 8554359Sroberto * synchronized to the indicated time as returned. 8654359Sroberto * 8754359Sroberto * This driver does not need to be told which format is in use - it 88182007Sroberto * figures out which one from the length of the message. The driver 89182007Sroberto * makes no attempt to correct for the intrinsic jitter of the radio 90182007Sroberto * itself, which is a known problem with the older radios. 9154359Sroberto * 92290001Sglebius * PPS Signal Processing 93290001Sglebius * 94290001Sglebius * When PPS signal processing is enabled, and when the system clock has 95290001Sglebius * been set by this or another driver and the PPS signal offset is 96290001Sglebius * within 0.4 s of the system clock offset, the PPS signal replaces the 97290001Sglebius * timecode for as long as the PPS signal is active. If for some reason 98290001Sglebius * the PPS signal fails for one or more poll intervals, the driver 99290001Sglebius * reverts to the timecode. If the timecode fails for one or more poll 100290001Sglebius * intervals, the PPS signal is disconnected. 101290001Sglebius * 10254359Sroberto * Fudge Factors 10354359Sroberto * 10454359Sroberto * This driver can retrieve a table of quality data maintained 10554359Sroberto * internally by the Netclock/2 clock. If flag4 of the fudge 10654359Sroberto * configuration command is set to 1, the driver will retrieve this 107182007Sroberto * table and write it to the clockstats file when the first timecode 10854359Sroberto * message of a new day is received. 109182007Sroberto * 110182007Sroberto * PPS calibration fudge time 1: format 0 .003134, format 2 .004034 11154359Sroberto */ 11254359Sroberto/* 11354359Sroberto * Interface definitions 11454359Sroberto */ 11554359Sroberto#define DEVICE "/dev/wwvb%d" /* device name and unit */ 11654359Sroberto#define SPEED232 B9600 /* uart speed (9600 baud) */ 11754359Sroberto#define PRECISION (-13) /* precision assumed (about 100 us) */ 118290001Sglebius#define PPS_PRECISION (-13) /* precision assumed (about 100 us) */ 11954359Sroberto#define REFID "WWVB" /* reference ID */ 120182007Sroberto#define DESCRIPTION "Spectracom WWVB/GPS Receiver" /* WRU */ 12154359Sroberto 12254359Sroberto#define LENWWVB0 22 /* format 0 timecode length */ 12354359Sroberto#define LENWWVB2 24 /* format 2 timecode length */ 124290001Sglebius#define LENWWVB3 29 /* format 3 timecode length */ 12554359Sroberto#define MONLIN 15 /* number of monitoring lines */ 12654359Sroberto 12754359Sroberto/* 12854359Sroberto * WWVB unit control structure 12954359Sroberto */ 13054359Srobertostruct wwvbunit { 131290001Sglebius#ifdef HAVE_PPSAPI 132290001Sglebius struct refclock_atom atom; /* PPSAPI structure */ 133290001Sglebius int ppsapi_tried; /* attempt PPSAPI once */ 134290001Sglebius int ppsapi_lit; /* time_pps_create() worked */ 135290001Sglebius int tcount; /* timecode sample counter */ 136290001Sglebius int pcount; /* PPS sample counter */ 137290001Sglebius#endif /* HAVE_PPSAPI */ 138290001Sglebius l_fp laststamp; /* last <CR> timestamp */ 139290001Sglebius int prev_eol_cr; /* was last EOL <CR> (not <LF>)? */ 14054359Sroberto u_char lasthour; /* last hour (for monitor) */ 14154359Sroberto u_char linect; /* count ignored lines (for monitor */ 14254359Sroberto}; 14354359Sroberto 14454359Sroberto/* 14554359Sroberto * Function prototypes 14654359Sroberto */ 147290001Sglebiusstatic int wwvb_start (int, struct peer *); 148290001Sglebiusstatic void wwvb_shutdown (int, struct peer *); 149290001Sglebiusstatic void wwvb_receive (struct recvbuf *); 150290001Sglebiusstatic void wwvb_poll (int, struct peer *); 151290001Sglebiusstatic void wwvb_timer (int, struct peer *); 152290001Sglebius#ifdef HAVE_PPSAPI 153290001Sglebiusstatic void wwvb_control (int, const struct refclockstat *, 154290001Sglebius struct refclockstat *, struct peer *); 155290001Sglebius#define WWVB_CONTROL wwvb_control 156290001Sglebius#else 157290001Sglebius#define WWVB_CONTROL noentry 158290001Sglebius#endif /* HAVE_PPSAPI */ 15954359Sroberto 16054359Sroberto/* 16154359Sroberto * Transfer vector 16254359Sroberto */ 16354359Srobertostruct refclock refclock_wwvb = { 16454359Sroberto wwvb_start, /* start up driver */ 16554359Sroberto wwvb_shutdown, /* shut down driver */ 16654359Sroberto wwvb_poll, /* transmit poll message */ 167290001Sglebius WWVB_CONTROL, /* fudge set/change notification */ 16854359Sroberto noentry, /* initialize driver (not used) */ 16954359Sroberto noentry, /* not used (old wwvb_buginfo) */ 170182007Sroberto wwvb_timer /* called once per second */ 17154359Sroberto}; 17254359Sroberto 17354359Sroberto 17454359Sroberto/* 17554359Sroberto * wwvb_start - open the devices and initialize data for processing 17654359Sroberto */ 17754359Srobertostatic int 17854359Srobertowwvb_start( 17954359Sroberto int unit, 18054359Sroberto struct peer *peer 18154359Sroberto ) 18254359Sroberto{ 18354359Sroberto register struct wwvbunit *up; 18454359Sroberto struct refclockproc *pp; 18554359Sroberto int fd; 18654359Sroberto char device[20]; 18754359Sroberto 18854359Sroberto /* 18954359Sroberto * Open serial port. Use CLK line discipline, if available. 19054359Sroberto */ 191290001Sglebius snprintf(device, sizeof(device), DEVICE, unit); 192290001Sglebius fd = refclock_open(device, SPEED232, LDISC_CLK); 193290001Sglebius if (fd <= 0) 19454359Sroberto return (0); 19554359Sroberto 19654359Sroberto /* 19754359Sroberto * Allocate and initialize unit structure 19854359Sroberto */ 199290001Sglebius up = emalloc_zero(sizeof(*up)); 20054359Sroberto pp = peer->procptr; 20154359Sroberto pp->io.clock_recv = wwvb_receive; 202290001Sglebius pp->io.srcclock = peer; 20354359Sroberto pp->io.datalen = 0; 20454359Sroberto pp->io.fd = fd; 20554359Sroberto if (!io_addclock(&pp->io)) { 206182007Sroberto close(fd); 207290001Sglebius pp->io.fd = -1; 20854359Sroberto free(up); 20954359Sroberto return (0); 21054359Sroberto } 211290001Sglebius pp->unitptr = up; 21254359Sroberto 21354359Sroberto /* 21454359Sroberto * Initialize miscellaneous variables 21554359Sroberto */ 21654359Sroberto peer->precision = PRECISION; 21754359Sroberto pp->clockdesc = DESCRIPTION; 218290001Sglebius memcpy(&pp->refid, REFID, 4); 21954359Sroberto return (1); 22054359Sroberto} 22154359Sroberto 22254359Sroberto 22354359Sroberto/* 22454359Sroberto * wwvb_shutdown - shut down the clock 22554359Sroberto */ 22654359Srobertostatic void 22754359Srobertowwvb_shutdown( 22854359Sroberto int unit, 22954359Sroberto struct peer *peer 23054359Sroberto ) 23154359Sroberto{ 232290001Sglebius struct refclockproc * pp; 233290001Sglebius struct wwvbunit * up; 23454359Sroberto 23554359Sroberto pp = peer->procptr; 236290001Sglebius up = pp->unitptr; 237290001Sglebius if (-1 != pp->io.fd) 238290001Sglebius io_closeclock(&pp->io); 239290001Sglebius if (NULL != up) 240290001Sglebius free(up); 24154359Sroberto} 24254359Sroberto 24354359Sroberto 24454359Sroberto/* 24554359Sroberto * wwvb_receive - receive data from the serial interface 24654359Sroberto */ 24754359Srobertostatic void 24854359Srobertowwvb_receive( 24954359Sroberto struct recvbuf *rbufp 25054359Sroberto ) 25154359Sroberto{ 25254359Sroberto struct wwvbunit *up; 25354359Sroberto struct refclockproc *pp; 25454359Sroberto struct peer *peer; 25554359Sroberto 25654359Sroberto l_fp trtmp; /* arrival timestamp */ 25754359Sroberto int tz; /* time zone */ 25854359Sroberto int day, month; /* ddd conversion */ 25954359Sroberto int temp; /* int temp */ 26054359Sroberto char syncchar; /* synchronization indicator */ 26154359Sroberto char qualchar; /* quality indicator */ 26254359Sroberto char leapchar; /* leap indicator */ 26354359Sroberto char dstchar; /* daylight/standard indicator */ 26482498Sroberto char tmpchar; /* trashbin */ 26554359Sroberto 26654359Sroberto /* 26754359Sroberto * Initialize pointers and read the timecode and timestamp 26854359Sroberto */ 269290001Sglebius peer = rbufp->recv_peer; 27054359Sroberto pp = peer->procptr; 271290001Sglebius up = pp->unitptr; 27254359Sroberto temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); 27354359Sroberto 27454359Sroberto /* 27554359Sroberto * Note we get a buffer and timestamp for both a <cr> and <lf>, 27654359Sroberto * but only the <cr> timestamp is retained. Note: in format 0 on 27754359Sroberto * a Netclock/2 or upgraded 8170 the start bit is delayed 100 27854359Sroberto * +-50 us relative to the pps; however, on an unmodified 8170 27954359Sroberto * the start bit can be delayed up to 10 ms. In format 2 the 28054359Sroberto * reading precision is only to the millisecond. Thus, unless 281182007Sroberto * you have a PPS gadget and don't have to have the year, format 28254359Sroberto * 0 provides the lowest jitter. 283290001Sglebius * Save the timestamp of each <CR> in up->laststamp. Lines with 284290001Sglebius * no characters occur for every <LF>, and for some <CR>s when 285290001Sglebius * format 0 is used. Format 0 starts and ends each cycle with a 286290001Sglebius * <CR><LF> pair, format 2 starts each cycle with its only pair. 287290001Sglebius * The preceding <CR> is the on-time character for both formats. 288290001Sglebius * The timestamp provided with non-empty lines corresponds to 289290001Sglebius * the <CR> following the timecode, which is ultimately not used 290290001Sglebius * with format 0 and is used for the following timecode for 291290001Sglebius * format 2. 29254359Sroberto */ 29354359Sroberto if (temp == 0) { 294290001Sglebius if (up->prev_eol_cr) { 295290001Sglebius DPRINTF(2, ("wwvb: <LF> @ %s\n", 296290001Sglebius prettydate(&trtmp))); 297290001Sglebius } else { 298290001Sglebius up->laststamp = trtmp; 299290001Sglebius DPRINTF(2, ("wwvb: <CR> @ %s\n", 300290001Sglebius prettydate(&trtmp))); 301290001Sglebius } 302290001Sglebius up->prev_eol_cr = !up->prev_eol_cr; 30354359Sroberto return; 30454359Sroberto } 30554359Sroberto pp->lencode = temp; 30654359Sroberto pp->lastrec = up->laststamp; 307290001Sglebius up->laststamp = trtmp; 308290001Sglebius up->prev_eol_cr = TRUE; 309290001Sglebius DPRINTF(2, ("wwvb: code @ %s\n" 310290001Sglebius " using %s minus one char\n", 311290001Sglebius prettydate(&trtmp), prettydate(&pp->lastrec))); 312290001Sglebius if (L_ISZERO(&pp->lastrec)) 313290001Sglebius return; 31454359Sroberto 31554359Sroberto /* 31654359Sroberto * We get down to business, check the timecode format and decode 31754359Sroberto * its contents. This code uses the timecode length to determine 31854359Sroberto * format 0, 2 or 3. If the timecode has invalid length or is 31954359Sroberto * not in proper format, we declare bad format and exit. 32054359Sroberto */ 32154359Sroberto syncchar = qualchar = leapchar = dstchar = ' '; 32254359Sroberto tz = 0; 32354359Sroberto switch (pp->lencode) { 32454359Sroberto 325132451Sroberto case LENWWVB0: 32654359Sroberto 32754359Sroberto /* 32854359Sroberto * Timecode format 0: "I ddd hh:mm:ss DTZ=nn" 32954359Sroberto */ 33054359Sroberto if (sscanf(pp->a_lastcode, 33182498Sroberto "%c %3d %2d:%2d:%2d%c%cTZ=%2d", 33254359Sroberto &syncchar, &pp->day, &pp->hour, &pp->minute, 333290001Sglebius &pp->second, &tmpchar, &dstchar, &tz) == 8) { 334132451Sroberto pp->nsec = 0; 33554359Sroberto break; 336290001Sglebius } 337290001Sglebius goto bad_format; 33854359Sroberto 339132451Sroberto case LENWWVB2: 34054359Sroberto 34154359Sroberto /* 34282498Sroberto * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */ 34354359Sroberto if (sscanf(pp->a_lastcode, 344132451Sroberto "%c%c %2d %3d %2d:%2d:%2d.%3ld %c", 34554359Sroberto &syncchar, &qualchar, &pp->year, &pp->day, 346132451Sroberto &pp->hour, &pp->minute, &pp->second, &pp->nsec, 347290001Sglebius &leapchar) == 9) { 348132451Sroberto pp->nsec *= 1000000; 34954359Sroberto break; 350290001Sglebius } 351290001Sglebius goto bad_format; 35254359Sroberto 353132451Sroberto case LENWWVB3: 35454359Sroberto 355290001Sglebius /* 35654359Sroberto * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#" 357290001Sglebius * WARNING: Undocumented, and the on-time character # is 358290001Sglebius * not yet handled correctly by this driver. It may be 359290001Sglebius * as simple as compensating for an additional 1/960 s. 36054359Sroberto */ 36154359Sroberto if (sscanf(pp->a_lastcode, 36254359Sroberto "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c", 36354359Sroberto &syncchar, &pp->year, &month, &day, &pp->hour, 36454359Sroberto &pp->minute, &pp->second, &dstchar, &leapchar) == 8) 36554359Sroberto { 36654359Sroberto pp->day = ymd2yd(pp->year, month, day); 367132451Sroberto pp->nsec = 0; 36854359Sroberto break; 36954359Sroberto } 370290001Sglebius goto bad_format; 37154359Sroberto 372132451Sroberto default: 373290001Sglebius bad_format: 37454359Sroberto 37554359Sroberto /* 37654359Sroberto * Unknown format: If dumping internal table, record 37754359Sroberto * stats; otherwise, declare bad format. 37854359Sroberto */ 37954359Sroberto if (up->linect > 0) { 38054359Sroberto up->linect--; 38154359Sroberto record_clock_stats(&peer->srcadr, 38254359Sroberto pp->a_lastcode); 38354359Sroberto } else { 38454359Sroberto refclock_report(peer, CEVNT_BADREPLY); 38554359Sroberto } 38654359Sroberto return; 38754359Sroberto } 38854359Sroberto 38954359Sroberto /* 39054359Sroberto * Decode synchronization, quality and leap characters. If 39154359Sroberto * unsynchronized, set the leap bits accordingly and exit. 39254359Sroberto * Otherwise, set the leap bits according to the leap character. 39354359Sroberto * Once synchronized, the dispersion depends only on the 39454359Sroberto * quality character. 39554359Sroberto */ 39654359Sroberto switch (qualchar) { 39754359Sroberto 398290001Sglebius case ' ': 39954359Sroberto pp->disp = .001; 400132451Sroberto pp->lastref = pp->lastrec; 40154359Sroberto break; 40254359Sroberto 403290001Sglebius case 'A': 40454359Sroberto pp->disp = .01; 40554359Sroberto break; 40654359Sroberto 407290001Sglebius case 'B': 40854359Sroberto pp->disp = .1; 40954359Sroberto break; 41054359Sroberto 411290001Sglebius case 'C': 41254359Sroberto pp->disp = .5; 41354359Sroberto break; 41454359Sroberto 415290001Sglebius case 'D': 41654359Sroberto pp->disp = MAXDISPERSE; 41754359Sroberto break; 41854359Sroberto 419290001Sglebius default: 42054359Sroberto pp->disp = MAXDISPERSE; 42154359Sroberto refclock_report(peer, CEVNT_BADREPLY); 42254359Sroberto break; 42354359Sroberto } 42454359Sroberto if (syncchar != ' ') 42554359Sroberto pp->leap = LEAP_NOTINSYNC; 42654359Sroberto else if (leapchar == 'L') 42754359Sroberto pp->leap = LEAP_ADDSECOND; 42854359Sroberto else 42954359Sroberto pp->leap = LEAP_NOWARNING; 43054359Sroberto 43154359Sroberto /* 43254359Sroberto * Process the new sample in the median filter and determine the 433290001Sglebius * timecode timestamp, but only if the PPS is not in control. 43454359Sroberto */ 435290001Sglebius#ifdef HAVE_PPSAPI 436290001Sglebius up->tcount++; 437290001Sglebius if (peer->flags & FLAG_PPS) 438290001Sglebius return; 439290001Sglebius 440290001Sglebius#endif /* HAVE_PPSAPI */ 441290001Sglebius if (!refclock_process_f(pp, pp->fudgetime2)) 44254359Sroberto refclock_report(peer, CEVNT_BADTIME); 44354359Sroberto} 44454359Sroberto 44554359Sroberto 44654359Sroberto/* 447182007Sroberto * wwvb_timer - called once per second by the transmit procedure 44854359Sroberto */ 44954359Srobertostatic void 450182007Srobertowwvb_timer( 45154359Sroberto int unit, 45254359Sroberto struct peer *peer 45354359Sroberto ) 45454359Sroberto{ 45554359Sroberto register struct wwvbunit *up; 45654359Sroberto struct refclockproc *pp; 45754359Sroberto char pollchar; /* character sent to clock */ 458290001Sglebius#ifdef DEBUG 459290001Sglebius l_fp now; 460290001Sglebius#endif 46154359Sroberto 46254359Sroberto /* 46354359Sroberto * Time to poll the clock. The Spectracom clock responds to a 46454359Sroberto * 'T' by returning a timecode in the format(s) specified above. 46554359Sroberto * Note there is no checking on state, since this may not be the 46654359Sroberto * only customer reading the clock. Only one customer need poll 467182007Sroberto * the clock; all others just listen in. 46854359Sroberto */ 46954359Sroberto pp = peer->procptr; 470290001Sglebius up = pp->unitptr; 47154359Sroberto if (up->linect > 0) 47254359Sroberto pollchar = 'R'; 47354359Sroberto else 47454359Sroberto pollchar = 'T'; 47554359Sroberto if (write(pp->io.fd, &pollchar, 1) != 1) 47654359Sroberto refclock_report(peer, CEVNT_FAULT); 477290001Sglebius#ifdef DEBUG 478290001Sglebius get_systime(&now); 479290001Sglebius if (debug) 480290001Sglebius printf("%c poll at %s\n", pollchar, prettydate(&now)); 481290001Sglebius#endif 482290001Sglebius#ifdef HAVE_PPSAPI 483290001Sglebius if (up->ppsapi_lit && 484290001Sglebius refclock_pps(peer, &up->atom, pp->sloppyclockflag) > 0) { 485290001Sglebius up->pcount++, 486290001Sglebius peer->flags |= FLAG_PPS; 487290001Sglebius peer->precision = PPS_PRECISION; 488290001Sglebius } 489290001Sglebius#endif /* HAVE_PPSAPI */ 490182007Sroberto} 491182007Sroberto 492182007Sroberto 493182007Sroberto/* 494182007Sroberto * wwvb_poll - called by the transmit procedure 495182007Sroberto */ 496182007Srobertostatic void 497182007Srobertowwvb_poll( 498182007Sroberto int unit, 499182007Sroberto struct peer *peer 500182007Sroberto ) 501182007Sroberto{ 502182007Sroberto register struct wwvbunit *up; 503182007Sroberto struct refclockproc *pp; 504182007Sroberto 505182007Sroberto /* 506182007Sroberto * Sweep up the samples received since the last poll. If none 507182007Sroberto * are received, declare a timeout and keep going. 508182007Sroberto */ 509182007Sroberto pp = peer->procptr; 510290001Sglebius up = pp->unitptr; 511182007Sroberto pp->polls++; 512182007Sroberto 513182007Sroberto /* 514182007Sroberto * If the monitor flag is set (flag4), we dump the internal 515182007Sroberto * quality table at the first timecode beginning the day. 516182007Sroberto */ 517182007Sroberto if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour < 518182007Sroberto (int)up->lasthour) 519182007Sroberto up->linect = MONLIN; 520290001Sglebius up->lasthour = (u_char)pp->hour; 521182007Sroberto 522182007Sroberto /* 523182007Sroberto * Process median filter samples. If none received, declare a 524182007Sroberto * timeout and keep going. 525182007Sroberto */ 526290001Sglebius#ifdef HAVE_PPSAPI 527290001Sglebius if (up->pcount == 0) { 528290001Sglebius peer->flags &= ~FLAG_PPS; 529290001Sglebius peer->precision = PRECISION; 530290001Sglebius } 531290001Sglebius if (up->tcount == 0) { 532290001Sglebius pp->coderecv = pp->codeproc; 533290001Sglebius refclock_report(peer, CEVNT_TIMEOUT); 534290001Sglebius return; 535290001Sglebius } 536290001Sglebius up->pcount = up->tcount = 0; 537290001Sglebius#else /* HAVE_PPSAPI */ 53854359Sroberto if (pp->coderecv == pp->codeproc) { 53954359Sroberto refclock_report(peer, CEVNT_TIMEOUT); 54054359Sroberto return; 54154359Sroberto } 542290001Sglebius#endif /* HAVE_PPSAPI */ 543132451Sroberto refclock_receive(peer); 54454359Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 545132451Sroberto#ifdef DEBUG 546132451Sroberto if (debug) 547132451Sroberto printf("wwvb: timecode %d %s\n", pp->lencode, 548132451Sroberto pp->a_lastcode); 549132451Sroberto#endif 55054359Sroberto} 55154359Sroberto 552290001Sglebius 553290001Sglebius/* 554290001Sglebius * wwvb_control - fudge parameters have been set or changed 555290001Sglebius */ 556290001Sglebius#ifdef HAVE_PPSAPI 557290001Sglebiusstatic void 558290001Sglebiuswwvb_control( 559290001Sglebius int unit, 560290001Sglebius const struct refclockstat *in_st, 561290001Sglebius struct refclockstat *out_st, 562290001Sglebius struct peer *peer 563290001Sglebius ) 564290001Sglebius{ 565290001Sglebius register struct wwvbunit *up; 566290001Sglebius struct refclockproc *pp; 567290001Sglebius 568290001Sglebius pp = peer->procptr; 569290001Sglebius up = pp->unitptr; 570290001Sglebius 571290001Sglebius if (!(pp->sloppyclockflag & CLK_FLAG1)) { 572290001Sglebius if (!up->ppsapi_tried) 573290001Sglebius return; 574290001Sglebius up->ppsapi_tried = 0; 575290001Sglebius if (!up->ppsapi_lit) 576290001Sglebius return; 577290001Sglebius peer->flags &= ~FLAG_PPS; 578290001Sglebius peer->precision = PRECISION; 579290001Sglebius time_pps_destroy(up->atom.handle); 580290001Sglebius up->atom.handle = 0; 581290001Sglebius up->ppsapi_lit = 0; 582290001Sglebius return; 583290001Sglebius } 584290001Sglebius 585290001Sglebius if (up->ppsapi_tried) 586290001Sglebius return; 587290001Sglebius /* 588290001Sglebius * Light up the PPSAPI interface. 589290001Sglebius */ 590290001Sglebius up->ppsapi_tried = 1; 591290001Sglebius if (refclock_ppsapi(pp->io.fd, &up->atom)) { 592290001Sglebius up->ppsapi_lit = 1; 593290001Sglebius return; 594290001Sglebius } 595290001Sglebius 596290001Sglebius msyslog(LOG_WARNING, "%s flag1 1 but PPSAPI fails", 597290001Sglebius refnumtoa(&peer->srcadr)); 598290001Sglebius} 599290001Sglebius#endif /* HAVE_PPSAPI */ 600290001Sglebius 60154359Sroberto#else 60254359Srobertoint refclock_wwvb_bs; 60354359Sroberto#endif /* REFCLOCK */ 604