diff options
Diffstat (limited to 'usr.sbin/xntpd/xntpd/refclock_goes.c')
-rw-r--r-- | usr.sbin/xntpd/xntpd/refclock_goes.c | 1188 |
1 files changed, 354 insertions, 834 deletions
diff --git a/usr.sbin/xntpd/xntpd/refclock_goes.c b/usr.sbin/xntpd/xntpd/refclock_goes.c index 0b53326..512131b 100644 --- a/usr.sbin/xntpd/xntpd/refclock_goes.c +++ b/usr.sbin/xntpd/xntpd/refclock_goes.c @@ -1,9 +1,12 @@ /* - * refclock_goes - clock driver for the Kinimetrics Truetime GOES receiver - * Version 2.0 + * refclock_goes - clock driver for the Kinemetrics Truetime GOES + * Receiver Version 3.0C - tested plain, with CLKLDISC + * Developement work being done: + * - Properly handle varying satellite positions (more acurately) + * - Integrate GPSTM and/or OMEGA and/or TRAK and/or ??? drivers */ -#if defined(REFCLOCK) && (defined(GOES) || defined(GOESCLK) || defined(GOESPPS)) +#if defined(REFCLOCK) && defined(GOES) #include <stdio.h> #include <ctype.h> @@ -13,113 +16,102 @@ #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_unixtime.h" - -#if defined(HAVE_BSD_TTYS) -#include <sgtty.h> -#endif /* HAVE_BSD_TTYS */ - -#if defined(HAVE_SYSV_TTYS) -#include <termio.h> -#endif /* HAVE_SYSV_TTYS */ - -#if defined(HAVE_TERMIOS) -#include <termios.h> -#endif -#if defined(STREAM) -#include <stropts.h> -#if defined(GOESCLK) -#include <clkdefs.h> -#endif /* GOESCLK */ -#endif /* STREAM */ - -#if defined (GOESPPS) -#include <sys/ppsclock.h> -#endif /* GOESPPS */ - #include "ntp_stdlib.h" /* * Support for Kinemetrics Truetime 468-DC GOES Receiver + * OM-DC OMEGA and GPS-TM/TMD support in progress... + * + * Most of this code is originally from refclock_wwvb.c with thanks. + * It has been so mangled that wwvb is not a recognizable ancestor. + * + * Timcode format: ADDD:HH:MM:SSQCL + * A - control A + * Q Quality indication: indicates possible error of + * C - Carriage return + * L - Line feed * - * Most of this code is copied from refclock_goes.c with thanks. + * Quality codes indicate possible error of + * 468-DC GOES Receiver: + * GPS-TM/TMD Receiver: + * ? +/- 500 milliseconds # +/- 50 milliseconds + * * +/- 5 milliseconds . +/- 1 millisecond + * space less than 1 millisecond + * OM-DC OMEGA Receiver: + * > >+- 5 seconds + * ? >+/- 500 milliseconds # >+/- 50 milliseconds + * * >+/- 5 milliseconds . >+/- 1 millisecond + * A-H less than 1 millisecond. Character indicates which station + * is being received as follows: + * A = Norway, B = Liberia, C = Hawaii, D = North Dakota, + * E = La Reunion, F = Argentina, G = Australia, H = Japan. * - * the time code looks like follows; Send the clock a R or C and once per - * second a timestamp will appear that looks like this: - * ADDD:HH:MM:SSQCL - * A - control A - * Q Quality indication: indicates possible error of - * ? +/- 500 milliseconds # +/- 50 milliseconds - * * +/- 5 milliseconds . +/- 1 millisecond - * space less than 1 millisecond - * C - Carriage return - * L - Line feed - * The carriage return start bit begins on 0 seconds and extends to 1 bit time. + * The carriage return start bit begins on 0 seconds and extends to 1 + * bit time + * + * Notes on 468-DC and OMEGA receiver: + * + * Send the clock a 'R' or 'C' and once per second a timestamp will + * appear. Send a 'P' to get the satellite position once. + * + * Notes on the 468-DC receiver: + * + * Unless you live on 125 degrees west longitude, you can't + * set your clock propagation delay settings correctly and still use + * automatic mode. The manual says to use a compromise when setting the + * switches. This results in significant errors. The solution; use fudge + * time1 and time2 to incorporate corrections. If your clock is set for + * 50 and it should be 58 for using the west and 46 for using the east, + * use the line * - * Unless you live on 125 degrees west longitude, you can't set your clock - * propagation delay settings correctly and still use automatic mode. - * The manual says to use a compromise when setting the switches. This - * results in significant errors. The solution; use fudge time1 and time2 - * to incorporate corrections. If your clock is set for 50 and it should - * be 58 for using the west and 46 for using the east, use the line * fudge 127.127.5.0 time1 +0.008 time2 -0.004 - * This corrects the 4 milliseconds advance and 5 milliseconds retard needed. - * The software will ask the clock which satellite it sees. * - * Flag1 set to 1 will silence the clock side of xntpd, just reading the - * clock without trying to write to it. This is usefull if several - * xntpds listen to the same clock. This has not been tested yet... + * This corrects the 4 milliseconds advance and 8 milliseconds retard + * needed. The software will ask the clock which satellite it sees. + * + * Ntp.conf parameters: + * time1 - offset applied to samples when reading WEST satellite (default = 0) + * time2 - offset applied to samples when reading EAST satellite (default = 0) + * val1 - stratum to assign to this clock (default = 0) + * val2 - refid assigned to this clock (default = "GOES", see below) + * flag1 - will silence the clock side of xntpd, just reading the clock + * without trying to write to it. (default = 0) + * flag2 - not assigned + * flag3 - enable ppsclock streams module + * flag4 - not assigned + * */ /* * Definitions */ -#define MAXUNITS 4 /* max number of GOES units */ -#define GOES232 "/dev/goes%d" +#define DEVICE "/dev/goes%d" #define SPEED232 B9600 /* 9600 baud */ /* * Radio interface parameters */ -#define GOESMAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */ -#define GOESSKEWFACTOR 17 /* skew factor (for about 32 ppm) */ -#define GOESPRECISION (-10) /* precision assumed (about 1 ms) */ -#define GOESREFID "GOES" /* reference id */ -#define GOESDESCRIPTION "Kinemetrics GOES Receiver" /* who we are */ -#define GOESHSREFID 0x7f7f050a /* 127.127.5.10 refid hi strata */ -#define GMT 0 /* hour offset from Greenwich */ -#define NCODES 3 /* stages of median filter */ -#define LENGOES0 13 /* format 0 timecode length */ -#define LENGOES2 21 /* format 2 timecode length */ -#define FMTGOESU 0 /* unknown format timecode id */ -#define FMTGOES0 1 /* format 0 timecode id */ -#define FMTGOES2 2 /* format 2 timecode id */ -#define DEFFUDGETIME 0 /* default fudge time (ms) */ -#define BMAX 50 /* timecode buffer length */ -#define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */ +#define MAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */ +#define PRECISION (-10) /* precision assumed (about 1 ms) */ +#define REFID "GOES" /* reference id */ +#define DESCRIPTION "TrueTime GPS/GOES Receivers" /* WRU */ +#define NSAMPLES 3 /* stages of median filter */ /* - * Tag which satellite we see + * Tags which station (satellite) we see */ -#define GOES_SAT_NONE 0 -#define GOES_SAT_WEST 1 -#define GOES_SAT_EAST 2 -#define GOES_SAT_STAND 3 +#define GOES_WEST 0 /* Default to WEST satellite and apply time1 */ +#define GOES_EAST 1 /* until you discover otherwise */ /* - * Hack to avoid excercising the multiplier. I have no pride. + * used by the state machine */ -#define MULBY10(x) (((x)<<3) + ((x)<<1)) +enum goes_event {e_Init, e_F18, e_F50, e_F51, e_TS}; /* * Imported from the timer module */ -extern U_LONG current_time; -extern struct event timerqueue[]; - -/* - * Imported from ntp_loopfilter module - */ -extern int fdpps; /* pps file descriptor */ +extern u_long current_time; /* * Imported from ntpd module @@ -127,634 +119,294 @@ extern int fdpps; /* pps file descriptor */ extern int debug; /* global debug flag */ /* - * GOES unit control structure + * unit control structure */ struct goesunit { - struct peer *peer; /* associated peer structure */ - struct refclockio io; /* given to the I/O handler */ - l_fp lastrec; /* last receive time */ - l_fp lastref; /* last timecode time */ - l_fp offset[NCODES]; /* recent sample offsets */ - char lastcode[BMAX]; /* last timecode received */ - u_short satellite; /* which satellite we saw */ - u_short polled; /* Hand in a time sample? */ - u_char format; /* timecode format */ - u_char lencode; /* length of last timecode */ - U_LONG lasttime; /* last time clock heard from */ - u_char unit; /* unit number for this guy */ - u_char status; /* clock status */ - u_char lastevent; /* last clock event */ - u_char reason; /* reason for last abort */ - u_char year; /* year of eternity */ - u_short day; /* day of year */ - u_char hour; /* hour of day */ - u_char minute; /* minute of hour */ - u_char second; /* seconds of minute */ - u_char leap; /* leap indicators */ - u_short msec; /* millisecond of second */ - u_char quality; /* quality char from format 2 */ - U_LONG yearstart; /* start of current year */ - /* - * Status tallies - */ - U_LONG polls; /* polls sent */ - U_LONG noreply; /* no replies to polls */ - U_LONG coderecv; /* timecodes received */ - U_LONG badformat; /* bad format */ - U_LONG baddata; /* bad data */ - U_LONG timestarted; /* time we started this */ + int pollcnt; /* poll message counter */ + u_short station; /* which station we are on */ + u_short polled; /* Hand in a time sample? */ + enum {Base, Start, F18, F50, F51, F08} + State; /* State machine */ }; /* - * Data space for the unit structures. Note that we allocate these on - * the fly, but never give them back. - */ -static struct goesunit *goesunits[MAXUNITS]; -static u_char unitinuse[MAXUNITS]; - -/* - * Keep the fudge factors separately so they can be set even - * when no clock is configured. - */ -static l_fp fudgefactor1[MAXUNITS]; -static l_fp fudgefactor2[MAXUNITS]; -static u_char stratumtouse[MAXUNITS]; -static u_char readonlyclockflag[MAXUNITS]; - -/* * Function prototypes */ -static void goes_init P((void)); -static int goes_start P((u_int, struct peer *)); -static void goes_shutdown P((int)); -static void goes_report_event P((struct goesunit *, int)); +static int goes_start P((int, struct peer *)); +static void goes_shutdown P((int, struct peer *)); static void goes_receive P((struct recvbuf *)); -static char goes_process P((struct goesunit *, l_fp *, u_fp *)); static void goes_poll P((int, struct peer *)); -static void goes_control P((u_int, struct refclockstat *, struct refclockstat *)); -static void goes_buginfo P((int, struct refclockbug *)); -static void goes_send P((struct goesunit *, char *)); - -struct refclock refclock_goes = { - goes_start, goes_shutdown, goes_poll, - goes_control, goes_init, goes_buginfo, NOFLAGS -}; +static void goes_send P((struct peer *, char *)); +static void goes_initstate P((struct peer *)); +static void goes_doevent P((struct peer *, enum goes_event)); /* - * goes_init - initialize internal goes driver data + * Transfer vector */ -static void -goes_init() -{ - register int i; - /* - * Just zero the data arrays - */ - memset((char *)goesunits, 0, sizeof goesunits); - memset((char *)unitinuse, 0, sizeof unitinuse); - - /* - * Initialize fudge factors to default. - */ - for (i = 0; i < MAXUNITS; i++) { - fudgefactor1[i].l_ui = 0; - fudgefactor1[i].l_uf = DEFFUDGETIME; - fudgefactor2[i].l_ui = 0; - fudgefactor2[i].l_uf = DEFFUDGETIME; - stratumtouse[i] = 0; - readonlyclockflag[i] = 0; - } -} +struct refclock refclock_goes = { + goes_start, /* start up driver */ + goes_shutdown, /* shut down driver */ + goes_poll, /* transmit poll message */ + noentry, /* not used (old goes_control) */ + noentry, /* initialize driver (not used) */ + noentry, /* not used (old goes_buginfo) */ + NOFLAGS /* not used */ +}; /* - * goes_start - open the GOES devices and initialize data for processing + * goes_start - open the devices and initialize data for processing */ static int goes_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { - register struct goesunit *goes; - register int i; - int fd232; - char goesdev[20]; - - /* - * Check configuration info - */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "goes_start: unit %d invalid", unit); - return 0; - } - if (unitinuse[unit]) { - syslog(LOG_ERR, "goes_start: unit %d in use", unit); - return 0; - } + register struct goesunit *up; + struct refclockproc *pp; + int fd; + char device[20]; /* * Open serial port */ - (void) sprintf(goesdev, GOES232, unit); - fd232 = open(goesdev, O_RDWR, 0777); - if (fd232 == -1) { - syslog(LOG_ERR, "goes_start: open of %s: %m", goesdev); - return 0; - } - -#if defined(HAVE_SYSV_TTYS) - /* - * System V serial line parameters (termio interface) - * - */ - { struct termio ttyb; - if (ioctl(fd232, TCGETA, &ttyb) < 0) { - syslog(LOG_ERR, - "goes_start: ioctl(%s, TCGETA): %m", goesdev); - goto screwed; - } - ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL; - ttyb.c_oflag = 0; - ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD; - ttyb.c_lflag = ICANON; - ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0'; - if (ioctl(fd232, TCSETA, &ttyb) < 0) { - syslog(LOG_ERR, - "goes_start: ioctl(%s, TCSETA): %m", goesdev); - goto screwed; - } - } -#endif /* HAVE_SYSV_TTYS */ -#if defined(HAVE_TERMIOS) - /* - * POSIX serial line parameters (termios interface) - * - * The GOESCLK option provides timestamping at the driver level. - * It requires the tty_clk streams module. - * - * The GOESPPS option provides timestamping at the driver level. - * It uses a 1-pps signal and level converter (gadget box) and - * requires the ppsclock streams module and SunOS 4.1.1 or - * later. - */ - { struct termios ttyb, *ttyp; - ttyp = &ttyb; - - if (tcgetattr(fd232, ttyp) < 0) { - syslog(LOG_ERR, - "goes_start: tcgetattr(%s): %m", goesdev); - goto screwed; - } - ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL; - ttyp->c_oflag = 0; - ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD; - ttyp->c_lflag = ICANON; - ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; - if (tcsetattr(fd232, TCSANOW, ttyp) < 0) { - syslog(LOG_ERR, - "goes_start: tcsetattr(%s): %m", goesdev); - goto screwed; - } - if (tcflush(fd232, TCIOFLUSH) < 0) { - syslog(LOG_ERR, - "goes_start: tcflush(%s): %m", goesdev); - goto screwed; - } - } -#endif /* HAVE_TERMIOS */ -#ifdef STREAM -#if defined(GOESCLK) - if (ioctl(fd232, I_PUSH, "clk") < 0) - syslog(LOG_ERR, - "goes_start: ioctl(%s, I_PUSH, clk): %m", goesdev); - if (ioctl(fd232, CLK_SETSTR, "\n") < 0) - syslog(LOG_ERR, - "goes_start: ioctl(%s, CLK_SETSTR): %m", goesdev); -#endif /* GOESCLK */ -#if defined(GOESPPS) - if (ioctl(fd232, I_PUSH, "ppsclock") < 0) - syslog(LOG_ERR, - "goes_start: ioctl(%s, I_PUSH, ppsclock): %m", goesdev); - else - fdpps = fd232; -#endif /* GOESPPS */ -#endif /* STREAM */ -#if defined(HAVE_BSD_TTYS) - /* - * 4.3bsd serial line parameters (sgttyb interface) - * - * The GOESCLK option provides timestamping at the driver level. - * It requires the tty_clk line discipline and 4.3bsd or later. - */ - { struct sgttyb ttyb; -#if defined(GOESCLK) - int ldisc = CLKLDISC; -#endif /* GOESCLK */ - - if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { - syslog(LOG_ERR, - "goes_start: ioctl(%s, TIOCGETP): %m", goesdev); - goto screwed; - } - ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; -#if defined(GOESCLK) - ttyb.sg_erase = ttyb.sg_kill = '\r'; - ttyb.sg_flags = RAW; -#else - ttyb.sg_erase = ttyb.sg_kill = '\0'; - ttyb.sg_flags = EVENP|ODDP|CRMOD; -#endif /* GOESCLK */ - if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { - syslog(LOG_ERR, - "goes_start: ioctl(%s, TIOCSETP): %m", goesdev); - goto screwed; - } -#if defined(GOESCLK) - if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { - syslog(LOG_ERR, - "goes_start: ioctl(%s, TIOCSETD): %m",goesdev); - goto screwed; - } -#endif /* GOESCLK */ - } -#endif /* HAVE_BSD_TTYS */ - - /* - * Allocate unit structure - */ - if (goesunits[unit] != 0) { - goes = goesunits[unit]; /* The one we want is okay */ - } else { - for (i = 0; i < MAXUNITS; i++) { - if (!unitinuse[i] && goesunits[i] != 0) - break; - } - if (i < MAXUNITS) { - /* - * Reclaim this one - */ - goes = goesunits[i]; - goesunits[i] = 0; - } else { - goes = (struct goesunit *) - emalloc(sizeof(struct goesunit)); - } - } - memset((char *)goes, 0, sizeof(struct goesunit)); - goesunits[unit] = goes; + (void)sprintf(device, DEVICE, unit); + if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) + return (0); /* - * Set up the structures + * Allocate and initialize unit structure */ - goes->peer = peer; - goes->unit = (u_char)unit; - goes->timestarted = current_time; - goes->satellite = GOES_SAT_NONE; - - goes->io.clock_recv = goes_receive; - goes->io.srcclock = (caddr_t)goes; - goes->io.datalen = 0; - goes->io.fd = fd232; - if (!io_addclock(&goes->io)) { - goto screwed; + if (!(up = (struct goesunit *) + emalloc(sizeof(struct goesunit)))) { + (void) close(fd); + return (0); } - - /* - * All done. Initialize a few random peer variables, then - * return success. - */ - peer->precision = GOESPRECISION; - peer->rootdelay = 0; - peer->rootdispersion = 0; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, GOESREFID, 4); - else - peer->refid = htonl(GOESHSREFID); - unitinuse[unit] = 1; - return 1; - - /* - * Something broke; abandon ship - */ -screwed: - (void) close(fd232); - return 0; -} - -/* - * goes_shutdown - shut down a GOES clock - */ -static void -goes_shutdown(unit) - int unit; -{ - register struct goesunit *goes; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "goes_shutdown: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "goes_shutdown: unit %d not in use", unit); - return; + memset((char *)up, 0, sizeof(struct goesunit)); + pp = peer->procptr; + pp->io.clock_recv = goes_receive; + pp->io.srcclock = (caddr_t)peer; + pp->io.datalen = 0; + pp->io.fd = fd; + if (!io_addclock(&pp->io)) { + (void) close(fd); + free(up); + return (0); } + pp->unitptr = (caddr_t)up; /* - * Tell the I/O module to turn us off. We're history. + * Initialize miscellaneous variables */ - goes = goesunits[unit]; - io_closeclock(&goes->io); - unitinuse[unit] = 0; + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->pollcnt = 2; +/* goes_initstate(peer);*/ + return (1); } /* - * goes_report_event - note the occurance of an event + * goes_shutdown - shut down the clock */ static void -goes_report_event(goes, code) - struct goesunit *goes; - int code; -{ +goes_shutdown(unit, peer) + int unit; struct peer *peer; +{ + register struct goesunit *up; + struct refclockproc *pp; - peer = goes->peer; - if (goes->status != (u_char)code) { - goes->status = (u_char)code; - if (code != CEVNT_NOMINAL) - goes->lastevent = (u_char)code; - syslog(LOG_INFO, - "clock %s event %x\n", ntoa(&peer->srcadr), code); -#ifdef DEBUG - if (debug) { - printf("goes_report_event(goes%d, code %d)\n", - goes->unit, code); - } -#endif - } + pp = peer->procptr; + up = (struct goesunit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); } /* - * goes_receive - receive data from the serial interface on a Kinimetrics - * clock + * goes_receive - receive data from the serial interface on a + * Kinimetrics clock */ static void goes_receive(rbufp) struct recvbuf *rbufp; { - register int i; - register struct goesunit *goes; - register u_char *dpt; - register char *cp; - register u_char *dpend; - l_fp tstmp; - u_fp dispersion; + register struct goesunit *up; + struct refclockproc *pp; + struct peer *peer; + l_fp tmp_l_fp; + u_short new_station; + char sync, c1, c2; + int i; + int lat, lon, off; /* GOES Satellite position */ /* - * Get the clock this applies to and a pointers to the data + * Get the clock this applies to and pointers to the data */ - goes = (struct goesunit *)rbufp->recv_srcclock; - dpt = (u_char *)&rbufp->recv_space; + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct goesunit *)pp->unitptr; /* - * Edit timecode to remove control chars + * Read clock output. Automatically handles STREAMS, CLKLDISC */ - dpend = dpt + rbufp->recv_length; - cp = goes->lastcode; - while (dpt < dpend) { - if ((*cp = 0x7f & *dpt++) >= ' ') cp++; -#ifdef GOESCLK - else if (*cp == '\r') { - if (dpend - dpt < 8) { - /* short timestamp */ - return; - } - if (!buftvtots(dpt,&goes->lastrec)) { - /* screwy timestamp */ - return; - } - dpt += 8; - } -#endif - } - *cp = '\0'; - goes->lencode = cp - goes->lastcode; - if (goes->lencode == 0) return; -#ifndef GOESCLK - goes->lastrec = rbufp->recv_time; -#endif /* GOESCLK */ - tstmp = goes->lastrec; - -#ifdef DEBUG - if (debug) - printf("goes: timecode %d %s\n", - goes->lencode, goes->lastcode); -#endif + pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX, + &pp->lastrec); /* - * We get down to business, check the timecode format and decode - * its contents. This code checks for and decodes both format 0 - * and format 2 and need not be told which in advance. + * There is a case where <cr><lf> generates 2 timestamps */ - cp = goes->lastcode; - goes->leap = 0; - goes->format = FMTGOESU; - if (goes->lencode == LENGOES0) { + if (pp->lencode == 0) + return; - /* - * Check timecode format 0 - */ - if (!isdigit(cp[0]) || /* day of year */ - !isdigit(cp[1]) || - !isdigit(cp[2]) || - cp[3] != ':' || /* <sp> */ - !isdigit(cp[4]) || /* hours */ - !isdigit(cp[5]) || - cp[6] != ':' || /* : separator */ - !isdigit(cp[7]) || /* minutes */ - !isdigit(cp[8]) || - cp[9] != ':' || /* : separator */ - !isdigit(cp[10]) || /* seconds */ - !isdigit(cp[11])) { - goes->badformat++; - goes_report_event(goes, CEVNT_BADREPLY); - return; - } - else goes->format = FMTGOES0; + up->pollcnt = 2; + record_clock_stats(&peer->srcadr, pp->lastcode); - /* - * Convert format 0 and check values - */ - goes->year = 0; /* fake */ - goes->day = cp[0] - '0'; - goes->day = MULBY10(goes->day) + cp[1] - '0'; - goes->day = MULBY10(goes->day) + cp[2] - '0'; - goes->hour = MULBY10(cp[4] - '0') + cp[5] - '0'; - goes->minute = MULBY10(cp[7] - '0') + cp[8] - '0'; - goes->second = MULBY10(cp[10] - '0') + cp[11] - '0'; - goes->msec = 0; - - if (cp[12] != ' ' && cp[12] != '.' && cp[12] != '*') - goes->leap = LEAP_NOTINSYNC; - else - goes->lasttime = current_time; - - if (goes->day < 1 || goes->day > 366) { - goes->baddata++; - goes_report_event(goes, CEVNT_BADDATE); - return; - } - if (goes->hour > 23 || goes->minute > 59 - || goes->second > 59) { - goes->baddata++; - goes_report_event(goes, CEVNT_BADTIME); - return; - } + /* + * We get down to business, check the timecode format and decode + * its contents. This code decodes a multitude of different + * clock messages. Timecodes are processed if needed. All replies + * will be run through the state machine to tweak driver options + * and program the clock. + */ - } else if (goes->lencode == LENGOES2) { + /* + * Timecode: "nnnnn+nnn-nnn" + */ + if (sscanf(pp->lastcode, "%5d%c%3d%c%3d", + &lon, &c1, &lat, &c2, &off) == 5 && + (c1 == '+' || c1 == '-') && + (c2 == '+' || c2 == '-')) { /* - * Extended precision satelite location info + * This is less than perfect. Call the (satellite) + * either EAST or WEST and adjust slop accodingly + * Perfectionists would recalcuted the exact delay + * and adjust accordingly... */ - if (!isdigit(cp[0]) || /* longitude */ - !isdigit(cp[1]) || - !isdigit(cp[2]) || - cp[3] != '.' || - !isdigit(cp[4]) || - !isdigit(cp[5]) || - !isdigit(cp[6]) || - !isdigit(cp[7]) || - (cp[8] != '+' && cp[8] != '-') || - !isdigit(cp[9]) || /*latitude */ - cp[10] != '.' || - !isdigit(cp[11]) || - !isdigit(cp[12]) || - !isdigit(cp[13]) || - !isdigit(cp[14]) || - (cp[15] != '+' && cp[15] != '-') || - !isdigit(cp[16]) || /* height */ - !isdigit(cp[17]) || - !isdigit(cp[18]) || - cp[19] != '.' || - !isdigit(cp[20])) { - goes->badformat++; - goes_report_event(goes, CEVNT_BADREPLY); - return; + if (lon > 7000 && lon < 14000) { + if (lon < 10000) + new_station = GOES_EAST; + else + new_station = GOES_WEST; +#ifdef DEBUG + if (debug) { + if (new_station == GOES_EAST) + printf("goes: station EAST\n"); + if (new_station == GOES_WEST) + printf("goes: station WEST\n"); } - else goes->format = FMTGOES2; - - /* - * Figure out which satellite this is. - * This allows +-5 degrees from nominal. - */ - if (cp[0] == '1' && (cp[1] == '3' || cp[1] == '2')) - goes->satellite = GOES_SAT_WEST; - else if (cp[0] == '1' && cp[1] == '0') - goes->satellite = GOES_SAT_STAND; - else if (cp[0] == '0' && cp[1] == '7') - goes->satellite = GOES_SAT_EAST; - else - goes->satellite = GOES_SAT_NONE; - +#endif + if (new_station != up->station) { + tmp_l_fp = pp->fudgetime1; + pp->fudgetime1 = pp->fudgetime2; + pp->fudgetime2 = tmp_l_fp; + up->station = new_station; + } + } + else { + refclock_report(peer, CEVNT_BADREPLY); #ifdef DEBUG - if (debug) - printf("goes_receive: select satellite %d\n", - goes->satellite); + if (debug) + printf("goes: station UNKNONW\n"); #endif - + } /* - * Switch back to on-second time codes. + * Switch back to on-second time codes and return. */ - goes_send(goes,"C"); + goes_send(peer, "C"); - /* - * Since this is not a time code, just return... - */ - return; - } else { - goes_report_event(goes, CEVNT_BADREPLY); return; } /* - * The clock will blurt a timecode every second but we only - * want one when polled. If we havn't been polled, bail out. + * Timecode: "Fnn" */ - if (!goes->polled) - return; + if (sscanf(pp->lastcode, "F%2d", &i) == 1 && + i > 0 && i < 80) { + enum goes_event event = 0; + + if (i == 50) event = e_F50; + if (i == 51) event = e_F51; + if (i == 50 || i == 51) { + goes_doevent(peer, event); + return; + } + } /* - * Now, compute the reference time value. Use the heavy - * machinery for the seconds and the millisecond field for the - * fraction when present. - * - * this code does not yet know how to do the years + * Timecode:" TRUETIME Mk III" */ - tstmp = goes->lastrec; - if (!clocktime(goes->day, goes->hour, goes->minute, - goes->second, GMT, tstmp.l_ui, - &goes->yearstart, &goes->lastref.l_ui)) { - goes->baddata++; - goes_report_event(goes, CEVNT_BADTIME); + if (strcmp(pp->lastcode, " TRUETIME Mk III") == 0) { + enum goes_event event; + + event = e_F18; + goes_doevent(peer, event); return; } - MSUTOTSF(goes->msec, goes->lastref.l_uf); - /* - * Slop the read value by fudgefactor1 or fudgefactor2 depending - * on which satellite we are viewing last time we checked. - */ + * Timecode: "ddd:hh:mm:ssQ" + */ + if (sscanf(pp->lastcode, "%3d:%2d:%2d:%2d%c", + &pp->day, &pp->hour, &pp->minute, + &pp->second, &sync) == 5) { -#ifdef DEBUG - if (debug) - printf("GOES_RECEIVE: Slopping for satellite %d\n", - goes->satellite); -#endif - if (goes->satellite == GOES_SAT_WEST) - L_ADD(&goes->lastref, &fudgefactor1[goes->unit]); - else if (goes->satellite == GOES_SAT_EAST) - L_ADD(&goes->lastref, &fudgefactor2[goes->unit]); -/* else if (goes->satellite == GOES_SAT_STAND) - L_ADD(&goes->lastref, &((fudgefactor1[goes->unit] + - fudgefactor2[goes->unit]) / 2)); */ - - i = ((int)(goes->coderecv)) % NCODES; - goes->offset[i] = goes->lastref; - L_SUB(&goes->offset[i], &tstmp); - if (goes->coderecv == 0) - for (i = 1; i < NCODES; i++) - goes->offset[i] = goes->offset[0]; - - goes->coderecv++; + /* + * Adjust the synchronize indicator according to timecode + */ + if (sync !=' ' && sync !='.' && sync !='*') + pp->leap = LEAP_NOTINSYNC; + else { + pp->leap = 0; + pp->lasttime = current_time; + } - /* - * Check the satellite position - */ - goes_send(goes,"E"); + /* goes_doevent(peer, e_TS); */ + + /* + * The clock will blurt a timecode every second but we only + * want one when polled. If we havn't been polled, bail out. + */ + if (!up->polled) + return; + + /* + * After each poll, check the station (satellite) + */ + goes_send(peer, "P"); + + /* + * Process the new sample in the median filter and determine the + * reference clock offset and dispersion. We use lastrec as both + * the reference time and receive time in order to avoid being + * cute, like setting the reference time later than the receive + * time, which may cause a paranoid protocol module to chuck out + * the data. + */ + if (!refclock_process(pp, NSAMPLES, NSAMPLES)) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + refclock_receive(peer, &pp->offset, 0, pp->dispersion, + &pp->lastrec, &pp->lastrec, pp->leap); + + /* + * We have succedded in answering the poll. + * Turn off the flag and return + */ + up->polled = 0; - /* - * Process the median filter, add the fudge factor and pass the - * offset and dispersion along. We use lastrec as both the - * reference time and receive time in order to avoid being cute, - * like setting the reference time later than the receive time, - * which may cause a paranoid protocol module to chuck out the - * data. - */ - if (!goes_process(goes, &tstmp, &dispersion)) { - goes->baddata++; - goes_report_event(goes, CEVNT_BADTIME); return; } - refclock_receive(goes->peer, &tstmp, GMT, dispersion, - &goes->lastrec, &goes->lastrec, goes->leap); /* - * We have succedded in answering the poll. Turn off the flag + * No match to known timecodes, report failure and return */ - goes->polled = 0; + refclock_report(peer, CEVNT_BADREPLY); + return; } @@ -762,95 +414,89 @@ goes_receive(rbufp) * goes_send - time to send the clock a signal to cough up a time sample */ static void -goes_send(goes,cmd) - struct goesunit *goes; +goes_send(peer, cmd) + struct peer *peer; char *cmd; { - if (!readonlyclockflag[goes->unit]) { - /* - * Send a command to the clock. C for on-second timecodes. - * E for extended resolution satelite postion information. - */ - if (write(goes->io.fd, cmd, 1) != 1) { - syslog(LOG_ERR, "goes_send: unit %d: %m", goes->unit); - goes_report_event(goes, CEVNT_FAULT); - } else { - goes->polls++; - } + struct refclockproc *pp; + register int len = strlen(cmd); + + pp = peer->procptr; + if (!pp->sloppyclockflag & CLK_FLAG1) { +#ifdef DEBUG + if (debug) + printf("goes: Send '%s'\n", cmd); +#endif + if (write(pp->io.fd, cmd, len) != len) { + refclock_report(peer, CEVNT_FAULT); + } else { + pp->polls++; + } } } + /* - * goes_process - process a pile of samples from the clock + * state machine for initializing the clock */ -static char -goes_process(goes, offset, dispersion) - struct goesunit *goes; - l_fp *offset; - u_fp *dispersion; +static void +goes_doevent(peer, event) + struct peer *peer; + enum goes_event event; { - register int i, j; - register U_LONG tmp_ui, tmp_uf; - int not_median1 = -1; /* XXX correct? */ - int not_median2 = -1; /* XXX correct? */ - int median; - u_fp disp_tmp, disp_tmp2; + struct goesunit *up; + struct refclockproc *pp; - /* - * This code implements a three-stage median filter. First, we - * check if the samples are within 125 ms of each other. If not, - * dump the sample set. We take the median of the three offsets - * and use that as the sample offset. We take the maximum - * difference and use that as the sample dispersion. There - * probably is not much to be gained by a longer filter, since - * the clock filter in ntp_proto should do its thing. - */ - disp_tmp2 = 0; - for (i = 0; i < NCODES-1; i++) { - for (j = i+1; j < NCODES; j++) { - tmp_ui = goes->offset[i].l_ui; - tmp_uf = goes->offset[i].l_uf; - M_SUB(tmp_ui, tmp_uf, goes->offset[j].l_ui, - goes->offset[j].l_uf); - if (M_ISNEG(tmp_ui, tmp_uf)) { - M_NEG(tmp_ui, tmp_uf); - } - if (tmp_ui != 0 || tmp_uf > CODEDIFF) { - return 0; - } - disp_tmp = MFPTOFP(0, tmp_uf); - if (disp_tmp > disp_tmp2) { - disp_tmp2 = disp_tmp; - not_median1 = i; - not_median2 = j; - } - } + pp = peer->procptr; + up = (struct goesunit *)pp->unitptr; + +#ifdef DEBUG + if (debug) { + printf("goes_doevent: %d\n", (int)event); + } +#endif + if (event == e_TS && up->State != F51 && up->State != F08) { + goes_send(peer, "\03\r"); } - /* - * It seems as if all are within 125 ms of each other. - * Now to determine the median of the three. Whlie the - * 125 ms check was going on, we also subtly catch the - * dispersion and set-up for a very easy median calculation. - * The largest difference between any two samples constitutes - * the dispersion. The sample not involve in the dispersion is - * the median sample. EASY! - */ - if (goes->lasttime == 0 || disp_tmp2 > GOESMAXDISPERSE) - disp_tmp2 = GOESMAXDISPERSE; - if (not_median1 == 0) { - if (not_median2 == 1) - median = 2; - else - median = 1; - } else { - median = 0; - } - *offset = goes->offset[median]; - *dispersion = disp_tmp2; - return 1; + switch (event) { + case e_Init: + goes_send(peer, "F18\r"); + up->State = Start; + break; + case e_F18: + goes_send(peer, "F50\r"); + up->State = F18; + break; + case e_F50: + goes_send(peer, "F51\r"); + up->State = F50; + break; + case e_F51: + goes_send(peer, "F08\r"); + up->State = F51; + break; + case e_TS: + /* nothing to send - we like this mode */ + up->State = F08; + break; + } +} + +static void +goes_initstate(peer) + struct peer *peer; +{ + struct goesunit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct goesunit *)pp->unitptr; + up->State = Base; /* just in case */ + goes_doevent(peer, e_Init); } + /* * goes_poll - called by the transmit procedure */ @@ -859,155 +505,29 @@ goes_poll(unit, peer) int unit; struct peer *peer; { - struct goesunit *goes; + struct goesunit *up; + struct refclockproc *pp; /* * You don't need to poll this clock. It puts out timecodes * once per second. If asked for a timestamp, take note. * The next time a timecode comes in, it will be fed back. */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "goes_poll: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "goes_poll: unit %d not in use", unit); - return; - } - goes = goesunits[unit]; - if ((current_time - goes->lasttime) > 150) { - goes->noreply++; - goes_report_event(goesunits[unit], CEVNT_TIMEOUT); + pp = peer->procptr; + up = (struct goesunit *)pp->unitptr; + if (up->pollcnt == 0) { + refclock_report(peer, CEVNT_TIMEOUT); + goes_send(peer, "C"); } + else + up->pollcnt--; /* - * polled every 64 seconds. Ask GOES_RECEIVE to hand in a timestamp. + * polled every 64 seconds. Ask goes_receive to hand in a + * timestamp. */ - goes->polled = 1; - goes->polls++; - - goes_send(goes,"C"); -} - -/* - * goes_control - set fudge factors, return statistics - */ -static void -goes_control(unit, in, out) - u_int unit; - struct refclockstat *in; - struct refclockstat *out; -{ - register struct goesunit *goes; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "goes_control: unit %d invalid", unit); - return; - } - goes = goesunits[unit]; - - if (in != 0) { - if (in->haveflags & CLK_HAVETIME1) - fudgefactor1[unit] = in->fudgetime1; - if (in->haveflags & CLK_HAVETIME2) - fudgefactor2[unit] = in->fudgetime2; - if (in->haveflags & CLK_HAVEVAL1) { - stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf); - if (unitinuse[unit]) { - struct peer *peer; - - /* - * Should actually reselect clock, but - * will wait for the next timecode - */ - peer = goes->peer; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, - GOESREFID, 4); - else - peer->refid = htonl(GOESHSREFID); - } - } - if (in->haveflags & CLK_HAVEFLAG1) { - readonlyclockflag[unit] = in->flags & CLK_FLAG1; - } - } - - if (out != 0) { - out->type = REFCLK_GOES_TRUETIME; - out->haveflags - = CLK_HAVETIME1|CLK_HAVETIME2| - CLK_HAVEVAL1|CLK_HAVEVAL2| - CLK_HAVEFLAG1|CLK_HAVEFLAG2; - out->clockdesc = GOESDESCRIPTION; - out->fudgetime1 = fudgefactor1[unit]; - out->fudgetime2 = fudgefactor2[unit]; - out->fudgeval1 = (LONG)stratumtouse[unit]; - out->fudgeval2 = 0; - out->flags = readonlyclockflag[unit] | - (goes->satellite << 1); - if (unitinuse[unit]) { - out->lencode = goes->lencode; - out->lastcode = goes->lastcode; - out->timereset = current_time - goes->timestarted; - out->polls = goes->polls; - out->noresponse = goes->noreply; - out->badformat = goes->badformat; - out->baddata = goes->baddata; - out->lastevent = goes->lastevent; - out->currentstatus = goes->status; - } else { - out->lencode = 0; - out->lastcode = ""; - out->polls = out->noresponse = 0; - out->badformat = out->baddata = 0; - out->timereset = 0; - out->currentstatus = out->lastevent = CEVNT_NOMINAL; - } - } + up->polled = 1; + pp->polls++; } -/* - * goes_buginfo - return clock dependent debugging info - */ -static void -goes_buginfo(unit, bug) - int unit; - register struct refclockbug *bug; -{ - register struct goesunit *goes; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "goes_buginfo: unit %d invalid", unit); - return; - } - - if (!unitinuse[unit]) - return; - goes = goesunits[unit]; - - bug->nvalues = 11; - bug->ntimes = 5; - if (goes->lasttime != 0) - bug->values[0] = current_time - goes->lasttime; - else - bug->values[0] = 0; - bug->values[1] = (U_LONG)goes->reason; - bug->values[2] = (U_LONG)goes->year; - bug->values[3] = (U_LONG)goes->day; - bug->values[4] = (U_LONG)goes->hour; - bug->values[5] = (U_LONG)goes->minute; - bug->values[6] = (U_LONG)goes->second; - bug->values[7] = (U_LONG)goes->msec; - bug->values[8] = goes->noreply; - bug->values[9] = goes->yearstart; - bug->values[10] = goes->quality; - bug->stimes = 0x1c; - bug->times[0] = goes->lastref; - bug->times[1] = goes->lastrec; - bug->times[2] = goes->offset[0]; - bug->times[3] = goes->offset[1]; - bug->times[4] = goes->offset[2]; -} #endif |