1c0b746e5SOllivier Robert /* 22b15cb3dSCy Schubert * refclock_true - clock driver for the Kinemetrics/TrueTime receivers 3c0b746e5SOllivier Robert * Receiver Version 3.0C - tested plain, with CLKLDISC 42b15cb3dSCy Schubert * Development work being done: 52b15cb3dSCy Schubert * - Support TL-3 WWV TOD receiver 6c0b746e5SOllivier Robert */ 7c0b746e5SOllivier Robert 8c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 9c0b746e5SOllivier Robert #include <config.h> 10c0b746e5SOllivier Robert #endif 11c0b746e5SOllivier Robert 12c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_TRUETIME) 13c0b746e5SOllivier Robert 142b15cb3dSCy Schubert #include <stdio.h> 152b15cb3dSCy Schubert #include <ctype.h> 162b15cb3dSCy Schubert 17c0b746e5SOllivier Robert #include "ntpd.h" 18c0b746e5SOllivier Robert #include "ntp_io.h" 19c0b746e5SOllivier Robert #include "ntp_refclock.h" 20c0b746e5SOllivier Robert #include "ntp_unixtime.h" 21c0b746e5SOllivier Robert #include "ntp_stdlib.h" 22c0b746e5SOllivier Robert 23c0b746e5SOllivier Robert /* This should be an atom clock but those are very hard to build. 24c0b746e5SOllivier Robert * 25c0b746e5SOllivier Robert * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch 26c0b746e5SOllivier Robert * of TTL input and output pins, all brought out to the back panel. If you 27c0b746e5SOllivier Robert * wire a PPS signal (such as the TTL PPS coming out of a GOES or other 28c0b746e5SOllivier Robert * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the 29c0b746e5SOllivier Robert * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the 30c0b746e5SOllivier Robert * number of uSecs since the last PPS upward swing, mediated by reading OUT0 31c0b746e5SOllivier Robert * to find out if the counter has wrapped around (this happens if more than 32c0b746e5SOllivier Robert * 65535us (65ms) elapses between the PPS event and our being called.) 33c0b746e5SOllivier Robert */ 34c0b746e5SOllivier Robert #ifdef CLOCK_PPS720 35c0b746e5SOllivier Robert # undef min /* XXX */ 36c0b746e5SOllivier Robert # undef max /* XXX */ 37c0b746e5SOllivier Robert # include <machine/inline.h> 38c0b746e5SOllivier Robert # include <sys/pcl720.h> 39c0b746e5SOllivier Robert # include <sys/i8253.h> 40c0b746e5SOllivier Robert # define PCL720_IOB 0x2a0 /* XXX */ 41c0b746e5SOllivier Robert # define PCL720_CTR 0 /* XXX */ 42c0b746e5SOllivier Robert #endif 43c0b746e5SOllivier Robert 44c0b746e5SOllivier Robert /* 45c0b746e5SOllivier Robert * Support for Kinemetrics Truetime Receivers 462b15cb3dSCy Schubert * GOES: (468-DC, usable with GPS->GOES converting antenna) 472b15cb3dSCy Schubert * GPS/TM-TMD: 482b15cb3dSCy Schubert * XL-DC: (a 151-602-210, reported by the driver as a GPS/TM-TMD) 492b15cb3dSCy Schubert * GPS-800 TCU: (an 805-957 with the RS232 Talker/Listener module) 502b15cb3dSCy Schubert * TL-3: 3 channel WWV/H receiver w/ IRIG and RS-232 outputs 51c0b746e5SOllivier Robert * OM-DC: getting stale ("OMEGA") 52c0b746e5SOllivier Robert * 53c0b746e5SOllivier Robert * Most of this code is originally from refclock_wwvb.c with thanks. 54c0b746e5SOllivier Robert * It has been so mangled that wwvb is not a recognizable ancestor. 55c0b746e5SOllivier Robert * 56c0b746e5SOllivier Robert * Timcode format: ADDD:HH:MM:SSQCL 57c0b746e5SOllivier Robert * A - control A (this is stripped before we see it) 58c0b746e5SOllivier Robert * Q - Quality indication (see below) 59c0b746e5SOllivier Robert * C - Carriage return 60c0b746e5SOllivier Robert * L - Line feed 61c0b746e5SOllivier Robert * 62c0b746e5SOllivier Robert * Quality codes indicate possible error of 63c0b746e5SOllivier Robert * 468-DC GOES Receiver: 64224ba2bdSOllivier Robert * GPS-TM/TMD Receiver: (default quality codes for XL-DC) 65224ba2bdSOllivier Robert * ? +/- 1 milliseconds # +/- 100 microseconds 66224ba2bdSOllivier Robert * * +/- 10 microseconds . +/- 1 microsecond 67224ba2bdSOllivier Robert * space less than 1 microsecond 682b15cb3dSCy Schubert * TL-3 Receiver: (default quality codes for TL-3) 692b15cb3dSCy Schubert * ? unknown quality (receiver is unlocked) 702b15cb3dSCy Schubert * space +/- 5 milliseconds 71224ba2bdSOllivier Robert * OM-DC OMEGA Receiver: (default quality codes for OMEGA) 72224ba2bdSOllivier Robert * WARNING OMEGA navigation system is no longer existent 73c0b746e5SOllivier Robert * > >+- 5 seconds 74c0b746e5SOllivier Robert * ? >+/- 500 milliseconds # >+/- 50 milliseconds 75c0b746e5SOllivier Robert * * >+/- 5 milliseconds . >+/- 1 millisecond 76c0b746e5SOllivier Robert * A-H less than 1 millisecond. Character indicates which station 77c0b746e5SOllivier Robert * is being received as follows: 78c0b746e5SOllivier Robert * A = Norway, B = Liberia, C = Hawaii, D = North Dakota, 79c0b746e5SOllivier Robert * E = La Reunion, F = Argentina, G = Australia, H = Japan. 80c0b746e5SOllivier Robert * 81c0b746e5SOllivier Robert * The carriage return start bit begins on 0 seconds and extends to 1 bit time. 82c0b746e5SOllivier Robert * 83c0b746e5SOllivier Robert * Notes on 468-DC and OMEGA receiver: 84c0b746e5SOllivier Robert * 85c0b746e5SOllivier Robert * Send the clock a 'R' or 'C' and once per second a timestamp will 86c0b746e5SOllivier Robert * appear. Send a 'P' to get the satellite position once (GOES only.) 87c0b746e5SOllivier Robert * 88c0b746e5SOllivier Robert * Notes on the 468-DC receiver: 89c0b746e5SOllivier Robert * 90c0b746e5SOllivier Robert * Since the old east/west satellite locations are only historical, you can't 91c0b746e5SOllivier Robert * set your clock propagation delay settings correctly and still use 92c0b746e5SOllivier Robert * automatic mode. The manual says to use a compromise when setting the 93c0b746e5SOllivier Robert * switches. This results in significant errors. The solution; use fudge 94c0b746e5SOllivier Robert * time1 and time2 to incorporate corrections. If your clock is set for 95c0b746e5SOllivier Robert * 50 and it should be 58 for using the west and 46 for using the east, 96c0b746e5SOllivier Robert * use the line 97c0b746e5SOllivier Robert * 98c0b746e5SOllivier Robert * fudge 127.127.5.0 time1 +0.008 time2 -0.004 99c0b746e5SOllivier Robert * 100c0b746e5SOllivier Robert * This corrects the 4 milliseconds advance and 8 milliseconds retard 101c0b746e5SOllivier Robert * needed. The software will ask the clock which satellite it sees. 102c0b746e5SOllivier Robert * 1032b15cb3dSCy Schubert * Notes on the TrueTime TimeLink TL-3 WWV TOD receiver: 1042b15cb3dSCy Schubert * 1052b15cb3dSCy Schubert * This clock may be polled, or send one timecode per second. 1062b15cb3dSCy Schubert * That mode may be toggled via the front panel ("C" mode), or controlled 1072b15cb3dSCy Schubert * from the RS-232 port. Send the receiver "ST1" to turn it on, and 1082b15cb3dSCy Schubert * "ST0" to turn it off. Send "QV" to get the firmware revision (useful 1092b15cb3dSCy Schubert * for identifying this model.) 1102b15cb3dSCy Schubert * 1112b15cb3dSCy Schubert * Note that it can take several polling cycles, especially if the receiver 1122b15cb3dSCy Schubert * was in the continuous timecode mode. (It can be slow to leave that mode.) 1132b15cb3dSCy Schubert * 1142b15cb3dSCy Schubert * ntp.conf parameters: 115c0b746e5SOllivier Robert * time1 - offset applied to samples when reading WEST satellite (default = 0) 116c0b746e5SOllivier Robert * time2 - offset applied to samples when reading EAST satellite (default = 0) 1172b15cb3dSCy Schubert * stratum - stratum to assign to this clock (default = 0) 1182b15cb3dSCy Schubert * refid - refid assigned to this clock (default = "TRUE", see below) 119c0b746e5SOllivier Robert * flag1 - will silence the clock side of ntpd, just reading the clock 120c0b746e5SOllivier Robert * without trying to write to it. (default = 0) 121c0b746e5SOllivier Robert * flag2 - generate a debug file /tmp/true%d. 122c0b746e5SOllivier Robert * flag3 - enable ppsclock streams module 123c0b746e5SOllivier Robert * flag4 - use the PCL-720 (BSD/OS only) 124c0b746e5SOllivier Robert */ 125c0b746e5SOllivier Robert 126224ba2bdSOllivier Robert 127c0b746e5SOllivier Robert /* 128c0b746e5SOllivier Robert * Definitions 129c0b746e5SOllivier Robert */ 130c0b746e5SOllivier Robert #define DEVICE "/dev/true%d" 131c0b746e5SOllivier Robert #define SPEED232 B9600 /* 9600 baud */ 132c0b746e5SOllivier Robert 133c0b746e5SOllivier Robert /* 134c0b746e5SOllivier Robert * Radio interface parameters 135c0b746e5SOllivier Robert */ 136c0b746e5SOllivier Robert #define PRECISION (-10) /* precision assumed (about 1 ms) */ 137c0b746e5SOllivier Robert #define REFID "TRUE" /* reference id */ 138c0b746e5SOllivier Robert #define DESCRIPTION "Kinemetrics/TrueTime Receiver" 139c0b746e5SOllivier Robert 140c0b746e5SOllivier Robert /* 141c0b746e5SOllivier Robert * Tags which station (satellite) we see 142c0b746e5SOllivier Robert */ 143c0b746e5SOllivier Robert #define GOES_WEST 0 /* Default to WEST satellite and apply time1 */ 144c0b746e5SOllivier Robert #define GOES_EAST 1 /* until you discover otherwise */ 145c0b746e5SOllivier Robert 146c0b746e5SOllivier Robert /* 147c0b746e5SOllivier Robert * used by the state machine 148c0b746e5SOllivier Robert */ 149c0b746e5SOllivier Robert enum true_event {e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite, 1502b15cb3dSCy Schubert e_TL3, e_Poll, e_Location, e_TS, e_Max}; 151c0b746e5SOllivier Robert const char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite", 1522b15cb3dSCy Schubert "TL3", "Poll", "Location", "TS"}; 153c0b746e5SOllivier Robert #define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?") 154c0b746e5SOllivier Robert 155c0b746e5SOllivier Robert enum true_state {s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES, 1562b15cb3dSCy Schubert s_InqTL3, s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max}; 157c0b746e5SOllivier Robert const char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES", 1582b15cb3dSCy Schubert "InqTL3", "Init", "F18", "F50", "Start", "Auto"}; 159c0b746e5SOllivier Robert #define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?") 160c0b746e5SOllivier Robert 1612b15cb3dSCy Schubert enum true_type {t_unknown, t_goes, t_tm, t_tcu, t_omega, t_tl3, t_Max}; 1622b15cb3dSCy Schubert const char *types[] = {"unknown", "goes", "tm", "tcu", "omega", "tl3"}; 163c0b746e5SOllivier Robert #define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?") 164c0b746e5SOllivier Robert 165c0b746e5SOllivier Robert /* 166c0b746e5SOllivier Robert * unit control structure 167c0b746e5SOllivier Robert */ 168c0b746e5SOllivier Robert struct true_unit { 169c0b746e5SOllivier Robert unsigned int pollcnt; /* poll message counter */ 170c0b746e5SOllivier Robert unsigned int station; /* which station we are on */ 171c0b746e5SOllivier Robert unsigned int polled; /* Hand in a time sample? */ 172c0b746e5SOllivier Robert enum true_state state; /* state machine */ 173c0b746e5SOllivier Robert enum true_type type; /* what kind of clock is it? */ 174c0b746e5SOllivier Robert int unit; /* save an extra copy of this */ 175c0b746e5SOllivier Robert FILE *debug; /* debug logging file */ 176c0b746e5SOllivier Robert #ifdef CLOCK_PPS720 177c0b746e5SOllivier Robert int pcl720init; /* init flag for PCL 720 */ 178c0b746e5SOllivier Robert #endif 179c0b746e5SOllivier Robert }; 180c0b746e5SOllivier Robert 181c0b746e5SOllivier Robert /* 182c0b746e5SOllivier Robert * Function prototypes 183c0b746e5SOllivier Robert */ 1842b15cb3dSCy Schubert static int true_start (int, struct peer *); 1852b15cb3dSCy Schubert static void true_shutdown (int, struct peer *); 1862b15cb3dSCy Schubert static void true_receive (struct recvbuf *); 1872b15cb3dSCy Schubert static void true_poll (int, struct peer *); 1882b15cb3dSCy Schubert static void true_send (struct peer *, const char *); 1892b15cb3dSCy Schubert static void true_doevent (struct peer *, enum true_event); 190c0b746e5SOllivier Robert 191c0b746e5SOllivier Robert #ifdef CLOCK_PPS720 1922b15cb3dSCy Schubert static u_long true_sample720 (void); 193c0b746e5SOllivier Robert #endif 194c0b746e5SOllivier Robert 195c0b746e5SOllivier Robert /* 196c0b746e5SOllivier Robert * Transfer vector 197c0b746e5SOllivier Robert */ 198c0b746e5SOllivier Robert struct refclock refclock_true = { 199c0b746e5SOllivier Robert true_start, /* start up driver */ 200c0b746e5SOllivier Robert true_shutdown, /* shut down driver */ 201c0b746e5SOllivier Robert true_poll, /* transmit poll message */ 202c0b746e5SOllivier Robert noentry, /* not used (old true_control) */ 203c0b746e5SOllivier Robert noentry, /* initialize driver (not used) */ 204c0b746e5SOllivier Robert noentry, /* not used (old true_buginfo) */ 205c0b746e5SOllivier Robert NOFLAGS /* not used */ 206c0b746e5SOllivier Robert }; 207c0b746e5SOllivier Robert 208c0b746e5SOllivier Robert 209c0b746e5SOllivier Robert #if !defined(__STDC__) 210c0b746e5SOllivier Robert # define true_debug (void) 211c0b746e5SOllivier Robert #else 2122b15cb3dSCy Schubert NTP_PRINTF(2, 3) 213c0b746e5SOllivier Robert static void 214c0b746e5SOllivier Robert true_debug(struct peer *peer, const char *fmt, ...) 215c0b746e5SOllivier Robert { 216c0b746e5SOllivier Robert va_list ap; 217c0b746e5SOllivier Robert int want_debugging, now_debugging; 218c0b746e5SOllivier Robert struct refclockproc *pp; 219c0b746e5SOllivier Robert struct true_unit *up; 220c0b746e5SOllivier Robert 221c0b746e5SOllivier Robert va_start(ap, fmt); 222c0b746e5SOllivier Robert pp = peer->procptr; 2232b15cb3dSCy Schubert up = pp->unitptr; 224c0b746e5SOllivier Robert 225c0b746e5SOllivier Robert want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0; 226c0b746e5SOllivier Robert now_debugging = (up->debug != NULL); 227c0b746e5SOllivier Robert if (want_debugging != now_debugging) 228c0b746e5SOllivier Robert { 229c0b746e5SOllivier Robert if (want_debugging) { 230224ba2bdSOllivier Robert char filename[40]; 231224ba2bdSOllivier Robert int fd; 232c0b746e5SOllivier Robert 2332b15cb3dSCy Schubert snprintf(filename, sizeof(filename), 2342b15cb3dSCy Schubert "/tmp/true%d.debug", up->unit); 2352b15cb3dSCy Schubert fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 2362b15cb3dSCy Schubert 0600); 237276da39aSCy Schubert if (fd >= 0 && (up->debug = fdopen(fd, "w"))) { 238c0b746e5SOllivier Robert #ifdef HAVE_SETVBUF 239c0b746e5SOllivier Robert static char buf[BUFSIZ]; 2402b15cb3dSCy Schubert 241c0b746e5SOllivier Robert setvbuf(up->debug, buf, _IOLBF, BUFSIZ); 242c0b746e5SOllivier Robert #else 243c0b746e5SOllivier Robert setlinebuf(up->debug); 244c0b746e5SOllivier Robert #endif 245c0b746e5SOllivier Robert } 246c0b746e5SOllivier Robert } else { 247c0b746e5SOllivier Robert fclose(up->debug); 248c0b746e5SOllivier Robert up->debug = NULL; 249c0b746e5SOllivier Robert } 250c0b746e5SOllivier Robert } 251c0b746e5SOllivier Robert 252c0b746e5SOllivier Robert if (up->debug) { 253c0b746e5SOllivier Robert fprintf(up->debug, "true%d: ", up->unit); 254c0b746e5SOllivier Robert vfprintf(up->debug, fmt, ap); 255c0b746e5SOllivier Robert } 2562b15cb3dSCy Schubert va_end(ap); 257c0b746e5SOllivier Robert } 258c0b746e5SOllivier Robert #endif /*STDC*/ 259c0b746e5SOllivier Robert 260c0b746e5SOllivier Robert /* 261c0b746e5SOllivier Robert * true_start - open the devices and initialize data for processing 262c0b746e5SOllivier Robert */ 263c0b746e5SOllivier Robert static int 264c0b746e5SOllivier Robert true_start( 265c0b746e5SOllivier Robert int unit, 266c0b746e5SOllivier Robert struct peer *peer 267c0b746e5SOllivier Robert ) 268c0b746e5SOllivier Robert { 269c0b746e5SOllivier Robert register struct true_unit *up; 270c0b746e5SOllivier Robert struct refclockproc *pp; 271224ba2bdSOllivier Robert char device[40]; 272c0b746e5SOllivier Robert int fd; 273c0b746e5SOllivier Robert 274c0b746e5SOllivier Robert /* 275c0b746e5SOllivier Robert * Open serial port 276c0b746e5SOllivier Robert */ 2772b15cb3dSCy Schubert snprintf(device, sizeof(device), DEVICE, unit); 278a466cc55SCy Schubert fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_CLK); 2792b15cb3dSCy Schubert if (fd <= 0) 2802b15cb3dSCy Schubert return 0; 281c0b746e5SOllivier Robert 282c0b746e5SOllivier Robert /* 283c0b746e5SOllivier Robert * Allocate and initialize unit structure 284c0b746e5SOllivier Robert */ 2852b15cb3dSCy Schubert up = emalloc_zero(sizeof(*up)); 286c0b746e5SOllivier Robert pp = peer->procptr; 287c0b746e5SOllivier Robert pp->io.clock_recv = true_receive; 2882b15cb3dSCy Schubert pp->io.srcclock = peer; 289c0b746e5SOllivier Robert pp->io.datalen = 0; 290c0b746e5SOllivier Robert pp->io.fd = fd; 291c0b746e5SOllivier Robert if (!io_addclock(&pp->io)) { 2922b15cb3dSCy Schubert close(fd); 2932b15cb3dSCy Schubert pp->io.fd = -1; 294c0b746e5SOllivier Robert free(up); 295c0b746e5SOllivier Robert return (0); 296c0b746e5SOllivier Robert } 2972b15cb3dSCy Schubert pp->unitptr = up; 298c0b746e5SOllivier Robert 299c0b746e5SOllivier Robert /* 300c0b746e5SOllivier Robert * Initialize miscellaneous variables 301c0b746e5SOllivier Robert */ 302c0b746e5SOllivier Robert peer->precision = PRECISION; 303c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 3042b15cb3dSCy Schubert memcpy(&pp->refid, REFID, 4); 305c0b746e5SOllivier Robert up->pollcnt = 2; 306c0b746e5SOllivier Robert up->type = t_unknown; 307c0b746e5SOllivier Robert up->state = s_Base; 3082b15cb3dSCy Schubert 3092b15cb3dSCy Schubert /* 3102b15cb3dSCy Schubert * Send a CTRL-C character at the start, 3112b15cb3dSCy Schubert * just in case the clock is already 3122b15cb3dSCy Schubert * sending timecodes 3132b15cb3dSCy Schubert */ 3142b15cb3dSCy Schubert true_send(peer, "\03\r"); 3152b15cb3dSCy Schubert 316c0b746e5SOllivier Robert true_doevent(peer, e_Init); 3172b15cb3dSCy Schubert 318c0b746e5SOllivier Robert return (1); 319c0b746e5SOllivier Robert } 320c0b746e5SOllivier Robert 3212b15cb3dSCy Schubert 322c0b746e5SOllivier Robert /* 323c0b746e5SOllivier Robert * true_shutdown - shut down the clock 324c0b746e5SOllivier Robert */ 325c0b746e5SOllivier Robert static void 326c0b746e5SOllivier Robert true_shutdown( 327c0b746e5SOllivier Robert int unit, 328c0b746e5SOllivier Robert struct peer *peer 329c0b746e5SOllivier Robert ) 330c0b746e5SOllivier Robert { 331c0b746e5SOllivier Robert register struct true_unit *up; 332c0b746e5SOllivier Robert struct refclockproc *pp; 333c0b746e5SOllivier Robert 334c0b746e5SOllivier Robert pp = peer->procptr; 3352b15cb3dSCy Schubert up = pp->unitptr; 3362b15cb3dSCy Schubert if (pp->io.fd != -1) 337c0b746e5SOllivier Robert io_closeclock(&pp->io); 3382b15cb3dSCy Schubert if (up != NULL) 339c0b746e5SOllivier Robert free(up); 340c0b746e5SOllivier Robert } 341c0b746e5SOllivier Robert 342c0b746e5SOllivier Robert 343c0b746e5SOllivier Robert /* 344c0b746e5SOllivier Robert * true_receive - receive data from the serial interface on a clock 345c0b746e5SOllivier Robert */ 346c0b746e5SOllivier Robert static void 347c0b746e5SOllivier Robert true_receive( 348c0b746e5SOllivier Robert struct recvbuf *rbufp 349c0b746e5SOllivier Robert ) 350c0b746e5SOllivier Robert { 351c0b746e5SOllivier Robert register struct true_unit *up; 352c0b746e5SOllivier Robert struct refclockproc *pp; 353c0b746e5SOllivier Robert struct peer *peer; 354c0b746e5SOllivier Robert u_short new_station; 355c0b746e5SOllivier Robert char synced; 356c0b746e5SOllivier Robert int i; 357c0b746e5SOllivier Robert int lat, lon, off; /* GOES Satellite position */ 3582b15cb3dSCy Schubert /* These variables hold data until we decide to keep it */ 359224ba2bdSOllivier Robert char rd_lastcode[BMAX]; 360224ba2bdSOllivier Robert l_fp rd_tmp; 361224ba2bdSOllivier Robert u_short rd_lencode; 362c0b746e5SOllivier Robert 363c0b746e5SOllivier Robert /* 364c0b746e5SOllivier Robert * Get the clock this applies to and pointers to the data. 365c0b746e5SOllivier Robert */ 3662b15cb3dSCy Schubert peer = rbufp->recv_peer; 367c0b746e5SOllivier Robert pp = peer->procptr; 3682b15cb3dSCy Schubert up = pp->unitptr; 369c0b746e5SOllivier Robert 370c0b746e5SOllivier Robert /* 371c0b746e5SOllivier Robert * Read clock output. Automatically handles STREAMS, CLKLDISC. 372c0b746e5SOllivier Robert */ 373224ba2bdSOllivier Robert rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp); 374224ba2bdSOllivier Robert rd_lastcode[rd_lencode] = '\0'; 375c0b746e5SOllivier Robert 376c0b746e5SOllivier Robert /* 377c0b746e5SOllivier Robert * There is a case where <cr><lf> generates 2 timestamps. 378c0b746e5SOllivier Robert */ 379224ba2bdSOllivier Robert if (rd_lencode == 0) 380c0b746e5SOllivier Robert return; 381224ba2bdSOllivier Robert pp->lencode = rd_lencode; 3822b15cb3dSCy Schubert strlcpy(pp->a_lastcode, rd_lastcode, sizeof(pp->a_lastcode)); 383224ba2bdSOllivier Robert pp->lastrec = rd_tmp; 3842b15cb3dSCy Schubert true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode, 3852b15cb3dSCy Schubert pp->lencode); 386c0b746e5SOllivier Robert 387c0b746e5SOllivier Robert up->pollcnt = 2; 388c0b746e5SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 389c0b746e5SOllivier Robert 390c0b746e5SOllivier Robert /* 391c0b746e5SOllivier Robert * We get down to business, check the timecode format and decode 392c0b746e5SOllivier Robert * its contents. This code decodes a multitude of different 393c0b746e5SOllivier Robert * clock messages. Timecodes are processed if needed. All replies 394c0b746e5SOllivier Robert * will be run through the state machine to tweak driver options 395c0b746e5SOllivier Robert * and program the clock. 396c0b746e5SOllivier Robert */ 397c0b746e5SOllivier Robert 398c0b746e5SOllivier Robert /* 399c0b746e5SOllivier Robert * Clock misunderstood our last command? 400c0b746e5SOllivier Robert */ 401224ba2bdSOllivier Robert if (pp->a_lastcode[0] == '?' || 402224ba2bdSOllivier Robert strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) { 403c0b746e5SOllivier Robert true_doevent(peer, e_Huh); 404c0b746e5SOllivier Robert return; 405c0b746e5SOllivier Robert } 406c0b746e5SOllivier Robert 407c0b746e5SOllivier Robert /* 408c0b746e5SOllivier Robert * Timecode: "nnnnn+nnn-nnn" 409c0b746e5SOllivier Robert * (from GOES clock when asked about satellite position) 410c0b746e5SOllivier Robert */ 411c0b746e5SOllivier Robert if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') && 412c0b746e5SOllivier Robert (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') && 413c0b746e5SOllivier Robert sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3 414c0b746e5SOllivier Robert ) { 415c0b746e5SOllivier Robert const char *label = "Botch!"; 416c0b746e5SOllivier Robert 417c0b746e5SOllivier Robert /* 418c0b746e5SOllivier Robert * This is less than perfect. Call the (satellite) 419c0b746e5SOllivier Robert * either EAST or WEST and adjust slop accodingly 420c0b746e5SOllivier Robert * Perfectionists would recalculate the exact delay 421c0b746e5SOllivier Robert * and adjust accordingly... 422c0b746e5SOllivier Robert */ 423c0b746e5SOllivier Robert if (lon > 7000 && lon < 14000) { 424c0b746e5SOllivier Robert if (lon < 10000) { 425c0b746e5SOllivier Robert new_station = GOES_EAST; 426c0b746e5SOllivier Robert label = "EAST"; 427c0b746e5SOllivier Robert } else { 428c0b746e5SOllivier Robert new_station = GOES_WEST; 429c0b746e5SOllivier Robert label = "WEST"; 430c0b746e5SOllivier Robert } 431c0b746e5SOllivier Robert 432c0b746e5SOllivier Robert if (new_station != up->station) { 433c0b746e5SOllivier Robert double dtemp; 434c0b746e5SOllivier Robert 435c0b746e5SOllivier Robert dtemp = pp->fudgetime1; 436c0b746e5SOllivier Robert pp->fudgetime1 = pp->fudgetime2; 437c0b746e5SOllivier Robert pp->fudgetime2 = dtemp; 438c0b746e5SOllivier Robert up->station = new_station; 439c0b746e5SOllivier Robert } 440c0b746e5SOllivier Robert } 441c0b746e5SOllivier Robert else { 442ce265a54SOllivier Robert /*refclock_report(peer, CEVNT_BADREPLY);*/ 443c0b746e5SOllivier Robert label = "UNKNOWN"; 444c0b746e5SOllivier Robert } 445c0b746e5SOllivier Robert true_debug(peer, "GOES: station %s\n", label); 446c0b746e5SOllivier Robert true_doevent(peer, e_Satellite); 447c0b746e5SOllivier Robert return; 448c0b746e5SOllivier Robert } 449c0b746e5SOllivier Robert 450c0b746e5SOllivier Robert /* 451c0b746e5SOllivier Robert * Timecode: "Fnn" 452c0b746e5SOllivier Robert * (from TM/TMD clock when it wants to tell us what it's up to.) 453c0b746e5SOllivier Robert */ 454c0b746e5SOllivier Robert if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) { 455c0b746e5SOllivier Robert switch (i) { 456c0b746e5SOllivier Robert case 50: 457c0b746e5SOllivier Robert true_doevent(peer, e_F50); 458c0b746e5SOllivier Robert break; 459c0b746e5SOllivier Robert case 51: 460c0b746e5SOllivier Robert true_doevent(peer, e_F51); 461c0b746e5SOllivier Robert break; 462c0b746e5SOllivier Robert default: 463c0b746e5SOllivier Robert true_debug(peer, "got F%02d - ignoring\n", i); 464c0b746e5SOllivier Robert break; 465c0b746e5SOllivier Robert } 466c0b746e5SOllivier Robert return; 467c0b746e5SOllivier Robert } 468c0b746e5SOllivier Robert 469c0b746e5SOllivier Robert /* 4702b15cb3dSCy Schubert * Timecode: "VER xx.xx" 4712b15cb3dSCy Schubert * (from a TL3 when sent "QV", so id's it during initialization.) 4722b15cb3dSCy Schubert */ 4732b15cb3dSCy Schubert if (pp->a_lastcode[0] == 'V' && pp->a_lastcode[1] == 'E' && 4742b15cb3dSCy Schubert pp->a_lastcode[2] == 'R' && pp->a_lastcode[6] == '.') { 4752b15cb3dSCy Schubert true_doevent(peer, e_TL3); 4762b15cb3dSCy Schubert NLOG(NLOG_CLOCKSTATUS) { 4772b15cb3dSCy Schubert msyslog(LOG_INFO, "TL3: %s", pp->a_lastcode); 4782b15cb3dSCy Schubert } 4792b15cb3dSCy Schubert return; 4802b15cb3dSCy Schubert } 4812b15cb3dSCy Schubert 4822b15cb3dSCy Schubert /* 483224ba2bdSOllivier Robert * Timecode: " TRUETIME Mk III" or " TRUETIME XL" 484224ba2bdSOllivier Robert * (from a TM/TMD/XL clock during initialization.) 485c0b746e5SOllivier Robert */ 4862b15cb3dSCy Schubert if (strncmp(pp->a_lastcode, " TRUETIME Mk III ", 17) == 0 || 487224ba2bdSOllivier Robert strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) { 488c0b746e5SOllivier Robert true_doevent(peer, e_F18); 489c0b746e5SOllivier Robert NLOG(NLOG_CLOCKSTATUS) { 490224ba2bdSOllivier Robert msyslog(LOG_INFO, "TM/TMD/XL: %s", pp->a_lastcode); 491c0b746e5SOllivier Robert } 492c0b746e5SOllivier Robert return; 493c0b746e5SOllivier Robert } 494c0b746e5SOllivier Robert 495c0b746e5SOllivier Robert /* 496c0b746e5SOllivier Robert * Timecode: "N03726428W12209421+000033" 497c0b746e5SOllivier Robert * 1 2 4982b15cb3dSCy Schubert * index 0123456789012345678901234 499c0b746e5SOllivier Robert * (from a TCU during initialization) 500c0b746e5SOllivier Robert */ 501c0b746e5SOllivier Robert if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') && 502c0b746e5SOllivier Robert (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') && 503c0b746e5SOllivier Robert pp->a_lastcode[18] == '+') { 504c0b746e5SOllivier Robert true_doevent(peer, e_Location); 505c0b746e5SOllivier Robert NLOG(NLOG_CLOCKSTATUS) { 506c0b746e5SOllivier Robert msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode); 507c0b746e5SOllivier Robert } 508c0b746e5SOllivier Robert return; 509c0b746e5SOllivier Robert } 510c0b746e5SOllivier Robert /* 511c0b746e5SOllivier Robert * Timecode: "ddd:hh:mm:ssQ" 5122b15cb3dSCy Schubert * 1 2 5132b15cb3dSCy Schubert * index 0123456789012345678901234 514c0b746e5SOllivier Robert * (from all clocks supported by this driver.) 515c0b746e5SOllivier Robert */ 516c0b746e5SOllivier Robert if (pp->a_lastcode[3] == ':' && 517c0b746e5SOllivier Robert pp->a_lastcode[6] == ':' && 518c0b746e5SOllivier Robert pp->a_lastcode[9] == ':' && 519c0b746e5SOllivier Robert sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c", 520c0b746e5SOllivier Robert &pp->day, &pp->hour, &pp->minute, 521c0b746e5SOllivier Robert &pp->second, &synced) == 5) { 522c0b746e5SOllivier Robert 523c0b746e5SOllivier Robert /* 524c0b746e5SOllivier Robert * Adjust the synchronize indicator according to timecode 525224ba2bdSOllivier Robert * say were OK, and then say not if we really are not OK 526c0b746e5SOllivier Robert */ 5272b15cb3dSCy Schubert if (synced == '>' || synced == '#' || synced == '?' 5282b15cb3dSCy Schubert || synced == 'X') 529c0b746e5SOllivier Robert pp->leap = LEAP_NOTINSYNC; 530224ba2bdSOllivier Robert else 531224ba2bdSOllivier Robert pp->leap = LEAP_NOWARNING; 532c0b746e5SOllivier Robert 533c0b746e5SOllivier Robert true_doevent(peer, e_TS); 534c0b746e5SOllivier Robert 535c0b746e5SOllivier Robert #ifdef CLOCK_PPS720 536c0b746e5SOllivier Robert /* If it's taken more than 65ms to get here, we'll lose. */ 537c0b746e5SOllivier Robert if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) { 538c0b746e5SOllivier Robert l_fp off; 539c0b746e5SOllivier Robert 540c0b746e5SOllivier Robert #ifdef CLOCK_ATOM 541c0b746e5SOllivier Robert /* 542c0b746e5SOllivier Robert * find out what time it really is. Include 543c0b746e5SOllivier Robert * the count from the PCL720 544c0b746e5SOllivier Robert */ 545c0b746e5SOllivier Robert if (!clocktime(pp->day, pp->hour, pp->minute, 546c0b746e5SOllivier Robert pp->second, GMT, pp->lastrec.l_ui, 547c0b746e5SOllivier Robert &pp->yearstart, &off.l_ui)) { 548c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 549c0b746e5SOllivier Robert return; 550c0b746e5SOllivier Robert } 5519c2daa00SOllivier Robert off.l_uf = 0; 552c0b746e5SOllivier Robert #endif 553c0b746e5SOllivier Robert 554c0b746e5SOllivier Robert pp->usec = true_sample720(); 555c0b746e5SOllivier Robert #ifdef CLOCK_ATOM 556c0b746e5SOllivier Robert TVUTOTSF(pp->usec, off.l_uf); 557c0b746e5SOllivier Robert #endif 558c0b746e5SOllivier Robert 559c0b746e5SOllivier Robert /* 560c0b746e5SOllivier Robert * Stomp all over the timestamp that was pulled out 561c0b746e5SOllivier Robert * of the input stream. It's irrelevant since we've 562c0b746e5SOllivier Robert * adjusted the input time to reflect now (via pp->usec) 563c0b746e5SOllivier Robert * rather than when the data was collected. 564c0b746e5SOllivier Robert */ 565c0b746e5SOllivier Robert get_systime(&pp->lastrec); 566c0b746e5SOllivier Robert #ifdef CLOCK_ATOM 567c0b746e5SOllivier Robert /* 568c0b746e5SOllivier Robert * Create a true offset for feeding to pps_sample() 569c0b746e5SOllivier Robert */ 570c0b746e5SOllivier Robert L_SUB(&off, &pp->lastrec); 571c0b746e5SOllivier Robert 572c0b746e5SOllivier Robert pps_sample(peer, &off); 573c0b746e5SOllivier Robert #endif 574c0b746e5SOllivier Robert true_debug(peer, "true_sample720: %luus\n", pp->usec); 575c0b746e5SOllivier Robert } 576c0b746e5SOllivier Robert #endif 577c0b746e5SOllivier Robert 578c0b746e5SOllivier Robert /* 579c0b746e5SOllivier Robert * The clock will blurt a timecode every second but we only 580c0b746e5SOllivier Robert * want one when polled. If we havn't been polled, bail out. 581c0b746e5SOllivier Robert */ 582c0b746e5SOllivier Robert if (!up->polled) 583c0b746e5SOllivier Robert return; 584c0b746e5SOllivier Robert 5852b15cb3dSCy Schubert /* We only call doevent if additional things need be done 5862b15cb3dSCy Schubert * at poll interval. Currently, its only for GOES. We also 5872b15cb3dSCy Schubert * call it for clock unknown so that it gets logged. 5882b15cb3dSCy Schubert */ 5892b15cb3dSCy Schubert if (up->type == t_goes || up->type == t_unknown) 590c0b746e5SOllivier Robert true_doevent(peer, e_Poll); 5912b15cb3dSCy Schubert 592c0b746e5SOllivier Robert if (!refclock_process(pp)) { 593c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 594c0b746e5SOllivier Robert return; 595c0b746e5SOllivier Robert } 596224ba2bdSOllivier Robert /* 597224ba2bdSOllivier Robert * If clock is good we send a NOMINAL message so that 598224ba2bdSOllivier Robert * any previous BAD messages are nullified 599224ba2bdSOllivier Robert */ 6009c2daa00SOllivier Robert pp->lastref = pp->lastrec; 601c0b746e5SOllivier Robert refclock_receive(peer); 6029c2daa00SOllivier Robert refclock_report(peer, CEVNT_NOMINAL); 603c0b746e5SOllivier Robert 604c0b746e5SOllivier Robert /* 605c0b746e5SOllivier Robert * We have succedded in answering the poll. 606c0b746e5SOllivier Robert * Turn off the flag and return 607c0b746e5SOllivier Robert */ 608c0b746e5SOllivier Robert up->polled = 0; 609c0b746e5SOllivier Robert 610c0b746e5SOllivier Robert return; 611c0b746e5SOllivier Robert } 612c0b746e5SOllivier Robert 613c0b746e5SOllivier Robert /* 614c0b746e5SOllivier Robert * No match to known timecodes, report failure and return 615c0b746e5SOllivier Robert */ 616c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 617c0b746e5SOllivier Robert return; 618c0b746e5SOllivier Robert } 619c0b746e5SOllivier Robert 620c0b746e5SOllivier Robert 621c0b746e5SOllivier Robert /* 622c0b746e5SOllivier Robert * true_send - time to send the clock a signal to cough up a time sample 623c0b746e5SOllivier Robert */ 624c0b746e5SOllivier Robert static void 625c0b746e5SOllivier Robert true_send( 626c0b746e5SOllivier Robert struct peer *peer, 627c0b746e5SOllivier Robert const char *cmd 628c0b746e5SOllivier Robert ) 629c0b746e5SOllivier Robert { 630c0b746e5SOllivier Robert struct refclockproc *pp; 631c0b746e5SOllivier Robert 632c0b746e5SOllivier Robert pp = peer->procptr; 633c0b746e5SOllivier Robert if (!(pp->sloppyclockflag & CLK_FLAG1)) { 6343311ff84SXin LI size_t len = strlen(cmd); 635c0b746e5SOllivier Robert 636c0b746e5SOllivier Robert true_debug(peer, "Send '%s'\n", cmd); 637a466cc55SCy Schubert if (refclock_write(peer, cmd, len, NULL) != (ssize_t)len) 638c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 639c0b746e5SOllivier Robert else 640c0b746e5SOllivier Robert pp->polls++; 641c0b746e5SOllivier Robert } 642c0b746e5SOllivier Robert } 643c0b746e5SOllivier Robert 644c0b746e5SOllivier Robert 645c0b746e5SOllivier Robert /* 646c0b746e5SOllivier Robert * state machine for initializing and controlling a clock 647c0b746e5SOllivier Robert */ 648c0b746e5SOllivier Robert static void 649c0b746e5SOllivier Robert true_doevent( 650c0b746e5SOllivier Robert struct peer *peer, 651c0b746e5SOllivier Robert enum true_event event 652c0b746e5SOllivier Robert ) 653c0b746e5SOllivier Robert { 654c0b746e5SOllivier Robert struct true_unit *up; 655c0b746e5SOllivier Robert struct refclockproc *pp; 656c0b746e5SOllivier Robert 657c0b746e5SOllivier Robert pp = peer->procptr; 6582b15cb3dSCy Schubert up = pp->unitptr; 659c0b746e5SOllivier Robert if (event != e_TS) { 660c0b746e5SOllivier Robert NLOG(NLOG_CLOCKSTATUS) { 661c0b746e5SOllivier Robert msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s", 662c0b746e5SOllivier Robert typeStr(up->type), 663c0b746e5SOllivier Robert stateStr(up->state), 664c0b746e5SOllivier Robert eventStr(event)); 665c0b746e5SOllivier Robert } 666c0b746e5SOllivier Robert } 667c0b746e5SOllivier Robert true_debug(peer, "clock %s, state %s, event %s\n", 668c0b746e5SOllivier Robert typeStr(up->type), stateStr(up->state), eventStr(event)); 669c0b746e5SOllivier Robert switch (up->type) { 670c0b746e5SOllivier Robert case t_goes: 671c0b746e5SOllivier Robert switch (event) { 672c0b746e5SOllivier Robert case e_Init: /* FALLTHROUGH */ 673c0b746e5SOllivier Robert case e_Satellite: 674c0b746e5SOllivier Robert /* 675c0b746e5SOllivier Robert * Switch back to on-second time codes and return. 676c0b746e5SOllivier Robert */ 677c0b746e5SOllivier Robert true_send(peer, "C"); 678c0b746e5SOllivier Robert up->state = s_Start; 679c0b746e5SOllivier Robert break; 680c0b746e5SOllivier Robert case e_Poll: 681c0b746e5SOllivier Robert /* 682c0b746e5SOllivier Robert * After each poll, check the station (satellite). 683c0b746e5SOllivier Robert */ 684c0b746e5SOllivier Robert true_send(peer, "P"); 685c0b746e5SOllivier Robert /* No state change needed. */ 686c0b746e5SOllivier Robert break; 687c0b746e5SOllivier Robert default: 688c0b746e5SOllivier Robert break; 689c0b746e5SOllivier Robert } 690c0b746e5SOllivier Robert /* FALLTHROUGH */ 691c0b746e5SOllivier Robert case t_omega: 692c0b746e5SOllivier Robert switch (event) { 693c0b746e5SOllivier Robert case e_Init: 694c0b746e5SOllivier Robert true_send(peer, "C"); 695c0b746e5SOllivier Robert up->state = s_Start; 696c0b746e5SOllivier Robert break; 697c0b746e5SOllivier Robert case e_TS: 698c0b746e5SOllivier Robert if (up->state != s_Start && up->state != s_Auto) { 699c0b746e5SOllivier Robert true_send(peer, "\03\r"); 700c0b746e5SOllivier Robert break; 701c0b746e5SOllivier Robert } 702c0b746e5SOllivier Robert up->state = s_Auto; 703c0b746e5SOllivier Robert break; 704c0b746e5SOllivier Robert default: 705c0b746e5SOllivier Robert break; 706c0b746e5SOllivier Robert } 707c0b746e5SOllivier Robert break; 708c0b746e5SOllivier Robert case t_tm: 709c0b746e5SOllivier Robert switch (event) { 710c0b746e5SOllivier Robert case e_Init: 711c0b746e5SOllivier Robert true_send(peer, "F18\r"); 712c0b746e5SOllivier Robert up->state = s_Init; 713c0b746e5SOllivier Robert break; 714c0b746e5SOllivier Robert case e_F18: 715c0b746e5SOllivier Robert true_send(peer, "F50\r"); 7162b15cb3dSCy Schubert /* 7172b15cb3dSCy Schubert * Timecode: " TRUETIME Mk III" or " TRUETIME XL" 7182b15cb3dSCy Schubert * (from a TM/TMD/XL clock during initialization.) 7192b15cb3dSCy Schubert */ 7202b15cb3dSCy Schubert if ( strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 || 7212b15cb3dSCy Schubert strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) { 7222b15cb3dSCy Schubert true_doevent(peer, e_F18); 7232b15cb3dSCy Schubert NLOG(NLOG_CLOCKSTATUS) { 7242b15cb3dSCy Schubert msyslog(LOG_INFO, "TM/TMD/XL: %s", 7252b15cb3dSCy Schubert pp->a_lastcode); 7262b15cb3dSCy Schubert } 7272b15cb3dSCy Schubert return; 7282b15cb3dSCy Schubert } 729c0b746e5SOllivier Robert up->state = s_F18; 730c0b746e5SOllivier Robert break; 731c0b746e5SOllivier Robert case e_F50: 732c0b746e5SOllivier Robert true_send(peer, "F51\r"); 733c0b746e5SOllivier Robert up->state = s_F50; 734c0b746e5SOllivier Robert break; 735c0b746e5SOllivier Robert case e_F51: 736c0b746e5SOllivier Robert true_send(peer, "F08\r"); 737c0b746e5SOllivier Robert up->state = s_Start; 738c0b746e5SOllivier Robert break; 739c0b746e5SOllivier Robert case e_TS: 740c0b746e5SOllivier Robert if (up->state != s_Start && up->state != s_Auto) { 741c0b746e5SOllivier Robert true_send(peer, "\03\r"); 742c0b746e5SOllivier Robert break; 743c0b746e5SOllivier Robert } 744c0b746e5SOllivier Robert up->state = s_Auto; 745c0b746e5SOllivier Robert break; 746c0b746e5SOllivier Robert default: 747c0b746e5SOllivier Robert break; 748c0b746e5SOllivier Robert } 749c0b746e5SOllivier Robert break; 750c0b746e5SOllivier Robert case t_tcu: 751c0b746e5SOllivier Robert switch (event) { 752c0b746e5SOllivier Robert case e_Init: 753c0b746e5SOllivier Robert true_send(peer, "MD3\r"); /* GPS Synch'd Gen. */ 754c0b746e5SOllivier Robert true_send(peer, "TSU\r"); /* UTC, not GPS. */ 755c0b746e5SOllivier Robert true_send(peer, "AU\r"); /* Auto Timestamps. */ 756c0b746e5SOllivier Robert up->state = s_Start; 757c0b746e5SOllivier Robert break; 758c0b746e5SOllivier Robert case e_TS: 759c0b746e5SOllivier Robert if (up->state != s_Start && up->state != s_Auto) { 760c0b746e5SOllivier Robert true_send(peer, "\03\r"); 761c0b746e5SOllivier Robert break; 762c0b746e5SOllivier Robert } 763c0b746e5SOllivier Robert up->state = s_Auto; 764c0b746e5SOllivier Robert break; 765c0b746e5SOllivier Robert default: 766c0b746e5SOllivier Robert break; 767c0b746e5SOllivier Robert } 768c0b746e5SOllivier Robert break; 7692b15cb3dSCy Schubert case t_tl3: 7702b15cb3dSCy Schubert switch (event) { 7712b15cb3dSCy Schubert case e_Init: 7722b15cb3dSCy Schubert true_send(peer, "ST1"); /* Turn on continuous stream */ 7732b15cb3dSCy Schubert break; 7742b15cb3dSCy Schubert case e_TS: 7752b15cb3dSCy Schubert up->state = s_Auto; 7762b15cb3dSCy Schubert break; 7772b15cb3dSCy Schubert default: 7782b15cb3dSCy Schubert break; 7792b15cb3dSCy Schubert } 7802b15cb3dSCy Schubert break; 781c0b746e5SOllivier Robert case t_unknown: 7822b15cb3dSCy Schubert if (event == e_Poll) 7832b15cb3dSCy Schubert break; 784c0b746e5SOllivier Robert switch (up->state) { 785c0b746e5SOllivier Robert case s_Base: 786c0b746e5SOllivier Robert if (event != e_Init) 787c0b746e5SOllivier Robert abort(); 788c0b746e5SOllivier Robert true_send(peer, "P\r"); 789c0b746e5SOllivier Robert up->state = s_InqGOES; 790c0b746e5SOllivier Robert break; 791c0b746e5SOllivier Robert case s_InqGOES: 792c0b746e5SOllivier Robert switch (event) { 793c0b746e5SOllivier Robert case e_Satellite: 794c0b746e5SOllivier Robert up->type = t_goes; 795c0b746e5SOllivier Robert true_doevent(peer, e_Init); 796c0b746e5SOllivier Robert break; 797c0b746e5SOllivier Robert case e_Init: /*FALLTHROUGH*/ 7982b15cb3dSCy Schubert case e_Huh: 799c0b746e5SOllivier Robert case e_TS: 8002b15cb3dSCy Schubert true_send(peer, "ST0"); /* turn off TL3 auto */ 8012b15cb3dSCy Schubert sleep(1); /* wait for it */ 8022b15cb3dSCy Schubert up->state = s_InqTL3; 8032b15cb3dSCy Schubert true_send(peer, "QV"); /* see if its a TL3 */ 804c0b746e5SOllivier Robert break; 805c0b746e5SOllivier Robert default: 806c0b746e5SOllivier Robert abort(); 807c0b746e5SOllivier Robert } 808c0b746e5SOllivier Robert break; 8092b15cb3dSCy Schubert case s_InqTL3: 8102b15cb3dSCy Schubert switch (event) { 8112b15cb3dSCy Schubert case e_TL3: 8122b15cb3dSCy Schubert up->type = t_tl3; 8132b15cb3dSCy Schubert up->state = s_Auto; /* Inq side-effect. */ 8142b15cb3dSCy Schubert true_send(peer, "ST1"); /* Turn on 1/sec data */ 8152b15cb3dSCy Schubert break; 8162b15cb3dSCy Schubert case e_Init: /*FALLTHROUGH*/ 8172b15cb3dSCy Schubert case e_Huh: 8182b15cb3dSCy Schubert up->state = s_InqOmega; 8192b15cb3dSCy Schubert true_send(peer, "C\r"); 8202b15cb3dSCy Schubert break; 8212b15cb3dSCy Schubert case e_TS: 8222b15cb3dSCy Schubert up->type = t_tl3; /* Already sending data */ 8232b15cb3dSCy Schubert up->state = s_Auto; 8242b15cb3dSCy Schubert break; 8252b15cb3dSCy Schubert default: 8262b15cb3dSCy Schubert msyslog(LOG_INFO, 8272b15cb3dSCy Schubert "TRUE: TL3 init fellthrough! (%d)", event); 8282b15cb3dSCy Schubert break; 8292b15cb3dSCy Schubert } 8302b15cb3dSCy Schubert break; 831c0b746e5SOllivier Robert case s_InqOmega: 832c0b746e5SOllivier Robert switch (event) { 833c0b746e5SOllivier Robert case e_TS: 834c0b746e5SOllivier Robert up->type = t_omega; 835c0b746e5SOllivier Robert up->state = s_Auto; /* Inq side-effect. */ 836c0b746e5SOllivier Robert break; 837c0b746e5SOllivier Robert case e_Init: /*FALLTHROUGH*/ 838c0b746e5SOllivier Robert case e_Huh: 839c0b746e5SOllivier Robert up->state = s_InqTM; 840c0b746e5SOllivier Robert true_send(peer, "F18\r"); 841c0b746e5SOllivier Robert break; 842c0b746e5SOllivier Robert default: 843c0b746e5SOllivier Robert abort(); 844c0b746e5SOllivier Robert } 845c0b746e5SOllivier Robert break; 846c0b746e5SOllivier Robert case s_InqTM: 847c0b746e5SOllivier Robert switch (event) { 848c0b746e5SOllivier Robert case e_F18: 849c0b746e5SOllivier Robert up->type = t_tm; 850c0b746e5SOllivier Robert true_doevent(peer, e_Init); 851c0b746e5SOllivier Robert break; 852c0b746e5SOllivier Robert case e_Init: /*FALLTHROUGH*/ 853c0b746e5SOllivier Robert case e_Huh: 854c0b746e5SOllivier Robert true_send(peer, "PO\r"); 855c0b746e5SOllivier Robert up->state = s_InqTCU; 856c0b746e5SOllivier Robert break; 857c0b746e5SOllivier Robert default: 8582b15cb3dSCy Schubert msyslog(LOG_INFO, 8592b15cb3dSCy Schubert "TRUE: TM/TMD init fellthrough!"); 8602b15cb3dSCy Schubert break; 861c0b746e5SOllivier Robert } 862c0b746e5SOllivier Robert break; 863c0b746e5SOllivier Robert case s_InqTCU: 864c0b746e5SOllivier Robert switch (event) { 865c0b746e5SOllivier Robert case e_Location: 866c0b746e5SOllivier Robert up->type = t_tcu; 867c0b746e5SOllivier Robert true_doevent(peer, e_Init); 868c0b746e5SOllivier Robert break; 869c0b746e5SOllivier Robert case e_Init: /*FALLTHROUGH*/ 870c0b746e5SOllivier Robert case e_Huh: 871c0b746e5SOllivier Robert up->state = s_Base; 872c0b746e5SOllivier Robert sleep(1); /* XXX */ 873c0b746e5SOllivier Robert break; 874c0b746e5SOllivier Robert default: 8752b15cb3dSCy Schubert msyslog(LOG_INFO, 8762b15cb3dSCy Schubert "TRUE: TCU init fellthrough!"); 8772b15cb3dSCy Schubert break; 878c0b746e5SOllivier Robert } 879c0b746e5SOllivier Robert break; 880c0b746e5SOllivier Robert /* 881c0b746e5SOllivier Robert * An expedient hack to prevent lint complaints, 882c0b746e5SOllivier Robert * these don't actually need to be used here... 883c0b746e5SOllivier Robert */ 884c0b746e5SOllivier Robert case s_Init: 885c0b746e5SOllivier Robert case s_F18: 886c0b746e5SOllivier Robert case s_F50: 887c0b746e5SOllivier Robert case s_Start: 888c0b746e5SOllivier Robert case s_Auto: 889c0b746e5SOllivier Robert case s_Max: 8902b15cb3dSCy Schubert msyslog(LOG_INFO, "TRUE: state %s is unexpected!", 8912b15cb3dSCy Schubert stateStr(up->state)); 892c0b746e5SOllivier Robert } 893c0b746e5SOllivier Robert break; 894c0b746e5SOllivier Robert default: 8952b15cb3dSCy Schubert msyslog(LOG_INFO, "TRUE: cannot identify refclock!"); 896c0b746e5SOllivier Robert abort(); 897c0b746e5SOllivier Robert /* NOTREACHED */ 898c0b746e5SOllivier Robert } 899c0b746e5SOllivier Robert 900c0b746e5SOllivier Robert #ifdef CLOCK_PPS720 901c0b746e5SOllivier Robert if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) { 902c0b746e5SOllivier Robert /* Make counter trigger on gate0, count down from 65535. */ 903c0b746e5SOllivier Robert pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535); 904c0b746e5SOllivier Robert /* 905c0b746e5SOllivier Robert * (These constants are OK since 906c0b746e5SOllivier Robert * they represent hardware maximums.) 907c0b746e5SOllivier Robert */ 908c0b746e5SOllivier Robert NLOG(NLOG_CLOCKINFO) { 909c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "PCL-720 initialized"); 910c0b746e5SOllivier Robert } 911c0b746e5SOllivier Robert up->pcl720init++; 912c0b746e5SOllivier Robert } 913c0b746e5SOllivier Robert #endif 914c0b746e5SOllivier Robert 915c0b746e5SOllivier Robert 916c0b746e5SOllivier Robert } 917c0b746e5SOllivier Robert 918c0b746e5SOllivier Robert /* 919c0b746e5SOllivier Robert * true_poll - called by the transmit procedure 920c0b746e5SOllivier Robert */ 921c0b746e5SOllivier Robert static void 922c0b746e5SOllivier Robert true_poll( 923c0b746e5SOllivier Robert int unit, 924c0b746e5SOllivier Robert struct peer *peer 925c0b746e5SOllivier Robert ) 926c0b746e5SOllivier Robert { 927c0b746e5SOllivier Robert struct true_unit *up; 928c0b746e5SOllivier Robert struct refclockproc *pp; 929c0b746e5SOllivier Robert 930c0b746e5SOllivier Robert /* 931c0b746e5SOllivier Robert * You don't need to poll this clock. It puts out timecodes 932c0b746e5SOllivier Robert * once per second. If asked for a timestamp, take note. 933c0b746e5SOllivier Robert * The next time a timecode comes in, it will be fed back. 934c0b746e5SOllivier Robert */ 935c0b746e5SOllivier Robert pp = peer->procptr; 9362b15cb3dSCy Schubert up = pp->unitptr; 9372b15cb3dSCy Schubert if (up->pollcnt > 0) { 938c0b746e5SOllivier Robert up->pollcnt--; 9392b15cb3dSCy Schubert } else { 940c0b746e5SOllivier Robert true_doevent(peer, e_Init); 941c0b746e5SOllivier Robert refclock_report(peer, CEVNT_TIMEOUT); 942c0b746e5SOllivier Robert } 943c0b746e5SOllivier Robert 944c0b746e5SOllivier Robert /* 945c0b746e5SOllivier Robert * polled every 64 seconds. Ask true_receive to hand in a 946c0b746e5SOllivier Robert * timestamp. 947c0b746e5SOllivier Robert */ 948c0b746e5SOllivier Robert up->polled = 1; 949c0b746e5SOllivier Robert pp->polls++; 950c0b746e5SOllivier Robert } 951c0b746e5SOllivier Robert 952c0b746e5SOllivier Robert #ifdef CLOCK_PPS720 953c0b746e5SOllivier Robert /* 954c0b746e5SOllivier Robert * true_sample720 - sample the PCL-720 955c0b746e5SOllivier Robert */ 956c0b746e5SOllivier Robert static u_long 957c0b746e5SOllivier Robert true_sample720(void) 958c0b746e5SOllivier Robert { 959c0b746e5SOllivier Robert unsigned long f; 960c0b746e5SOllivier Robert 961c0b746e5SOllivier Robert /* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3. 962c0b746e5SOllivier Robert * If it is not being held low now, we did not get called 963c0b746e5SOllivier Robert * within 65535us. 964c0b746e5SOllivier Robert */ 965c0b746e5SOllivier Robert if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) { 966c0b746e5SOllivier Robert NLOG(NLOG_CLOCKINFO) { 967c0b746e5SOllivier Robert msyslog(LOG_NOTICE, "PCL-720 out of synch"); 968c0b746e5SOllivier Robert } 969c0b746e5SOllivier Robert return (0); 970c0b746e5SOllivier Robert } 971c0b746e5SOllivier Robert f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR)); 972c0b746e5SOllivier Robert #ifdef PPS720_DEBUG 973c0b746e5SOllivier Robert msyslog(LOG_DEBUG, "PCL-720: %luus", f); 974c0b746e5SOllivier Robert #endif 975c0b746e5SOllivier Robert return (f); 976c0b746e5SOllivier Robert } 977c0b746e5SOllivier Robert #endif 978c0b746e5SOllivier Robert 979c0b746e5SOllivier Robert #else 980*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT 981c0b746e5SOllivier Robert #endif /* REFCLOCK */ 982