2 * refclock_ulink - clock driver for Ultralink Model 320 WWVB receivers
3 * By Dave Strout <dstrout@linuxfoundary.com>
5 * Latest version is always on www.linuxfoundary.com
7 * Based on the Spectracom driver
14 #if defined(REFCLOCK) && defined(CLOCK_ULINK)
23 #include "ntp_refclock.h"
24 #include "ntp_calendar.h"
25 #include "ntp_stdlib.h"
28 * This driver supports the Ultralink Model 320 WWVB receiver. The Model 320 is
29 * an RS-232 powered unit which consists of two parts: a DB-25 shell that contains
30 * a microprocessor, and an approx 2"x4" plastic box that contains the antenna.
31 * The two are connected by a 6-wire RJ-25 cable of length up to 1000'. The
32 * microprocessor steals power from the RS-232 port, which means that the port must
33 * be kept open all of the time. The unit also has an internal clock for loss of signal
34 * periods. Claimed accuracy is 0.1 sec.
36 * The timecode format is:
38 * <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr>
42 * S = 'S' -- sync'd in last hour, '0'-'9' - hours x 10 since last update, else '?'
43 * Q = Number of correlating time-frames, from 0 to 5
44 * R = 'R' -- reception in progress, 'N' -- Noisy reception, ' ' -- standby mode
45 * YYYY = year from 1990 to 2089
46 * DDD = current day from 1 to 366
47 * + = '+' if current year is a leap year, else ' '
48 * HH = UTC hour 0 to 23
49 * MM = Minutes of current hour from 0 to 59
50 * SS = Seconds of current minute from 0 to 59
51 * mm = 10's milliseconds of the current second from 00 to 99
52 * L = Leap second pending at end of month -- 'I' = inset, 'D'=delete
53 * T = DST <-> STD transition indicators
55 * Note that this driver does not do anything with the L or T flags.
57 * The M320 also has a 'U' command which returns UT1 correction information. It
58 * is not used in this driver.
63 * Interface definitions
65 #define DEVICE "/dev/ulink%d" /* device name and unit */
66 #define SPEED232 B9600 /* uart speed (9600 baud) */
67 #define PRECISION (-13) /* precision assumed (about 100 us) */
68 #define REFID "M320" /* reference ID */
69 #define DESCRIPTION "Ultralink WWVB Receiver" /* WRU */
71 #define LENWWVB0 28 /* format 0 timecode length */
72 #define LENWWVB2 24 /* format 2 timecode length */
73 #define LENWWVB3 29 /* format 3 timecode length */
75 #define MONLIN 15 /* number of monitoring lines */
78 * ULINK unit control structure
81 u_char tcswitch; /* timecode switch */
82 l_fp laststamp; /* last receive timestamp */
83 u_char lasthour; /* last hour (for monitor) */
84 u_char linect; /* count ignored lines (for monitor */
90 static int ulink_start P((int, struct peer *));
91 static void ulink_shutdown P((int, struct peer *));
92 static void ulink_receive P((struct recvbuf *));
93 static void ulink_poll P((int, struct peer *));
94 static int fd; /* We need to keep the serial port open to power the ULM320 */
99 struct refclock refclock_ulink = {
100 ulink_start, /* start up driver */
101 ulink_shutdown, /* shut down driver */
102 ulink_poll, /* transmit poll message */
103 noentry, /* not used (old wwvb_control) */
104 noentry, /* initialize driver (not used) */
105 noentry, /* not used (old wwvb_buginfo) */
106 NOFLAGS /* not used */
111 * ulink_start - open the devices and initialize data for processing
119 register struct ulinkunit *up;
120 struct refclockproc *pp;
122 fprintf(stderr, "Starting Ulink driver\n");
124 * Open serial port. Use CLK line discipline, if available.
126 (void)sprintf(device, DEVICE, unit);
127 if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
131 * Allocate and initialize unit structure
133 if (!(up = (struct ulinkunit *)
134 emalloc(sizeof(struct ulinkunit)))) {
138 memset((char *)up, 0, sizeof(struct ulinkunit));
140 pp->unitptr = (caddr_t)up;
141 pp->io.clock_recv = ulink_receive;
142 pp->io.srcclock = (caddr_t)peer;
145 if (!io_addclock(&pp->io)) {
152 * Initialize miscellaneous variables
154 peer->precision = PRECISION;
155 peer->flags |= FLAG_BURST;
156 peer->burst = NSTAGE;
157 pp->clockdesc = DESCRIPTION;
158 memcpy((char *)&pp->refid, REFID, 4);
164 * ulink_shutdown - shut down the clock
172 register struct ulinkunit *up;
173 struct refclockproc *pp;
176 up = (struct ulinkunit *)pp->unitptr;
177 io_closeclock(&pp->io);
184 * ulink_receive - receive data from the serial interface
188 struct recvbuf *rbufp
191 struct ulinkunit *up;
192 struct refclockproc *pp;
195 l_fp trtmp; /* arrival timestamp */
196 char syncchar; /* synchronization indicator */
197 char qualchar; /* quality indicator */
198 char modechar; /* Modes: 'R'=rx, 'N'=noise, ' '=standby */
199 char leapchar; /* leap indicator */
200 int temp; /* int temp */
203 * Initialize pointers and read the timecode and timestamp
205 peer = (struct peer *)rbufp->recv_srcclock;
207 up = (struct ulinkunit *)pp->unitptr;
208 temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
211 * Note we get a buffer and timestamp for both a <cr> and <lf>,
212 * but only the <cr> timestamp is retained. Note: in format 0 on
213 * a Netclock/2 or upgraded 8170 the start bit is delayed 100
214 * +-50 us relative to the pps; however, on an unmodified 8170
215 * the start bit can be delayed up to 10 ms. In format 2 the
216 * reading precision is only to the millisecond. Thus, unless
217 * you have a pps gadget and don't have to have the year, format
218 * 0 provides the lowest jitter.
221 if (up->tcswitch == 0) {
223 up->laststamp = trtmp;
229 pp->lastrec = up->laststamp;
230 up->laststamp = trtmp;
234 printf("ulink: timecode %d %s\n", pp->lencode,
239 * We get down to business, check the timecode format and decode
240 * its contents. This code uses the timecode length to determine
241 * whether format 0 or format 2. If the timecode has invalid
242 * length or is not in proper format, we declare bad format and
245 syncchar = qualchar = leapchar = ' ';
249 * Timecode format SQRYYYYDDD+HH:MM:SS.mmLT
251 sscanf(pp->a_lastcode, "%c%c%c%4d%3d%c%2d:%2d:%2d.%2d",
252 &syncchar, &qualchar, &modechar, &pp->year, &pp->day,
253 &leapchar,&pp->hour, &pp->minute, &pp->second,&pp->msec);
255 pp->msec *= 10; /* M320 returns 10's of msecs */
259 * Decode synchronization, quality and leap characters. If
260 * unsynchronized, set the leap bits accordingly and exit.
261 * Otherwise, set the leap bits according to the leap character.
262 * Once synchronized, the dispersion depends only on the
266 pp->leap = LEAP_NOWARNING;
269 * Process the new sample in the median filter and determine the
270 * timecode timestamp.
272 if (!refclock_process(pp))
273 refclock_report(peer, CEVNT_BADTIME);
278 * ulink_poll - called by the transmit procedure
286 register struct ulinkunit *up;
287 struct refclockproc *pp;
291 up = (struct ulinkunit *)pp->unitptr;
293 if (write(pp->io.fd, &pollchar, 1) != 1)
294 refclock_report(peer, CEVNT_FAULT);
299 if (pp->coderecv == pp->codeproc) {
300 refclock_report(peer, CEVNT_TIMEOUT);
303 record_clock_stats(&peer->srcadr, pp->a_lastcode);
304 refclock_receive(peer);
305 peer->burst = NSTAGE;
308 * If the monitor flag is set (flag4), we dump the internal
309 * quality table at the first timecode beginning the day.
311 if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour <
314 up->lasthour = pp->hour;
318 int refclock_ulink_bs;
319 #endif /* REFCLOCK */