aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/xntpd/xntpd/refclock_omega.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/xntpd/xntpd/refclock_omega.c')
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_omega.c1015
1 files changed, 1015 insertions, 0 deletions
diff --git a/usr.sbin/xntpd/xntpd/refclock_omega.c b/usr.sbin/xntpd/xntpd/refclock_omega.c
new file mode 100644
index 000000000000..4dee7aa74aa7
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_omega.c
@@ -0,0 +1,1015 @@
+/*
+ * refclock_omega - clock driver for the Kinemetrics Truetime OM-DC OMEGA
+ * receiver.
+ *
+ * Version 1.0 11-Dec-92 Steve Clift (clift@ml.csiro.au)
+ * Initial version, mostly lifted from refclock_goes.c.
+ *
+ * 1.1 03-May-93 Steve Clift
+ * Tarted up the sample filtering mechanism to give improved
+ * one-off measurements. Improved measurement dispersion code
+ * to account for accumulated drift when the clock loses lock.
+ *
+ */
+
+#if defined(REFCLOCK) && (defined(OMEGA) || defined(OMEGACLK) || defined(OMEGAPPS))
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#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(STREAM)
+#include <termios.h>
+#include <stropts.h>
+#if defined(OMEGACLK)
+#include <sys/clkdefs.h>
+#endif /* OMEGACLK */
+#endif /* STREAM */
+
+#if defined (OMEGAPPS)
+#include <sys/ppsclock.h>
+#endif /* OMEGAPPS */
+
+#include "ntp_stdlib.h"
+
+/*
+ * Support for Kinemetrics Truetime OM-DC OMEGA Receiver
+ *
+ * Most of this code is copied from refclock_goes.c with thanks.
+ *
+ * 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
+ * > >+- 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.
+ * C - Carriage return
+ * L - Line feed
+ * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
+ */
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* max number of OMEGA units */
+#define OMEGA232 "/dev/omega%d"
+#define SPEED232 B9600 /* 9600 baud */
+
+/*
+ * Radio interface parameters
+ */
+#define OMEGADESCRIPTION "Kinemetrics OM-DC OMEGA Receiver" /* who we are */
+#define OMEGAMAXDISPERSE (FP_SECOND/32) /* max allowed sample dispersion */
+#define OMEGAPRECISION (-10) /* precision assumed (about 1 ms) */
+#define OMEGAREFID "VLF\0" /* reference id */
+#define OMEGAHSREFID 0x7f7f0b0a /* 127.127.11.10 refid hi strata */
+#define LENOMEGA 13 /* length of standard response */
+#define GMT 0 /* hour offset from Greenwich */
+#define NSTAMPS 9 /* samples collected when polled */
+#define NSKEEP 5 /* samples to keep after discards */
+#define BMAX 50 /* timecode buffer length */
+
+/*
+ * The OM-DC puts out the start bit of the <CR> on the second, but
+ * we see the result after the <LF> is received, about 2ms later at
+ * 9600 baud. Use this as the default fudge time, and let the user
+ * fiddle it to account for driver latency etc.
+ */
+#define DEFFUDGETIME 0x00830000 /* default fudge time (~2ms) */
+
+/*
+ * Clock drift errors as u_fp values.
+ */
+#define U_FP5000MS (5*FP_SECOND) /* 5 seconds */
+#define U_FP500MS (FP_SECOND/2) /* 500 msec */
+#define U_FP50MS (FP_SECOND/20) /* 50 msec */
+#define U_FP5MS (FP_SECOND/200) /* 5 msec */
+
+/*
+ * Station codes
+ */
+#define STATION_NONE 0
+#define STATION_NORWAY 1
+#define STATION_LIBERIA 2
+#define STATION_HAWAII 3
+#define STATION_N_DAKOTA 4
+#define STATION_LA_REUNION 5
+#define STATION_ARGENTINA 6
+#define STATION_AUSTRALIA 7
+#define STATION_JAPAN 8
+
+/*
+ * Hack to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+
+/*
+ * 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 */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * OMEGA unit control structure
+ */
+struct omegaunit {
+ 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[NSTAMPS]; /* recent sample offsets */
+ char lastcode[BMAX]; /* last timecode received */
+ u_short station; /* which station we're locked to */
+ u_short polled; /* Hand in a time sample? */
+ U_LONG coderecv; /* timecodes received */
+ 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 failure */
+ 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 last timecode */
+ U_LONG yearstart; /* start of current year */
+ /*
+ * Status tallies
+ */
+ U_LONG polls; /* polls sent */
+ U_LONG noreply; /* no replies to polls */
+ U_LONG badformat; /* bad format */
+ U_LONG baddata; /* bad data */
+ U_LONG timestarted; /* time we started this */
+};
+
+/*
+ * Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back.
+ */
+static struct omegaunit *omegaunits[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 omega_init P((void));
+static int omega_start P((u_int, struct peer *));
+static void omega_shutdown P((int));
+static void omega_report_event P((struct omegaunit *, int));
+static void omega_receive P((struct recvbuf *));
+static char omega_process P((struct omegaunit *, l_fp *, u_fp *));
+static void omega_poll P((int, struct peer *));
+static void omega_control P((u_int, struct refclockstat *, struct refclockstat *));
+static void omega_buginfo P((int, struct refclockbug *));
+static void omega_send P((struct omegaunit *, char *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_omega = {
+ omega_start, omega_shutdown, omega_poll,
+ omega_control, omega_init, omega_buginfo, NOFLAGS
+};
+
+/*
+ * omega_init - initialize internal omega driver data
+ */
+static void
+omega_init()
+{
+ register int i;
+ /*
+ * Just zero the data arrays
+ */
+ bzero((char *)omegaunits, sizeof omegaunits);
+ bzero((char *)unitinuse, 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 = 0;
+ stratumtouse[i] = 0;
+ readonlyclockflag[i] = 0;
+ }
+}
+
+
+/*
+ * omega_start - open the OMEGA devices and initialize data for processing
+ */
+static int
+omega_start(unit, peer)
+ u_int unit;
+ struct peer *peer;
+{
+ register struct omegaunit *omega;
+ register int i;
+ int fd232;
+ char omegadev[20];
+
+ /*
+ * Check configuration info
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR,"omega_start: unit %d invalid", unit);
+ return 0;
+ }
+ if (unitinuse[unit]) {
+ syslog(LOG_ERR, "omega_start: unit %d in use", unit);
+ return 0;
+ }
+
+ /*
+ * Open serial port
+ */
+ (void) sprintf(omegadev, OMEGA232, unit);
+ fd232 = open(omegadev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ syslog(LOG_ERR, "omega_start: open of %s: %m", omegadev);
+ 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,
+ "omega_start: ioctl(%s, TCGETA): %m", omegadev);
+ 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,
+ "omega_start: ioctl(%s, TCSETA): %m", omegadev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(STREAM)
+ /*
+ * POSIX/STREAMS serial line parameters (termios interface)
+ *
+ * The OMEGACLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module.
+ *
+ * The OMEGAPPS 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,
+ "omega_start: tcgetattr(%s): %m", omegadev);
+ 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,
+ "omega_start: tcsetattr(%s): %m", omegadev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: tcflush(%s): %m", omegadev);
+ goto screwed;
+ }
+#if defined(OMEGACLK)
+ if (ioctl(fd232, I_PUSH, "clk") < 0)
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, I_PUSH, clk): %m", omegadev);
+ if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, CLK_SETSTR): %m", omegadev);
+#endif /* OMEGACLK */
+#if defined(OMEGAPPS)
+ if (ioctl(fd232, I_PUSH, "ppsclock") < 0)
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, I_PUSH, ppsclock): %m", omegadev);
+ else
+ fdpps = fd232;
+#endif /* OMEGAPPS */
+ }
+#endif /* STREAM */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ *
+ * The OMEGACLK option provides timestamping at the driver level.
+ * It requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ { struct sgttyb ttyb;
+#if defined(OMEGACLK)
+ int ldisc = CLKLDISC;
+#endif /* OMEGACLK */
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TIOCGETP): %m", omegadev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+#if defined(OMEGACLK)
+ 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 /* OMEGACLK */
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TIOCSETP): %m", omegadev);
+ goto screwed;
+ }
+#if defined(OMEGACLK)
+ if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TIOCSETD): %m",omegadev);
+ goto screwed;
+ }
+#endif /* OMEGACLK */
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Allocate unit structure
+ */
+ if (omegaunits[unit] != 0) {
+ omega = omegaunits[unit]; /* The one we want is okay */
+ } else {
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && omegaunits[i] != 0)
+ break;
+ }
+ if (i < MAXUNITS) {
+ /*
+ * Reclaim this one
+ */
+ omega = omegaunits[i];
+ omegaunits[i] = 0;
+ } else {
+ omega = (struct omegaunit *)
+ emalloc(sizeof(struct omegaunit));
+ }
+ }
+ bzero((char *)omega, sizeof(struct omegaunit));
+ omegaunits[unit] = omega;
+
+ /*
+ * Set up the structures
+ */
+ omega->peer = peer;
+ omega->unit = (u_char)unit;
+ omega->timestarted = current_time;
+ omega->station = STATION_NONE;
+
+ omega->io.clock_recv = omega_receive;
+ omega->io.srcclock = (caddr_t)omega;
+ omega->io.datalen = 0;
+ omega->io.fd = fd232;
+ if (!io_addclock(&omega->io)) {
+ goto screwed;
+ }
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success.
+ */
+ peer->precision = OMEGAPRECISION;
+ peer->rootdelay = 0;
+ peer->rootdispersion = 0;
+ peer->stratum = stratumtouse[unit];
+ if (stratumtouse[unit] <= 1)
+ bcopy(OMEGAREFID, (char *)&peer->refid, 4);
+ else
+ peer->refid = htonl(OMEGAHSREFID);
+ unitinuse[unit] = 1;
+ return 1;
+
+ /*
+ * Something broke; abandon ship
+ */
+screwed:
+ (void) close(fd232);
+ return 0;
+}
+
+
+/*
+ * omega_shutdown - shut down a OMEGA clock
+ */
+static void
+omega_shutdown(unit)
+ int unit;
+{
+ register struct omegaunit *omega;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "omega_shutdown: unit %d invalid",
+ unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "omega_shutdown: unit %d not in use", unit);
+ return;
+ }
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ omega = omegaunits[unit];
+ io_closeclock(&omega->io);
+ unitinuse[unit] = 0;
+}
+
+
+/*
+ * omega_report_event - note the occurance of an event
+ */
+static void
+omega_report_event(omega, code)
+ struct omegaunit *omega;
+ int code;
+{
+ struct peer *peer;
+
+ peer = omega->peer;
+ if (omega->status != (u_char)code) {
+ omega->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ omega->lastevent = (u_char)code;
+ syslog(LOG_INFO,
+ "omega clock %s event %x\n", ntoa(&peer->srcadr), code);
+ }
+}
+
+
+/*
+ * omega_receive - receive data from the serial interface on a
+ * Kinemetrics OM-DC OMEGA clock.
+ */
+static void
+omega_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register int i;
+ register struct omegaunit *omega;
+ register u_char *dpt;
+ register char *cp, *cpend;
+ register u_char *dpend;
+ l_fp tstmp;
+ u_fp dispersion, drift;
+
+ /*
+ * Get the clock this applies to and a pointers to the data
+ */
+ omega = (struct omegaunit *)rbufp->recv_srcclock;
+ dpt = (u_char *)&rbufp->recv_space;
+
+#ifndef PEDANTIC
+ /*
+ * The OM-DC outputs a timecode every second, but we only want
+ * a set of NSTAMPS timecodes when polled (every 64 seconds).
+ * Setting PEDANTIC causes a sanity check on every timecode.
+ */
+ if (!omega->polled)
+ return;
+#endif
+
+ /*
+ * Edit timecode to remove control chars
+ */
+ dpend = dpt + rbufp->recv_length;
+ cp = omega->lastcode;
+ cpend = omega->lastcode + BMAX - 1;
+ while (dpt < dpend && cp < cpend) {
+ if ((*cp = 0x7f & *dpt++) >= ' ') cp++;
+#ifdef OMEGACLK
+ else if (*cp == '\r') {
+ if (dpend - dpt < 8) {
+ /* short timestamp */
+ return;
+ }
+ if (!buftvtots(dpt,&omega->lastrec)) {
+ /* screwy timestamp */
+ return;
+ }
+ dpt += 8;
+ }
+#endif
+ }
+ *cp = '\0';
+ omega->lencode = cp - omega->lastcode;
+
+ if (omega->lencode == 0)
+ return;
+ else if (omega->lencode != LENOMEGA) {
+ omega->badformat++;
+ /* Sometimes get a lot of these, filling the log with noise */
+ /* omega_report_event(omega, CEVNT_BADREPLY); */
+ return;
+ }
+
+#ifndef OMEGACLK
+ omega->lastrec = rbufp->recv_time;
+#endif
+
+#ifdef DEBUG
+ if (debug)
+ printf("omega: timecode %d %s\n",
+ omega->lencode, omega->lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format
+ * and decode its contents.
+ */
+ cp = omega->lastcode;
+ omega->leap = 0;
+ /*
+ * Check timecode format.
+ */
+ 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])) {
+ omega->badformat++;
+ omega_report_event(omega, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Convert and check values.
+ */
+ omega->year = 0; /* fake */
+ omega->day = cp[0] - '0';
+ omega->day = MULBY10(omega->day) + cp[1] - '0';
+ omega->day = MULBY10(omega->day) + cp[2] - '0';
+ omega->hour = MULBY10(cp[4] - '0') + cp[5] - '0';
+ omega->minute = MULBY10(cp[7] - '0') + cp[8] - '0';
+ omega->second = MULBY10(cp[10] - '0') + cp[11] - '0';
+ omega->msec = 0;
+
+ if (omega->day < 1 || omega->day > 366) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADDATE);
+ return;
+ }
+ if (omega->hour > 23 || omega->minute > 59 || omega->second > 59) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Check quality/station-id flag. The OM-DC should normally stay
+ * permanently locked to a station, and its time error should be less
+ * than 1 msec. If it loses lock for any reason, it makes a worst
+ * case drift estimate based on the internally stored stability figure
+ * for its reference oscillator. The stability figure can be adjusted
+ * by the user based on experience. The default value is 1E05, which
+ * is pretty bad - 2E07 is about right for the unit I have.
+ *
+ * The following is arbitrary, change it if you're offended:
+ * For errors less than 50 msec, just clear the station indicator.
+ * For errors greater than 50 msec, flag loss of sync and report a
+ * propagation problem. If the error is greater than 500 msec,
+ * something is dreadfully wrong - report a clock fault.
+ *
+ * In each case, we set a drift estimate which is used below as an
+ * estimate of measurement accuracy.
+ */
+ omega->quality = cp[12];
+ if (cp[12] == '>' || cp[12] == '?') {
+ /* Error 500 to 5000 msec */
+ omega_report_event(omega, CEVNT_FAULT);
+ omega->leap = LEAP_NOTINSYNC;
+ omega->station = STATION_NONE;
+ drift = U_FP5000MS;
+ } else if (cp[12] == '#') {
+ /* Error 50 to 500 msec */
+ omega_report_event(omega, CEVNT_PROP);
+ omega->leap = LEAP_NOTINSYNC;
+ omega->station = STATION_NONE;
+ drift = U_FP500MS;
+ } else if (cp[12] == '*') {
+ /* Error 5 to 50 msec */
+ omega->lasttime = current_time;
+ omega->station = STATION_NONE;
+ drift = U_FP50MS;
+ } else if (cp[12] == '.') {
+ /* Error 1 to 5 msec */
+ omega->lasttime = current_time;
+ omega->station = STATION_NONE;
+ drift = U_FP5MS;
+ } else if ('A' <= cp[12] && cp[12] <= 'H') {
+ /* Error less than 1 msec */
+ omega->lasttime = current_time;
+ omega->station = cp[12] - 'A' + 1;
+ drift = 0;
+ } else {
+ omega->badformat++;
+ omega_report_event(omega, CEVNT_BADREPLY);
+ return;
+ }
+
+#ifdef PEDANTIC
+ /* If we haven't been polled, bail out. */
+ if (!omega->polled)
+ return;
+#endif
+
+ /*
+ * 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
+ */
+ tstmp = omega->lastrec;
+ if (!clocktime(omega->day, omega->hour, omega->minute,
+ omega->second, GMT, tstmp.l_ui,
+ &omega->yearstart, &omega->lastref.l_ui)) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADTIME);
+ return;
+ }
+ MSUTOTSF(omega->msec, omega->lastref.l_uf);
+
+ /*
+ * Adjust the read value by fudgefactor1 to correct RS232 delays.
+ */
+ L_ADD(&omega->lastref, &fudgefactor1[omega->unit]);
+
+ /* Carousel of NSTAMPS offsets. */
+ i = omega->coderecv % NSTAMPS;
+ omega->offset[i] = omega->lastref;
+ L_SUB(&omega->offset[i], &tstmp);
+ omega->coderecv++;
+
+ /* If we don't yet have a full set, return. */
+ if (omega->coderecv < NSTAMPS)
+ return;
+
+ /*
+ * Filter the samples, 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 the sample filter chokes because of excessive
+ * dispersion or whatever, get a new sample (omega->coderecv
+ * is still >= NSTAMPS) and try again.
+ */
+ if (!omega_process(omega, &tstmp, &dispersion)) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Add accumulated clock drift to the dispersion to get
+ * a (hopefully) meaningful measurement accuracy estimate.
+ */
+ dispersion += drift;
+ refclock_receive(omega->peer, &tstmp, GMT, dispersion,
+ &omega->lastrec, &omega->lastrec, omega->leap);
+
+ /*
+ * We have succeeded in answering the poll. If the clock
+ * is locked, we're nominal.
+ */
+ omega->polled = 0;
+ omega->coderecv = 0;
+ if (omega->leap != LEAP_NOTINSYNC)
+ omega_report_event(omega, CEVNT_NOMINAL);
+}
+
+
+/*
+ * omega_send - time to send the clock a signal to cough up a time sample
+ */
+static void
+omega_send(omega,cmd)
+ struct omegaunit *omega;
+ char *cmd;
+{
+ if (!readonlyclockflag[omega->unit]) {
+ /*
+ * Send a command to the clock.
+ */
+ if (write(omega->io.fd, cmd, 1) != 1) {
+ syslog(LOG_ERR, "omega_send: unit %d: %m", omega->unit);
+ omega_report_event(omega, CEVNT_FAULT);
+ }
+ }
+}
+
+
+/*
+ * Compare two l_fp's, used with qsort()
+ */
+static int
+omega_cmpl_fp(p1, p2)
+ register void *p1, *p2;
+{
+
+ if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2))
+ return (-1);
+ if (L_ISEQU((l_fp *)p1, (l_fp *)p2))
+ return (0);
+ return (1);
+}
+
+
+/*
+ * omega_process - process a pile of samples from the clock
+ */
+static char
+omega_process(omega, offset, dispersion)
+ struct omegaunit *omega;
+ l_fp *offset;
+ u_fp *dispersion;
+{
+ register int i, n;
+ register U_LONG med_ui, med_uf, tmp_ui, tmp_uf;
+ l_fp off[NSTAMPS];
+ u_fp disp;
+
+ /* Copy in offsets and sort into ascending order */
+ for (i = 0; i < NSTAMPS; i++)
+ off[i] = omega->offset[i];
+ qsort((char *)off, NSTAMPS, sizeof(l_fp), omega_cmpl_fp);
+ /*
+ * Reject the furthest from the median until NSKEEP samples remain
+ */
+ i = 0;
+ n = NSTAMPS;
+ while ((n - i) > NSKEEP) {
+ tmp_ui = off[n-1].l_ui;
+ tmp_uf = off[n-1].l_uf;
+ med_ui = off[(n+i)/2].l_ui;
+ med_uf = off[(n+i)/2].l_uf;
+ M_SUB(tmp_ui, tmp_uf, med_ui, med_uf);
+ M_SUB(med_ui, med_uf, off[i].l_ui, off[i].l_uf);
+ if (M_ISHIS(med_ui, med_uf, tmp_ui, tmp_uf)) {
+ /* reject low end */
+ i++;
+ } else {
+ /* reject high end */
+ n--;
+ }
+ }
+
+ /*
+ * Compute the dispersion based on the difference between the
+ * extremes of the remaining offsets. If this is greater than
+ * the allowed sample set dispersion, bail out. Otherwise,
+ * return the median offset and the dispersion.
+ */
+ tmp_ui = off[n-1].l_ui;
+ tmp_uf = off[n-1].l_uf;
+ M_SUB(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf);
+ disp = MFPTOFP(tmp_ui, tmp_uf);
+ if (disp > OMEGAMAXDISPERSE)
+ return 0;
+ *offset = off[(n+1)/2];
+ *dispersion = disp;
+ return 1;
+}
+
+
+/*
+ * omega_poll - called by the transmit procedure
+ */
+static void
+omega_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct omegaunit *omega;
+
+ /*
+ * 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, "omega_poll: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "omega_poll: unit %d not in use", unit);
+ return;
+ }
+ omega = omegaunits[unit];
+ if ((current_time - omega->lasttime) > 150) {
+ omega->noreply++;
+ omega_report_event(omegaunits[unit], CEVNT_TIMEOUT);
+ }
+
+ /*
+ * polled every 64 seconds. Ask OMEGA_RECEIVE to hand in a timestamp.
+ */
+ omega->polled = 1;
+ omega->polls++;
+ /*
+ * Ensure the clock is running in the correct mode - on-second
+ * timestamps.
+ */
+ omega_send(omega,"C");
+}
+
+
+/*
+ * omega_control - set fudge factors, return statistics
+ */
+static void
+omega_control(unit, in, out)
+ u_int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ register struct omegaunit *omega;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "omega_control: unit %d invalid", unit);
+ return;
+ }
+
+ 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
+ */
+ omega = omegaunits[unit];
+ peer = omega->peer;
+ peer->stratum = stratumtouse[unit];
+ if (stratumtouse[unit] <= 1)
+ bcopy(OMEGAREFID, (char *)&peer->refid,
+ 4);
+ else
+ peer->refid = htonl(OMEGAHSREFID);
+ }
+ }
+ if (in->haveflags & CLK_HAVEFLAG1) {
+ readonlyclockflag[unit] = in->flags & CLK_FLAG1;
+ }
+ }
+
+ if (out != 0) {
+ out->type = REFCLK_OMEGA_TRUETIME;
+ out->haveflags
+ = CLK_HAVETIME1|CLK_HAVETIME2|
+ CLK_HAVEVAL1|CLK_HAVEVAL2|
+ CLK_HAVEFLAG1|CLK_HAVEFLAG2;
+ out->clockdesc = OMEGADESCRIPTION;
+ out->fudgetime1 = fudgefactor1[unit];
+ out->fudgetime2 = fudgefactor2[unit];
+ out->fudgeval1 = (LONG)stratumtouse[unit];
+ out->fudgeval2 = 0;
+ out->flags = readonlyclockflag[unit];
+ if (unitinuse[unit]) {
+ omega = omegaunits[unit];
+ out->flags |= omega->station << 1;
+ out->lencode = omega->lencode;
+ out->lastcode = omega->lastcode;
+ out->timereset = current_time - omega->timestarted;
+ out->polls = omega->polls;
+ out->noresponse = omega->noreply;
+ out->badformat = omega->badformat;
+ out->baddata = omega->baddata;
+ out->lastevent = omega->lastevent;
+ out->currentstatus = omega->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;
+ }
+ }
+}
+
+
+/*
+ * omega_buginfo - return clock dependent debugging info
+ */
+static void
+omega_buginfo(unit, bug)
+ int unit;
+ register struct refclockbug *bug;
+{
+ register struct omegaunit *omega;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "omega_buginfo: unit %d invalid", unit);
+ return;
+ }
+
+ if (!unitinuse[unit])
+ return;
+ omega = omegaunits[unit];
+
+ bug->nvalues = 11;
+ bug->ntimes = 5;
+ if (omega->lasttime != 0)
+ bug->values[0] = current_time - omega->lasttime;
+ else
+ bug->values[0] = 0;
+ bug->values[1] = (U_LONG)omega->reason;
+ bug->values[2] = (U_LONG)omega->year;
+ bug->values[3] = (U_LONG)omega->day;
+ bug->values[4] = (U_LONG)omega->hour;
+ bug->values[5] = (U_LONG)omega->minute;
+ bug->values[6] = (U_LONG)omega->second;
+ bug->values[7] = (U_LONG)omega->msec;
+ bug->values[8] = omega->noreply;
+ bug->values[9] = omega->yearstart;
+ bug->values[10] = omega->quality;
+ bug->stimes = 0x1c;
+ bug->times[0] = omega->lastref;
+ bug->times[1] = omega->lastrec;
+ bug->times[2] = omega->offset[0];
+ bug->times[3] = omega->offset[1];
+ bug->times[4] = omega->offset[2];
+}
+#endif