refclock_true.c revision 54359
154359Sroberto/* 254359Sroberto * refclock_true - clock driver for the Kinemetrics Truetime receivers 354359Sroberto * Receiver Version 3.0C - tested plain, with CLKLDISC 454359Sroberto * Developement work being done: 554359Sroberto * - Properly handle varying satellite positions (more acurately) 654359Sroberto * - Integrate GPSTM and/or OMEGA and/or TRAK and/or ??? drivers 754359Sroberto */ 854359Sroberto 954359Sroberto#ifdef HAVE_CONFIG_H 1054359Sroberto#include <config.h> 1154359Sroberto#endif 1254359Sroberto 1354359Sroberto#if defined(REFCLOCK) && defined(CLOCK_TRUETIME) 1454359Sroberto 1554359Sroberto#include <stdio.h> 1654359Sroberto#include <ctype.h> 1754359Sroberto#include <sys/time.h> 1854359Sroberto 1954359Sroberto#include "ntpd.h" 2054359Sroberto#include "ntp_io.h" 2154359Sroberto#include "ntp_refclock.h" 2254359Sroberto#include "ntp_unixtime.h" 2354359Sroberto#include "ntp_stdlib.h" 2454359Sroberto 2554359Sroberto/* This should be an atom clock but those are very hard to build. 2654359Sroberto * 2754359Sroberto * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch 2854359Sroberto * of TTL input and output pins, all brought out to the back panel. If you 2954359Sroberto * wire a PPS signal (such as the TTL PPS coming out of a GOES or other 3054359Sroberto * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the 3154359Sroberto * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the 3254359Sroberto * number of uSecs since the last PPS upward swing, mediated by reading OUT0 3354359Sroberto * to find out if the counter has wrapped around (this happens if more than 3454359Sroberto * 65535us (65ms) elapses between the PPS event and our being called.) 3554359Sroberto */ 3654359Sroberto#ifdef CLOCK_PPS720 3754359Sroberto# undef min /* XXX */ 3854359Sroberto# undef max /* XXX */ 3954359Sroberto# include <machine/inline.h> 4054359Sroberto# include <sys/pcl720.h> 4154359Sroberto# include <sys/i8253.h> 4254359Sroberto# define PCL720_IOB 0x2a0 /* XXX */ 4354359Sroberto# define PCL720_CTR 0 /* XXX */ 4454359Sroberto#endif 4554359Sroberto 4654359Sroberto/* 4754359Sroberto * Support for Kinemetrics Truetime Receivers 4854359Sroberto * GOES 4954359Sroberto * GPS/TM-TMD 5054359Sroberto * XL-DC (a 151-602-210, reported by the driver as a GPS/TM-TMD) 5154359Sroberto * GPS-800 TCU (an 805-957 with the RS232 Talker/Listener module) 5254359Sroberto * OM-DC: getting stale ("OMEGA") 5354359Sroberto * 5454359Sroberto * Most of this code is originally from refclock_wwvb.c with thanks. 5554359Sroberto * It has been so mangled that wwvb is not a recognizable ancestor. 5654359Sroberto * 5754359Sroberto * Timcode format: ADDD:HH:MM:SSQCL 5854359Sroberto * A - control A (this is stripped before we see it) 5954359Sroberto * Q - Quality indication (see below) 6054359Sroberto * C - Carriage return 6154359Sroberto * L - Line feed 6254359Sroberto * 6354359Sroberto * Quality codes indicate possible error of 6454359Sroberto * 468-DC GOES Receiver: 6554359Sroberto * GPS-TM/TMD Receiver: 6654359Sroberto * ? +/- 500 milliseconds # +/- 50 milliseconds 6754359Sroberto * * +/- 5 milliseconds . +/- 1 millisecond 6854359Sroberto * space less than 1 millisecond 6954359Sroberto * OM-DC OMEGA Receiver: 7054359Sroberto * > >+- 5 seconds 7154359Sroberto * ? >+/- 500 milliseconds # >+/- 50 milliseconds 7254359Sroberto * * >+/- 5 milliseconds . >+/- 1 millisecond 7354359Sroberto * A-H less than 1 millisecond. Character indicates which station 7454359Sroberto * is being received as follows: 7554359Sroberto * A = Norway, B = Liberia, C = Hawaii, D = North Dakota, 7654359Sroberto * E = La Reunion, F = Argentina, G = Australia, H = Japan. 7754359Sroberto * 7854359Sroberto * The carriage return start bit begins on 0 seconds and extends to 1 bit time. 7954359Sroberto * 8054359Sroberto * Notes on 468-DC and OMEGA receiver: 8154359Sroberto * 8254359Sroberto * Send the clock a 'R' or 'C' and once per second a timestamp will 8354359Sroberto * appear. Send a 'P' to get the satellite position once (GOES only.) 8454359Sroberto * 8554359Sroberto * Notes on the 468-DC receiver: 8654359Sroberto * 8754359Sroberto * Since the old east/west satellite locations are only historical, you can't 8854359Sroberto * set your clock propagation delay settings correctly and still use 8954359Sroberto * automatic mode. The manual says to use a compromise when setting the 9054359Sroberto * switches. This results in significant errors. The solution; use fudge 9154359Sroberto * time1 and time2 to incorporate corrections. If your clock is set for 9254359Sroberto * 50 and it should be 58 for using the west and 46 for using the east, 9354359Sroberto * use the line 9454359Sroberto * 9554359Sroberto * fudge 127.127.5.0 time1 +0.008 time2 -0.004 9654359Sroberto * 9754359Sroberto * This corrects the 4 milliseconds advance and 8 milliseconds retard 9854359Sroberto * needed. The software will ask the clock which satellite it sees. 9954359Sroberto * 10054359Sroberto * Ntp.conf parameters: 10154359Sroberto * time1 - offset applied to samples when reading WEST satellite (default = 0) 10254359Sroberto * time2 - offset applied to samples when reading EAST satellite (default = 0) 10354359Sroberto * val1 - stratum to assign to this clock (default = 0) 10454359Sroberto * val2 - refid assigned to this clock (default = "TRUE", see below) 10554359Sroberto * flag1 - will silence the clock side of ntpd, just reading the clock 10654359Sroberto * without trying to write to it. (default = 0) 10754359Sroberto * flag2 - generate a debug file /tmp/true%d. 10854359Sroberto * flag3 - enable ppsclock streams module 10954359Sroberto * flag4 - use the PCL-720 (BSD/OS only) 11054359Sroberto */ 11154359Sroberto 11254359Sroberto/* 11354359Sroberto * Definitions 11454359Sroberto */ 11554359Sroberto#define DEVICE "/dev/true%d" 11654359Sroberto#define SPEED232 B9600 /* 9600 baud */ 11754359Sroberto 11854359Sroberto/* 11954359Sroberto * Radio interface parameters 12054359Sroberto */ 12154359Sroberto#define PRECISION (-10) /* precision assumed (about 1 ms) */ 12254359Sroberto#define REFID "TRUE" /* reference id */ 12354359Sroberto#define DESCRIPTION "Kinemetrics/TrueTime Receiver" 12454359Sroberto 12554359Sroberto/* 12654359Sroberto * Tags which station (satellite) we see 12754359Sroberto */ 12854359Sroberto#define GOES_WEST 0 /* Default to WEST satellite and apply time1 */ 12954359Sroberto#define GOES_EAST 1 /* until you discover otherwise */ 13054359Sroberto 13154359Sroberto/* 13254359Sroberto * used by the state machine 13354359Sroberto */ 13454359Srobertoenum true_event {e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite, 13554359Sroberto e_Poll, e_Location, e_TS, e_Max}; 13654359Srobertoconst char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite", 13754359Sroberto "Poll", "Location", "TS"}; 13854359Sroberto#define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?") 13954359Sroberto 14054359Srobertoenum true_state {s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES, 14154359Sroberto s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max}; 14254359Srobertoconst char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES", 14354359Sroberto "Init", "F18", "F50", "Start", "Auto"}; 14454359Sroberto#define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?") 14554359Sroberto 14654359Srobertoenum true_type {t_unknown, t_goes, t_tm, t_tcu, t_omega, t_Max}; 14754359Srobertoconst char *types[] = {"unknown", "goes", "tm", "tcu", "omega"}; 14854359Sroberto#define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?") 14954359Sroberto 15054359Sroberto/* 15154359Sroberto * unit control structure 15254359Sroberto */ 15354359Srobertostruct true_unit { 15454359Sroberto unsigned int pollcnt; /* poll message counter */ 15554359Sroberto unsigned int station; /* which station we are on */ 15654359Sroberto unsigned int polled; /* Hand in a time sample? */ 15754359Sroberto enum true_state state; /* state machine */ 15854359Sroberto enum true_type type; /* what kind of clock is it? */ 15954359Sroberto int unit; /* save an extra copy of this */ 16054359Sroberto FILE *debug; /* debug logging file */ 16154359Sroberto#ifdef CLOCK_PPS720 16254359Sroberto int pcl720init; /* init flag for PCL 720 */ 16354359Sroberto#endif 16454359Sroberto}; 16554359Sroberto 16654359Sroberto/* 16754359Sroberto * Function prototypes 16854359Sroberto */ 16954359Srobertostatic int true_start P((int, struct peer *)); 17054359Srobertostatic void true_shutdown P((int, struct peer *)); 17154359Srobertostatic void true_receive P((struct recvbuf *)); 17254359Srobertostatic void true_poll P((int, struct peer *)); 17354359Srobertostatic void true_send P((struct peer *, const char *)); 17454359Srobertostatic void true_doevent P((struct peer *, enum true_event)); 17554359Sroberto 17654359Sroberto#ifdef CLOCK_PPS720 17754359Srobertostatic u_long true_sample720 P((void)); 17854359Sroberto#endif 17954359Sroberto 18054359Sroberto/* 18154359Sroberto * Transfer vector 18254359Sroberto */ 18354359Srobertostruct refclock refclock_true = { 18454359Sroberto true_start, /* start up driver */ 18554359Sroberto true_shutdown, /* shut down driver */ 18654359Sroberto true_poll, /* transmit poll message */ 18754359Sroberto noentry, /* not used (old true_control) */ 18854359Sroberto noentry, /* initialize driver (not used) */ 18954359Sroberto noentry, /* not used (old true_buginfo) */ 19054359Sroberto NOFLAGS /* not used */ 19154359Sroberto}; 19254359Sroberto 19354359Sroberto 19454359Sroberto#if !defined(__STDC__) 19554359Sroberto# define true_debug (void) 19654359Sroberto#else 19754359Srobertostatic void 19854359Srobertotrue_debug(struct peer *peer, const char *fmt, ...) 19954359Sroberto{ 20054359Sroberto va_list ap; 20154359Sroberto int want_debugging, now_debugging; 20254359Sroberto struct refclockproc *pp; 20354359Sroberto struct true_unit *up; 20454359Sroberto 20554359Sroberto va_start(ap, fmt); 20654359Sroberto pp = peer->procptr; 20754359Sroberto up = (struct true_unit *)pp->unitptr; 20854359Sroberto 20954359Sroberto want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0; 21054359Sroberto now_debugging = (up->debug != NULL); 21154359Sroberto if (want_debugging != now_debugging) 21254359Sroberto { 21354359Sroberto if (want_debugging) { 21454359Sroberto char filename[20]; 21554359Sroberto 21654359Sroberto sprintf(filename, "/tmp/true%d.debug", up->unit); 21754359Sroberto up->debug = fopen(filename, "w"); 21854359Sroberto if (up->debug) { 21954359Sroberto#ifdef HAVE_SETVBUF 22054359Sroberto static char buf[BUFSIZ]; 22154359Sroberto setvbuf(up->debug, buf, _IOLBF, BUFSIZ); 22254359Sroberto#else 22354359Sroberto setlinebuf(up->debug); 22454359Sroberto#endif 22554359Sroberto } 22654359Sroberto } else { 22754359Sroberto fclose(up->debug); 22854359Sroberto up->debug = NULL; 22954359Sroberto } 23054359Sroberto } 23154359Sroberto 23254359Sroberto if (up->debug) { 23354359Sroberto fprintf(up->debug, "true%d: ", up->unit); 23454359Sroberto vfprintf(up->debug, fmt, ap); 23554359Sroberto } 23654359Sroberto} 23754359Sroberto#endif /*STDC*/ 23854359Sroberto 23954359Sroberto/* 24054359Sroberto * true_start - open the devices and initialize data for processing 24154359Sroberto */ 24254359Srobertostatic int 24354359Srobertotrue_start( 24454359Sroberto int unit, 24554359Sroberto struct peer *peer 24654359Sroberto ) 24754359Sroberto{ 24854359Sroberto register struct true_unit *up; 24954359Sroberto struct refclockproc *pp; 25054359Sroberto char device[20]; 25154359Sroberto int fd; 25254359Sroberto 25354359Sroberto /* 25454359Sroberto * Open serial port 25554359Sroberto */ 25654359Sroberto (void)sprintf(device, DEVICE, unit); 25754359Sroberto if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) 25854359Sroberto return (0); 25954359Sroberto 26054359Sroberto /* 26154359Sroberto * Allocate and initialize unit structure 26254359Sroberto */ 26354359Sroberto if (!(up = (struct true_unit *) 26454359Sroberto emalloc(sizeof(struct true_unit)))) { 26554359Sroberto (void) close(fd); 26654359Sroberto return (0); 26754359Sroberto } 26854359Sroberto memset((char *)up, 0, sizeof(struct true_unit)); 26954359Sroberto pp = peer->procptr; 27054359Sroberto pp->io.clock_recv = true_receive; 27154359Sroberto pp->io.srcclock = (caddr_t)peer; 27254359Sroberto pp->io.datalen = 0; 27354359Sroberto pp->io.fd = fd; 27454359Sroberto if (!io_addclock(&pp->io)) { 27554359Sroberto (void) close(fd); 27654359Sroberto free(up); 27754359Sroberto return (0); 27854359Sroberto } 27954359Sroberto pp->unitptr = (caddr_t)up; 28054359Sroberto 28154359Sroberto /* 28254359Sroberto * Initialize miscellaneous variables 28354359Sroberto */ 28454359Sroberto peer->precision = PRECISION; 28554359Sroberto pp->clockdesc = DESCRIPTION; 28654359Sroberto memcpy((char *)&pp->refid, REFID, 4); 28754359Sroberto up->pollcnt = 2; 28854359Sroberto up->type = t_unknown; 28954359Sroberto up->state = s_Base; 29054359Sroberto true_doevent(peer, e_Init); 29154359Sroberto return (1); 29254359Sroberto} 29354359Sroberto 29454359Sroberto/* 29554359Sroberto * true_shutdown - shut down the clock 29654359Sroberto */ 29754359Srobertostatic void 29854359Srobertotrue_shutdown( 29954359Sroberto int unit, 30054359Sroberto struct peer *peer 30154359Sroberto ) 30254359Sroberto{ 30354359Sroberto register struct true_unit *up; 30454359Sroberto struct refclockproc *pp; 30554359Sroberto 30654359Sroberto pp = peer->procptr; 30754359Sroberto up = (struct true_unit *)pp->unitptr; 30854359Sroberto io_closeclock(&pp->io); 30954359Sroberto free(up); 31054359Sroberto} 31154359Sroberto 31254359Sroberto 31354359Sroberto/* 31454359Sroberto * true_receive - receive data from the serial interface on a clock 31554359Sroberto */ 31654359Srobertostatic void 31754359Srobertotrue_receive( 31854359Sroberto struct recvbuf *rbufp 31954359Sroberto ) 32054359Sroberto{ 32154359Sroberto register struct true_unit *up; 32254359Sroberto struct refclockproc *pp; 32354359Sroberto struct peer *peer; 32454359Sroberto u_short new_station; 32554359Sroberto char synced; 32654359Sroberto int i; 32754359Sroberto int lat, lon, off; /* GOES Satellite position */ 32854359Sroberto 32954359Sroberto /* 33054359Sroberto * Get the clock this applies to and pointers to the data. 33154359Sroberto */ 33254359Sroberto peer = (struct peer *)rbufp->recv_srcclock; 33354359Sroberto pp = peer->procptr; 33454359Sroberto up = (struct true_unit *)pp->unitptr; 33554359Sroberto 33654359Sroberto /* 33754359Sroberto * Read clock output. Automatically handles STREAMS, CLKLDISC. 33854359Sroberto */ 33954359Sroberto pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); 34054359Sroberto 34154359Sroberto /* 34254359Sroberto * There is a case where <cr><lf> generates 2 timestamps. 34354359Sroberto */ 34454359Sroberto if (pp->lencode == 0) 34554359Sroberto return; 34654359Sroberto pp->a_lastcode[pp->lencode] = '\0'; 34754359Sroberto true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode, pp->lencode); 34854359Sroberto 34954359Sroberto up->pollcnt = 2; 35054359Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 35154359Sroberto 35254359Sroberto /* 35354359Sroberto * We get down to business, check the timecode format and decode 35454359Sroberto * its contents. This code decodes a multitude of different 35554359Sroberto * clock messages. Timecodes are processed if needed. All replies 35654359Sroberto * will be run through the state machine to tweak driver options 35754359Sroberto * and program the clock. 35854359Sroberto */ 35954359Sroberto 36054359Sroberto /* 36154359Sroberto * Clock misunderstood our last command? 36254359Sroberto */ 36354359Sroberto if (pp->a_lastcode[0] == '?') { 36454359Sroberto true_doevent(peer, e_Huh); 36554359Sroberto return; 36654359Sroberto } 36754359Sroberto 36854359Sroberto /* 36954359Sroberto * Timecode: "nnnnn+nnn-nnn" 37054359Sroberto * (from GOES clock when asked about satellite position) 37154359Sroberto */ 37254359Sroberto if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') && 37354359Sroberto (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') && 37454359Sroberto sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3 37554359Sroberto ) { 37654359Sroberto const char *label = "Botch!"; 37754359Sroberto 37854359Sroberto /* 37954359Sroberto * This is less than perfect. Call the (satellite) 38054359Sroberto * either EAST or WEST and adjust slop accodingly 38154359Sroberto * Perfectionists would recalculate the exact delay 38254359Sroberto * and adjust accordingly... 38354359Sroberto */ 38454359Sroberto if (lon > 7000 && lon < 14000) { 38554359Sroberto if (lon < 10000) { 38654359Sroberto new_station = GOES_EAST; 38754359Sroberto label = "EAST"; 38854359Sroberto } else { 38954359Sroberto new_station = GOES_WEST; 39054359Sroberto label = "WEST"; 39154359Sroberto } 39254359Sroberto 39354359Sroberto if (new_station != up->station) { 39454359Sroberto double dtemp; 39554359Sroberto 39654359Sroberto dtemp = pp->fudgetime1; 39754359Sroberto pp->fudgetime1 = pp->fudgetime2; 39854359Sroberto pp->fudgetime2 = dtemp; 39954359Sroberto up->station = new_station; 40054359Sroberto } 40154359Sroberto } 40254359Sroberto else { 40354359Sroberto refclock_report(peer, CEVNT_BADREPLY); 40454359Sroberto label = "UNKNOWN"; 40554359Sroberto } 40654359Sroberto true_debug(peer, "GOES: station %s\n", label); 40754359Sroberto true_doevent(peer, e_Satellite); 40854359Sroberto return; 40954359Sroberto } 41054359Sroberto 41154359Sroberto /* 41254359Sroberto * Timecode: "Fnn" 41354359Sroberto * (from TM/TMD clock when it wants to tell us what it's up to.) 41454359Sroberto */ 41554359Sroberto if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) { 41654359Sroberto switch (i) { 41754359Sroberto case 50: 41854359Sroberto true_doevent(peer, e_F50); 41954359Sroberto break; 42054359Sroberto case 51: 42154359Sroberto true_doevent(peer, e_F51); 42254359Sroberto break; 42354359Sroberto default: 42454359Sroberto true_debug(peer, "got F%02d - ignoring\n", i); 42554359Sroberto break; 42654359Sroberto } 42754359Sroberto return; 42854359Sroberto } 42954359Sroberto 43054359Sroberto /* 43154359Sroberto * Timecode: " TRUETIME Mk III" 43254359Sroberto * (from a TM/TMD clock during initialization.) 43354359Sroberto */ 43454359Sroberto if (strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0) { 43554359Sroberto true_doevent(peer, e_F18); 43654359Sroberto NLOG(NLOG_CLOCKSTATUS) { 43754359Sroberto msyslog(LOG_INFO, "TM/TMD: %s", pp->a_lastcode); 43854359Sroberto } 43954359Sroberto return; 44054359Sroberto } 44154359Sroberto 44254359Sroberto /* 44354359Sroberto * Timecode: "N03726428W12209421+000033" 44454359Sroberto * 1 2 44554359Sroberto * 0123456789012345678901234 44654359Sroberto * (from a TCU during initialization) 44754359Sroberto */ 44854359Sroberto if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') && 44954359Sroberto (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') && 45054359Sroberto pp->a_lastcode[18] == '+') { 45154359Sroberto true_doevent(peer, e_Location); 45254359Sroberto NLOG(NLOG_CLOCKSTATUS) { 45354359Sroberto msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode); 45454359Sroberto } 45554359Sroberto return; 45654359Sroberto } 45754359Sroberto /* 45854359Sroberto * Timecode: "ddd:hh:mm:ssQ" 45954359Sroberto * (from all clocks supported by this driver.) 46054359Sroberto */ 46154359Sroberto if (pp->a_lastcode[3] == ':' && 46254359Sroberto pp->a_lastcode[6] == ':' && 46354359Sroberto pp->a_lastcode[9] == ':' && 46454359Sroberto sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c", 46554359Sroberto &pp->day, &pp->hour, &pp->minute, 46654359Sroberto &pp->second, &synced) == 5) { 46754359Sroberto 46854359Sroberto /* 46954359Sroberto * Adjust the synchronize indicator according to timecode 47054359Sroberto */ 47154359Sroberto if (synced != ' ' && synced != '.' && synced != '*') 47254359Sroberto pp->leap = LEAP_NOTINSYNC; 47354359Sroberto 47454359Sroberto true_doevent(peer, e_TS); 47554359Sroberto 47654359Sroberto#ifdef CLOCK_PPS720 47754359Sroberto /* If it's taken more than 65ms to get here, we'll lose. */ 47854359Sroberto if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) { 47954359Sroberto l_fp off; 48054359Sroberto 48154359Sroberto#ifdef CLOCK_ATOM 48254359Sroberto /* 48354359Sroberto * find out what time it really is. Include 48454359Sroberto * the count from the PCL720 48554359Sroberto */ 48654359Sroberto if (!clocktime(pp->day, pp->hour, pp->minute, 48754359Sroberto pp->second, GMT, pp->lastrec.l_ui, 48854359Sroberto &pp->yearstart, &off.l_ui)) { 48954359Sroberto refclock_report(peer, CEVNT_BADTIME); 49054359Sroberto return; 49154359Sroberto } 49254359Sroberto#endif 49354359Sroberto 49454359Sroberto pp->usec = true_sample720(); 49554359Sroberto#ifdef CLOCK_ATOM 49654359Sroberto TVUTOTSF(pp->usec, off.l_uf); 49754359Sroberto#endif 49854359Sroberto 49954359Sroberto /* 50054359Sroberto * Stomp all over the timestamp that was pulled out 50154359Sroberto * of the input stream. It's irrelevant since we've 50254359Sroberto * adjusted the input time to reflect now (via pp->usec) 50354359Sroberto * rather than when the data was collected. 50454359Sroberto */ 50554359Sroberto get_systime(&pp->lastrec); 50654359Sroberto#ifdef CLOCK_ATOM 50754359Sroberto /* 50854359Sroberto * Create a true offset for feeding to pps_sample() 50954359Sroberto */ 51054359Sroberto L_SUB(&off, &pp->lastrec); 51154359Sroberto 51254359Sroberto pps_sample(peer, &off); 51354359Sroberto#endif 51454359Sroberto true_debug(peer, "true_sample720: %luus\n", pp->usec); 51554359Sroberto } 51654359Sroberto#endif 51754359Sroberto 51854359Sroberto /* 51954359Sroberto * The clock will blurt a timecode every second but we only 52054359Sroberto * want one when polled. If we havn't been polled, bail out. 52154359Sroberto */ 52254359Sroberto if (!up->polled) 52354359Sroberto return; 52454359Sroberto 52554359Sroberto true_doevent(peer, e_Poll); 52654359Sroberto if (!refclock_process(pp)) { 52754359Sroberto refclock_report(peer, CEVNT_BADTIME); 52854359Sroberto return; 52954359Sroberto } 53054359Sroberto refclock_receive(peer); 53154359Sroberto 53254359Sroberto /* 53354359Sroberto * We have succedded in answering the poll. 53454359Sroberto * Turn off the flag and return 53554359Sroberto */ 53654359Sroberto up->polled = 0; 53754359Sroberto 53854359Sroberto return; 53954359Sroberto } 54054359Sroberto 54154359Sroberto /* 54254359Sroberto * No match to known timecodes, report failure and return 54354359Sroberto */ 54454359Sroberto refclock_report(peer, CEVNT_BADREPLY); 54554359Sroberto return; 54654359Sroberto} 54754359Sroberto 54854359Sroberto 54954359Sroberto/* 55054359Sroberto * true_send - time to send the clock a signal to cough up a time sample 55154359Sroberto */ 55254359Srobertostatic void 55354359Srobertotrue_send( 55454359Sroberto struct peer *peer, 55554359Sroberto const char *cmd 55654359Sroberto ) 55754359Sroberto{ 55854359Sroberto struct refclockproc *pp; 55954359Sroberto 56054359Sroberto pp = peer->procptr; 56154359Sroberto if (!(pp->sloppyclockflag & CLK_FLAG1)) { 56254359Sroberto register int len = strlen(cmd); 56354359Sroberto 56454359Sroberto true_debug(peer, "Send '%s'\n", cmd); 56554359Sroberto if (write(pp->io.fd, cmd, (unsigned)len) != len) 56654359Sroberto refclock_report(peer, CEVNT_FAULT); 56754359Sroberto else 56854359Sroberto pp->polls++; 56954359Sroberto } 57054359Sroberto} 57154359Sroberto 57254359Sroberto 57354359Sroberto/* 57454359Sroberto * state machine for initializing and controlling a clock 57554359Sroberto */ 57654359Srobertostatic void 57754359Srobertotrue_doevent( 57854359Sroberto struct peer *peer, 57954359Sroberto enum true_event event 58054359Sroberto ) 58154359Sroberto{ 58254359Sroberto struct true_unit *up; 58354359Sroberto struct refclockproc *pp; 58454359Sroberto 58554359Sroberto pp = peer->procptr; 58654359Sroberto up = (struct true_unit *)pp->unitptr; 58754359Sroberto if (event != e_TS) { 58854359Sroberto NLOG(NLOG_CLOCKSTATUS) { 58954359Sroberto msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s", 59054359Sroberto typeStr(up->type), 59154359Sroberto stateStr(up->state), 59254359Sroberto eventStr(event)); 59354359Sroberto } 59454359Sroberto } 59554359Sroberto true_debug(peer, "clock %s, state %s, event %s\n", 59654359Sroberto typeStr(up->type), stateStr(up->state), eventStr(event)); 59754359Sroberto switch (up->type) { 59854359Sroberto case t_goes: 59954359Sroberto switch (event) { 60054359Sroberto case e_Init: /* FALLTHROUGH */ 60154359Sroberto case e_Satellite: 60254359Sroberto /* 60354359Sroberto * Switch back to on-second time codes and return. 60454359Sroberto */ 60554359Sroberto true_send(peer, "C"); 60654359Sroberto up->state = s_Start; 60754359Sroberto break; 60854359Sroberto case e_Poll: 60954359Sroberto /* 61054359Sroberto * After each poll, check the station (satellite). 61154359Sroberto */ 61254359Sroberto true_send(peer, "P"); 61354359Sroberto /* No state change needed. */ 61454359Sroberto break; 61554359Sroberto default: 61654359Sroberto break; 61754359Sroberto } 61854359Sroberto /* FALLTHROUGH */ 61954359Sroberto case t_omega: 62054359Sroberto switch (event) { 62154359Sroberto case e_Init: 62254359Sroberto true_send(peer, "C"); 62354359Sroberto up->state = s_Start; 62454359Sroberto break; 62554359Sroberto case e_TS: 62654359Sroberto if (up->state != s_Start && up->state != s_Auto) { 62754359Sroberto true_send(peer, "\03\r"); 62854359Sroberto break; 62954359Sroberto } 63054359Sroberto up->state = s_Auto; 63154359Sroberto break; 63254359Sroberto default: 63354359Sroberto break; 63454359Sroberto } 63554359Sroberto break; 63654359Sroberto case t_tm: 63754359Sroberto switch (event) { 63854359Sroberto case e_Init: 63954359Sroberto true_send(peer, "F18\r"); 64054359Sroberto up->state = s_Init; 64154359Sroberto break; 64254359Sroberto case e_F18: 64354359Sroberto true_send(peer, "F50\r"); 64454359Sroberto up->state = s_F18; 64554359Sroberto break; 64654359Sroberto case e_F50: 64754359Sroberto true_send(peer, "F51\r"); 64854359Sroberto up->state = s_F50; 64954359Sroberto break; 65054359Sroberto case e_F51: 65154359Sroberto true_send(peer, "F08\r"); 65254359Sroberto up->state = s_Start; 65354359Sroberto break; 65454359Sroberto case e_TS: 65554359Sroberto if (up->state != s_Start && up->state != s_Auto) { 65654359Sroberto true_send(peer, "\03\r"); 65754359Sroberto break; 65854359Sroberto } 65954359Sroberto up->state = s_Auto; 66054359Sroberto break; 66154359Sroberto default: 66254359Sroberto break; 66354359Sroberto } 66454359Sroberto break; 66554359Sroberto case t_tcu: 66654359Sroberto switch (event) { 66754359Sroberto case e_Init: 66854359Sroberto true_send(peer, "MD3\r"); /* GPS Synch'd Gen. */ 66954359Sroberto true_send(peer, "TSU\r"); /* UTC, not GPS. */ 67054359Sroberto true_send(peer, "AU\r"); /* Auto Timestamps. */ 67154359Sroberto up->state = s_Start; 67254359Sroberto break; 67354359Sroberto case e_TS: 67454359Sroberto if (up->state != s_Start && up->state != s_Auto) { 67554359Sroberto true_send(peer, "\03\r"); 67654359Sroberto break; 67754359Sroberto } 67854359Sroberto up->state = s_Auto; 67954359Sroberto break; 68054359Sroberto default: 68154359Sroberto break; 68254359Sroberto } 68354359Sroberto break; 68454359Sroberto case t_unknown: 68554359Sroberto switch (up->state) { 68654359Sroberto case s_Base: 68754359Sroberto if (event != e_Init) 68854359Sroberto abort(); 68954359Sroberto true_send(peer, "P\r"); 69054359Sroberto up->state = s_InqGOES; 69154359Sroberto break; 69254359Sroberto case s_InqGOES: 69354359Sroberto switch (event) { 69454359Sroberto case e_Satellite: 69554359Sroberto up->type = t_goes; 69654359Sroberto true_doevent(peer, e_Init); 69754359Sroberto break; 69854359Sroberto case e_Init: /*FALLTHROUGH*/ 69954359Sroberto case e_Huh: /*FALLTHROUGH*/ 70054359Sroberto case e_TS: 70154359Sroberto up->state = s_InqOmega; 70254359Sroberto true_send(peer, "C\r"); 70354359Sroberto break; 70454359Sroberto default: 70554359Sroberto abort(); 70654359Sroberto } 70754359Sroberto break; 70854359Sroberto case s_InqOmega: 70954359Sroberto switch (event) { 71054359Sroberto case e_TS: 71154359Sroberto up->type = t_omega; 71254359Sroberto up->state = s_Auto; /* Inq side-effect. */ 71354359Sroberto break; 71454359Sroberto case e_Init: /*FALLTHROUGH*/ 71554359Sroberto case e_Huh: 71654359Sroberto up->state = s_InqTM; 71754359Sroberto true_send(peer, "F18\r"); 71854359Sroberto break; 71954359Sroberto default: 72054359Sroberto abort(); 72154359Sroberto } 72254359Sroberto break; 72354359Sroberto case s_InqTM: 72454359Sroberto switch (event) { 72554359Sroberto case e_F18: 72654359Sroberto up->type = t_tm; 72754359Sroberto true_doevent(peer, e_Init); 72854359Sroberto break; 72954359Sroberto case e_Init: /*FALLTHROUGH*/ 73054359Sroberto case e_Huh: 73154359Sroberto true_send(peer, "PO\r"); 73254359Sroberto up->state = s_InqTCU; 73354359Sroberto break; 73454359Sroberto default: 73554359Sroberto abort(); 73654359Sroberto } 73754359Sroberto break; 73854359Sroberto case s_InqTCU: 73954359Sroberto switch (event) { 74054359Sroberto case e_Location: 74154359Sroberto up->type = t_tcu; 74254359Sroberto true_doevent(peer, e_Init); 74354359Sroberto break; 74454359Sroberto case e_Init: /*FALLTHROUGH*/ 74554359Sroberto case e_Huh: 74654359Sroberto up->state = s_Base; 74754359Sroberto sleep(1); /* XXX */ 74854359Sroberto break; 74954359Sroberto default: 75054359Sroberto abort(); 75154359Sroberto } 75254359Sroberto break; 75354359Sroberto /* 75454359Sroberto * An expedient hack to prevent lint complaints, 75554359Sroberto * these don't actually need to be used here... 75654359Sroberto */ 75754359Sroberto case s_Init: 75854359Sroberto case s_F18: 75954359Sroberto case s_F50: 76054359Sroberto case s_Start: 76154359Sroberto case s_Auto: 76254359Sroberto case s_Max: 76354359Sroberto msyslog(LOG_INFO, "TRUE: state %s is unexpected!", stateStr(up->state)); 76454359Sroberto } 76554359Sroberto break; 76654359Sroberto default: 76754359Sroberto abort(); 76854359Sroberto /* NOTREACHED */ 76954359Sroberto } 77054359Sroberto 77154359Sroberto#ifdef CLOCK_PPS720 77254359Sroberto if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) { 77354359Sroberto /* Make counter trigger on gate0, count down from 65535. */ 77454359Sroberto pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535); 77554359Sroberto /* 77654359Sroberto * (These constants are OK since 77754359Sroberto * they represent hardware maximums.) 77854359Sroberto */ 77954359Sroberto NLOG(NLOG_CLOCKINFO) { 78054359Sroberto msyslog(LOG_NOTICE, "PCL-720 initialized"); 78154359Sroberto } 78254359Sroberto up->pcl720init++; 78354359Sroberto } 78454359Sroberto#endif 78554359Sroberto 78654359Sroberto 78754359Sroberto} 78854359Sroberto 78954359Sroberto/* 79054359Sroberto * true_poll - called by the transmit procedure 79154359Sroberto */ 79254359Srobertostatic void 79354359Srobertotrue_poll( 79454359Sroberto int unit, 79554359Sroberto struct peer *peer 79654359Sroberto ) 79754359Sroberto{ 79854359Sroberto struct true_unit *up; 79954359Sroberto struct refclockproc *pp; 80054359Sroberto 80154359Sroberto /* 80254359Sroberto * You don't need to poll this clock. It puts out timecodes 80354359Sroberto * once per second. If asked for a timestamp, take note. 80454359Sroberto * The next time a timecode comes in, it will be fed back. 80554359Sroberto */ 80654359Sroberto pp = peer->procptr; 80754359Sroberto up = (struct true_unit *)pp->unitptr; 80854359Sroberto if (up->pollcnt > 0) 80954359Sroberto up->pollcnt--; 81054359Sroberto else { 81154359Sroberto true_doevent(peer, e_Init); 81254359Sroberto refclock_report(peer, CEVNT_TIMEOUT); 81354359Sroberto } 81454359Sroberto 81554359Sroberto /* 81654359Sroberto * polled every 64 seconds. Ask true_receive to hand in a 81754359Sroberto * timestamp. 81854359Sroberto */ 81954359Sroberto up->polled = 1; 82054359Sroberto pp->polls++; 82154359Sroberto} 82254359Sroberto 82354359Sroberto#ifdef CLOCK_PPS720 82454359Sroberto/* 82554359Sroberto * true_sample720 - sample the PCL-720 82654359Sroberto */ 82754359Srobertostatic u_long 82854359Srobertotrue_sample720(void) 82954359Sroberto{ 83054359Sroberto unsigned long f; 83154359Sroberto 83254359Sroberto /* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3. 83354359Sroberto * If it is not being held low now, we did not get called 83454359Sroberto * within 65535us. 83554359Sroberto */ 83654359Sroberto if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) { 83754359Sroberto NLOG(NLOG_CLOCKINFO) { 83854359Sroberto msyslog(LOG_NOTICE, "PCL-720 out of synch"); 83954359Sroberto } 84054359Sroberto return (0); 84154359Sroberto } 84254359Sroberto f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR)); 84354359Sroberto#ifdef PPS720_DEBUG 84454359Sroberto msyslog(LOG_DEBUG, "PCL-720: %luus", f); 84554359Sroberto#endif 84654359Sroberto return (f); 84754359Sroberto} 84854359Sroberto#endif 84954359Sroberto 85054359Sroberto#else 85154359Srobertoint refclock_true_bs; 85254359Sroberto#endif /* REFCLOCK */ 853