Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / ntp / ntpd / refclock_ulink.c
blobb7b55048f84f427c178f257fab51352c686ab116
1 /* $NetBSD: refclock_ulink.c,v 1.2 2003/12/04 16:23:37 drochner Exp $ */
3 /*
4 * refclock_ulink - clock driver for Ultralink WWVB receiver
5 */
7 /***********************************************************************
8 * *
9 * Copyright (c) David L. Mills 1992-1998 *
10 * *
11 * Permission to use, copy, modify, and distribute this software and *
12 * its documentation for any purpose and without fee is hereby *
13 * granted, provided that the above copyright notice appears in all *
14 * copies and that both the copyright notice and this permission *
15 * notice appear in supporting documentation, and that the name *
16 * University of Delaware not be used in advertising or publicity *
17 * pertaining to distribution of the software without specific, *
18 * written prior permission. The University of Delaware makes no *
19 * representations about the suitability this software for any *
20 * purpose. It is provided "as is" without express or implied *
21 * warranty. *
22 **********************************************************************/
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #if defined(REFCLOCK) && defined(CLOCK_ULINK)
30 #include <stdio.h>
31 #include <ctype.h>
33 #include "ntpd.h"
34 #include "ntp_io.h"
35 #include "ntp_refclock.h"
36 #include "ntp_stdlib.h"
38 /* This driver supports ultralink Model 320,325,330,331,332 WWVB radios
40 * this driver was based on the refclock_wwvb.c driver
41 * in the ntp distribution.
43 * Fudge Factors
45 * fudge flag1 0 don't poll clock
46 * 1 send poll character
48 * revision history:
49 * 99/9/09 j.c.lang original edit's
50 * 99/9/11 j.c.lang changed timecode parse to
51 * match what the radio actually
52 * sends.
53 * 99/10/11 j.c.lang added support for continous
54 * time code mode (dipsw2)
55 * 99/11/26 j.c.lang added support for 320 decoder
56 * (taken from Dave Strout's
57 * Model 320 driver)
58 * 99/11/29 j.c.lang added fudge flag 1 to control
59 * clock polling
60 * 99/12/15 j.c.lang fixed 320 quality flag
61 * 01/02/21 s.l.smith fixed 33x quality flag
62 * added more debugging stuff
63 * updated 33x time code explanation
64 * 04/01/23 frank migge added support for 325 decoder
65 * (tested with ULM325.F)
67 * Questions, bugs, ideas send to:
68 * Joseph C. Lang
69 * tcnojl1@earthlink.net
71 * Dave Strout
72 * dstrout@linuxfoundry.com
74 * Frank Migge
75 * frank.migge@oracle.com
78 * on the Ultralink model 33X decoder Dip switch 2 controls
79 * polled or continous timecode
80 * set fudge flag1 if using polled (needed for model 320 and 325)
81 * dont set fudge flag1 if dip switch 2 is set on model 33x decoder
86 * Interface definitions
88 #define DEVICE "/dev/wwvb%d" /* device name and unit */
89 #define SPEED232 B9600 /* uart speed (9600 baud) */
90 #define PRECISION (-10) /* precision assumed (about 10 ms) */
91 #define REFID "WWVB" /* reference ID */
92 #define DESCRIPTION "Ultralink WWVB Receiver" /* WRU */
94 #define LEN33X 32 /* timecode length Model 33X and 325 */
95 #define LEN320 24 /* timecode length Model 320 */
97 #define SIGLCHAR33x 'S' /* signal strength identifier char 325 */
98 #define SIGLCHAR325 'R' /* signal strength identifier char 33x */
101 * unit control structure
103 struct ulinkunit {
104 u_char tcswitch; /* timecode switch */
105 l_fp laststamp; /* last receive timestamp */
109 * Function prototypes
111 static int ulink_start P((int, struct peer *));
112 static void ulink_shutdown P((int, struct peer *));
113 static void ulink_receive P((struct recvbuf *));
114 static void ulink_poll P((int, struct peer *));
117 * Transfer vector
119 struct refclock refclock_ulink = {
120 ulink_start, /* start up driver */
121 ulink_shutdown, /* shut down driver */
122 ulink_poll, /* transmit poll message */
123 noentry, /* not used */
124 noentry, /* not used */
125 noentry, /* not used */
126 NOFLAGS
131 * ulink_start - open the devices and initialize data for processing
133 static int
134 ulink_start(
135 int unit,
136 struct peer *peer
139 register struct ulinkunit *up;
140 struct refclockproc *pp;
141 int fd;
142 char device[20];
145 * Open serial port. Use CLK line discipline, if available.
147 (void)sprintf(device, DEVICE, unit);
148 if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
149 return (0);
152 * Allocate and initialize unit structure
154 if (!(up = (struct ulinkunit *)
155 emalloc(sizeof(struct ulinkunit)))) {
156 (void) close(fd);
157 return (0);
159 memset((char *)up, 0, sizeof(struct ulinkunit));
160 pp = peer->procptr;
161 pp->unitptr = (caddr_t)up;
162 pp->io.clock_recv = ulink_receive;
163 pp->io.srcclock = (caddr_t)peer;
164 pp->io.datalen = 0;
165 pp->io.fd = fd;
166 if (!io_addclock(&pp->io)) {
167 (void) close(fd);
168 free(up);
169 return (0);
173 * Initialize miscellaneous variables
175 peer->precision = PRECISION;
176 peer->burst = NSTAGE;
177 pp->clockdesc = DESCRIPTION;
178 memcpy((char *)&pp->refid, REFID, 4);
179 return (1);
184 * ulink_shutdown - shut down the clock
186 static void
187 ulink_shutdown(
188 int unit,
189 struct peer *peer
192 register struct ulinkunit *up;
193 struct refclockproc *pp;
195 pp = peer->procptr;
196 up = (struct ulinkunit *)pp->unitptr;
197 io_closeclock(&pp->io);
198 free(up);
203 * ulink_receive - receive data from the serial interface
205 static void
206 ulink_receive(
207 struct recvbuf *rbufp
210 struct ulinkunit *up;
211 struct refclockproc *pp;
212 struct peer *peer;
214 l_fp trtmp; /* arrival timestamp */
215 int quality; /* quality indicator */
216 int temp; /* int temp */
217 char syncchar; /* synchronization indicator */
218 char leapchar; /* leap indicator */
219 char modechar; /* model 320 mode flag */
220 char siglchar; /* model difference between 33x/325 */
221 char char_quality[2]; /* temp quality flag */
224 * Initialize pointers and read the timecode and timestamp
226 peer = (struct peer *)rbufp->recv_srcclock;
227 pp = peer->procptr;
228 up = (struct ulinkunit *)pp->unitptr;
229 temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
232 * Note we get a buffer and timestamp for both a <cr> and <lf>,
233 * but only the <cr> timestamp is retained.
235 if (temp == 0) {
236 if (up->tcswitch == 0) {
237 up->tcswitch = 1;
238 up->laststamp = trtmp;
239 } else
240 up->tcswitch = 0;
241 return;
243 pp->lencode = temp;
244 pp->lastrec = up->laststamp;
245 up->laststamp = trtmp;
246 up->tcswitch = 1;
247 #ifdef DEBUG
248 if (debug)
249 printf("ulink: timecode %d %s\n", pp->lencode,
250 pp->a_lastcode);
251 #endif
254 * We get down to business, check the timecode format and decode
255 * its contents. If the timecode has invalid length or is not in
256 * proper format, we declare bad format and exit.
258 syncchar = leapchar = modechar = siglchar = ' ';
259 switch (pp->lencode ) {
260 case LEN33X:
263 * First we check if the format is 33x or 325:
264 * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 (33x)
265 * <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5 (325)
266 * simply by comparing if the signal level is 'S' or 'R'
269 if (sscanf(pp->a_lastcode, "%c%*31c",
270 &siglchar) == 1) {
272 if(siglchar == SIGLCHAR325) {
275 * decode for a Model 325 decoder.
276 * Timecode format from January 23, 2004 datasheet is:
278 * <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5
280 * R WWVB decodersignal readability R1 - R5
281 * 5 R1 is unreadable, R5 is best
282 * space a space (0x20)
283 * 1 Data bit 0, 1, M (pos mark), or ? (unknown).
284 * C Reception from either (C)olorado or (H)awaii
285 * 00 Hours since last good WWVB frame sync. Will
286 * be 00-99
287 * space Space char (0x20) or (0xa5) if locked to wwvb
288 * YYYY Current year, 2000-2099
289 * + Leap year indicator. '+' if a leap year,
290 * a space (0x20) if not.
291 * DDD Day of year, 000 - 365.
292 * UTC Timezone (always 'UTC').
293 * S Daylight savings indicator
294 * S - standard time (STD) in effect
295 * O - during STD to DST day 0000-2400
296 * D - daylight savings time (DST) in effect
297 * I - during DST to STD day 0000-2400
298 * space Space character (0x20)
299 * HH Hours 00-23
300 * : This is the REAL in sync indicator (: = insync)
301 * MM Minutes 00-59
302 * : : = in sync ? = NOT in sync
303 * SS Seconds 00-59
304 * L Leap second flag. Changes from space (0x20)
305 * to 'I' or 'D' during month preceding leap
306 * second adjustment. (I)nsert or (D)elete
307 * +5 UT1 correction (sign + digit ))
310 if (sscanf(pp->a_lastcode,
311 "%*2c %*2c%2c%*c%4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
312 char_quality, &pp->year, &pp->day,
313 &pp->hour, &syncchar, &pp->minute, &pp->second,
314 &leapchar) == 8) {
316 if (char_quality[0] == '0') {
317 quality = 0;
318 } else if (char_quality[0] == '0') {
319 quality = (char_quality[1] & 0x0f);
320 } else {
321 quality = 99;
324 if (leapchar == 'I' ) leapchar = '+';
325 if (leapchar == 'D' ) leapchar = '-';
328 #ifdef DEBUG
329 if (debug) {
330 printf("ulink: char_quality %c %c\n",
331 char_quality[0], char_quality[1]);
332 printf("ulink: quality %d\n", quality);
333 printf("ulink: syncchar %x\n", syncchar);
334 printf("ulink: leapchar %x\n", leapchar);
336 #endif
342 if(siglchar == SIGLCHAR33x) {
345 * We got a Model 33X decoder.
346 * Timecode format from January 29, 2001 datasheet is:
347 * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5
348 * S WWVB decoder sync indicator. S for in-sync(?)
349 * or N for noisy signal.
350 * 9+ RF signal level in S-units, 0-9 followed by
351 * a space (0x20). The space turns to '+' if the
352 * level is over 9.
353 * D Data bit 0, 1, 2 (position mark), or
354 * 3 (unknown).
355 * space Space character (0x20)
356 * 00 Hours since last good WWVB frame sync. Will
357 * be 00-23 hrs, or '1d' to '7d'. Will be 'Lk'
358 * if currently in sync.
359 * space Space character (0x20)
360 * YYYY Current year, 1990-2089
361 * + Leap year indicator. '+' if a leap year,
362 * a space (0x20) if not.
363 * DDD Day of year, 001 - 366.
364 * UTC Timezone (always 'UTC').
365 * S Daylight savings indicator
366 * S - standard time (STD) in effect
367 * O - during STD to DST day 0000-2400
368 * D - daylight savings time (DST) in effect
369 * I - during DST to STD day 0000-2400
370 * space Space character (0x20)
371 * HH Hours 00-23
372 * : This is the REAL in sync indicator (: = insync)
373 * MM Minutes 00-59
374 * : : = in sync ? = NOT in sync
375 * SS Seconds 00-59
376 * L Leap second flag. Changes from space (0x20)
377 * to '+' or '-' during month preceding leap
378 * second adjustment.
379 * +5 UT1 correction (sign + digit ))
382 if (sscanf(pp->a_lastcode,
383 "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
384 char_quality, &pp->year, &pp->day,
385 &pp->hour, &syncchar, &pp->minute, &pp->second,
386 &leapchar) == 8) {
388 if (char_quality[0] == 'L') {
389 quality = 0;
390 } else if (char_quality[0] == '0') {
391 quality = (char_quality[1] & 0x0f);
392 } else {
393 quality = 99;
397 #ifdef DEBUG
398 if (debug) {
399 printf("ulink: char_quality %c %c\n",
400 char_quality[0], char_quality[1]);
401 printf("ulink: quality %d\n", quality);
402 printf("ulink: syncchar %x\n", syncchar);
403 printf("ulink: leapchar %x\n", leapchar);
405 #endif
410 break;
413 case LEN320:
416 * Model 320 Decoder
417 * The timecode format is:
419 * <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr>
421 * where:
423 * S = 'S' -- sync'd in last hour,
424 * '0'-'9' - hours x 10 since last update,
425 * '?' -- not in sync
426 * Q = Number of correlating time-frames, from 0 to 5
427 * R = 'R' -- reception in progress,
428 * 'N' -- Noisy reception,
429 * ' ' -- standby mode
430 * YYYY = year from 1990 to 2089
431 * DDD = current day from 1 to 366
432 * + = '+' if current year is a leap year, else ' '
433 * HH = UTC hour 0 to 23
434 * MM = Minutes of current hour from 0 to 59
435 * SS = Seconds of current minute from 0 to 59
436 * mm = 10's milliseconds of the current second from 00 to 99
437 * L = Leap second pending at end of month
438 * 'I' = insert, 'D'= delete
439 * T = DST <-> STD transition indicators
443 if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2ld%c",
444 &syncchar, &quality, &modechar, &pp->year, &pp->day,
445 &pp->hour, &pp->minute, &pp->second,
446 &pp->nsec, &leapchar) == 10) {
447 pp->nsec *= 10000000; /* M320 returns 10's of msecs */
448 if (leapchar == 'I' ) leapchar = '+';
449 if (leapchar == 'D' ) leapchar = '-';
450 if (syncchar != '?' ) syncchar = ':';
452 break;
455 default:
456 refclock_report(peer, CEVNT_BADREPLY);
457 return;
461 * Decode quality indicator
462 * For the 325 & 33x series, the lower the number the "better"
463 * the time is. I used the dispersion as the measure of time
464 * quality. The quality indicator in the 320 is the number of
465 * correlating time frames (the more the better)
469 * The spec sheet for the 325 & 33x series states the clock will
470 * maintain +/-0.002 seconds accuracy when locked to WWVB. This
471 * is indicated by 'Lk' in the quality portion of the incoming
472 * string. When not in lock, a drift of +/-0.015 seconds should
473 * be allowed for.
474 * With the quality indicator decoding scheme above, the 'Lk'
475 * condition will produce a quality value of 0. If the quality
476 * indicator starts with '0' then the second character is the
477 * number of hours since we were last locked. If the first
478 * character is anything other than 'L' or '0' then we have been
479 * out of lock for more than 9 hours so we assume the worst and
480 * force a quality value that selects the 'default' maximum
481 * dispersion. The dispersion values below are what came with the
482 * driver. They're not unreasonable so they've not been changed.
485 if (pp->lencode == LEN33X) {
486 switch (quality) {
487 case 0 :
488 pp->disp=.002;
489 break;
490 case 1 :
491 pp->disp=.02;
492 break;
493 case 2 :
494 pp->disp=.04;
495 break;
496 case 3 :
497 pp->disp=.08;
498 break;
499 default:
500 pp->disp=MAXDISPERSE;
501 break;
503 } else {
504 switch (quality) {
505 case 5 :
506 pp->disp=.002;
507 break;
508 case 4 :
509 pp->disp=.02;
510 break;
511 case 3 :
512 pp->disp=.04;
513 break;
514 case 2 :
515 pp->disp=.08;
516 break;
517 case 1 :
518 pp->disp=.16;
519 break;
520 default:
521 pp->disp=MAXDISPERSE;
522 break;
528 * Decode synchronization, and leap characters. If
529 * unsynchronized, set the leap bits accordingly and exit.
530 * Otherwise, set the leap bits according to the leap character.
533 if (syncchar != ':')
534 pp->leap = LEAP_NOTINSYNC;
535 else if (leapchar == '+')
536 pp->leap = LEAP_ADDSECOND;
537 else if (leapchar == '-')
538 pp->leap = LEAP_DELSECOND;
539 else
540 pp->leap = LEAP_NOWARNING;
543 * Process the new sample in the median filter and determine the
544 * timecode timestamp.
546 if (!refclock_process(pp)) {
547 refclock_report(peer, CEVNT_BADTIME);
553 * ulink_poll - called by the transmit procedure
556 static void
557 ulink_poll(
558 int unit,
559 struct peer *peer
562 struct refclockproc *pp;
563 char pollchar;
565 pp = peer->procptr;
566 pollchar = 'T';
567 if (pp->sloppyclockflag & CLK_FLAG1) {
568 if (write(pp->io.fd, &pollchar, 1) != 1)
569 refclock_report(peer, CEVNT_FAULT);
570 else
571 pp->polls++;
573 else
574 pp->polls++;
576 if (peer->burst > 0)
577 return;
578 if (pp->coderecv == pp->codeproc) {
579 refclock_report(peer, CEVNT_TIMEOUT);
580 return;
582 pp->lastref = pp->lastrec;
583 refclock_receive(peer);
584 record_clock_stats(&peer->srcadr, pp->a_lastcode);
585 peer->burst = NSTAGE;
589 #else
590 int refclock_ulink_bs;
591 #endif /* REFCLOCK */