1c0b746e5SOllivier Robert /* 2224ba2bdSOllivier Robert * refclock_ulink - clock driver for Ultralink WWVB receiver 3c0b746e5SOllivier Robert */ 4c0b746e5SOllivier Robert 5c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 6c0b746e5SOllivier Robert #include <config.h> 7c0b746e5SOllivier Robert #endif 8c0b746e5SOllivier Robert 9c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_ULINK) 10c0b746e5SOllivier Robert 11c0b746e5SOllivier Robert #include <stdio.h> 12c0b746e5SOllivier Robert #include <ctype.h> 13c0b746e5SOllivier Robert 14c0b746e5SOllivier Robert #include "ntpd.h" 15c0b746e5SOllivier Robert #include "ntp_io.h" 16c0b746e5SOllivier Robert #include "ntp_refclock.h" 17c0b746e5SOllivier Robert #include "ntp_stdlib.h" 18c0b746e5SOllivier Robert 19ea906c41SOllivier Robert /* This driver supports ultralink Model 320,325,330,331,332 WWVB radios 20c0b746e5SOllivier Robert * 21224ba2bdSOllivier Robert * this driver was based on the refclock_wwvb.c driver 22224ba2bdSOllivier Robert * in the ntp distribution. 23c0b746e5SOllivier Robert * 24224ba2bdSOllivier Robert * Fudge Factors 25c0b746e5SOllivier Robert * 26224ba2bdSOllivier Robert * fudge flag1 0 don't poll clock 27224ba2bdSOllivier Robert * 1 send poll character 28c0b746e5SOllivier Robert * 29224ba2bdSOllivier Robert * revision history: 30224ba2bdSOllivier Robert * 99/9/09 j.c.lang original edit's 31224ba2bdSOllivier Robert * 99/9/11 j.c.lang changed timecode parse to 32224ba2bdSOllivier Robert * match what the radio actually 33224ba2bdSOllivier Robert * sends. 34224ba2bdSOllivier Robert * 99/10/11 j.c.lang added support for continous 35224ba2bdSOllivier Robert * time code mode (dipsw2) 36224ba2bdSOllivier Robert * 99/11/26 j.c.lang added support for 320 decoder 37224ba2bdSOllivier Robert * (taken from Dave Strout's 38224ba2bdSOllivier Robert * Model 320 driver) 39224ba2bdSOllivier Robert * 99/11/29 j.c.lang added fudge flag 1 to control 40224ba2bdSOllivier Robert * clock polling 41224ba2bdSOllivier Robert * 99/12/15 j.c.lang fixed 320 quality flag 42224ba2bdSOllivier Robert * 01/02/21 s.l.smith fixed 33x quality flag 43224ba2bdSOllivier Robert * added more debugging stuff 44224ba2bdSOllivier Robert * updated 33x time code explanation 45ea906c41SOllivier Robert * 04/01/23 frank migge added support for 325 decoder 46ea906c41SOllivier Robert * (tested with ULM325.F) 47c0b746e5SOllivier Robert * 48224ba2bdSOllivier Robert * Questions, bugs, ideas send to: 49224ba2bdSOllivier Robert * Joseph C. Lang 50224ba2bdSOllivier Robert * tcnojl1@earthlink.net 51c0b746e5SOllivier Robert * 52224ba2bdSOllivier Robert * Dave Strout 53224ba2bdSOllivier Robert * dstrout@linuxfoundry.com 54c0b746e5SOllivier Robert * 55ea906c41SOllivier Robert * Frank Migge 56ea906c41SOllivier Robert * frank.migge@oracle.com 57ea906c41SOllivier Robert * 58224ba2bdSOllivier Robert * 59224ba2bdSOllivier Robert * on the Ultralink model 33X decoder Dip switch 2 controls 60224ba2bdSOllivier Robert * polled or continous timecode 61ea906c41SOllivier Robert * set fudge flag1 if using polled (needed for model 320 and 325) 62224ba2bdSOllivier Robert * dont set fudge flag1 if dip switch 2 is set on model 33x decoder 63c0b746e5SOllivier Robert */ 64c0b746e5SOllivier Robert 65224ba2bdSOllivier Robert 66c0b746e5SOllivier Robert /* 67c0b746e5SOllivier Robert * Interface definitions 68c0b746e5SOllivier Robert */ 69224ba2bdSOllivier Robert #define DEVICE "/dev/wwvb%d" /* device name and unit */ 70c0b746e5SOllivier Robert #define SPEED232 B9600 /* uart speed (9600 baud) */ 71224ba2bdSOllivier Robert #define PRECISION (-10) /* precision assumed (about 10 ms) */ 72224ba2bdSOllivier Robert #define REFID "WWVB" /* reference ID */ 73c0b746e5SOllivier Robert #define DESCRIPTION "Ultralink WWVB Receiver" /* WRU */ 74c0b746e5SOllivier Robert 75ea906c41SOllivier Robert #define LEN33X 32 /* timecode length Model 33X and 325 */ 76224ba2bdSOllivier Robert #define LEN320 24 /* timecode length Model 320 */ 77c0b746e5SOllivier Robert 78ea906c41SOllivier Robert #define SIGLCHAR33x 'S' /* signal strength identifier char 325 */ 79ea906c41SOllivier Robert #define SIGLCHAR325 'R' /* signal strength identifier char 33x */ 80ea906c41SOllivier Robert 81c0b746e5SOllivier Robert /* 82224ba2bdSOllivier Robert * unit control structure 83c0b746e5SOllivier Robert */ 84c0b746e5SOllivier Robert struct ulinkunit { 85c0b746e5SOllivier Robert u_char tcswitch; /* timecode switch */ 86c0b746e5SOllivier Robert l_fp laststamp; /* last receive timestamp */ 87c0b746e5SOllivier Robert }; 88c0b746e5SOllivier Robert 89c0b746e5SOllivier Robert /* 90c0b746e5SOllivier Robert * Function prototypes 91c0b746e5SOllivier Robert */ 922b15cb3dSCy Schubert static int ulink_start (int, struct peer *); 932b15cb3dSCy Schubert static void ulink_shutdown (int, struct peer *); 942b15cb3dSCy Schubert static void ulink_receive (struct recvbuf *); 952b15cb3dSCy Schubert static void ulink_poll (int, struct peer *); 96c0b746e5SOllivier Robert 97c0b746e5SOllivier Robert /* 98c0b746e5SOllivier Robert * Transfer vector 99c0b746e5SOllivier Robert */ 100c0b746e5SOllivier Robert struct refclock refclock_ulink = { 101c0b746e5SOllivier Robert ulink_start, /* start up driver */ 102c0b746e5SOllivier Robert ulink_shutdown, /* shut down driver */ 103c0b746e5SOllivier Robert ulink_poll, /* transmit poll message */ 104224ba2bdSOllivier Robert noentry, /* not used */ 105224ba2bdSOllivier Robert noentry, /* not used */ 106224ba2bdSOllivier Robert noentry, /* not used */ 107224ba2bdSOllivier Robert NOFLAGS 108c0b746e5SOllivier Robert }; 109c0b746e5SOllivier Robert 110c0b746e5SOllivier Robert 111c0b746e5SOllivier Robert /* 112c0b746e5SOllivier Robert * ulink_start - open the devices and initialize data for processing 113c0b746e5SOllivier Robert */ 114c0b746e5SOllivier Robert static int 115c0b746e5SOllivier Robert ulink_start( 116c0b746e5SOllivier Robert int unit, 117c0b746e5SOllivier Robert struct peer *peer 118c0b746e5SOllivier Robert ) 119c0b746e5SOllivier Robert { 120c0b746e5SOllivier Robert register struct ulinkunit *up; 121c0b746e5SOllivier Robert struct refclockproc *pp; 122224ba2bdSOllivier Robert int fd; 123c0b746e5SOllivier Robert char device[20]; 124224ba2bdSOllivier Robert 125c0b746e5SOllivier Robert /* 126c0b746e5SOllivier Robert * Open serial port. Use CLK line discipline, if available. 127c0b746e5SOllivier Robert */ 1282b15cb3dSCy Schubert snprintf(device, sizeof(device), DEVICE, unit); 129a466cc55SCy Schubert fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_CLK); 1302b15cb3dSCy Schubert if (fd <= 0) 131c0b746e5SOllivier Robert return (0); 132c0b746e5SOllivier Robert 133c0b746e5SOllivier Robert /* 134c0b746e5SOllivier Robert * Allocate and initialize unit structure 135c0b746e5SOllivier Robert */ 1362b15cb3dSCy Schubert up = emalloc(sizeof(struct ulinkunit)); 1372b15cb3dSCy Schubert memset(up, 0, sizeof(struct ulinkunit)); 138c0b746e5SOllivier Robert pp = peer->procptr; 139c0b746e5SOllivier Robert pp->io.clock_recv = ulink_receive; 1402b15cb3dSCy Schubert pp->io.srcclock = peer; 141c0b746e5SOllivier Robert pp->io.datalen = 0; 142c0b746e5SOllivier Robert pp->io.fd = fd; 143c0b746e5SOllivier Robert if (!io_addclock(&pp->io)) { 1442b15cb3dSCy Schubert close(fd); 1452b15cb3dSCy Schubert pp->io.fd = -1; 146c0b746e5SOllivier Robert free(up); 147c0b746e5SOllivier Robert return (0); 148c0b746e5SOllivier Robert } 1492b15cb3dSCy Schubert pp->unitptr = up; 150c0b746e5SOllivier Robert 151c0b746e5SOllivier Robert /* 152c0b746e5SOllivier Robert * Initialize miscellaneous variables 153c0b746e5SOllivier Robert */ 154c0b746e5SOllivier Robert peer->precision = PRECISION; 155c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 156c0b746e5SOllivier Robert memcpy((char *)&pp->refid, REFID, 4); 157c0b746e5SOllivier Robert return (1); 158c0b746e5SOllivier Robert } 159c0b746e5SOllivier Robert 160c0b746e5SOllivier Robert 161c0b746e5SOllivier Robert /* 162c0b746e5SOllivier Robert * ulink_shutdown - shut down the clock 163c0b746e5SOllivier Robert */ 164c0b746e5SOllivier Robert static void 165c0b746e5SOllivier Robert ulink_shutdown( 166c0b746e5SOllivier Robert int unit, 167c0b746e5SOllivier Robert struct peer *peer 168c0b746e5SOllivier Robert ) 169c0b746e5SOllivier Robert { 170c0b746e5SOllivier Robert register struct ulinkunit *up; 171c0b746e5SOllivier Robert struct refclockproc *pp; 172c0b746e5SOllivier Robert 173c0b746e5SOllivier Robert pp = peer->procptr; 1742b15cb3dSCy Schubert up = pp->unitptr; 1752b15cb3dSCy Schubert if (pp->io.fd != -1) 176c0b746e5SOllivier Robert io_closeclock(&pp->io); 1772b15cb3dSCy Schubert if (up != NULL) 178c0b746e5SOllivier Robert free(up); 179c0b746e5SOllivier Robert } 180c0b746e5SOllivier Robert 181c0b746e5SOllivier Robert 182c0b746e5SOllivier Robert /* 183c0b746e5SOllivier Robert * ulink_receive - receive data from the serial interface 184c0b746e5SOllivier Robert */ 185c0b746e5SOllivier Robert static void 186c0b746e5SOllivier Robert ulink_receive( 187c0b746e5SOllivier Robert struct recvbuf *rbufp 188c0b746e5SOllivier Robert ) 189c0b746e5SOllivier Robert { 190c0b746e5SOllivier Robert struct ulinkunit *up; 191c0b746e5SOllivier Robert struct refclockproc *pp; 192c0b746e5SOllivier Robert struct peer *peer; 193c0b746e5SOllivier Robert 194c0b746e5SOllivier Robert l_fp trtmp; /* arrival timestamp */ 1952b15cb3dSCy Schubert int quality = INT_MAX; /* quality indicator */ 196c0b746e5SOllivier Robert int temp; /* int temp */ 197224ba2bdSOllivier Robert char syncchar; /* synchronization indicator */ 198224ba2bdSOllivier Robert char leapchar; /* leap indicator */ 199224ba2bdSOllivier Robert char modechar; /* model 320 mode flag */ 200ea906c41SOllivier Robert char siglchar; /* model difference between 33x/325 */ 201224ba2bdSOllivier Robert char char_quality[2]; /* temp quality flag */ 202c0b746e5SOllivier Robert 203c0b746e5SOllivier Robert /* 204c0b746e5SOllivier Robert * Initialize pointers and read the timecode and timestamp 205c0b746e5SOllivier Robert */ 2062b15cb3dSCy Schubert peer = rbufp->recv_peer; 207c0b746e5SOllivier Robert pp = peer->procptr; 2082b15cb3dSCy Schubert up = pp->unitptr; 209c0b746e5SOllivier Robert temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); 210c0b746e5SOllivier Robert 211c0b746e5SOllivier Robert /* 212c0b746e5SOllivier Robert * Note we get a buffer and timestamp for both a <cr> and <lf>, 213224ba2bdSOllivier Robert * but only the <cr> timestamp is retained. 214c0b746e5SOllivier Robert */ 215c0b746e5SOllivier Robert if (temp == 0) { 216c0b746e5SOllivier Robert if (up->tcswitch == 0) { 217c0b746e5SOllivier Robert up->tcswitch = 1; 218c0b746e5SOllivier Robert up->laststamp = trtmp; 219c0b746e5SOllivier Robert } else 220c0b746e5SOllivier Robert up->tcswitch = 0; 221c0b746e5SOllivier Robert return; 222c0b746e5SOllivier Robert } 223c0b746e5SOllivier Robert pp->lencode = temp; 224c0b746e5SOllivier Robert pp->lastrec = up->laststamp; 225c0b746e5SOllivier Robert up->laststamp = trtmp; 226c0b746e5SOllivier Robert up->tcswitch = 1; 227c0b746e5SOllivier Robert #ifdef DEBUG 228c0b746e5SOllivier Robert if (debug) 229c0b746e5SOllivier Robert printf("ulink: timecode %d %s\n", pp->lencode, 230c0b746e5SOllivier Robert pp->a_lastcode); 231c0b746e5SOllivier Robert #endif 232c0b746e5SOllivier Robert 233c0b746e5SOllivier Robert /* 234c0b746e5SOllivier Robert * We get down to business, check the timecode format and decode 235224ba2bdSOllivier Robert * its contents. If the timecode has invalid length or is not in 236224ba2bdSOllivier Robert * proper format, we declare bad format and exit. 237c0b746e5SOllivier Robert */ 238ea906c41SOllivier Robert syncchar = leapchar = modechar = siglchar = ' '; 239224ba2bdSOllivier Robert switch (pp->lencode ) { 240224ba2bdSOllivier Robert case LEN33X: 241ea906c41SOllivier Robert 242c0b746e5SOllivier Robert /* 243ea906c41SOllivier Robert * First we check if the format is 33x or 325: 244ea906c41SOllivier Robert * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 (33x) 245ea906c41SOllivier Robert * <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5 (325) 246ea906c41SOllivier Robert * simply by comparing if the signal level is 'S' or 'R' 247ea906c41SOllivier Robert */ 248ea906c41SOllivier Robert 249ea906c41SOllivier Robert if (sscanf(pp->a_lastcode, "%c%*31c", 250ea906c41SOllivier Robert &siglchar) == 1) { 251ea906c41SOllivier Robert 252ea906c41SOllivier Robert if(siglchar == SIGLCHAR325) { 253ea906c41SOllivier Robert 254ea906c41SOllivier Robert /* 255ea906c41SOllivier Robert * decode for a Model 325 decoder. 256ea906c41SOllivier Robert * Timecode format from January 23, 2004 datasheet is: 257ea906c41SOllivier Robert * 258ea906c41SOllivier Robert * <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5 259ea906c41SOllivier Robert * 260ea906c41SOllivier Robert * R WWVB decodersignal readability R1 - R5 261ea906c41SOllivier Robert * 5 R1 is unreadable, R5 is best 262ea906c41SOllivier Robert * space a space (0x20) 263ea906c41SOllivier Robert * 1 Data bit 0, 1, M (pos mark), or ? (unknown). 264ea906c41SOllivier Robert * C Reception from either (C)olorado or (H)awaii 265ea906c41SOllivier Robert * 00 Hours since last good WWVB frame sync. Will 266ea906c41SOllivier Robert * be 00-99 267ea906c41SOllivier Robert * space Space char (0x20) or (0xa5) if locked to wwvb 268ea906c41SOllivier Robert * YYYY Current year, 2000-2099 269ea906c41SOllivier Robert * + Leap year indicator. '+' if a leap year, 270ea906c41SOllivier Robert * a space (0x20) if not. 271ea906c41SOllivier Robert * DDD Day of year, 000 - 365. 272ea906c41SOllivier Robert * UTC Timezone (always 'UTC'). 273ea906c41SOllivier Robert * S Daylight savings indicator 274ea906c41SOllivier Robert * S - standard time (STD) in effect 275ea906c41SOllivier Robert * O - during STD to DST day 0000-2400 276ea906c41SOllivier Robert * D - daylight savings time (DST) in effect 277ea906c41SOllivier Robert * I - during DST to STD day 0000-2400 278ea906c41SOllivier Robert * space Space character (0x20) 279ea906c41SOllivier Robert * HH Hours 00-23 280ea906c41SOllivier Robert * : This is the REAL in sync indicator (: = insync) 281ea906c41SOllivier Robert * MM Minutes 00-59 282ea906c41SOllivier Robert * : : = in sync ? = NOT in sync 283ea906c41SOllivier Robert * SS Seconds 00-59 284ea906c41SOllivier Robert * L Leap second flag. Changes from space (0x20) 285ea906c41SOllivier Robert * to 'I' or 'D' during month preceding leap 286ea906c41SOllivier Robert * second adjustment. (I)nsert or (D)elete 287ea906c41SOllivier Robert * +5 UT1 correction (sign + digit )) 288ea906c41SOllivier Robert */ 289ea906c41SOllivier Robert 290ea906c41SOllivier Robert if (sscanf(pp->a_lastcode, 291ea906c41SOllivier Robert "%*2c %*2c%2c%*c%4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c", 292ea906c41SOllivier Robert char_quality, &pp->year, &pp->day, 293ea906c41SOllivier Robert &pp->hour, &syncchar, &pp->minute, &pp->second, 294ea906c41SOllivier Robert &leapchar) == 8) { 295ea906c41SOllivier Robert 296ea906c41SOllivier Robert if (char_quality[0] == '0') { 297ea906c41SOllivier Robert quality = 0; 298ea906c41SOllivier Robert } else if (char_quality[0] == '0') { 299ea906c41SOllivier Robert quality = (char_quality[1] & 0x0f); 300ea906c41SOllivier Robert } else { 301ea906c41SOllivier Robert quality = 99; 302ea906c41SOllivier Robert } 303ea906c41SOllivier Robert 304ea906c41SOllivier Robert if (leapchar == 'I' ) leapchar = '+'; 305ea906c41SOllivier Robert if (leapchar == 'D' ) leapchar = '-'; 306ea906c41SOllivier Robert 307ea906c41SOllivier Robert /* 308ea906c41SOllivier Robert #ifdef DEBUG 309ea906c41SOllivier Robert if (debug) { 310ea906c41SOllivier Robert printf("ulink: char_quality %c %c\n", 311ea906c41SOllivier Robert char_quality[0], char_quality[1]); 312ea906c41SOllivier Robert printf("ulink: quality %d\n", quality); 313ea906c41SOllivier Robert printf("ulink: syncchar %x\n", syncchar); 314ea906c41SOllivier Robert printf("ulink: leapchar %x\n", leapchar); 315ea906c41SOllivier Robert } 316ea906c41SOllivier Robert #endif 317ea906c41SOllivier Robert */ 318ea906c41SOllivier Robert 319ea906c41SOllivier Robert } 320ea906c41SOllivier Robert 321ea906c41SOllivier Robert } 322ea906c41SOllivier Robert if(siglchar == SIGLCHAR33x) { 323ea906c41SOllivier Robert 324ea906c41SOllivier Robert /* 325ea906c41SOllivier Robert * We got a Model 33X decoder. 326224ba2bdSOllivier Robert * Timecode format from January 29, 2001 datasheet is: 327224ba2bdSOllivier Robert * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 328224ba2bdSOllivier Robert * S WWVB decoder sync indicator. S for in-sync(?) 329224ba2bdSOllivier Robert * or N for noisy signal. 330224ba2bdSOllivier Robert * 9+ RF signal level in S-units, 0-9 followed by 331224ba2bdSOllivier Robert * a space (0x20). The space turns to '+' if the 332224ba2bdSOllivier Robert * level is over 9. 333224ba2bdSOllivier Robert * D Data bit 0, 1, 2 (position mark), or 334224ba2bdSOllivier Robert * 3 (unknown). 335224ba2bdSOllivier Robert * space Space character (0x20) 336224ba2bdSOllivier Robert * 00 Hours since last good WWVB frame sync. Will 337224ba2bdSOllivier Robert * be 00-23 hrs, or '1d' to '7d'. Will be 'Lk' 338224ba2bdSOllivier Robert * if currently in sync. 339224ba2bdSOllivier Robert * space Space character (0x20) 340224ba2bdSOllivier Robert * YYYY Current year, 1990-2089 341224ba2bdSOllivier Robert * + Leap year indicator. '+' if a leap year, 342224ba2bdSOllivier Robert * a space (0x20) if not. 343224ba2bdSOllivier Robert * DDD Day of year, 001 - 366. 344224ba2bdSOllivier Robert * UTC Timezone (always 'UTC'). 345224ba2bdSOllivier Robert * S Daylight savings indicator 346224ba2bdSOllivier Robert * S - standard time (STD) in effect 347224ba2bdSOllivier Robert * O - during STD to DST day 0000-2400 348224ba2bdSOllivier Robert * D - daylight savings time (DST) in effect 349224ba2bdSOllivier Robert * I - during DST to STD day 0000-2400 350224ba2bdSOllivier Robert * space Space character (0x20) 351224ba2bdSOllivier Robert * HH Hours 00-23 352224ba2bdSOllivier Robert * : This is the REAL in sync indicator (: = insync) 353224ba2bdSOllivier Robert * MM Minutes 00-59 354224ba2bdSOllivier Robert * : : = in sync ? = NOT in sync 355224ba2bdSOllivier Robert * SS Seconds 00-59 356224ba2bdSOllivier Robert * L Leap second flag. Changes from space (0x20) 357224ba2bdSOllivier Robert * to '+' or '-' during month preceding leap 358224ba2bdSOllivier Robert * second adjustment. 359224ba2bdSOllivier Robert * +5 UT1 correction (sign + digit )) 360c0b746e5SOllivier Robert */ 361c0b746e5SOllivier Robert 362224ba2bdSOllivier Robert if (sscanf(pp->a_lastcode, 363224ba2bdSOllivier Robert "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c", 364224ba2bdSOllivier Robert char_quality, &pp->year, &pp->day, 365224ba2bdSOllivier Robert &pp->hour, &syncchar, &pp->minute, &pp->second, 366224ba2bdSOllivier Robert &leapchar) == 8) { 367224ba2bdSOllivier Robert 368224ba2bdSOllivier Robert if (char_quality[0] == 'L') { 369224ba2bdSOllivier Robert quality = 0; 370224ba2bdSOllivier Robert } else if (char_quality[0] == '0') { 371224ba2bdSOllivier Robert quality = (char_quality[1] & 0x0f); 372224ba2bdSOllivier Robert } else { 373224ba2bdSOllivier Robert quality = 99; 374224ba2bdSOllivier Robert } 375c0b746e5SOllivier Robert 376c0b746e5SOllivier Robert /* 377224ba2bdSOllivier Robert #ifdef DEBUG 378224ba2bdSOllivier Robert if (debug) { 379224ba2bdSOllivier Robert printf("ulink: char_quality %c %c\n", 380224ba2bdSOllivier Robert char_quality[0], char_quality[1]); 381224ba2bdSOllivier Robert printf("ulink: quality %d\n", quality); 382224ba2bdSOllivier Robert printf("ulink: syncchar %x\n", syncchar); 383224ba2bdSOllivier Robert printf("ulink: leapchar %x\n", leapchar); 384224ba2bdSOllivier Robert } 385224ba2bdSOllivier Robert #endif 386224ba2bdSOllivier Robert */ 387224ba2bdSOllivier Robert 388ea906c41SOllivier Robert } 389ea906c41SOllivier Robert } 390224ba2bdSOllivier Robert break; 391224ba2bdSOllivier Robert } 392224ba2bdSOllivier Robert 393224ba2bdSOllivier Robert case LEN320: 394ea906c41SOllivier Robert 395224ba2bdSOllivier Robert /* 396224ba2bdSOllivier Robert * Model 320 Decoder 397224ba2bdSOllivier Robert * The timecode format is: 398224ba2bdSOllivier Robert * 399224ba2bdSOllivier Robert * <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr> 400224ba2bdSOllivier Robert * 401224ba2bdSOllivier Robert * where: 402224ba2bdSOllivier Robert * 403224ba2bdSOllivier Robert * S = 'S' -- sync'd in last hour, 404224ba2bdSOllivier Robert * '0'-'9' - hours x 10 since last update, 405224ba2bdSOllivier Robert * '?' -- not in sync 406224ba2bdSOllivier Robert * Q = Number of correlating time-frames, from 0 to 5 407224ba2bdSOllivier Robert * R = 'R' -- reception in progress, 408224ba2bdSOllivier Robert * 'N' -- Noisy reception, 409224ba2bdSOllivier Robert * ' ' -- standby mode 410224ba2bdSOllivier Robert * YYYY = year from 1990 to 2089 411224ba2bdSOllivier Robert * DDD = current day from 1 to 366 412224ba2bdSOllivier Robert * + = '+' if current year is a leap year, else ' ' 413224ba2bdSOllivier Robert * HH = UTC hour 0 to 23 414224ba2bdSOllivier Robert * MM = Minutes of current hour from 0 to 59 415224ba2bdSOllivier Robert * SS = Seconds of current minute from 0 to 59 416224ba2bdSOllivier Robert * mm = 10's milliseconds of the current second from 00 to 99 417224ba2bdSOllivier Robert * L = Leap second pending at end of month 418224ba2bdSOllivier Robert * 'I' = insert, 'D'= delete 419224ba2bdSOllivier Robert * T = DST <-> STD transition indicators 420224ba2bdSOllivier Robert * 421224ba2bdSOllivier Robert */ 422ea906c41SOllivier Robert 4239c2daa00SOllivier Robert if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2ld%c", 424224ba2bdSOllivier Robert &syncchar, &quality, &modechar, &pp->year, &pp->day, 425224ba2bdSOllivier Robert &pp->hour, &pp->minute, &pp->second, 4269c2daa00SOllivier Robert &pp->nsec, &leapchar) == 10) { 4279c2daa00SOllivier Robert pp->nsec *= 10000000; /* M320 returns 10's of msecs */ 428224ba2bdSOllivier Robert if (leapchar == 'I' ) leapchar = '+'; 429224ba2bdSOllivier Robert if (leapchar == 'D' ) leapchar = '-'; 430224ba2bdSOllivier Robert if (syncchar != '?' ) syncchar = ':'; 431224ba2bdSOllivier Robert 432224ba2bdSOllivier Robert break; 433224ba2bdSOllivier Robert } 434224ba2bdSOllivier Robert 435224ba2bdSOllivier Robert default: 436224ba2bdSOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 437224ba2bdSOllivier Robert return; 438224ba2bdSOllivier Robert } 439224ba2bdSOllivier Robert 440224ba2bdSOllivier Robert /* 441224ba2bdSOllivier Robert * Decode quality indicator 442224ba2bdSOllivier Robert * For the 325 & 33x series, the lower the number the "better" 443224ba2bdSOllivier Robert * the time is. I used the dispersion as the measure of time 444224ba2bdSOllivier Robert * quality. The quality indicator in the 320 is the number of 445224ba2bdSOllivier Robert * correlating time frames (the more the better) 446224ba2bdSOllivier Robert */ 447224ba2bdSOllivier Robert 448224ba2bdSOllivier Robert /* 449224ba2bdSOllivier Robert * The spec sheet for the 325 & 33x series states the clock will 450224ba2bdSOllivier Robert * maintain +/-0.002 seconds accuracy when locked to WWVB. This 451224ba2bdSOllivier Robert * is indicated by 'Lk' in the quality portion of the incoming 452224ba2bdSOllivier Robert * string. When not in lock, a drift of +/-0.015 seconds should 453224ba2bdSOllivier Robert * be allowed for. 454224ba2bdSOllivier Robert * With the quality indicator decoding scheme above, the 'Lk' 455224ba2bdSOllivier Robert * condition will produce a quality value of 0. If the quality 456224ba2bdSOllivier Robert * indicator starts with '0' then the second character is the 457224ba2bdSOllivier Robert * number of hours since we were last locked. If the first 458224ba2bdSOllivier Robert * character is anything other than 'L' or '0' then we have been 459224ba2bdSOllivier Robert * out of lock for more than 9 hours so we assume the worst and 460224ba2bdSOllivier Robert * force a quality value that selects the 'default' maximum 461224ba2bdSOllivier Robert * dispersion. The dispersion values below are what came with the 462224ba2bdSOllivier Robert * driver. They're not unreasonable so they've not been changed. 463224ba2bdSOllivier Robert */ 464224ba2bdSOllivier Robert 465224ba2bdSOllivier Robert if (pp->lencode == LEN33X) { 466224ba2bdSOllivier Robert switch (quality) { 467224ba2bdSOllivier Robert case 0 : 468224ba2bdSOllivier Robert pp->disp=.002; 469224ba2bdSOllivier Robert break; 470224ba2bdSOllivier Robert case 1 : 471224ba2bdSOllivier Robert pp->disp=.02; 472224ba2bdSOllivier Robert break; 473224ba2bdSOllivier Robert case 2 : 474224ba2bdSOllivier Robert pp->disp=.04; 475224ba2bdSOllivier Robert break; 476224ba2bdSOllivier Robert case 3 : 477224ba2bdSOllivier Robert pp->disp=.08; 478224ba2bdSOllivier Robert break; 479224ba2bdSOllivier Robert default: 480224ba2bdSOllivier Robert pp->disp=MAXDISPERSE; 481224ba2bdSOllivier Robert break; 482224ba2bdSOllivier Robert } 483224ba2bdSOllivier Robert } else { 484224ba2bdSOllivier Robert switch (quality) { 485224ba2bdSOllivier Robert case 5 : 486224ba2bdSOllivier Robert pp->disp=.002; 487224ba2bdSOllivier Robert break; 488224ba2bdSOllivier Robert case 4 : 489224ba2bdSOllivier Robert pp->disp=.02; 490224ba2bdSOllivier Robert break; 491224ba2bdSOllivier Robert case 3 : 492224ba2bdSOllivier Robert pp->disp=.04; 493224ba2bdSOllivier Robert break; 494224ba2bdSOllivier Robert case 2 : 495224ba2bdSOllivier Robert pp->disp=.08; 496224ba2bdSOllivier Robert break; 497224ba2bdSOllivier Robert case 1 : 498224ba2bdSOllivier Robert pp->disp=.16; 499224ba2bdSOllivier Robert break; 500224ba2bdSOllivier Robert default: 501224ba2bdSOllivier Robert pp->disp=MAXDISPERSE; 502224ba2bdSOllivier Robert break; 503224ba2bdSOllivier Robert } 504224ba2bdSOllivier Robert 505224ba2bdSOllivier Robert } 506224ba2bdSOllivier Robert 507224ba2bdSOllivier Robert /* 508224ba2bdSOllivier Robert * Decode synchronization, and leap characters. If 509c0b746e5SOllivier Robert * unsynchronized, set the leap bits accordingly and exit. 510c0b746e5SOllivier Robert * Otherwise, set the leap bits according to the leap character. 511c0b746e5SOllivier Robert */ 512224ba2bdSOllivier Robert 513224ba2bdSOllivier Robert if (syncchar != ':') 514224ba2bdSOllivier Robert pp->leap = LEAP_NOTINSYNC; 515224ba2bdSOllivier Robert else if (leapchar == '+') 516224ba2bdSOllivier Robert pp->leap = LEAP_ADDSECOND; 517224ba2bdSOllivier Robert else if (leapchar == '-') 518224ba2bdSOllivier Robert pp->leap = LEAP_DELSECOND; 519224ba2bdSOllivier Robert else 520c0b746e5SOllivier Robert pp->leap = LEAP_NOWARNING; 521c0b746e5SOllivier Robert 522c0b746e5SOllivier Robert /* 523c0b746e5SOllivier Robert * Process the new sample in the median filter and determine the 524c0b746e5SOllivier Robert * timecode timestamp. 525c0b746e5SOllivier Robert */ 526224ba2bdSOllivier Robert if (!refclock_process(pp)) { 527c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 528c0b746e5SOllivier Robert } 529c0b746e5SOllivier Robert 530224ba2bdSOllivier Robert } 531224ba2bdSOllivier Robert 532c0b746e5SOllivier Robert /* 533c0b746e5SOllivier Robert * ulink_poll - called by the transmit procedure 534c0b746e5SOllivier Robert */ 535ea906c41SOllivier Robert 536c0b746e5SOllivier Robert static void 537c0b746e5SOllivier Robert ulink_poll( 538c0b746e5SOllivier Robert int unit, 539c0b746e5SOllivier Robert struct peer *peer 540c0b746e5SOllivier Robert ) 541c0b746e5SOllivier Robert { 542c0b746e5SOllivier Robert struct refclockproc *pp; 543c0b746e5SOllivier Robert char pollchar; 544c0b746e5SOllivier Robert 545c0b746e5SOllivier Robert pp = peer->procptr; 546c0b746e5SOllivier Robert pollchar = 'T'; 547224ba2bdSOllivier Robert if (pp->sloppyclockflag & CLK_FLAG1) { 548c0b746e5SOllivier Robert if (write(pp->io.fd, &pollchar, 1) != 1) 549c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 550c0b746e5SOllivier Robert else 551c0b746e5SOllivier Robert pp->polls++; 552224ba2bdSOllivier Robert } 553224ba2bdSOllivier Robert else 554224ba2bdSOllivier Robert pp->polls++; 555224ba2bdSOllivier Robert 556c0b746e5SOllivier Robert if (pp->coderecv == pp->codeproc) { 557c0b746e5SOllivier Robert refclock_report(peer, CEVNT_TIMEOUT); 558c0b746e5SOllivier Robert return; 559c0b746e5SOllivier Robert } 5609c2daa00SOllivier Robert pp->lastref = pp->lastrec; 561c0b746e5SOllivier Robert refclock_receive(peer); 5629c2daa00SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 563c0b746e5SOllivier Robert 564c0b746e5SOllivier Robert } 565c0b746e5SOllivier Robert 566c0b746e5SOllivier Robert #else 567*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT 568c0b746e5SOllivier Robert #endif /* REFCLOCK */ 569