aboutsummaryrefslogtreecommitdiff
path: root/contrib/ntp/ntpd/refclock_ulink.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/ntpd/refclock_ulink.c')
-rw-r--r--contrib/ntp/ntpd/refclock_ulink.c395
1 files changed, 287 insertions, 108 deletions
diff --git a/contrib/ntp/ntpd/refclock_ulink.c b/contrib/ntp/ntpd/refclock_ulink.c
index 347040dea64e..5bf569a66080 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