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