1c0b746e5SOllivier Robert /* 2ea906c41SOllivier Robert * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time 3ea906c41SOllivier Robert * Services 4c0b746e5SOllivier Robert */ 5c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 6c0b746e5SOllivier Robert #include <config.h> 7c0b746e5SOllivier Robert #endif 8c0b746e5SOllivier Robert 92b15cb3dSCy Schubert #if defined(REFCLOCK) && defined(CLOCK_ACTS) 10c0b746e5SOllivier Robert 11c0b746e5SOllivier Robert #include "ntpd.h" 12c0b746e5SOllivier Robert #include "ntp_io.h" 13c0b746e5SOllivier Robert #include "ntp_unixtime.h" 14c0b746e5SOllivier Robert #include "ntp_refclock.h" 15c0b746e5SOllivier Robert #include "ntp_stdlib.h" 16c0b746e5SOllivier Robert #include "ntp_control.h" 17c0b746e5SOllivier Robert 18224ba2bdSOllivier Robert #include <stdio.h> 19224ba2bdSOllivier Robert #include <ctype.h> 20224ba2bdSOllivier Robert #ifdef HAVE_SYS_IOCTL_H 21224ba2bdSOllivier Robert # include <sys/ioctl.h> 22224ba2bdSOllivier Robert #endif /* HAVE_SYS_IOCTL_H */ 23224ba2bdSOllivier Robert 24c0b746e5SOllivier Robert /* 25ea906c41SOllivier Robert * This driver supports the US (NIST, USNO) and European (PTB, NPL, 26ea906c41SOllivier Robert * etc.) modem time services, as well as Spectracom GPS and WWVB 27ea906c41SOllivier Robert * receivers connected via a modem. The driver periodically dials a 28ea906c41SOllivier Robert * number from a telephone list, receives the timecode data and 29ea906c41SOllivier Robert * calculates the local clock correction. It is designed primarily for 30ea906c41SOllivier Robert * use as backup when neither a radio clock nor connectivity to Internet 31ea906c41SOllivier Robert * time servers is available. 32c0b746e5SOllivier Robert * 33ea906c41SOllivier Robert * This driver requires a modem with a Hayes-compatible command set and 34ea906c41SOllivier Robert * control over the modem data terminal ready (DTR) control line. The 35ea906c41SOllivier Robert * modem setup string is hard-coded in the driver and may require 362b15cb3dSCy Schubert * changes for nonstandard modems or special circumstances. 37c0b746e5SOllivier Robert * 382b15cb3dSCy Schubert * When enabled, the calling program dials the first number in the 392b15cb3dSCy Schubert * phones file. If that call fails, it dials the second number and 402b15cb3dSCy Schubert * so on. The phone number is specified by the Hayes ATDT prefix 412b15cb3dSCy Schubert * followed by the number itself, including the long-distance prefix 422b15cb3dSCy Schubert * and delay code, if necessary. The calling program is enabled 432b15cb3dSCy Schubert * when (a) fudge flag1 is set by ntpdc, (b) at each poll interval 442b15cb3dSCy Schubert * when no other synchronization sources are present, and (c) at each 452b15cb3dSCy Schubert * poll interval whether or not other synchronization sources are 462b15cb3dSCy Schubert * present. The calling program disconnects if (a) the called party 472b15cb3dSCy Schubert * is busy or does not answer, (b) the called party disconnects 482b15cb3dSCy Schubert * before a sufficient nuimber of timecodes have been received. 49c0b746e5SOllivier Robert * 50ea906c41SOllivier Robert * The driver is transparent to each of the modem time services and 51ea906c41SOllivier Robert * Spectracom radios. It selects the parsing algorithm depending on the 52ea906c41SOllivier Robert * message length. There is some hazard should the message be corrupted. 53ea906c41SOllivier Robert * However, the data format is checked carefully and only if all checks 54ea906c41SOllivier Robert * succeed is the message accepted. Corrupted lines are discarded 55ea906c41SOllivier Robert * without complaint. 56c0b746e5SOllivier Robert * 57ea906c41SOllivier Robert * Fudge controls 58c0b746e5SOllivier Robert * 59ea906c41SOllivier Robert * flag1 force a call in manual mode 60ea906c41SOllivier Robert * flag2 enable port locking (not verified) 612b15cb3dSCy Schubert * flag3 not used 62ea906c41SOllivier Robert * flag4 not used 63c0b746e5SOllivier Robert * 64ea906c41SOllivier Robert * time1 offset adjustment (s) 65c0b746e5SOllivier Robert * 662b15cb3dSCy Schubert * Ordinarily, the serial port is connected to a modem and the phones 672b15cb3dSCy Schubert * list is defined. If no phones list is defined, the port can be 682b15cb3dSCy Schubert * connected directly to a device or another computer. In this case the 692b15cb3dSCy Schubert * driver will send a single character 'T' at each poll event. If 702b15cb3dSCy Schubert * fudge flag2 is enabled, port locking allows the modem to be shared 712b15cb3dSCy Schubert * when not in use by this driver. 72c0b746e5SOllivier Robert */ 73c0b746e5SOllivier Robert /* 74ea906c41SOllivier Robert * National Institute of Science and Technology (NIST) 75c0b746e5SOllivier Robert * 76ea906c41SOllivier Robert * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii) 77ea906c41SOllivier Robert * 78ea906c41SOllivier Robert * Data Format 79c0b746e5SOllivier Robert * 80c0b746e5SOllivier Robert * National Institute of Standards and Technology 81c0b746e5SOllivier Robert * Telephone Time Service, Generator 3B 82c0b746e5SOllivier Robert * Enter question mark "?" for HELP 83c0b746e5SOllivier Robert * D L D 84c0b746e5SOllivier Robert * MJD YR MO DA H M S ST S UT1 msADV <OTM> 85ea906c41SOllivier Robert * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF> 86ea906c41SOllivier Robert * ... 87c0b746e5SOllivier Robert * 88ea906c41SOllivier Robert * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is 89ea906c41SOllivier Robert * the on-time markers echoed by the driver and used by NIST to measure 902b15cb3dSCy Schubert * and correct for the propagation delay. Note: the ACTS timecode has 912b15cb3dSCy Schubert * recently been changed to eliminate the * on-time indicator. The 922b15cb3dSCy Schubert * reason for this and the long term implications are not clear. 93c0b746e5SOllivier Robert * 94ea906c41SOllivier Robert * US Naval Observatory (USNO) 95c0b746e5SOllivier Robert * 96ea906c41SOllivier Robert * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO) 97c0b746e5SOllivier Robert * 98ea906c41SOllivier Robert * Data Format (two lines, repeating at one-second intervals) 99c0b746e5SOllivier Robert * 100ea906c41SOllivier Robert * jjjjj nnn hhmmss UTC<CR><LF> 101ea906c41SOllivier Robert * *<CR><LF> 102c0b746e5SOllivier Robert * 103ea906c41SOllivier Robert * jjjjj modified Julian day number (not used) 104ea906c41SOllivier Robert * nnn day of year 105ea906c41SOllivier Robert * hhmmss second of day 106ea906c41SOllivier Robert * * on-time marker for previous timecode 107ea906c41SOllivier Robert * ... 108c0b746e5SOllivier Robert * 109ea906c41SOllivier Robert * USNO does not correct for the propagation delay. A fudge time1 of 110ea906c41SOllivier Robert * about .06 s is advisable. 111c0b746e5SOllivier Robert * 112ea906c41SOllivier Robert * European Services (PTB, NPL, etc.) 113c0b746e5SOllivier Robert * 114ea906c41SOllivier Robert * PTB: +49 531 512038 (Germany) 115ea906c41SOllivier Robert * NPL: 0906 851 6333 (UK only) 116c0b746e5SOllivier Robert * 117ea906c41SOllivier Robert * Data format (see the documentation for phone numbers and formats.) 118c0b746e5SOllivier Robert * 119ea906c41SOllivier Robert * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF> 120c0b746e5SOllivier Robert * 121ea906c41SOllivier Robert * Spectracom GPS and WWVB Receivers 122c0b746e5SOllivier Robert * 123ea906c41SOllivier Robert * If a modem is connected to a Spectracom receiver, this driver will 124ea906c41SOllivier Robert * call it up and retrieve the time in one of two formats. As this 125ea906c41SOllivier Robert * driver does not send anything, the radio will have to either be 126ea906c41SOllivier Robert * configured in continuous mode or be polled by another local driver. 127c0b746e5SOllivier Robert */ 128c0b746e5SOllivier Robert /* 129c0b746e5SOllivier Robert * Interface definitions 130c0b746e5SOllivier Robert */ 131c0b746e5SOllivier Robert #define DEVICE "/dev/acts%d" /* device name and unit */ 1322b15cb3dSCy Schubert #define SPEED232 B19200 /* uart speed (19200 bps) */ 133ea906c41SOllivier Robert #define PRECISION (-10) /* precision assumed (about 1 ms) */ 1342b15cb3dSCy Schubert #define LOCKFILE "/var/spool/lock/LCK..cua%d" 135ea906c41SOllivier Robert #define DESCRIPTION "Automated Computer Time Service" /* WRU */ 136ea906c41SOllivier Robert #define REFID "NONE" /* default reference ID */ 137ea906c41SOllivier Robert #define MSGCNT 20 /* max message count */ 1382b15cb3dSCy Schubert #define MAXPHONE 10 /* max number of phone numbers */ 139ea906c41SOllivier Robert 140ea906c41SOllivier Robert /* 1412b15cb3dSCy Schubert * Calling program modes (mode) 142ea906c41SOllivier Robert */ 1432b15cb3dSCy Schubert #define MODE_BACKUP 0 /* backup mode */ 1442b15cb3dSCy Schubert #define MODE_AUTO 1 /* automatic mode */ 145c0b746e5SOllivier Robert #define MODE_MANUAL 2 /* manual mode */ 146c0b746e5SOllivier Robert 147ea906c41SOllivier Robert /* 1482b15cb3dSCy Schubert * Service identifiers (message length) 149ea906c41SOllivier Robert */ 150ea906c41SOllivier Robert #define REFACTS "NIST" /* NIST reference ID */ 1512b15cb3dSCy Schubert #define LENACTS 50 /* NIST format A */ 152ea906c41SOllivier Robert #define REFUSNO "USNO" /* USNO reference ID */ 153ea906c41SOllivier Robert #define LENUSNO 20 /* USNO */ 154ea906c41SOllivier Robert #define REFPTB "PTB\0" /* PTB/NPL reference ID */ 155ea906c41SOllivier Robert #define LENPTB 78 /* PTB/NPL format */ 156ea906c41SOllivier Robert #define REFWWVB "WWVB" /* WWVB reference ID */ 157ea906c41SOllivier Robert #define LENWWVB0 22 /* WWVB format 0 */ 158ea906c41SOllivier Robert #define LENWWVB2 24 /* WWVB format 2 */ 159ea906c41SOllivier Robert #define LF 0x0a /* ASCII LF */ 160c0b746e5SOllivier Robert 161c0b746e5SOllivier Robert /* 1622b15cb3dSCy Schubert * Modem setup strings. These may have to be changed for 1632b15cb3dSCy Schubert * some modems. 164c0b746e5SOllivier Robert * 165c0b746e5SOllivier Robert * AT command prefix 166ea906c41SOllivier Robert * B1 US answer tone 167ea906c41SOllivier Robert * &C0 disable carrier detect 168c0b746e5SOllivier Robert * &D2 hang up and return to command mode on DTR transition 169c0b746e5SOllivier Robert * E0 modem command echo disabled 1702b15cb3dSCy Schubert * L1 set modem speaker volume to low level 171ea906c41SOllivier Robert * M1 speaker enabled until carrier detect 172c0b746e5SOllivier Robert * Q0 return result codes 173c0b746e5SOllivier Robert * V1 return result codes as English words 1742b15cb3dSCy Schubert * Y1 enable long-space disconnect 175c0b746e5SOllivier Robert */ 1762b15cb3dSCy Schubert const char def_modem_setup[] = "ATB1&C0&D2E0L1M1Q0V1Y1"; 1772b15cb3dSCy Schubert const char *modem_setup = def_modem_setup; 178c0b746e5SOllivier Robert 179c0b746e5SOllivier Robert /* 180ea906c41SOllivier Robert * Timeouts (all in seconds) 181c0b746e5SOllivier Robert */ 182ea906c41SOllivier Robert #define SETUP 3 /* setup timeout */ 1832b15cb3dSCy Schubert #define REDIAL 30 /* redial timeout */ 184ea906c41SOllivier Robert #define ANSWER 60 /* answer timeout */ 1852b15cb3dSCy Schubert #define TIMECODE 60 /* message timeout */ 1862b15cb3dSCy Schubert #define MAXCODE 20 /* max timecodes */ 187c0b746e5SOllivier Robert 188c0b746e5SOllivier Robert /* 189ea906c41SOllivier Robert * State machine codes 190c0b746e5SOllivier Robert */ 1912b15cb3dSCy Schubert typedef enum { 1922b15cb3dSCy Schubert S_IDLE, /* wait for poll */ 1932b15cb3dSCy Schubert S_SETUP, /* send modem setup */ 1942b15cb3dSCy Schubert S_CONNECT, /* wait for answer */ 1952b15cb3dSCy Schubert S_MSG /* wait for timecode */ 1962b15cb3dSCy Schubert } teModemState; 197c0b746e5SOllivier Robert 198c0b746e5SOllivier Robert /* 199c0b746e5SOllivier Robert * Unit control structure 200c0b746e5SOllivier Robert */ 201c0b746e5SOllivier Robert struct actsunit { 202ea906c41SOllivier Robert int unit; /* unit number */ 203c0b746e5SOllivier Robert int state; /* the first one was Delaware */ 204ea906c41SOllivier Robert int timer; /* timeout counter */ 205ea906c41SOllivier Robert int retry; /* retry index */ 206ea906c41SOllivier Robert int msgcnt; /* count of messages received */ 207ea906c41SOllivier Robert l_fp tstamp; /* on-time timestamp */ 2082b15cb3dSCy Schubert char *bufptr; /* next incoming char stored here */ 2092b15cb3dSCy Schubert char buf[BMAX]; /* bufptr roams within buf[] */ 210c0b746e5SOllivier Robert }; 211c0b746e5SOllivier Robert 212c0b746e5SOllivier Robert /* 213c0b746e5SOllivier Robert * Function prototypes 214c0b746e5SOllivier Robert */ 2152b15cb3dSCy Schubert static int acts_start (int, struct peer *); 2162b15cb3dSCy Schubert static void acts_shutdown (int, struct peer *); 2172b15cb3dSCy Schubert static void acts_receive (struct recvbuf *); 2182b15cb3dSCy Schubert static void acts_message (struct peer *, const char *); 2192b15cb3dSCy Schubert static void acts_timecode (struct peer *, const char *); 2202b15cb3dSCy Schubert static void acts_poll (int, struct peer *); 2212b15cb3dSCy Schubert static void acts_timeout (struct peer *, teModemState); 2222b15cb3dSCy Schubert static void acts_timer (int, struct peer *); 2232b15cb3dSCy Schubert static void acts_close (struct peer *); 224c0b746e5SOllivier Robert 225c0b746e5SOllivier Robert /* 226c0b746e5SOllivier Robert * Transfer vector (conditional structure name) 227c0b746e5SOllivier Robert */ 228ea906c41SOllivier Robert struct refclock refclock_acts = { 229c0b746e5SOllivier Robert acts_start, /* start up driver */ 230c0b746e5SOllivier Robert acts_shutdown, /* shut down driver */ 231c0b746e5SOllivier Robert acts_poll, /* transmit poll message */ 232ea906c41SOllivier Robert noentry, /* not used */ 233ea906c41SOllivier Robert noentry, /* not used */ 234ea906c41SOllivier Robert noentry, /* not used */ 235ea906c41SOllivier Robert acts_timer /* housekeeping timer */ 236c0b746e5SOllivier Robert }; 237c0b746e5SOllivier Robert 238c0b746e5SOllivier Robert /* 239ea906c41SOllivier Robert * Initialize data for processing 240c0b746e5SOllivier Robert */ 241c0b746e5SOllivier Robert static int 242c0b746e5SOllivier Robert acts_start( 243c0b746e5SOllivier Robert int unit, 244c0b746e5SOllivier Robert struct peer *peer 245c0b746e5SOllivier Robert ) 246c0b746e5SOllivier Robert { 247ea906c41SOllivier Robert struct actsunit *up; 248c0b746e5SOllivier Robert struct refclockproc *pp; 2492b15cb3dSCy Schubert const char *setup; 250c0b746e5SOllivier Robert 251c0b746e5SOllivier Robert /* 252c0b746e5SOllivier Robert * Allocate and initialize unit structure 253c0b746e5SOllivier Robert */ 2542b15cb3dSCy Schubert up = emalloc_zero(sizeof(struct actsunit)); 255ea906c41SOllivier Robert up->unit = unit; 256c0b746e5SOllivier Robert pp = peer->procptr; 2572b15cb3dSCy Schubert pp->unitptr = up; 258c0b746e5SOllivier Robert pp->io.clock_recv = acts_receive; 2592b15cb3dSCy Schubert pp->io.srcclock = peer; 260c0b746e5SOllivier Robert pp->io.datalen = 0; 2612b15cb3dSCy Schubert pp->io.fd = -1; 262c0b746e5SOllivier Robert 263c0b746e5SOllivier Robert /* 264c0b746e5SOllivier Robert * Initialize miscellaneous variables 265c0b746e5SOllivier Robert */ 266c0b746e5SOllivier Robert peer->precision = PRECISION; 267c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 2682b15cb3dSCy Schubert memcpy(&pp->refid, REFID, 4); 269c0b746e5SOllivier Robert peer->sstclktype = CTL_SST_TS_TELEPHONE; 2702b15cb3dSCy Schubert up->bufptr = up->buf; 2712b15cb3dSCy Schubert if (def_modem_setup == modem_setup) { 2722b15cb3dSCy Schubert setup = get_ext_sys_var("modemsetup"); 2732b15cb3dSCy Schubert if (setup != NULL) 2742b15cb3dSCy Schubert modem_setup = estrdup(setup); 2752b15cb3dSCy Schubert } 2762b15cb3dSCy Schubert 277c0b746e5SOllivier Robert return (1); 278c0b746e5SOllivier Robert } 279c0b746e5SOllivier Robert 280c0b746e5SOllivier Robert 281c0b746e5SOllivier Robert /* 282c0b746e5SOllivier Robert * acts_shutdown - shut down the clock 283c0b746e5SOllivier Robert */ 284c0b746e5SOllivier Robert static void 285c0b746e5SOllivier Robert acts_shutdown( 286c0b746e5SOllivier Robert int unit, 287c0b746e5SOllivier Robert struct peer *peer 288c0b746e5SOllivier Robert ) 289c0b746e5SOllivier Robert { 290ea906c41SOllivier Robert struct actsunit *up; 291c0b746e5SOllivier Robert struct refclockproc *pp; 292c0b746e5SOllivier Robert 293ea906c41SOllivier Robert /* 294ea906c41SOllivier Robert * Warning: do this only when a call is not in progress. 295ea906c41SOllivier Robert */ 296c0b746e5SOllivier Robert pp = peer->procptr; 2972b15cb3dSCy Schubert up = pp->unitptr; 2982b15cb3dSCy Schubert acts_close(peer); 299c0b746e5SOllivier Robert free(up); 300c0b746e5SOllivier Robert } 301c0b746e5SOllivier Robert 302c0b746e5SOllivier Robert 303c0b746e5SOllivier Robert /* 304c0b746e5SOllivier Robert * acts_receive - receive data from the serial interface 305c0b746e5SOllivier Robert */ 306c0b746e5SOllivier Robert static void 307c0b746e5SOllivier Robert acts_receive( 308c0b746e5SOllivier Robert struct recvbuf *rbufp 309c0b746e5SOllivier Robert ) 310c0b746e5SOllivier Robert { 311ea906c41SOllivier Robert struct actsunit *up; 312c0b746e5SOllivier Robert struct refclockproc *pp; 313c0b746e5SOllivier Robert struct peer *peer; 3142b15cb3dSCy Schubert char tbuf[sizeof(up->buf)]; 315ea906c41SOllivier Robert char * tptr; 3162b15cb3dSCy Schubert int octets; 317ea906c41SOllivier Robert 318c0b746e5SOllivier Robert /* 319ea906c41SOllivier Robert * Initialize pointers and read the timecode and timestamp. Note 320ea906c41SOllivier Robert * we are in raw mode and victim of whatever the terminal 321ea906c41SOllivier Robert * interface kicks up; so, we have to reassemble messages from 322ea906c41SOllivier Robert * arbitrary fragments. Capture the timecode at the beginning of 323ea906c41SOllivier Robert * the message and at the '*' and '#' on-time characters. 324c0b746e5SOllivier Robert */ 3252b15cb3dSCy Schubert peer = rbufp->recv_peer; 326c0b746e5SOllivier Robert pp = peer->procptr; 3272b15cb3dSCy Schubert up = pp->unitptr; 3282b15cb3dSCy Schubert octets = sizeof(up->buf) - (up->bufptr - up->buf); 3292b15cb3dSCy Schubert refclock_gtraw(rbufp, tbuf, octets, &pp->lastrec); 330ea906c41SOllivier Robert for (tptr = tbuf; *tptr != '\0'; tptr++) { 331ea906c41SOllivier Robert if (*tptr == LF) { 3322b15cb3dSCy Schubert if (up->bufptr == up->buf) { 333ea906c41SOllivier Robert up->tstamp = pp->lastrec; 334ea906c41SOllivier Robert continue; 335ea906c41SOllivier Robert } else { 336ea906c41SOllivier Robert *up->bufptr = '\0'; 3372b15cb3dSCy Schubert up->bufptr = up->buf; 3382b15cb3dSCy Schubert acts_message(peer, up->buf); 339c0b746e5SOllivier Robert } 3402b15cb3dSCy Schubert } else if (!iscntrl((unsigned char)*tptr)) { 341ea906c41SOllivier Robert *up->bufptr++ = *tptr; 342ea906c41SOllivier Robert if (*tptr == '*' || *tptr == '#') { 343ea906c41SOllivier Robert up->tstamp = pp->lastrec; 344a466cc55SCy Schubert refclock_write(peer, tptr, 1, "data"); 345ea906c41SOllivier Robert } 346ea906c41SOllivier Robert } 347ea906c41SOllivier Robert } 348ea906c41SOllivier Robert } 349ea906c41SOllivier Robert 350ea906c41SOllivier Robert 351ea906c41SOllivier Robert /* 352ea906c41SOllivier Robert * acts_message - process message 353ea906c41SOllivier Robert */ 354ea906c41SOllivier Robert void 355ea906c41SOllivier Robert acts_message( 3562b15cb3dSCy Schubert struct peer *peer, 3572b15cb3dSCy Schubert const char *msg 3582b15cb3dSCy Schubert ) 3592b15cb3dSCy Schubert { 3602b15cb3dSCy Schubert struct actsunit *up; 3612b15cb3dSCy Schubert struct refclockproc *pp; 3622b15cb3dSCy Schubert char tbuf[BMAX]; 3632b15cb3dSCy Schubert int dtr = TIOCM_DTR; 3642b15cb3dSCy Schubert 3652b15cb3dSCy Schubert DPRINTF(1, ("acts: %d %s\n", (int)strlen(msg), msg)); 3662b15cb3dSCy Schubert 3672b15cb3dSCy Schubert /* 3682b15cb3dSCy Schubert * What to do depends on the state and the first token in the 3692b15cb3dSCy Schubert * message. 3702b15cb3dSCy Schubert */ 3712b15cb3dSCy Schubert pp = peer->procptr; 3722b15cb3dSCy Schubert up = pp->unitptr; 3732b15cb3dSCy Schubert 3742b15cb3dSCy Schubert /* 3752b15cb3dSCy Schubert * Extract the first token in the line. 3762b15cb3dSCy Schubert */ 3772b15cb3dSCy Schubert strlcpy(tbuf, msg, sizeof(tbuf)); 3782b15cb3dSCy Schubert strtok(tbuf, " "); 3792b15cb3dSCy Schubert switch (up->state) { 3802b15cb3dSCy Schubert 3812b15cb3dSCy Schubert /* 3822b15cb3dSCy Schubert * We are waiting for the OK response to the modem setup 3832b15cb3dSCy Schubert * command. When this happens, dial the number followed. 3842b15cb3dSCy Schubert * If anything other than OK is received, just ignore it 3852b15cb3dSCy Schubert * and wait for timeoue. 3862b15cb3dSCy Schubert */ 3872b15cb3dSCy Schubert case S_SETUP: 3882b15cb3dSCy Schubert if (strcmp(tbuf, "OK") != 0) { 3892b15cb3dSCy Schubert /* 3902b15cb3dSCy Schubert * We disable echo with MODEM_SETUP's E0 but 3912b15cb3dSCy Schubert * if the modem was previously E1, we will 3922b15cb3dSCy Schubert * see MODEM_SETUP echoed before the OK/ERROR. 3932b15cb3dSCy Schubert * Ignore it. 3942b15cb3dSCy Schubert */ 3952b15cb3dSCy Schubert if (!strcmp(tbuf, modem_setup)) 3962b15cb3dSCy Schubert return; 3972b15cb3dSCy Schubert break; 3982b15cb3dSCy Schubert } 3992b15cb3dSCy Schubert 4002b15cb3dSCy Schubert mprintf_event(PEVNT_CLOCK, peer, "DIAL #%d %s", 4012b15cb3dSCy Schubert up->retry, sys_phone[up->retry]); 4022b15cb3dSCy Schubert if (ioctl(pp->io.fd, TIOCMBIS, &dtr) < 0) 4032b15cb3dSCy Schubert msyslog(LOG_ERR, "acts: ioctl(TIOCMBIS) failed: %m"); 404a466cc55SCy Schubert refclock_write(peer, sys_phone[up->retry], 405a466cc55SCy Schubert strlen(sys_phone[up->retry]), 406a466cc55SCy Schubert "DIAL"); 407a466cc55SCy Schubert refclock_write(peer, "\r", 1, "CR"); 4082b15cb3dSCy Schubert up->retry++; 4092b15cb3dSCy Schubert up->state = S_CONNECT; 4102b15cb3dSCy Schubert up->timer = ANSWER; 4112b15cb3dSCy Schubert return; 4122b15cb3dSCy Schubert 4132b15cb3dSCy Schubert /* 4142b15cb3dSCy Schubert * We are waiting for the CONNECT response to the dial 4152b15cb3dSCy Schubert * command. When this happens, listen for timecodes. If 4162b15cb3dSCy Schubert * somthing other than CONNECT is received, like BUSY 4172b15cb3dSCy Schubert * or NO CARRIER, abort the call. 4182b15cb3dSCy Schubert */ 4192b15cb3dSCy Schubert case S_CONNECT: 4202b15cb3dSCy Schubert if (strcmp(tbuf, "CONNECT") != 0) 4212b15cb3dSCy Schubert break; 4222b15cb3dSCy Schubert 4232b15cb3dSCy Schubert report_event(PEVNT_CLOCK, peer, msg); 4242b15cb3dSCy Schubert up->state = S_MSG; 4252b15cb3dSCy Schubert up->timer = TIMECODE; 4262b15cb3dSCy Schubert return; 4272b15cb3dSCy Schubert 4282b15cb3dSCy Schubert /* 4292b15cb3dSCy Schubert * We are waiting for a timecode response. Pass it to 4302b15cb3dSCy Schubert * the parser. If NO CARRIER is received, save the 4312b15cb3dSCy Schubert * messages and abort the call. 4322b15cb3dSCy Schubert */ 4332b15cb3dSCy Schubert case S_MSG: 4342b15cb3dSCy Schubert if (strcmp(tbuf, "NO") == 0) 4352b15cb3dSCy Schubert report_event(PEVNT_CLOCK, peer, msg); 4362b15cb3dSCy Schubert if (up->msgcnt < MAXCODE) 4372b15cb3dSCy Schubert acts_timecode(peer, msg); 4382b15cb3dSCy Schubert else 4392b15cb3dSCy Schubert acts_timeout(peer, S_MSG); 4402b15cb3dSCy Schubert return; 4412b15cb3dSCy Schubert } 4422b15cb3dSCy Schubert 4432b15cb3dSCy Schubert /* 4442b15cb3dSCy Schubert * Other response. Tell us about it. 4452b15cb3dSCy Schubert */ 4462b15cb3dSCy Schubert report_event(PEVNT_CLOCK, peer, msg); 4472b15cb3dSCy Schubert acts_close(peer); 4482b15cb3dSCy Schubert } 4492b15cb3dSCy Schubert 4502b15cb3dSCy Schubert 4512b15cb3dSCy Schubert /* 4522b15cb3dSCy Schubert * acts_timeout - called on timeout 4532b15cb3dSCy Schubert */ 4542b15cb3dSCy Schubert static void 4552b15cb3dSCy Schubert acts_timeout( 4562b15cb3dSCy Schubert struct peer *peer, 4572b15cb3dSCy Schubert teModemState dstate 4582b15cb3dSCy Schubert ) 4592b15cb3dSCy Schubert { 4602b15cb3dSCy Schubert struct actsunit *up; 4612b15cb3dSCy Schubert struct refclockproc *pp; 4622b15cb3dSCy Schubert int fd; 4632b15cb3dSCy Schubert char device[20]; 4642b15cb3dSCy Schubert char lockfile[128], pidbuf[8]; 4652b15cb3dSCy Schubert 4662b15cb3dSCy Schubert /* 4672b15cb3dSCy Schubert * The state machine is driven by messages from the modem, 4682b15cb3dSCy Schubert * when first started and at timeout. 4692b15cb3dSCy Schubert */ 4702b15cb3dSCy Schubert pp = peer->procptr; 4712b15cb3dSCy Schubert up = pp->unitptr; 4722b15cb3dSCy Schubert switch (dstate) { 4732b15cb3dSCy Schubert 4742b15cb3dSCy Schubert /* 4752b15cb3dSCy Schubert * System poll event. Lock the modem port, open the device 4762b15cb3dSCy Schubert * and send the setup command. 4772b15cb3dSCy Schubert */ 4782b15cb3dSCy Schubert case S_IDLE: 4792b15cb3dSCy Schubert if (-1 != pp->io.fd) 4802b15cb3dSCy Schubert return; /* port is already open */ 4812b15cb3dSCy Schubert 4822b15cb3dSCy Schubert /* 4832b15cb3dSCy Schubert * Lock the modem port. If busy, retry later. Note: if 4842b15cb3dSCy Schubert * something fails between here and the close, the lock 4852b15cb3dSCy Schubert * file may not be removed. 4862b15cb3dSCy Schubert */ 4872b15cb3dSCy Schubert if (pp->sloppyclockflag & CLK_FLAG2) { 4882b15cb3dSCy Schubert snprintf(lockfile, sizeof(lockfile), LOCKFILE, 4892b15cb3dSCy Schubert up->unit); 4902b15cb3dSCy Schubert fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, 4912b15cb3dSCy Schubert 0644); 4922b15cb3dSCy Schubert if (fd < 0) { 4932b15cb3dSCy Schubert report_event(PEVNT_CLOCK, peer, "acts: port busy"); 4942b15cb3dSCy Schubert return; 4952b15cb3dSCy Schubert } 4962b15cb3dSCy Schubert snprintf(pidbuf, sizeof(pidbuf), "%d\n", 4972b15cb3dSCy Schubert (u_int)getpid()); 4982b15cb3dSCy Schubert if (write(fd, pidbuf, strlen(pidbuf)) < 0) 4992b15cb3dSCy Schubert msyslog(LOG_ERR, "acts: write lock fails %m"); 5002b15cb3dSCy Schubert close(fd); 5012b15cb3dSCy Schubert } 5022b15cb3dSCy Schubert 5032b15cb3dSCy Schubert /* 5042b15cb3dSCy Schubert * Open the device in raw mode and link the I/O. 5052b15cb3dSCy Schubert */ 5062b15cb3dSCy Schubert snprintf(device, sizeof(device), DEVICE, 5072b15cb3dSCy Schubert up->unit); 508a466cc55SCy Schubert fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_ACTS | 5092b15cb3dSCy Schubert LDISC_RAW | LDISC_REMOTE); 5102b15cb3dSCy Schubert if (fd < 0) { 5112b15cb3dSCy Schubert msyslog(LOG_ERR, "acts: open fails %m"); 5122b15cb3dSCy Schubert return; 5132b15cb3dSCy Schubert } 5142b15cb3dSCy Schubert pp->io.fd = fd; 5152b15cb3dSCy Schubert if (!io_addclock(&pp->io)) { 5162b15cb3dSCy Schubert msyslog(LOG_ERR, "acts: addclock fails"); 5172b15cb3dSCy Schubert close(fd); 5182b15cb3dSCy Schubert pp->io.fd = -1; 5192b15cb3dSCy Schubert return; 5202b15cb3dSCy Schubert } 5212b15cb3dSCy Schubert up->msgcnt = 0; 5222b15cb3dSCy Schubert up->bufptr = up->buf; 5232b15cb3dSCy Schubert 5242b15cb3dSCy Schubert /* 5252b15cb3dSCy Schubert * If the port is directly connected to the device, skip 5262b15cb3dSCy Schubert * the modem business and send 'T' for Spectrabum. 5272b15cb3dSCy Schubert */ 5282b15cb3dSCy Schubert if (sys_phone[up->retry] == NULL) { 529a466cc55SCy Schubert refclock_write(peer, "T", 1, "T"); 5302b15cb3dSCy Schubert up->state = S_MSG; 5312b15cb3dSCy Schubert up->timer = TIMECODE; 5322b15cb3dSCy Schubert return; 5332b15cb3dSCy Schubert } 5342b15cb3dSCy Schubert 5352b15cb3dSCy Schubert /* 5362b15cb3dSCy Schubert * Initialize the modem. This works with Hayes- 5372b15cb3dSCy Schubert * compatible modems. 5382b15cb3dSCy Schubert */ 5392b15cb3dSCy Schubert mprintf_event(PEVNT_CLOCK, peer, "SETUP %s", 5402b15cb3dSCy Schubert modem_setup); 541a466cc55SCy Schubert refclock_write(peer, modem_setup, strlen(modem_setup), 542a466cc55SCy Schubert "SETUP"); 543a466cc55SCy Schubert refclock_write(peer, "\r", 1, "CR"); 5442b15cb3dSCy Schubert up->state = S_SETUP; 5452b15cb3dSCy Schubert up->timer = SETUP; 5462b15cb3dSCy Schubert return; 5472b15cb3dSCy Schubert 5482b15cb3dSCy Schubert /* 5492b15cb3dSCy Schubert * In SETUP state the modem did not respond OK to setup string. 5502b15cb3dSCy Schubert */ 5512b15cb3dSCy Schubert case S_SETUP: 5522b15cb3dSCy Schubert report_event(PEVNT_CLOCK, peer, "no modem"); 5532b15cb3dSCy Schubert break; 5542b15cb3dSCy Schubert 5552b15cb3dSCy Schubert /* 5562b15cb3dSCy Schubert * In CONNECT state the call did not complete. Abort the call. 5572b15cb3dSCy Schubert */ 5582b15cb3dSCy Schubert case S_CONNECT: 5592b15cb3dSCy Schubert report_event(PEVNT_CLOCK, peer, "no answer"); 5602b15cb3dSCy Schubert break; 5612b15cb3dSCy Schubert 5622b15cb3dSCy Schubert /* 5632b15cb3dSCy Schubert * In MSG states no further timecodes are expected. If any 5642b15cb3dSCy Schubert * timecodes have arrived, update the clock. In any case, 5652b15cb3dSCy Schubert * terminate the call. 5662b15cb3dSCy Schubert */ 5672b15cb3dSCy Schubert case S_MSG: 5682b15cb3dSCy Schubert if (up->msgcnt == 0) { 5692b15cb3dSCy Schubert report_event(PEVNT_CLOCK, peer, "no timecodes"); 5702b15cb3dSCy Schubert } else { 5712b15cb3dSCy Schubert pp->lastref = pp->lastrec; 5722b15cb3dSCy Schubert record_clock_stats(&peer->srcadr, pp->a_lastcode); 5732b15cb3dSCy Schubert refclock_receive(peer); 5742b15cb3dSCy Schubert } 5752b15cb3dSCy Schubert break; 5762b15cb3dSCy Schubert } 5772b15cb3dSCy Schubert acts_close(peer); 5782b15cb3dSCy Schubert } 5792b15cb3dSCy Schubert 5802b15cb3dSCy Schubert 5812b15cb3dSCy Schubert /* 5822b15cb3dSCy Schubert * acts_close - close and prepare for next call. 5832b15cb3dSCy Schubert * 5842b15cb3dSCy Schubert * In ClOSE state no further protocol actions are required 5852b15cb3dSCy Schubert * other than to close and release the device and prepare to 5862b15cb3dSCy Schubert * dial the next number if necessary. 5872b15cb3dSCy Schubert */ 5882b15cb3dSCy Schubert void 5892b15cb3dSCy Schubert acts_close( 590ea906c41SOllivier Robert struct peer *peer 591ea906c41SOllivier Robert ) 592ea906c41SOllivier Robert { 593ea906c41SOllivier Robert struct actsunit *up; 594ea906c41SOllivier Robert struct refclockproc *pp; 5952b15cb3dSCy Schubert char lockfile[128]; 5962b15cb3dSCy Schubert int dtr; 5972b15cb3dSCy Schubert 5982b15cb3dSCy Schubert pp = peer->procptr; 5992b15cb3dSCy Schubert up = pp->unitptr; 6002b15cb3dSCy Schubert if (pp->io.fd != -1) { 6012b15cb3dSCy Schubert report_event(PEVNT_CLOCK, peer, "close"); 6022b15cb3dSCy Schubert dtr = TIOCM_DTR; 6032b15cb3dSCy Schubert if (ioctl(pp->io.fd, TIOCMBIC, &dtr) < 0) 6042b15cb3dSCy Schubert msyslog(LOG_ERR, "acts: ioctl(TIOCMBIC) failed: %m"); 6052b15cb3dSCy Schubert io_closeclock(&pp->io); 6062b15cb3dSCy Schubert pp->io.fd = -1; 6072b15cb3dSCy Schubert } 6082b15cb3dSCy Schubert if (pp->sloppyclockflag & CLK_FLAG2) { 6092b15cb3dSCy Schubert snprintf(lockfile, sizeof(lockfile), 6102b15cb3dSCy Schubert LOCKFILE, up->unit); 6112b15cb3dSCy Schubert unlink(lockfile); 6122b15cb3dSCy Schubert } 6132b15cb3dSCy Schubert if (up->msgcnt == 0 && up->retry > 0) { 6142b15cb3dSCy Schubert if (sys_phone[up->retry] != NULL) { 6152b15cb3dSCy Schubert up->state = S_IDLE; 6162b15cb3dSCy Schubert up->timer = REDIAL; 6172b15cb3dSCy Schubert return; 6182b15cb3dSCy Schubert } 6192b15cb3dSCy Schubert } 6202b15cb3dSCy Schubert up->state = S_IDLE; 6212b15cb3dSCy Schubert up->timer = 0; 6222b15cb3dSCy Schubert } 6232b15cb3dSCy Schubert 624c0b746e5SOllivier Robert 625ea906c41SOllivier Robert /* 6262b15cb3dSCy Schubert * acts_poll - called by the transmit routine 6272b15cb3dSCy Schubert */ 6282b15cb3dSCy Schubert static void 6292b15cb3dSCy Schubert acts_poll( 6302b15cb3dSCy Schubert int unit, 6312b15cb3dSCy Schubert struct peer *peer 6322b15cb3dSCy Schubert ) 6332b15cb3dSCy Schubert { 6342b15cb3dSCy Schubert struct actsunit *up; 6352b15cb3dSCy Schubert struct refclockproc *pp; 6362b15cb3dSCy Schubert 6372b15cb3dSCy Schubert /* 6382b15cb3dSCy Schubert * This routine is called at every system poll. All it does is 6392b15cb3dSCy Schubert * set flag1 under certain conditions. The real work is done by 6402b15cb3dSCy Schubert * the timeout routine and state machine. 641ea906c41SOllivier Robert */ 642ea906c41SOllivier Robert pp = peer->procptr; 6432b15cb3dSCy Schubert up = pp->unitptr; 6442b15cb3dSCy Schubert switch (peer->ttl) { 645c0b746e5SOllivier Robert 646c0b746e5SOllivier Robert /* 6472b15cb3dSCy Schubert * In manual mode the calling program is activated by the ntpdc 6482b15cb3dSCy Schubert * program using the enable flag (fudge flag1), either manually 6492b15cb3dSCy Schubert * or by a cron job. 650c0b746e5SOllivier Robert */ 6512b15cb3dSCy Schubert case MODE_MANUAL: 652c0b746e5SOllivier Robert return; 653c0b746e5SOllivier Robert 654ea906c41SOllivier Robert /* 6552b15cb3dSCy Schubert * In automatic mode the calling program runs continuously at 6562b15cb3dSCy Schubert * intervals determined by the poll event or specified timeout. 657ea906c41SOllivier Robert */ 6582b15cb3dSCy Schubert case MODE_AUTO: 659ea906c41SOllivier Robert break; 6602b15cb3dSCy Schubert 6612b15cb3dSCy Schubert /* 6622b15cb3dSCy Schubert * In backup mode the calling program runs continuously as long 6632b15cb3dSCy Schubert * as either no peers are available or this peer is selected. 6642b15cb3dSCy Schubert */ 6652b15cb3dSCy Schubert case MODE_BACKUP: 6662b15cb3dSCy Schubert if (!(sys_peer == NULL || sys_peer == peer)) 6672b15cb3dSCy Schubert return; 6682b15cb3dSCy Schubert 6692b15cb3dSCy Schubert break; 6702b15cb3dSCy Schubert } 6712b15cb3dSCy Schubert pp->polls++; 6722b15cb3dSCy Schubert if (S_IDLE == up->state) { 6732b15cb3dSCy Schubert up->retry = 0; 6742b15cb3dSCy Schubert acts_timeout(peer, S_IDLE); 6752b15cb3dSCy Schubert } 6762b15cb3dSCy Schubert } 6772b15cb3dSCy Schubert 6782b15cb3dSCy Schubert 6792b15cb3dSCy Schubert /* 6802b15cb3dSCy Schubert * acts_timer - called at one-second intervals 6812b15cb3dSCy Schubert */ 6822b15cb3dSCy Schubert static void 6832b15cb3dSCy Schubert acts_timer( 6842b15cb3dSCy Schubert int unit, 6852b15cb3dSCy Schubert struct peer *peer 6862b15cb3dSCy Schubert ) 6872b15cb3dSCy Schubert { 6882b15cb3dSCy Schubert struct actsunit *up; 6892b15cb3dSCy Schubert struct refclockproc *pp; 6902b15cb3dSCy Schubert 6912b15cb3dSCy Schubert /* 6922b15cb3dSCy Schubert * This routine implments a timeout which runs for a programmed 6932b15cb3dSCy Schubert * interval. The counter is initialized by the state machine and 6942b15cb3dSCy Schubert * counts down to zero. Upon reaching zero, the state machine is 6952b15cb3dSCy Schubert * called. If flag1 is set while timer is zero, force a call. 6962b15cb3dSCy Schubert */ 6972b15cb3dSCy Schubert pp = peer->procptr; 6982b15cb3dSCy Schubert up = pp->unitptr; 6992b15cb3dSCy Schubert if (up->timer == 0) { 7002b15cb3dSCy Schubert if (pp->sloppyclockflag & CLK_FLAG1) { 7012b15cb3dSCy Schubert pp->sloppyclockflag &= ~CLK_FLAG1; 7022b15cb3dSCy Schubert acts_timeout(peer, S_IDLE); 7032b15cb3dSCy Schubert } 7042b15cb3dSCy Schubert } else { 7052b15cb3dSCy Schubert up->timer--; 7062b15cb3dSCy Schubert if (up->timer == 0) 7072b15cb3dSCy Schubert acts_timeout(peer, up->state); 708ea906c41SOllivier Robert } 709ea906c41SOllivier Robert } 710ea906c41SOllivier Robert 711ea906c41SOllivier Robert /* 712ea906c41SOllivier Robert * acts_timecode - identify the service and parse the timecode message 713ea906c41SOllivier Robert */ 714ea906c41SOllivier Robert void 715ea906c41SOllivier Robert acts_timecode( 716ea906c41SOllivier Robert struct peer * peer, /* peer structure pointer */ 7172b15cb3dSCy Schubert const char * str /* timecode string */ 718ea906c41SOllivier Robert ) 719ea906c41SOllivier Robert { 720ea906c41SOllivier Robert struct actsunit *up; 721ea906c41SOllivier Robert struct refclockproc *pp; 722ea906c41SOllivier Robert int day; /* day of the month */ 723ea906c41SOllivier Robert int month; /* month of the year */ 724ea906c41SOllivier Robert u_long mjd; /* Modified Julian Day */ 725ea906c41SOllivier Robert double dut1; /* DUT adjustment */ 726ea906c41SOllivier Robert 727ea906c41SOllivier Robert u_int dst; /* ACTS daylight/standard time */ 728ea906c41SOllivier Robert u_int leap; /* ACTS leap indicator */ 729ea906c41SOllivier Robert double msADV; /* ACTS transmit advance (ms) */ 730ea906c41SOllivier Robert char utc[10]; /* ACTS timescale */ 731ea906c41SOllivier Robert char flag; /* ACTS on-time character (* or #) */ 732ea906c41SOllivier Robert 733ea906c41SOllivier Robert char synchar; /* WWVB synchronized indicator */ 734ea906c41SOllivier Robert char qualchar; /* WWVB quality indicator */ 735ea906c41SOllivier Robert char leapchar; /* WWVB leap indicator */ 736ea906c41SOllivier Robert char dstchar; /* WWVB daylight/savings indicator */ 737ea906c41SOllivier Robert int tz; /* WWVB timezone */ 738ea906c41SOllivier Robert 7392b15cb3dSCy Schubert int leapmonth; /* PTB/NPL month of leap */ 740ea906c41SOllivier Robert char leapdir; /* PTB/NPL leap direction */ 741ea906c41SOllivier Robert 742ea906c41SOllivier Robert /* 743ea906c41SOllivier Robert * The parser selects the modem format based on the message 744ea906c41SOllivier Robert * length. Since the data are checked carefully, occasional 745ea906c41SOllivier Robert * errors due noise are forgivable. 746ea906c41SOllivier Robert */ 747ea906c41SOllivier Robert pp = peer->procptr; 7482b15cb3dSCy Schubert up = pp->unitptr; 749ea906c41SOllivier Robert pp->nsec = 0; 750ea906c41SOllivier Robert switch (strlen(str)) { 751ea906c41SOllivier Robert 752ea906c41SOllivier Robert /* 753ea906c41SOllivier Robert * For USNO format on-time character '*', which is on a line by 754ea906c41SOllivier Robert * itself. Be sure a timecode has been received. 755ea906c41SOllivier Robert */ 756c0b746e5SOllivier Robert case 1: 757ea906c41SOllivier Robert if (*str == '*' && up->msgcnt > 0) 758ea906c41SOllivier Robert break; 759c0b746e5SOllivier Robert 760c0b746e5SOllivier Robert return; 761c0b746e5SOllivier Robert 762c0b746e5SOllivier Robert /* 7632b15cb3dSCy Schubert * ACTS format A: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa 7642b15cb3dSCy Schubert * UTC(NIST) *". 765c0b746e5SOllivier Robert */ 766ea906c41SOllivier Robert case LENACTS: 767ea906c41SOllivier Robert if (sscanf(str, 768ea906c41SOllivier Robert "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c", 769ea906c41SOllivier Robert &mjd, &pp->year, &month, &day, &pp->hour, 770ea906c41SOllivier Robert &pp->minute, &pp->second, &dst, &leap, &dut1, 771ea906c41SOllivier Robert &msADV, utc, &flag) != 13) { 772c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 773c0b746e5SOllivier Robert return; 774c0b746e5SOllivier Robert } 775ea906c41SOllivier Robert pp->day = ymd2yd(pp->year, month, day); 776ea906c41SOllivier Robert pp->leap = LEAP_NOWARNING; 777c0b746e5SOllivier Robert if (leap == 1) 778c0b746e5SOllivier Robert pp->leap = LEAP_ADDSECOND; 7792b15cb3dSCy Schubert else if (leap == 2) 780c0b746e5SOllivier Robert pp->leap = LEAP_DELSECOND; 781ea906c41SOllivier Robert memcpy(&pp->refid, REFACTS, 4); 782ea906c41SOllivier Robert up->msgcnt++; 7832b15cb3dSCy Schubert if (flag != '#' && up->msgcnt < 10) 7842b15cb3dSCy Schubert return; 7852b15cb3dSCy Schubert 786ea906c41SOllivier Robert break; 787ea906c41SOllivier Robert 788ea906c41SOllivier Robert /* 789ea906c41SOllivier Robert * USNO format: "jjjjj nnn hhmmss UTC" 790ea906c41SOllivier Robert */ 791ea906c41SOllivier Robert case LENUSNO: 792ea906c41SOllivier Robert if (sscanf(str, "%5ld %3d %2d%2d%2d %3s", 793ea906c41SOllivier Robert &mjd, &pp->day, &pp->hour, &pp->minute, 794ea906c41SOllivier Robert &pp->second, utc) != 6) { 795ea906c41SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 796ea906c41SOllivier Robert return; 797ea906c41SOllivier Robert } 798ea906c41SOllivier Robert 799ea906c41SOllivier Robert /* 800ea906c41SOllivier Robert * Wait for the on-time character, which follows in a 801ea906c41SOllivier Robert * separate message. There is no provision for leap 802ea906c41SOllivier Robert * warning. 803ea906c41SOllivier Robert */ 804ea906c41SOllivier Robert pp->leap = LEAP_NOWARNING; 805ea906c41SOllivier Robert memcpy(&pp->refid, REFUSNO, 4); 806ea906c41SOllivier Robert up->msgcnt++; 8072b15cb3dSCy Schubert break; 808ea906c41SOllivier Robert 809ea906c41SOllivier Robert /* 810ea906c41SOllivier Robert * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ" 811ea906c41SOllivier Robert */ 812ea906c41SOllivier Robert case LENPTB: 813ea906c41SOllivier Robert if (sscanf(str, 814ea906c41SOllivier Robert "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c", 815ea906c41SOllivier Robert &pp->second, &pp->year, &month, &day, &pp->hour, 816ea906c41SOllivier Robert &pp->minute, &mjd, &dut1, &leapdir, &leapmonth, 817ea906c41SOllivier Robert &msADV, &flag) != 12) { 818ea906c41SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 819ea906c41SOllivier Robert return; 820ea906c41SOllivier Robert } 821ea906c41SOllivier Robert pp->leap = LEAP_NOWARNING; 822c0b746e5SOllivier Robert if (leapmonth == month) { 823c0b746e5SOllivier Robert if (leapdir == '+') 824c0b746e5SOllivier Robert pp->leap = LEAP_ADDSECOND; 825c0b746e5SOllivier Robert else if (leapdir == '-') 826c0b746e5SOllivier Robert pp->leap = LEAP_DELSECOND; 827c0b746e5SOllivier Robert } 828ea906c41SOllivier Robert pp->day = ymd2yd(pp->year, month, day); 829ea906c41SOllivier Robert memcpy(&pp->refid, REFPTB, 4); 830ea906c41SOllivier Robert up->msgcnt++; 831ea906c41SOllivier Robert break; 832ea906c41SOllivier Robert 833c0b746e5SOllivier Robert 834c0b746e5SOllivier Robert /* 835ea906c41SOllivier Robert * WWVB format 0: "I ddd hh:mm:ss DTZ=nn" 836c0b746e5SOllivier Robert */ 837ea906c41SOllivier Robert case LENWWVB0: 838ea906c41SOllivier Robert if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d", 839ea906c41SOllivier Robert &synchar, &pp->day, &pp->hour, &pp->minute, 840ea906c41SOllivier Robert &pp->second, &dstchar, &tz) != 7) { 841ea906c41SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 842c0b746e5SOllivier Robert return; 843ea906c41SOllivier Robert } 844ea906c41SOllivier Robert pp->leap = LEAP_NOWARNING; 845ea906c41SOllivier Robert if (synchar != ' ') 846ea906c41SOllivier Robert pp->leap = LEAP_NOTINSYNC; 847ea906c41SOllivier Robert memcpy(&pp->refid, REFWWVB, 4); 848ea906c41SOllivier Robert up->msgcnt++; 849ea906c41SOllivier Robert break; 850ea906c41SOllivier Robert 851ea906c41SOllivier Robert /* 852ea906c41SOllivier Robert * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD" 853ea906c41SOllivier Robert */ 854ea906c41SOllivier Robert case LENWWVB2: 855ea906c41SOllivier Robert if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c", 856ea906c41SOllivier Robert &synchar, &qualchar, &pp->year, &pp->day, 857ea906c41SOllivier Robert &pp->hour, &pp->minute, &pp->second, &pp->nsec, 858ea906c41SOllivier Robert &dstchar, &leapchar, &dstchar) != 11) { 859ea906c41SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 860ea906c41SOllivier Robert return; 861ea906c41SOllivier Robert } 862ea906c41SOllivier Robert pp->nsec *= 1000000; 863ea906c41SOllivier Robert pp->leap = LEAP_NOWARNING; 864ea906c41SOllivier Robert if (synchar != ' ') 865ea906c41SOllivier Robert pp->leap = LEAP_NOTINSYNC; 866ea906c41SOllivier Robert else if (leapchar == 'L') 867ea906c41SOllivier Robert pp->leap = LEAP_ADDSECOND; 868ea906c41SOllivier Robert memcpy(&pp->refid, REFWWVB, 4); 869ea906c41SOllivier Robert up->msgcnt++; 870ea906c41SOllivier Robert break; 871ea906c41SOllivier Robert 872ea906c41SOllivier Robert /* 873ea906c41SOllivier Robert * None of the above. Just forget about it and wait for the next 874ea906c41SOllivier Robert * message or timeout. 875ea906c41SOllivier Robert */ 876ea906c41SOllivier Robert default: 877c0b746e5SOllivier Robert return; 878c0b746e5SOllivier Robert } 879c0b746e5SOllivier Robert 880c0b746e5SOllivier Robert /* 881ea906c41SOllivier Robert * We have a valid timecode. The fudge time1 value is added to 882ea906c41SOllivier Robert * each sample by the main line routines. Note that in current 883ea906c41SOllivier Robert * telephone networks the propatation time can be different for 884ea906c41SOllivier Robert * each call and can reach 200 ms for some calls. 885c0b746e5SOllivier Robert */ 886ea906c41SOllivier Robert peer->refid = pp->refid; 887ea906c41SOllivier Robert pp->lastrec = up->tstamp; 8882b15cb3dSCy Schubert if (up->msgcnt == 0) 8892b15cb3dSCy Schubert return; 8902b15cb3dSCy Schubert 8912b15cb3dSCy Schubert strlcpy(pp->a_lastcode, str, sizeof(pp->a_lastcode)); 8922b15cb3dSCy Schubert pp->lencode = strlen(pp->a_lastcode); 893ea906c41SOllivier Robert if (!refclock_process(pp)) { 894ea906c41SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 895ea906c41SOllivier Robert return; 896ea906c41SOllivier Robert } 8979c2daa00SOllivier Robert pp->lastref = pp->lastrec; 898ea906c41SOllivier Robert } 899c0b746e5SOllivier Robert #else 900*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT 901c0b746e5SOllivier Robert #endif /* REFCLOCK */ 902