diff options
Diffstat (limited to 'contrib/ntp/ntpd/refclock_ulink.c')
-rw-r--r-- | contrib/ntp/ntpd/refclock_ulink.c | 395 |
1 files changed, 287 insertions, 108 deletions
diff --git a/contrib/ntp/ntpd/refclock_ulink.c b/contrib/ntp/ntpd/refclock_ulink.c index 347040d..5bf569a 100644 --- a/contrib/ntp/ntpd/refclock_ulink.c +++ b/contrib/ntp/ntpd/refclock_ulink.c @@ -1,12 +1,25 @@ /* - * refclock_ulink - clock driver for Ultralink Model 320 WWVB receivers - * By Dave Strout <dstrout@linuxfoundary.com> - * - * Latest version is always on www.linuxfoundary.com - * - * Based on the Spectracom driver + * refclock_ulink - clock driver for Ultralink WWVB receiver + * */ +/*********************************************************************** + * * + * Copyright (c) David L. Mills 1992-1998 * + * * + * Permission to use, copy, modify, and distribute this software and * + * its documentation for any purpose and without fee is hereby * + * granted, provided that the above copyright notice appears in all * + * copies and that both the copyright notice and this permission * + * notice appear in supporting documentation, and that the name * + * University of Delaware not be used in advertising or publicity * + * pertaining to distribution of the software without specific, * + * written prior permission. The University of Delaware makes no * + * representations about the suitability this software for any * + * purpose. It is provided "as is" without express or implied * + * warranty. * + **********************************************************************/ + #ifdef HAVE_CONFIG_H #include <config.h> #endif @@ -15,8 +28,6 @@ #include <stdio.h> #include <ctype.h> -#include <sys/time.h> -#include <time.h> #include "ntpd.h" #include "ntp_io.h" @@ -25,63 +36,66 @@ #include "ntp_stdlib.h" /* - * This driver supports the Ultralink Model 320 WWVB receiver. The Model 320 is - * an RS-232 powered unit which consists of two parts: a DB-25 shell that contains - * a microprocessor, and an approx 2"x4" plastic box that contains the antenna. - * The two are connected by a 6-wire RJ-25 cable of length up to 1000'. The - * microprocessor steals power from the RS-232 port, which means that the port must - * be kept open all of the time. The unit also has an internal clock for loss of signal - * periods. Claimed accuracy is 0.1 sec. - * - * The timecode format is: + * This driver supports ultralink Model 320,330,331,332 WWVB radios * - * <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr> + * this driver was based on the refclock_wwvb.c driver + * in the ntp distribution. * - * where: + * Fudge Factors * - * S = 'S' -- sync'd in last hour, '0'-'9' - hours x 10 since last update, else '?' - * Q = Number of correlating time-frames, from 0 to 5 - * R = 'R' -- reception in progress, 'N' -- Noisy reception, ' ' -- standby mode - * YYYY = year from 1990 to 2089 - * DDD = current day from 1 to 366 - * + = '+' if current year is a leap year, else ' ' - * HH = UTC hour 0 to 23 - * MM = Minutes of current hour from 0 to 59 - * SS = Seconds of current minute from 0 to 59 - * mm = 10's milliseconds of the current second from 00 to 99 - * L = Leap second pending at end of month -- 'I' = inset, 'D'=delete - * T = DST <-> STD transition indicators + * fudge flag1 0 don't poll clock + * 1 send poll character * - * Note that this driver does not do anything with the L or T flags. + * revision history: + * 99/9/09 j.c.lang original edit's + * 99/9/11 j.c.lang changed timecode parse to + * match what the radio actually + * sends. + * 99/10/11 j.c.lang added support for continous + * time code mode (dipsw2) + * 99/11/26 j.c.lang added support for 320 decoder + * (taken from Dave Strout's + * Model 320 driver) + * 99/11/29 j.c.lang added fudge flag 1 to control + * clock polling + * 99/12/15 j.c.lang fixed 320 quality flag + * 01/02/21 s.l.smith fixed 33x quality flag + * added more debugging stuff + * updated 33x time code explanation * - * The M320 also has a 'U' command which returns UT1 correction information. It - * is not used in this driver. + * Questions, bugs, ideas send to: + * Joseph C. Lang + * tcnojl1@earthlink.net * - */ + * Dave Strout + * dstrout@linuxfoundry.com + * + * + * on the Ultralink model 33X decoder Dip switch 2 controls + * polled or continous timecode + * set fudge flag1 if using polled (needed for model 320) + * dont set fudge flag1 if dip switch 2 is set on model 33x decoder +*/ + /* * Interface definitions */ -#define DEVICE "/dev/ulink%d" /* device name and unit */ +#define DEVICE "/dev/wwvb%d" /* device name and unit */ #define SPEED232 B9600 /* uart speed (9600 baud) */ -#define PRECISION (-13) /* precision assumed (about 100 us) */ -#define REFID "M320" /* reference ID */ +#define PRECISION (-10) /* precision assumed (about 10 ms) */ +#define REFID "WWVB" /* reference ID */ #define DESCRIPTION "Ultralink WWVB Receiver" /* WRU */ -#define LENWWVB0 28 /* format 0 timecode length */ -#define LENWWVB2 24 /* format 2 timecode length */ -#define LENWWVB3 29 /* format 3 timecode length */ - -#define MONLIN 15 /* number of monitoring lines */ +#define LEN33X 32 /* timecode length Model 325 & 33X */ +#define LEN320 24 /* timecode length Model 320 */ /* - * ULINK unit control structure + * unit control structure */ struct ulinkunit { u_char tcswitch; /* timecode switch */ l_fp laststamp; /* last receive timestamp */ - u_char lasthour; /* last hour (for monitor) */ - u_char linect; /* count ignored lines (for monitor */ }; /* @@ -91,7 +105,6 @@ static int ulink_start P((int, struct peer *)); static void ulink_shutdown P((int, struct peer *)); static void ulink_receive P((struct recvbuf *)); static void ulink_poll P((int, struct peer *)); -static int fd; /* We need to keep the serial port open to power the ULM320 */ /* * Transfer vector @@ -100,10 +113,10 @@ struct refclock refclock_ulink = { ulink_start, /* start up driver */ ulink_shutdown, /* shut down driver */ ulink_poll, /* transmit poll message */ - noentry, /* not used (old wwvb_control) */ - noentry, /* initialize driver (not used) */ - noentry, /* not used (old wwvb_buginfo) */ - NOFLAGS /* not used */ + noentry, /* not used */ + noentry, /* not used */ + noentry, /* not used */ + NOFLAGS }; @@ -118,8 +131,9 @@ ulink_start( { register struct ulinkunit *up; struct refclockproc *pp; + int fd; char device[20]; - fprintf(stderr, "Starting Ulink driver\n"); + /* * Open serial port. Use CLK line discipline, if available. */ @@ -152,7 +166,6 @@ ulink_start( * Initialize miscellaneous variables */ peer->precision = PRECISION; - peer->flags |= FLAG_BURST; peer->burst = NSTAGE; pp->clockdesc = DESCRIPTION; memcpy((char *)&pp->refid, REFID, 4); @@ -176,7 +189,6 @@ ulink_shutdown( up = (struct ulinkunit *)pp->unitptr; io_closeclock(&pp->io); free(up); - close(fd); } @@ -193,11 +205,12 @@ ulink_receive( struct peer *peer; l_fp trtmp; /* arrival timestamp */ + int quality; /* quality indicator */ + int temp; /* int temp */ char syncchar; /* synchronization indicator */ - char qualchar; /* quality indicator */ - char modechar; /* Modes: 'R'=rx, 'N'=noise, ' '=standby */ char leapchar; /* leap indicator */ - int temp; /* int temp */ + char modechar; /* model 320 mode flag */ + char char_quality[2]; /* temp quality flag */ /* * Initialize pointers and read the timecode and timestamp @@ -209,13 +222,7 @@ ulink_receive( /* * Note we get a buffer and timestamp for both a <cr> and <lf>, - * but only the <cr> timestamp is retained. Note: in format 0 on - * a Netclock/2 or upgraded 8170 the start bit is delayed 100 - * +-50 us relative to the pps; however, on an unmodified 8170 - * the start bit can be delayed up to 10 ms. In format 2 the - * reading precision is only to the millisecond. Thus, unless - * you have a pps gadget and don't have to have the year, format - * 0 provides the lowest jitter. + * but only the <cr> timestamp is retained. */ if (temp == 0) { if (up->tcswitch == 0) { @@ -237,40 +244,217 @@ ulink_receive( /* * We get down to business, check the timecode format and decode - * its contents. This code uses the timecode length to determine - * whether format 0 or format 2. If the timecode has invalid - * length or is not in proper format, we declare bad format and - * exit. + * its contents. If the timecode has invalid length or is not in + * proper format, we declare bad format and exit. */ - syncchar = qualchar = leapchar = ' '; + syncchar = leapchar = modechar = ' '; pp->msec = 0; + + switch (pp->lencode ) { + case LEN33X: + /* + * Model 33X decoder: + * Timecode format from January 29, 2001 datasheet is: + * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 + * S WWVB decoder sync indicator. S for in-sync(?) + * or N for noisy signal. + * 9+ RF signal level in S-units, 0-9 followed by + * a space (0x20). The space turns to '+' if the + * level is over 9. + * D Data bit 0, 1, 2 (position mark), or + * 3 (unknown). + * space Space character (0x20) + * 00 Hours since last good WWVB frame sync. Will + * be 00-23 hrs, or '1d' to '7d'. Will be 'Lk' + * if currently in sync. + * space Space character (0x20) + * YYYY Current year, 1990-2089 + * + Leap year indicator. '+' if a leap year, + * a space (0x20) if not. + * DDD Day of year, 001 - 366. + * UTC Timezone (always 'UTC'). + * S Daylight savings indicator + * S - standard time (STD) in effect + * O - during STD to DST day 0000-2400 + * D - daylight savings time (DST) in effect + * I - during DST to STD day 0000-2400 + * space Space character (0x20) + * HH Hours 00-23 + * : This is the REAL in sync indicator (: = insync) + * MM Minutes 00-59 + * : : = in sync ? = NOT in sync + * SS Seconds 00-59 + * L Leap second flag. Changes from space (0x20) + * to '+' or '-' during month preceding leap + * second adjustment. + * +5 UT1 correction (sign + digit )) + */ + + if (sscanf(pp->a_lastcode, + "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c", + char_quality, &pp->year, &pp->day, + &pp->hour, &syncchar, &pp->minute, &pp->second, + &leapchar) == 8) { + + if (char_quality[0] == 'L') { + quality = 0; + } else if (char_quality[0] == '0') { + quality = (char_quality[1] & 0x0f); + } else { + quality = 99; + } +/* +#ifdef DEBUG + if (debug) { + printf("ulink: char_quality %c %c\n", + char_quality[0], char_quality[1]); + printf("ulink: quality %d\n", quality); + printf("ulink: syncchar %x\n", syncchar); + printf("ulink: leapchar %x\n", leapchar); + } +#endif +*/ + + break; + } + + case LEN320: + /* + * Model 320 Decoder + * The timecode format is: + * + * <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr> + * + * where: + * + * S = 'S' -- sync'd in last hour, + * '0'-'9' - hours x 10 since last update, + * '?' -- not in sync + * Q = Number of correlating time-frames, from 0 to 5 + * R = 'R' -- reception in progress, + * 'N' -- Noisy reception, + * ' ' -- standby mode + * YYYY = year from 1990 to 2089 + * DDD = current day from 1 to 366 + * + = '+' if current year is a leap year, else ' ' + * HH = UTC hour 0 to 23 + * MM = Minutes of current hour from 0 to 59 + * SS = Seconds of current minute from 0 to 59 + * mm = 10's milliseconds of the current second from 00 to 99 + * L = Leap second pending at end of month + * 'I' = insert, 'D'= delete + * T = DST <-> STD transition indicators + * + */ + if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2d%c", + &syncchar, &quality, &modechar, &pp->year, &pp->day, + &pp->hour, &pp->minute, &pp->second, + &pp->msec,&leapchar) == 10) { + pp->msec *= 10; /* M320 returns 10's of msecs */ + if (leapchar == 'I' ) leapchar = '+'; + if (leapchar == 'D' ) leapchar = '-'; + if (syncchar != '?' ) syncchar = ':'; + + break; + } + + default: + refclock_report(peer, CEVNT_BADREPLY); + return; + } + + /* - * Timecode format SQRYYYYDDD+HH:MM:SS.mmLT + * Decode quality indicator + * For the 325 & 33x series, the lower the number the "better" + * the time is. I used the dispersion as the measure of time + * quality. The quality indicator in the 320 is the number of + * correlating time frames (the more the better) */ - sscanf(pp->a_lastcode, "%c%c%c%4d%3d%c%2d:%2d:%2d.%2d", - &syncchar, &qualchar, &modechar, &pp->year, &pp->day, - &leapchar,&pp->hour, &pp->minute, &pp->second,&pp->msec); - pp->msec *= 10; /* M320 returns 10's of msecs */ - qualchar = ' '; + /* + * The spec sheet for the 325 & 33x series states the clock will + * maintain +/-0.002 seconds accuracy when locked to WWVB. This + * is indicated by 'Lk' in the quality portion of the incoming + * string. When not in lock, a drift of +/-0.015 seconds should + * be allowed for. + * With the quality indicator decoding scheme above, the 'Lk' + * condition will produce a quality value of 0. If the quality + * indicator starts with '0' then the second character is the + * number of hours since we were last locked. If the first + * character is anything other than 'L' or '0' then we have been + * out of lock for more than 9 hours so we assume the worst and + * force a quality value that selects the 'default' maximum + * dispersion. The dispersion values below are what came with the + * driver. They're not unreasonable so they've not been changed. + */ + + if (pp->lencode == LEN33X) { + switch (quality) { + case 0 : + pp->disp=.002; + break; + case 1 : + pp->disp=.02; + break; + case 2 : + pp->disp=.04; + break; + case 3 : + pp->disp=.08; + break; + default: + pp->disp=MAXDISPERSE; + break; + } + } else { + switch (quality) { + case 5 : + pp->disp=.002; + break; + case 4 : + pp->disp=.02; + break; + case 3 : + pp->disp=.04; + break; + case 2 : + pp->disp=.08; + break; + case 1 : + pp->disp=.16; + break; + default: + pp->disp=MAXDISPERSE; + break; + } + + } /* - * Decode synchronization, quality and leap characters. If + * Decode synchronization, and leap characters. If * unsynchronized, set the leap bits accordingly and exit. * Otherwise, set the leap bits according to the leap character. - * Once synchronized, the dispersion depends only on the - * quality character. */ - pp->disp = .001; - pp->leap = LEAP_NOWARNING; + + if (syncchar != ':') + pp->leap = LEAP_NOTINSYNC; + else if (leapchar == '+') + pp->leap = LEAP_ADDSECOND; + else if (leapchar == '-') + pp->leap = LEAP_DELSECOND; + else + pp->leap = LEAP_NOWARNING; /* * Process the new sample in the median filter and determine the * timecode timestamp. */ - if (!refclock_process(pp)) + if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); + } + } @@ -283,35 +467,30 @@ ulink_poll( struct peer *peer ) { - register struct ulinkunit *up; - struct refclockproc *pp; - char pollchar; - - pp = peer->procptr; - up = (struct ulinkunit *)pp->unitptr; - pollchar = 'T'; - if (write(pp->io.fd, &pollchar, 1) != 1) - refclock_report(peer, CEVNT_FAULT); - else - pp->polls++; - if (peer->burst > 0) - return; - if (pp->coderecv == pp->codeproc) { - refclock_report(peer, CEVNT_TIMEOUT); - return; + struct refclockproc *pp; + char pollchar; + + pp = peer->procptr; + pollchar = 'T'; + if (pp->sloppyclockflag & CLK_FLAG1) { + if (write(pp->io.fd, &pollchar, 1) != 1) + refclock_report(peer, CEVNT_FAULT); + else + pp->polls++; } - record_clock_stats(&peer->srcadr, pp->a_lastcode); - refclock_receive(peer); - peer->burst = NSTAGE; + else + pp->polls++; + + if (peer->burst > 0) + return; + if (pp->coderecv == pp->codeproc) { + refclock_report(peer, CEVNT_TIMEOUT); + return; + } + record_clock_stats(&peer->srcadr, pp->a_lastcode); + refclock_receive(peer); + peer->burst = NSTAGE; - /* - * If the monitor flag is set (flag4), we dump the internal - * quality table at the first timecode beginning the day. - */ - if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour < - (int)up->lasthour) - up->linect = MONLIN; - up->lasthour = pp->hour; } #else |