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