Sync usage with man page.
[netbsd-mini2440.git] / dist / ntp / ntpd / refclock_wwvb.c
blobb8b8f947adff56478b799f7f80c1bc6e98e5bd20
1 /* $NetBSD: refclock_wwvb.c,v 1.2 2003/12/04 16:23:38 drochner Exp $ */
3 /*
4 * refclock_wwvb - clock driver for Spectracom WWVB and GPS receivers
5 */
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
11 #if defined(REFCLOCK) && defined(CLOCK_SPECTRACOM)
13 #include "ntpd.h"
14 #include "ntp_io.h"
15 #include "ntp_refclock.h"
16 #include "ntp_calendar.h"
17 #include "ntp_stdlib.h"
19 #include <stdio.h>
20 #include <ctype.h>
23 * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
24 * Synchronized Clocks and the Netclock/GPS Master Clock. Both the WWVB
25 * and GPS clocks have proven reliable sources of time; however, the
26 * WWVB clocks have proven vulnerable to high ambient conductive RF
27 * interference. The claimed accuracy of the WWVB clocks is 100 us
28 * relative to the broadcast signal, while the claimed accuracy of the
29 * GPS clock is 50 ns; however, in most cases the actual accuracy is
30 * limited by the resolution of the timecode and the latencies of the
31 * serial interface and operating system.
33 * The WWVB and GPS clocks should be configured for 24-hour display,
34 * AUTO DST off, time zone 0 (UTC), data format 0 or 2 (see below) and
35 * baud rate 9600. If the clock is to used as the source for the IRIG
36 * Audio Decoder (refclock_irig.c in this distribution), it should be
37 * configured for AM IRIG output and IRIG format 1 (IRIG B with
38 * signature control). The GPS clock can be configured either to respond
39 * to a 'T' poll character or left running continuously.
41 * There are two timecode formats used by these clocks. Format 0, which
42 * is available with both the Netclock/2 and 8170, and format 2, which
43 * is available only with the Netclock/2, specially modified 8170 and
44 * GPS.
46 * Format 0 (22 ASCII printing characters):
48 * <cr><lf>i ddd hh:mm:ss TZ=zz<cr><lf>
50 * on-time = first <cr>
51 * hh:mm:ss = hours, minutes, seconds
52 * i = synchronization flag (' ' = in synch, '?' = out of synch)
54 * The alarm condition is indicated by other than ' ' at a, which occurs
55 * during initial synchronization and when received signal is lost for
56 * about ten hours.
58 * Format 2 (24 ASCII printing characters):
60 * <cr><lf>iqyy ddd hh:mm:ss.fff ld
62 * on-time = <cr>
63 * i = synchronization flag (' ' = in synch, '?' = out of synch)
64 * q = quality indicator (' ' = locked, 'A'...'D' = unlocked)
65 * yy = year (as broadcast)
66 * ddd = day of year
67 * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
69 * The alarm condition is indicated by other than ' ' at a, which occurs
70 * during initial synchronization and when received signal is lost for
71 * about ten hours. The unlock condition is indicated by other than ' '
72 * at q.
74 * The q is normally ' ' when the time error is less than 1 ms and a
75 * character in the set 'A'...'D' when the time error is less than 10,
76 * 100, 500 and greater than 500 ms respectively. The l is normally ' ',
77 * but is set to 'L' early in the month of an upcoming UTC leap second
78 * and reset to ' ' on the first day of the following month. The d is
79 * set to 'S' for standard time 'I' on the day preceding a switch to
80 * daylight time, 'D' for daylight time and 'O' on the day preceding a
81 * switch to standard time. The start bit of the first <cr> is
82 * synchronized to the indicated time as returned.
84 * This driver does not need to be told which format is in use - it
85 * figures out which one from the length of the message. The driver
86 * makes no attempt to correct for the intrinsic jitter of the radio
87 * itself, which is a known problem with the older radios.
89 * Fudge Factors
91 * This driver can retrieve a table of quality data maintained
92 * internally by the Netclock/2 clock. If flag4 of the fudge
93 * configuration command is set to 1, the driver will retrieve this
94 * table and write it to the clockstats file when the first timecode
95 * message of a new day is received.
97 * PPS calibration fudge time 1: format 0 .003134, format 2 .004034
100 * Interface definitions
102 #define DEVICE "/dev/wwvb%d" /* device name and unit */
103 #define SPEED232 B9600 /* uart speed (9600 baud) */
104 #define PRECISION (-13) /* precision assumed (about 100 us) */
105 #define REFID "WWVB" /* reference ID */
106 #define DESCRIPTION "Spectracom WWVB/GPS Receiver" /* WRU */
108 #define LENWWVB0 22 /* format 0 timecode length */
109 #define LENWWVB1 22 /* format 1 timecode length */
110 #define LENWWVB2 24 /* format 2 timecode length */
111 #define LENWWVB3 29 /* format 3 timecode length */
112 #define MONLIN 15 /* number of monitoring lines */
115 * WWVB unit control structure
117 struct wwvbunit {
118 l_fp laststamp; /* last receive timestamp */
119 u_char lasthour; /* last hour (for monitor) */
120 u_char linect; /* count ignored lines (for monitor */
124 * Function prototypes
126 static int wwvb_start P((int, struct peer *));
127 static void wwvb_shutdown P((int, struct peer *));
128 static void wwvb_receive P((struct recvbuf *));
129 static void wwvb_poll P((int, struct peer *));
130 static void wwvb_timer P((int, struct peer *));
133 * Transfer vector
135 struct refclock refclock_wwvb = {
136 wwvb_start, /* start up driver */
137 wwvb_shutdown, /* shut down driver */
138 wwvb_poll, /* transmit poll message */
139 noentry, /* not used (old wwvb_control) */
140 noentry, /* initialize driver (not used) */
141 noentry, /* not used (old wwvb_buginfo) */
142 wwvb_timer /* called once per second */
147 * wwvb_start - open the devices and initialize data for processing
149 static int
150 wwvb_start(
151 int unit,
152 struct peer *peer
155 register struct wwvbunit *up;
156 struct refclockproc *pp;
157 int fd;
158 char device[20];
161 * Open serial port. Use CLK line discipline, if available.
163 sprintf(device, DEVICE, unit);
164 if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
165 return (0);
168 * Allocate and initialize unit structure
170 if (!(up = (struct wwvbunit *)
171 emalloc(sizeof(struct wwvbunit)))) {
172 close(fd);
173 return (0);
175 memset((char *)up, 0, sizeof(struct wwvbunit));
176 pp = peer->procptr;
177 pp->unitptr = (caddr_t)up;
178 pp->io.clock_recv = wwvb_receive;
179 pp->io.srcclock = (caddr_t)peer;
180 pp->io.datalen = 0;
181 pp->io.fd = fd;
182 if (!io_addclock(&pp->io)) {
183 close(fd);
184 free(up);
185 return (0);
189 * Initialize miscellaneous variables
191 peer->precision = PRECISION;
192 pp->clockdesc = DESCRIPTION;
193 memcpy((char *)&pp->refid, REFID, 4);
194 return (1);
199 * wwvb_shutdown - shut down the clock
201 static void
202 wwvb_shutdown(
203 int unit,
204 struct peer *peer
207 register struct wwvbunit *up;
208 struct refclockproc *pp;
210 pp = peer->procptr;
211 up = (struct wwvbunit *)pp->unitptr;
212 io_closeclock(&pp->io);
213 free(up);
218 * wwvb_receive - receive data from the serial interface
220 static void
221 wwvb_receive(
222 struct recvbuf *rbufp
225 struct wwvbunit *up;
226 struct refclockproc *pp;
227 struct peer *peer;
229 l_fp trtmp; /* arrival timestamp */
230 int tz; /* time zone */
231 int day, month; /* ddd conversion */
232 int temp; /* int temp */
233 char syncchar; /* synchronization indicator */
234 char qualchar; /* quality indicator */
235 char leapchar; /* leap indicator */
236 char dstchar; /* daylight/standard indicator */
237 char tmpchar; /* trashbin */
240 * Initialize pointers and read the timecode and timestamp
242 peer = (struct peer *)rbufp->recv_srcclock;
243 pp = peer->procptr;
244 up = (struct wwvbunit *)pp->unitptr;
245 temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
248 * Note we get a buffer and timestamp for both a <cr> and <lf>,
249 * but only the <cr> timestamp is retained. Note: in format 0 on
250 * a Netclock/2 or upgraded 8170 the start bit is delayed 100
251 * +-50 us relative to the pps; however, on an unmodified 8170
252 * the start bit can be delayed up to 10 ms. In format 2 the
253 * reading precision is only to the millisecond. Thus, unless
254 * you have a PPS gadget and don't have to have the year, format
255 * 0 provides the lowest jitter.
257 if (temp == 0) {
258 up->laststamp = trtmp;
259 return;
261 pp->lencode = temp;
262 pp->lastrec = up->laststamp;
265 * We get down to business, check the timecode format and decode
266 * its contents. This code uses the timecode length to determine
267 * format 0, 2 or 3. If the timecode has invalid length or is
268 * not in proper format, we declare bad format and exit.
270 syncchar = qualchar = leapchar = dstchar = ' ';
271 tz = 0;
272 switch (pp->lencode) {
274 case LENWWVB0:
277 * Timecode format 0: "I ddd hh:mm:ss DTZ=nn"
279 if (sscanf(pp->a_lastcode,
280 "%c %3d %2d:%2d:%2d%c%cTZ=%2d",
281 &syncchar, &pp->day, &pp->hour, &pp->minute,
282 &pp->second, &tmpchar, &dstchar, &tz) == 8)
283 pp->nsec = 0;
284 break;
286 case LENWWVB2:
289 * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */
290 if (sscanf(pp->a_lastcode,
291 "%c%c %2d %3d %2d:%2d:%2d.%3ld %c",
292 &syncchar, &qualchar, &pp->year, &pp->day,
293 &pp->hour, &pp->minute, &pp->second, &pp->nsec,
294 &leapchar) == 9)
295 pp->nsec *= 1000000;
296 break;
298 case LENWWVB3:
301 * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#"
303 if (sscanf(pp->a_lastcode,
304 "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c",
305 &syncchar, &pp->year, &month, &day, &pp->hour,
306 &pp->minute, &pp->second, &dstchar, &leapchar) == 8)
308 pp->day = ymd2yd(pp->year, month, day);
309 pp->nsec = 0;
310 break;
313 default:
316 * Unknown format: If dumping internal table, record
317 * stats; otherwise, declare bad format.
319 if (up->linect > 0) {
320 up->linect--;
321 record_clock_stats(&peer->srcadr,
322 pp->a_lastcode);
323 } else {
324 refclock_report(peer, CEVNT_BADREPLY);
326 return;
330 * Decode synchronization, quality and leap characters. If
331 * unsynchronized, set the leap bits accordingly and exit.
332 * Otherwise, set the leap bits according to the leap character.
333 * Once synchronized, the dispersion depends only on the
334 * quality character.
336 switch (qualchar) {
338 case ' ':
339 pp->disp = .001;
340 pp->lastref = pp->lastrec;
341 break;
343 case 'A':
344 pp->disp = .01;
345 break;
347 case 'B':
348 pp->disp = .1;
349 break;
351 case 'C':
352 pp->disp = .5;
353 break;
355 case 'D':
356 pp->disp = MAXDISPERSE;
357 break;
359 default:
360 pp->disp = MAXDISPERSE;
361 refclock_report(peer, CEVNT_BADREPLY);
362 break;
364 if (syncchar != ' ')
365 pp->leap = LEAP_NOTINSYNC;
366 else if (leapchar == 'L')
367 pp->leap = LEAP_ADDSECOND;
368 else
369 pp->leap = LEAP_NOWARNING;
372 * Process the new sample in the median filter and determine the
373 * timecode timestamp.
375 if (!refclock_process(pp))
376 refclock_report(peer, CEVNT_BADTIME);
377 if (peer->disp > MAXDISTANCE)
378 refclock_receive(peer);
383 * wwvb_timer - called once per second by the transmit procedure
385 static void
386 wwvb_timer(
387 int unit,
388 struct peer *peer
391 register struct wwvbunit *up;
392 struct refclockproc *pp;
393 char pollchar; /* character sent to clock */
396 * Time to poll the clock. The Spectracom clock responds to a
397 * 'T' by returning a timecode in the format(s) specified above.
398 * Note there is no checking on state, since this may not be the
399 * only customer reading the clock. Only one customer need poll
400 * the clock; all others just listen in.
402 pp = peer->procptr;
403 up = (struct wwvbunit *)pp->unitptr;
404 if (up->linect > 0)
405 pollchar = 'R';
406 else
407 pollchar = 'T';
408 if (write(pp->io.fd, &pollchar, 1) != 1)
409 refclock_report(peer, CEVNT_FAULT);
414 * wwvb_poll - called by the transmit procedure
416 static void
417 wwvb_poll(
418 int unit,
419 struct peer *peer
422 register struct wwvbunit *up;
423 struct refclockproc *pp;
426 * Sweep up the samples received since the last poll. If none
427 * are received, declare a timeout and keep going.
429 pp = peer->procptr;
430 up = (struct wwvbunit *)pp->unitptr;
431 pp->polls++;
434 * If the monitor flag is set (flag4), we dump the internal
435 * quality table at the first timecode beginning the day.
437 if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour <
438 (int)up->lasthour)
439 up->linect = MONLIN;
440 up->lasthour = pp->hour;
443 * Process median filter samples. If none received, declare a
444 * timeout and keep going.
446 if (pp->coderecv == pp->codeproc) {
447 refclock_report(peer, CEVNT_TIMEOUT);
448 return;
450 refclock_receive(peer);
451 record_clock_stats(&peer->srcadr, pp->a_lastcode);
452 #ifdef DEBUG
453 if (debug)
454 printf("wwvb: timecode %d %s\n", pp->lencode,
455 pp->a_lastcode);
456 #endif
459 #else
460 int refclock_wwvb_bs;
461 #endif /* REFCLOCK */