154359Sroberto/* 2285612Sdelphij * refclock_true - clock driver for the Kinemetrics/TrueTime receivers 354359Sroberto * Receiver Version 3.0C - tested plain, with CLKLDISC 4285612Sdelphij * Development work being done: 5285612Sdelphij * - Support TL-3 WWV TOD receiver 654359Sroberto */ 754359Sroberto 854359Sroberto#ifdef HAVE_CONFIG_H 954359Sroberto#include <config.h> 1054359Sroberto#endif 1154359Sroberto 1254359Sroberto#if defined(REFCLOCK) && defined(CLOCK_TRUETIME) 1354359Sroberto 14285612Sdelphij#include <stdio.h> 15285612Sdelphij#include <ctype.h> 16285612Sdelphij 1754359Sroberto#include "ntpd.h" 1854359Sroberto#include "ntp_io.h" 1954359Sroberto#include "ntp_refclock.h" 2054359Sroberto#include "ntp_unixtime.h" 2154359Sroberto#include "ntp_stdlib.h" 2254359Sroberto 23285612Sdelphij#ifdef SYS_WINNT 24285612Sdelphijextern int async_write(int, const void *, unsigned int); 25285612Sdelphij#undef write 26285612Sdelphij#define write(fd, data, octets) async_write(fd, data, octets) 27285612Sdelphij#endif 2882498Sroberto 2954359Sroberto/* This should be an atom clock but those are very hard to build. 3054359Sroberto * 3154359Sroberto * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch 3254359Sroberto * of TTL input and output pins, all brought out to the back panel. If you 3354359Sroberto * wire a PPS signal (such as the TTL PPS coming out of a GOES or other 3454359Sroberto * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the 3554359Sroberto * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the 3654359Sroberto * number of uSecs since the last PPS upward swing, mediated by reading OUT0 3754359Sroberto * to find out if the counter has wrapped around (this happens if more than 3854359Sroberto * 65535us (65ms) elapses between the PPS event and our being called.) 3954359Sroberto */ 4054359Sroberto#ifdef CLOCK_PPS720 4154359Sroberto# undef min /* XXX */ 4254359Sroberto# undef max /* XXX */ 4354359Sroberto# include <machine/inline.h> 4454359Sroberto# include <sys/pcl720.h> 4554359Sroberto# include <sys/i8253.h> 4654359Sroberto# define PCL720_IOB 0x2a0 /* XXX */ 4754359Sroberto# define PCL720_CTR 0 /* XXX */ 4854359Sroberto#endif 4954359Sroberto 5054359Sroberto/* 5154359Sroberto * Support for Kinemetrics Truetime Receivers 52285612Sdelphij * GOES: (468-DC, usable with GPS->GOES converting antenna) 53285612Sdelphij * GPS/TM-TMD: 54285612Sdelphij * XL-DC: (a 151-602-210, reported by the driver as a GPS/TM-TMD) 55285612Sdelphij * GPS-800 TCU: (an 805-957 with the RS232 Talker/Listener module) 56285612Sdelphij * TL-3: 3 channel WWV/H receiver w/ IRIG and RS-232 outputs 5754359Sroberto * OM-DC: getting stale ("OMEGA") 5854359Sroberto * 5954359Sroberto * Most of this code is originally from refclock_wwvb.c with thanks. 6054359Sroberto * It has been so mangled that wwvb is not a recognizable ancestor. 6154359Sroberto * 6254359Sroberto * Timcode format: ADDD:HH:MM:SSQCL 6354359Sroberto * A - control A (this is stripped before we see it) 6454359Sroberto * Q - Quality indication (see below) 6554359Sroberto * C - Carriage return 6654359Sroberto * L - Line feed 6754359Sroberto * 6854359Sroberto * Quality codes indicate possible error of 6954359Sroberto * 468-DC GOES Receiver: 7082498Sroberto * GPS-TM/TMD Receiver: (default quality codes for XL-DC) 7182498Sroberto * ? +/- 1 milliseconds # +/- 100 microseconds 7282498Sroberto * * +/- 10 microseconds . +/- 1 microsecond 7382498Sroberto * space less than 1 microsecond 74285612Sdelphij * TL-3 Receiver: (default quality codes for TL-3) 75285612Sdelphij * ? unknown quality (receiver is unlocked) 76285612Sdelphij * space +/- 5 milliseconds 7782498Sroberto * OM-DC OMEGA Receiver: (default quality codes for OMEGA) 7882498Sroberto * WARNING OMEGA navigation system is no longer existent 7954359Sroberto * > >+- 5 seconds 8054359Sroberto * ? >+/- 500 milliseconds # >+/- 50 milliseconds 8154359Sroberto * * >+/- 5 milliseconds . >+/- 1 millisecond 8254359Sroberto * A-H less than 1 millisecond. Character indicates which station 83285612Sdelphij * is being received as follows: 84285612Sdelphij * A = Norway, B = Liberia, C = Hawaii, D = North Dakota, 85285612Sdelphij * E = La Reunion, F = Argentina, G = Australia, H = Japan. 8654359Sroberto * 8754359Sroberto * The carriage return start bit begins on 0 seconds and extends to 1 bit time. 8854359Sroberto * 8954359Sroberto * Notes on 468-DC and OMEGA receiver: 9054359Sroberto * 9154359Sroberto * Send the clock a 'R' or 'C' and once per second a timestamp will 9254359Sroberto * appear. Send a 'P' to get the satellite position once (GOES only.) 9354359Sroberto * 9454359Sroberto * Notes on the 468-DC receiver: 9554359Sroberto * 9654359Sroberto * Since the old east/west satellite locations are only historical, you can't 9754359Sroberto * set your clock propagation delay settings correctly and still use 9854359Sroberto * automatic mode. The manual says to use a compromise when setting the 9954359Sroberto * switches. This results in significant errors. The solution; use fudge 10054359Sroberto * time1 and time2 to incorporate corrections. If your clock is set for 10154359Sroberto * 50 and it should be 58 for using the west and 46 for using the east, 10254359Sroberto * use the line 10354359Sroberto * 10454359Sroberto * fudge 127.127.5.0 time1 +0.008 time2 -0.004 10554359Sroberto * 10654359Sroberto * This corrects the 4 milliseconds advance and 8 milliseconds retard 10754359Sroberto * needed. The software will ask the clock which satellite it sees. 10854359Sroberto * 109285612Sdelphij * Notes on the TrueTime TimeLink TL-3 WWV TOD receiver: 110285612Sdelphij * 111285612Sdelphij * This clock may be polled, or send one timecode per second. 112285612Sdelphij * That mode may be toggled via the front panel ("C" mode), or controlled 113285612Sdelphij * from the RS-232 port. Send the receiver "ST1" to turn it on, and 114285612Sdelphij * "ST0" to turn it off. Send "QV" to get the firmware revision (useful 115285612Sdelphij * for identifying this model.) 116285612Sdelphij * 117285612Sdelphij * Note that it can take several polling cycles, especially if the receiver 118285612Sdelphij * was in the continuous timecode mode. (It can be slow to leave that mode.) 119285612Sdelphij * 120285612Sdelphij * ntp.conf parameters: 121285612Sdelphij * time1 - offset applied to samples when reading WEST satellite (default = 0) 122285612Sdelphij * time2 - offset applied to samples when reading EAST satellite (default = 0) 123285612Sdelphij * stratum - stratum to assign to this clock (default = 0) 124285612Sdelphij * refid - refid assigned to this clock (default = "TRUE", see below) 125285612Sdelphij * flag1 - will silence the clock side of ntpd, just reading the clock 126285612Sdelphij * without trying to write to it. (default = 0) 127285612Sdelphij * flag2 - generate a debug file /tmp/true%d. 128285612Sdelphij * flag3 - enable ppsclock streams module 129285612Sdelphij * flag4 - use the PCL-720 (BSD/OS only) 13054359Sroberto */ 13154359Sroberto 13282498Sroberto 13354359Sroberto/* 13454359Sroberto * Definitions 13554359Sroberto */ 13654359Sroberto#define DEVICE "/dev/true%d" 13754359Sroberto#define SPEED232 B9600 /* 9600 baud */ 13854359Sroberto 13954359Sroberto/* 14054359Sroberto * Radio interface parameters 14154359Sroberto */ 14254359Sroberto#define PRECISION (-10) /* precision assumed (about 1 ms) */ 14354359Sroberto#define REFID "TRUE" /* reference id */ 14454359Sroberto#define DESCRIPTION "Kinemetrics/TrueTime Receiver" 14554359Sroberto 14654359Sroberto/* 14754359Sroberto * Tags which station (satellite) we see 14854359Sroberto */ 14954359Sroberto#define GOES_WEST 0 /* Default to WEST satellite and apply time1 */ 15054359Sroberto#define GOES_EAST 1 /* until you discover otherwise */ 15154359Sroberto 15254359Sroberto/* 15354359Sroberto * used by the state machine 15454359Sroberto */ 15554359Srobertoenum true_event {e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite, 156285612Sdelphij e_TL3, e_Poll, e_Location, e_TS, e_Max}; 15754359Srobertoconst char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite", 158285612Sdelphij "TL3", "Poll", "Location", "TS"}; 15954359Sroberto#define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?") 16054359Sroberto 16154359Srobertoenum true_state {s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES, 162285612Sdelphij s_InqTL3, s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max}; 16354359Srobertoconst char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES", 164285612Sdelphij "InqTL3", "Init", "F18", "F50", "Start", "Auto"}; 16554359Sroberto#define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?") 16654359Sroberto 167285612Sdelphijenum true_type {t_unknown, t_goes, t_tm, t_tcu, t_omega, t_tl3, t_Max}; 168285612Sdelphijconst char *types[] = {"unknown", "goes", "tm", "tcu", "omega", "tl3"}; 16954359Sroberto#define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?") 17054359Sroberto 17154359Sroberto/* 17254359Sroberto * unit control structure 17354359Sroberto */ 17454359Srobertostruct true_unit { 17554359Sroberto unsigned int pollcnt; /* poll message counter */ 17654359Sroberto unsigned int station; /* which station we are on */ 17754359Sroberto unsigned int polled; /* Hand in a time sample? */ 17854359Sroberto enum true_state state; /* state machine */ 17954359Sroberto enum true_type type; /* what kind of clock is it? */ 18054359Sroberto int unit; /* save an extra copy of this */ 18154359Sroberto FILE *debug; /* debug logging file */ 18254359Sroberto#ifdef CLOCK_PPS720 18354359Sroberto int pcl720init; /* init flag for PCL 720 */ 18454359Sroberto#endif 18554359Sroberto}; 18654359Sroberto 18754359Sroberto/* 18854359Sroberto * Function prototypes 18954359Sroberto */ 190285612Sdelphijstatic int true_start (int, struct peer *); 191285612Sdelphijstatic void true_shutdown (int, struct peer *); 192285612Sdelphijstatic void true_receive (struct recvbuf *); 193285612Sdelphijstatic void true_poll (int, struct peer *); 194285612Sdelphijstatic void true_send (struct peer *, const char *); 195285612Sdelphijstatic void true_doevent (struct peer *, enum true_event); 19654359Sroberto 19754359Sroberto#ifdef CLOCK_PPS720 198285612Sdelphijstatic u_long true_sample720 (void); 19954359Sroberto#endif 20054359Sroberto 20154359Sroberto/* 20254359Sroberto * Transfer vector 20354359Sroberto */ 20454359Srobertostruct refclock refclock_true = { 20554359Sroberto true_start, /* start up driver */ 20654359Sroberto true_shutdown, /* shut down driver */ 20754359Sroberto true_poll, /* transmit poll message */ 20854359Sroberto noentry, /* not used (old true_control) */ 20954359Sroberto noentry, /* initialize driver (not used) */ 21054359Sroberto noentry, /* not used (old true_buginfo) */ 21154359Sroberto NOFLAGS /* not used */ 21254359Sroberto}; 21354359Sroberto 21454359Sroberto 21554359Sroberto#if !defined(__STDC__) 21654359Sroberto# define true_debug (void) 21754359Sroberto#else 218285612SdelphijNTP_PRINTF(2, 3) 21954359Srobertostatic void 22054359Srobertotrue_debug(struct peer *peer, const char *fmt, ...) 22154359Sroberto{ 22254359Sroberto va_list ap; 22354359Sroberto int want_debugging, now_debugging; 22454359Sroberto struct refclockproc *pp; 22554359Sroberto struct true_unit *up; 22654359Sroberto 22754359Sroberto va_start(ap, fmt); 22854359Sroberto pp = peer->procptr; 229285612Sdelphij up = pp->unitptr; 23054359Sroberto 23154359Sroberto want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0; 23254359Sroberto now_debugging = (up->debug != NULL); 23354359Sroberto if (want_debugging != now_debugging) 23454359Sroberto { 23554359Sroberto if (want_debugging) { 236285612Sdelphij char filename[40]; 237285612Sdelphij int fd; 23854359Sroberto 239285612Sdelphij snprintf(filename, sizeof(filename), 240285612Sdelphij "/tmp/true%d.debug", up->unit); 241285612Sdelphij fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 242285612Sdelphij 0600); 243285612Sdelphij if (fd >= 0 && (up->debug = fdopen(fd, "w"))) { 24454359Sroberto#ifdef HAVE_SETVBUF 245285612Sdelphij static char buf[BUFSIZ]; 246285612Sdelphij 247285612Sdelphij setvbuf(up->debug, buf, _IOLBF, BUFSIZ); 24854359Sroberto#else 249285612Sdelphij setlinebuf(up->debug); 25054359Sroberto#endif 251285612Sdelphij } 252285612Sdelphij } else { 253285612Sdelphij fclose(up->debug); 254285612Sdelphij up->debug = NULL; 255285612Sdelphij } 25654359Sroberto } 25754359Sroberto 25854359Sroberto if (up->debug) { 25954359Sroberto fprintf(up->debug, "true%d: ", up->unit); 26054359Sroberto vfprintf(up->debug, fmt, ap); 26154359Sroberto } 262285612Sdelphij va_end(ap); 26354359Sroberto} 26454359Sroberto#endif /*STDC*/ 26554359Sroberto 26654359Sroberto/* 26754359Sroberto * true_start - open the devices and initialize data for processing 26854359Sroberto */ 26954359Srobertostatic int 27054359Srobertotrue_start( 27154359Sroberto int unit, 27254359Sroberto struct peer *peer 27354359Sroberto ) 27454359Sroberto{ 27554359Sroberto register struct true_unit *up; 27654359Sroberto struct refclockproc *pp; 27782498Sroberto char device[40]; 27854359Sroberto int fd; 27954359Sroberto 28054359Sroberto /* 28154359Sroberto * Open serial port 28254359Sroberto */ 283285612Sdelphij snprintf(device, sizeof(device), DEVICE, unit); 284285612Sdelphij fd = refclock_open(device, SPEED232, LDISC_CLK); 285285612Sdelphij if (fd <= 0) 286285612Sdelphij return 0; 28754359Sroberto 28854359Sroberto /* 28954359Sroberto * Allocate and initialize unit structure 29054359Sroberto */ 291285612Sdelphij up = emalloc_zero(sizeof(*up)); 29254359Sroberto pp = peer->procptr; 29354359Sroberto pp->io.clock_recv = true_receive; 294285612Sdelphij pp->io.srcclock = peer; 29554359Sroberto pp->io.datalen = 0; 29654359Sroberto pp->io.fd = fd; 29754359Sroberto if (!io_addclock(&pp->io)) { 298285612Sdelphij close(fd); 299285612Sdelphij pp->io.fd = -1; 30054359Sroberto free(up); 30154359Sroberto return (0); 30254359Sroberto } 303285612Sdelphij pp->unitptr = up; 30454359Sroberto 30554359Sroberto /* 30654359Sroberto * Initialize miscellaneous variables 30754359Sroberto */ 30854359Sroberto peer->precision = PRECISION; 30954359Sroberto pp->clockdesc = DESCRIPTION; 310285612Sdelphij memcpy(&pp->refid, REFID, 4); 31154359Sroberto up->pollcnt = 2; 31254359Sroberto up->type = t_unknown; 31354359Sroberto up->state = s_Base; 314285612Sdelphij 315285612Sdelphij /* 316285612Sdelphij * Send a CTRL-C character at the start, 317285612Sdelphij * just in case the clock is already 318285612Sdelphij * sending timecodes 319285612Sdelphij */ 320285612Sdelphij true_send(peer, "\03\r"); 321285612Sdelphij 32254359Sroberto true_doevent(peer, e_Init); 323285612Sdelphij 32454359Sroberto return (1); 32554359Sroberto} 32654359Sroberto 327285612Sdelphij 32854359Sroberto/* 32954359Sroberto * true_shutdown - shut down the clock 33054359Sroberto */ 33154359Srobertostatic void 33254359Srobertotrue_shutdown( 33354359Sroberto int unit, 33454359Sroberto struct peer *peer 33554359Sroberto ) 33654359Sroberto{ 33754359Sroberto register struct true_unit *up; 33854359Sroberto struct refclockproc *pp; 33954359Sroberto 34054359Sroberto pp = peer->procptr; 341285612Sdelphij up = pp->unitptr; 342285612Sdelphij if (pp->io.fd != -1) 343285612Sdelphij io_closeclock(&pp->io); 344285612Sdelphij if (up != NULL) 345285612Sdelphij free(up); 34654359Sroberto} 34754359Sroberto 34854359Sroberto 34954359Sroberto/* 35054359Sroberto * true_receive - receive data from the serial interface on a clock 35154359Sroberto */ 35254359Srobertostatic void 35354359Srobertotrue_receive( 35454359Sroberto struct recvbuf *rbufp 35554359Sroberto ) 35654359Sroberto{ 35754359Sroberto register struct true_unit *up; 35854359Sroberto struct refclockproc *pp; 35954359Sroberto struct peer *peer; 36054359Sroberto u_short new_station; 36154359Sroberto char synced; 36254359Sroberto int i; 36354359Sroberto int lat, lon, off; /* GOES Satellite position */ 364285612Sdelphij /* These variables hold data until we decide to keep it */ 365285612Sdelphij char rd_lastcode[BMAX]; 366285612Sdelphij l_fp rd_tmp; 367285612Sdelphij u_short rd_lencode; 36854359Sroberto 36954359Sroberto /* 37054359Sroberto * Get the clock this applies to and pointers to the data. 37154359Sroberto */ 372285612Sdelphij peer = rbufp->recv_peer; 37354359Sroberto pp = peer->procptr; 374285612Sdelphij up = pp->unitptr; 37554359Sroberto 37654359Sroberto /* 37754359Sroberto * Read clock output. Automatically handles STREAMS, CLKLDISC. 37854359Sroberto */ 379285612Sdelphij rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp); 380285612Sdelphij rd_lastcode[rd_lencode] = '\0'; 38154359Sroberto 38254359Sroberto /* 38354359Sroberto * There is a case where <cr><lf> generates 2 timestamps. 38454359Sroberto */ 385285612Sdelphij if (rd_lencode == 0) 386285612Sdelphij return; 387285612Sdelphij pp->lencode = rd_lencode; 388285612Sdelphij strlcpy(pp->a_lastcode, rd_lastcode, sizeof(pp->a_lastcode)); 389285612Sdelphij pp->lastrec = rd_tmp; 390285612Sdelphij true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode, 391285612Sdelphij pp->lencode); 39254359Sroberto 39354359Sroberto up->pollcnt = 2; 39454359Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 39554359Sroberto 39654359Sroberto /* 39754359Sroberto * We get down to business, check the timecode format and decode 39854359Sroberto * its contents. This code decodes a multitude of different 39954359Sroberto * clock messages. Timecodes are processed if needed. All replies 40054359Sroberto * will be run through the state machine to tweak driver options 40154359Sroberto * and program the clock. 40254359Sroberto */ 40354359Sroberto 40454359Sroberto /* 40554359Sroberto * Clock misunderstood our last command? 40654359Sroberto */ 40782498Sroberto if (pp->a_lastcode[0] == '?' || 40882498Sroberto strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) { 40954359Sroberto true_doevent(peer, e_Huh); 41054359Sroberto return; 41154359Sroberto } 41254359Sroberto 41354359Sroberto /* 41454359Sroberto * Timecode: "nnnnn+nnn-nnn" 41554359Sroberto * (from GOES clock when asked about satellite position) 41654359Sroberto */ 41754359Sroberto if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') && 41854359Sroberto (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') && 41954359Sroberto sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3 42054359Sroberto ) { 42154359Sroberto const char *label = "Botch!"; 42254359Sroberto 42354359Sroberto /* 42454359Sroberto * This is less than perfect. Call the (satellite) 42554359Sroberto * either EAST or WEST and adjust slop accodingly 42654359Sroberto * Perfectionists would recalculate the exact delay 42754359Sroberto * and adjust accordingly... 42854359Sroberto */ 42954359Sroberto if (lon > 7000 && lon < 14000) { 43054359Sroberto if (lon < 10000) { 43154359Sroberto new_station = GOES_EAST; 43254359Sroberto label = "EAST"; 43354359Sroberto } else { 43454359Sroberto new_station = GOES_WEST; 43554359Sroberto label = "WEST"; 43654359Sroberto } 43754359Sroberto 43854359Sroberto if (new_station != up->station) { 43954359Sroberto double dtemp; 44054359Sroberto 44154359Sroberto dtemp = pp->fudgetime1; 44254359Sroberto pp->fudgetime1 = pp->fudgetime2; 44354359Sroberto pp->fudgetime2 = dtemp; 44454359Sroberto up->station = new_station; 44554359Sroberto } 44654359Sroberto } 44754359Sroberto else { 448106163Sroberto /*refclock_report(peer, CEVNT_BADREPLY);*/ 44954359Sroberto label = "UNKNOWN"; 45054359Sroberto } 45154359Sroberto true_debug(peer, "GOES: station %s\n", label); 45254359Sroberto true_doevent(peer, e_Satellite); 45354359Sroberto return; 45454359Sroberto } 45554359Sroberto 45654359Sroberto /* 45754359Sroberto * Timecode: "Fnn" 45854359Sroberto * (from TM/TMD clock when it wants to tell us what it's up to.) 45954359Sroberto */ 46054359Sroberto if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) { 46154359Sroberto switch (i) { 462285612Sdelphij case 50: 46354359Sroberto true_doevent(peer, e_F50); 46454359Sroberto break; 465285612Sdelphij case 51: 46654359Sroberto true_doevent(peer, e_F51); 46754359Sroberto break; 468285612Sdelphij default: 46954359Sroberto true_debug(peer, "got F%02d - ignoring\n", i); 47054359Sroberto break; 47154359Sroberto } 47254359Sroberto return; 47354359Sroberto } 47454359Sroberto 475285612Sdelphij /* 476285612Sdelphij * Timecode: "VER xx.xx" 477285612Sdelphij * (from a TL3 when sent "QV", so id's it during initialization.) 478285612Sdelphij */ 479285612Sdelphij if (pp->a_lastcode[0] == 'V' && pp->a_lastcode[1] == 'E' && 480285612Sdelphij pp->a_lastcode[2] == 'R' && pp->a_lastcode[6] == '.') { 481285612Sdelphij true_doevent(peer, e_TL3); 482285612Sdelphij NLOG(NLOG_CLOCKSTATUS) { 483285612Sdelphij msyslog(LOG_INFO, "TL3: %s", pp->a_lastcode); 484285612Sdelphij } 485285612Sdelphij return; 486285612Sdelphij } 487285612Sdelphij 48854359Sroberto /* 48982498Sroberto * Timecode: " TRUETIME Mk III" or " TRUETIME XL" 49082498Sroberto * (from a TM/TMD/XL clock during initialization.) 49154359Sroberto */ 492285612Sdelphij if (strncmp(pp->a_lastcode, " TRUETIME Mk III ", 17) == 0 || 49382498Sroberto strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) { 49454359Sroberto true_doevent(peer, e_F18); 49554359Sroberto NLOG(NLOG_CLOCKSTATUS) { 49682498Sroberto msyslog(LOG_INFO, "TM/TMD/XL: %s", pp->a_lastcode); 49754359Sroberto } 49854359Sroberto return; 49954359Sroberto } 50054359Sroberto 50154359Sroberto /* 50254359Sroberto * Timecode: "N03726428W12209421+000033" 503285612Sdelphij * 1 2 504285612Sdelphij * index 0123456789012345678901234 50554359Sroberto * (from a TCU during initialization) 50654359Sroberto */ 50754359Sroberto if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') && 50854359Sroberto (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') && 50954359Sroberto pp->a_lastcode[18] == '+') { 51054359Sroberto true_doevent(peer, e_Location); 51154359Sroberto NLOG(NLOG_CLOCKSTATUS) { 51254359Sroberto msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode); 51354359Sroberto } 51454359Sroberto return; 51554359Sroberto } 51654359Sroberto /* 51754359Sroberto * Timecode: "ddd:hh:mm:ssQ" 518285612Sdelphij * 1 2 519285612Sdelphij * index 0123456789012345678901234 52054359Sroberto * (from all clocks supported by this driver.) 52154359Sroberto */ 52254359Sroberto if (pp->a_lastcode[3] == ':' && 52354359Sroberto pp->a_lastcode[6] == ':' && 52454359Sroberto pp->a_lastcode[9] == ':' && 52554359Sroberto sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c", 52654359Sroberto &pp->day, &pp->hour, &pp->minute, 52754359Sroberto &pp->second, &synced) == 5) { 52854359Sroberto 52954359Sroberto /* 53054359Sroberto * Adjust the synchronize indicator according to timecode 53182498Sroberto * say were OK, and then say not if we really are not OK 53254359Sroberto */ 533285612Sdelphij if (synced == '>' || synced == '#' || synced == '?' 534285612Sdelphij || synced == 'X') 535285612Sdelphij pp->leap = LEAP_NOTINSYNC; 53682498Sroberto else 537285612Sdelphij pp->leap = LEAP_NOWARNING; 53854359Sroberto 53954359Sroberto true_doevent(peer, e_TS); 54054359Sroberto 54154359Sroberto#ifdef CLOCK_PPS720 54254359Sroberto /* If it's taken more than 65ms to get here, we'll lose. */ 54354359Sroberto if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) { 54454359Sroberto l_fp off; 54554359Sroberto 54654359Sroberto#ifdef CLOCK_ATOM 54754359Sroberto /* 54854359Sroberto * find out what time it really is. Include 54954359Sroberto * the count from the PCL720 55054359Sroberto */ 55154359Sroberto if (!clocktime(pp->day, pp->hour, pp->minute, 55254359Sroberto pp->second, GMT, pp->lastrec.l_ui, 55354359Sroberto &pp->yearstart, &off.l_ui)) { 55454359Sroberto refclock_report(peer, CEVNT_BADTIME); 55554359Sroberto return; 55654359Sroberto } 557132451Sroberto off.l_uf = 0; 55854359Sroberto#endif 55954359Sroberto 56054359Sroberto pp->usec = true_sample720(); 56154359Sroberto#ifdef CLOCK_ATOM 56254359Sroberto TVUTOTSF(pp->usec, off.l_uf); 56354359Sroberto#endif 56454359Sroberto 56554359Sroberto /* 56654359Sroberto * Stomp all over the timestamp that was pulled out 56754359Sroberto * of the input stream. It's irrelevant since we've 56854359Sroberto * adjusted the input time to reflect now (via pp->usec) 56954359Sroberto * rather than when the data was collected. 57054359Sroberto */ 57154359Sroberto get_systime(&pp->lastrec); 57254359Sroberto#ifdef CLOCK_ATOM 57354359Sroberto /* 57454359Sroberto * Create a true offset for feeding to pps_sample() 57554359Sroberto */ 57654359Sroberto L_SUB(&off, &pp->lastrec); 57754359Sroberto 57854359Sroberto pps_sample(peer, &off); 57954359Sroberto#endif 58054359Sroberto true_debug(peer, "true_sample720: %luus\n", pp->usec); 58154359Sroberto } 58254359Sroberto#endif 58354359Sroberto 58454359Sroberto /* 58554359Sroberto * The clock will blurt a timecode every second but we only 58654359Sroberto * want one when polled. If we havn't been polled, bail out. 58754359Sroberto */ 58854359Sroberto if (!up->polled) 589285612Sdelphij return; 59054359Sroberto 591285612Sdelphij /* We only call doevent if additional things need be done 592285612Sdelphij * at poll interval. Currently, its only for GOES. We also 593285612Sdelphij * call it for clock unknown so that it gets logged. 594285612Sdelphij */ 595285612Sdelphij if (up->type == t_goes || up->type == t_unknown) 596285612Sdelphij true_doevent(peer, e_Poll); 597285612Sdelphij 59854359Sroberto if (!refclock_process(pp)) { 59954359Sroberto refclock_report(peer, CEVNT_BADTIME); 60054359Sroberto return; 60154359Sroberto } 60282498Sroberto /* 60382498Sroberto * If clock is good we send a NOMINAL message so that 60482498Sroberto * any previous BAD messages are nullified 60582498Sroberto */ 606285612Sdelphij pp->lastref = pp->lastrec; 60754359Sroberto refclock_receive(peer); 608132451Sroberto refclock_report(peer, CEVNT_NOMINAL); 60954359Sroberto 61054359Sroberto /* 61154359Sroberto * We have succedded in answering the poll. 61254359Sroberto * Turn off the flag and return 61354359Sroberto */ 61454359Sroberto up->polled = 0; 61554359Sroberto 61654359Sroberto return; 61754359Sroberto } 61854359Sroberto 61954359Sroberto /* 62054359Sroberto * No match to known timecodes, report failure and return 62154359Sroberto */ 62254359Sroberto refclock_report(peer, CEVNT_BADREPLY); 62354359Sroberto return; 62454359Sroberto} 62554359Sroberto 62654359Sroberto 62754359Sroberto/* 62854359Sroberto * true_send - time to send the clock a signal to cough up a time sample 62954359Sroberto */ 63054359Srobertostatic void 63154359Srobertotrue_send( 63254359Sroberto struct peer *peer, 63354359Sroberto const char *cmd 63454359Sroberto ) 63554359Sroberto{ 63654359Sroberto struct refclockproc *pp; 63754359Sroberto 63854359Sroberto pp = peer->procptr; 63954359Sroberto if (!(pp->sloppyclockflag & CLK_FLAG1)) { 640293650Sglebius size_t len = strlen(cmd); 64154359Sroberto 64254359Sroberto true_debug(peer, "Send '%s'\n", cmd); 64354359Sroberto if (write(pp->io.fd, cmd, (unsigned)len) != len) 644285612Sdelphij refclock_report(peer, CEVNT_FAULT); 64554359Sroberto else 646285612Sdelphij pp->polls++; 64754359Sroberto } 64854359Sroberto} 64954359Sroberto 65054359Sroberto 65154359Sroberto/* 65254359Sroberto * state machine for initializing and controlling a clock 65354359Sroberto */ 65454359Srobertostatic void 65554359Srobertotrue_doevent( 65654359Sroberto struct peer *peer, 65754359Sroberto enum true_event event 65854359Sroberto ) 65954359Sroberto{ 66054359Sroberto struct true_unit *up; 66154359Sroberto struct refclockproc *pp; 66254359Sroberto 66354359Sroberto pp = peer->procptr; 664285612Sdelphij up = pp->unitptr; 66554359Sroberto if (event != e_TS) { 66654359Sroberto NLOG(NLOG_CLOCKSTATUS) { 66754359Sroberto msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s", 66854359Sroberto typeStr(up->type), 66954359Sroberto stateStr(up->state), 67054359Sroberto eventStr(event)); 67154359Sroberto } 67254359Sroberto } 67354359Sroberto true_debug(peer, "clock %s, state %s, event %s\n", 67454359Sroberto typeStr(up->type), stateStr(up->state), eventStr(event)); 67554359Sroberto switch (up->type) { 676285612Sdelphij case t_goes: 67754359Sroberto switch (event) { 678285612Sdelphij case e_Init: /* FALLTHROUGH */ 679285612Sdelphij case e_Satellite: 68054359Sroberto /* 68154359Sroberto * Switch back to on-second time codes and return. 68254359Sroberto */ 68354359Sroberto true_send(peer, "C"); 68454359Sroberto up->state = s_Start; 68554359Sroberto break; 686285612Sdelphij case e_Poll: 68754359Sroberto /* 68854359Sroberto * After each poll, check the station (satellite). 68954359Sroberto */ 69054359Sroberto true_send(peer, "P"); 69154359Sroberto /* No state change needed. */ 69254359Sroberto break; 693285612Sdelphij default: 69454359Sroberto break; 69554359Sroberto } 69654359Sroberto /* FALLTHROUGH */ 697285612Sdelphij case t_omega: 69854359Sroberto switch (event) { 699285612Sdelphij case e_Init: 70054359Sroberto true_send(peer, "C"); 70154359Sroberto up->state = s_Start; 70254359Sroberto break; 703285612Sdelphij case e_TS: 70454359Sroberto if (up->state != s_Start && up->state != s_Auto) { 70554359Sroberto true_send(peer, "\03\r"); 70654359Sroberto break; 70754359Sroberto } 70854359Sroberto up->state = s_Auto; 70954359Sroberto break; 710285612Sdelphij default: 71154359Sroberto break; 71254359Sroberto } 71354359Sroberto break; 714285612Sdelphij case t_tm: 71554359Sroberto switch (event) { 716285612Sdelphij case e_Init: 71754359Sroberto true_send(peer, "F18\r"); 71854359Sroberto up->state = s_Init; 71954359Sroberto break; 720285612Sdelphij case e_F18: 72154359Sroberto true_send(peer, "F50\r"); 722285612Sdelphij /* 723285612Sdelphij * Timecode: " TRUETIME Mk III" or " TRUETIME XL" 724285612Sdelphij * (from a TM/TMD/XL clock during initialization.) 725285612Sdelphij */ 726285612Sdelphij if ( strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 || 727285612Sdelphij strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) { 728285612Sdelphij true_doevent(peer, e_F18); 729285612Sdelphij NLOG(NLOG_CLOCKSTATUS) { 730285612Sdelphij msyslog(LOG_INFO, "TM/TMD/XL: %s", 731285612Sdelphij pp->a_lastcode); 732285612Sdelphij } 733285612Sdelphij return; 734285612Sdelphij } 73554359Sroberto up->state = s_F18; 73654359Sroberto break; 737285612Sdelphij case e_F50: 73854359Sroberto true_send(peer, "F51\r"); 73954359Sroberto up->state = s_F50; 74054359Sroberto break; 741285612Sdelphij case e_F51: 74254359Sroberto true_send(peer, "F08\r"); 74354359Sroberto up->state = s_Start; 74454359Sroberto break; 745285612Sdelphij case e_TS: 74654359Sroberto if (up->state != s_Start && up->state != s_Auto) { 74754359Sroberto true_send(peer, "\03\r"); 74854359Sroberto break; 74954359Sroberto } 75054359Sroberto up->state = s_Auto; 75154359Sroberto break; 752285612Sdelphij default: 75354359Sroberto break; 75454359Sroberto } 75554359Sroberto break; 756285612Sdelphij case t_tcu: 75754359Sroberto switch (event) { 758285612Sdelphij case e_Init: 75954359Sroberto true_send(peer, "MD3\r"); /* GPS Synch'd Gen. */ 76054359Sroberto true_send(peer, "TSU\r"); /* UTC, not GPS. */ 76154359Sroberto true_send(peer, "AU\r"); /* Auto Timestamps. */ 76254359Sroberto up->state = s_Start; 76354359Sroberto break; 764285612Sdelphij case e_TS: 76554359Sroberto if (up->state != s_Start && up->state != s_Auto) { 76654359Sroberto true_send(peer, "\03\r"); 76754359Sroberto break; 76854359Sroberto } 76954359Sroberto up->state = s_Auto; 77054359Sroberto break; 771285612Sdelphij default: 77254359Sroberto break; 77354359Sroberto } 77454359Sroberto break; 775285612Sdelphij case t_tl3: 776285612Sdelphij switch (event) { 777285612Sdelphij case e_Init: 778285612Sdelphij true_send(peer, "ST1"); /* Turn on continuous stream */ 779285612Sdelphij break; 780285612Sdelphij case e_TS: 781285612Sdelphij up->state = s_Auto; 782285612Sdelphij break; 783285612Sdelphij default: 784285612Sdelphij break; 785285612Sdelphij } 786285612Sdelphij break; 787285612Sdelphij case t_unknown: 788285612Sdelphij if (event == e_Poll) 789285612Sdelphij break; 79054359Sroberto switch (up->state) { 791285612Sdelphij case s_Base: 79254359Sroberto if (event != e_Init) 79354359Sroberto abort(); 79454359Sroberto true_send(peer, "P\r"); 79554359Sroberto up->state = s_InqGOES; 79654359Sroberto break; 797285612Sdelphij case s_InqGOES: 79854359Sroberto switch (event) { 799285612Sdelphij case e_Satellite: 80054359Sroberto up->type = t_goes; 80154359Sroberto true_doevent(peer, e_Init); 80254359Sroberto break; 803285612Sdelphij case e_Init: /*FALLTHROUGH*/ 804285612Sdelphij case e_Huh: 805285612Sdelphij case e_TS: 806285612Sdelphij true_send(peer, "ST0"); /* turn off TL3 auto */ 807285612Sdelphij sleep(1); /* wait for it */ 808285612Sdelphij up->state = s_InqTL3; 809285612Sdelphij true_send(peer, "QV"); /* see if its a TL3 */ 810285612Sdelphij break; 811285612Sdelphij default: 812285612Sdelphij abort(); 813285612Sdelphij } 814285612Sdelphij break; 815285612Sdelphij case s_InqTL3: 816285612Sdelphij switch (event) { 817285612Sdelphij case e_TL3: 818285612Sdelphij up->type = t_tl3; 819285612Sdelphij up->state = s_Auto; /* Inq side-effect. */ 820285612Sdelphij true_send(peer, "ST1"); /* Turn on 1/sec data */ 821285612Sdelphij break; 822285612Sdelphij case e_Init: /*FALLTHROUGH*/ 823285612Sdelphij case e_Huh: 82454359Sroberto up->state = s_InqOmega; 82554359Sroberto true_send(peer, "C\r"); 82654359Sroberto break; 827285612Sdelphij case e_TS: 828285612Sdelphij up->type = t_tl3; /* Already sending data */ 829285612Sdelphij up->state = s_Auto; 830285612Sdelphij break; 83154359Sroberto default: 832285612Sdelphij msyslog(LOG_INFO, 833285612Sdelphij "TRUE: TL3 init fellthrough! (%d)", event); 834285612Sdelphij break; 83554359Sroberto } 83654359Sroberto break; 837285612Sdelphij case s_InqOmega: 83854359Sroberto switch (event) { 839285612Sdelphij case e_TS: 84054359Sroberto up->type = t_omega; 84154359Sroberto up->state = s_Auto; /* Inq side-effect. */ 84254359Sroberto break; 843285612Sdelphij case e_Init: /*FALLTHROUGH*/ 844285612Sdelphij case e_Huh: 84554359Sroberto up->state = s_InqTM; 84654359Sroberto true_send(peer, "F18\r"); 84754359Sroberto break; 848285612Sdelphij default: 84954359Sroberto abort(); 85054359Sroberto } 85154359Sroberto break; 852285612Sdelphij case s_InqTM: 85354359Sroberto switch (event) { 854285612Sdelphij case e_F18: 85554359Sroberto up->type = t_tm; 85654359Sroberto true_doevent(peer, e_Init); 85754359Sroberto break; 858285612Sdelphij case e_Init: /*FALLTHROUGH*/ 859285612Sdelphij case e_Huh: 86054359Sroberto true_send(peer, "PO\r"); 86154359Sroberto up->state = s_InqTCU; 86254359Sroberto break; 863285612Sdelphij default: 864285612Sdelphij msyslog(LOG_INFO, 865285612Sdelphij "TRUE: TM/TMD init fellthrough!"); 866285612Sdelphij break; 86754359Sroberto } 86854359Sroberto break; 869285612Sdelphij case s_InqTCU: 87054359Sroberto switch (event) { 871285612Sdelphij case e_Location: 87254359Sroberto up->type = t_tcu; 87354359Sroberto true_doevent(peer, e_Init); 87454359Sroberto break; 875285612Sdelphij case e_Init: /*FALLTHROUGH*/ 876285612Sdelphij case e_Huh: 87754359Sroberto up->state = s_Base; 87854359Sroberto sleep(1); /* XXX */ 87954359Sroberto break; 880285612Sdelphij default: 881285612Sdelphij msyslog(LOG_INFO, 882285612Sdelphij "TRUE: TCU init fellthrough!"); 883285612Sdelphij break; 88454359Sroberto } 88554359Sroberto break; 88654359Sroberto /* 88754359Sroberto * An expedient hack to prevent lint complaints, 88854359Sroberto * these don't actually need to be used here... 88954359Sroberto */ 890285612Sdelphij case s_Init: 891285612Sdelphij case s_F18: 892285612Sdelphij case s_F50: 893285612Sdelphij case s_Start: 894285612Sdelphij case s_Auto: 895285612Sdelphij case s_Max: 896285612Sdelphij msyslog(LOG_INFO, "TRUE: state %s is unexpected!", 897285612Sdelphij stateStr(up->state)); 89854359Sroberto } 89954359Sroberto break; 900285612Sdelphij default: 901285612Sdelphij msyslog(LOG_INFO, "TRUE: cannot identify refclock!"); 902285612Sdelphij abort(); 90354359Sroberto /* NOTREACHED */ 90454359Sroberto } 90554359Sroberto 90654359Sroberto#ifdef CLOCK_PPS720 90754359Sroberto if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) { 90854359Sroberto /* Make counter trigger on gate0, count down from 65535. */ 90954359Sroberto pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535); 91054359Sroberto /* 91154359Sroberto * (These constants are OK since 91254359Sroberto * they represent hardware maximums.) 91354359Sroberto */ 91454359Sroberto NLOG(NLOG_CLOCKINFO) { 91554359Sroberto msyslog(LOG_NOTICE, "PCL-720 initialized"); 91654359Sroberto } 91754359Sroberto up->pcl720init++; 91854359Sroberto } 91954359Sroberto#endif 92054359Sroberto 92154359Sroberto 92254359Sroberto} 92354359Sroberto 92454359Sroberto/* 92554359Sroberto * true_poll - called by the transmit procedure 92654359Sroberto */ 92754359Srobertostatic void 92854359Srobertotrue_poll( 92954359Sroberto int unit, 93054359Sroberto struct peer *peer 93154359Sroberto ) 93254359Sroberto{ 93354359Sroberto struct true_unit *up; 93454359Sroberto struct refclockproc *pp; 93554359Sroberto 93654359Sroberto /* 93754359Sroberto * You don't need to poll this clock. It puts out timecodes 93854359Sroberto * once per second. If asked for a timestamp, take note. 93954359Sroberto * The next time a timecode comes in, it will be fed back. 94054359Sroberto */ 94154359Sroberto pp = peer->procptr; 942285612Sdelphij up = pp->unitptr; 943285612Sdelphij if (up->pollcnt > 0) { 944285612Sdelphij up->pollcnt--; 945285612Sdelphij } else { 94654359Sroberto true_doevent(peer, e_Init); 94754359Sroberto refclock_report(peer, CEVNT_TIMEOUT); 94854359Sroberto } 94954359Sroberto 95054359Sroberto /* 95154359Sroberto * polled every 64 seconds. Ask true_receive to hand in a 95254359Sroberto * timestamp. 95354359Sroberto */ 95454359Sroberto up->polled = 1; 95554359Sroberto pp->polls++; 95654359Sroberto} 95754359Sroberto 95854359Sroberto#ifdef CLOCK_PPS720 95954359Sroberto/* 96054359Sroberto * true_sample720 - sample the PCL-720 96154359Sroberto */ 96254359Srobertostatic u_long 96354359Srobertotrue_sample720(void) 96454359Sroberto{ 96554359Sroberto unsigned long f; 96654359Sroberto 96754359Sroberto /* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3. 96854359Sroberto * If it is not being held low now, we did not get called 96954359Sroberto * within 65535us. 97054359Sroberto */ 97154359Sroberto if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) { 97254359Sroberto NLOG(NLOG_CLOCKINFO) { 97354359Sroberto msyslog(LOG_NOTICE, "PCL-720 out of synch"); 97454359Sroberto } 97554359Sroberto return (0); 97654359Sroberto } 97754359Sroberto f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR)); 97854359Sroberto#ifdef PPS720_DEBUG 97954359Sroberto msyslog(LOG_DEBUG, "PCL-720: %luus", f); 98054359Sroberto#endif 98154359Sroberto return (f); 98254359Sroberto} 98354359Sroberto#endif 98454359Sroberto 98554359Sroberto#else 98654359Srobertoint refclock_true_bs; 98754359Sroberto#endif /* REFCLOCK */ 988