summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/ntpd/refclock_oncore.c
diff options
context:
space:
mode:
authorroberto <roberto@FreeBSD.org>1999-12-09 13:01:21 +0000
committerroberto <roberto@FreeBSD.org>1999-12-09 13:01:21 +0000
commitef64b99e8412f2273dd2e8b3291c2f78ffc4667f (patch)
treefc0cfa1aab0ff6b228f511b410733ef4f35d1ead /contrib/ntp/ntpd/refclock_oncore.c
downloadFreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.zip
FreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.tar.gz
Virgin import of ntpd 4.0.98f
Diffstat (limited to 'contrib/ntp/ntpd/refclock_oncore.c')
-rw-r--r--contrib/ntp/ntpd/refclock_oncore.c1660
1 files changed, 1660 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/refclock_oncore.c b/contrib/ntp/ntpd/refclock_oncore.c
new file mode 100644
index 0000000..9ff3d23
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_oncore.c
@@ -0,0 +1,1660 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * refclock_oncore.c
+ *
+ * Driver for some of the various the Motorola Oncore GPS receivers.
+ *
+ * Tested with:
+ *
+ * (UT) (VP)
+ * COPYRIGHT 1991-1997 MOTOROLA INC. COPYRIGHT 1991-1996 MOTOROLA INC.
+ * SFTW P/N # 98-P36848P SFTW P/N # 98-P36830P
+ * SOFTWARE VER # 2 SOFTWARE VER # 8
+ * SOFTWARE REV # 2 SOFTWARE REV # 8
+ * SOFTWARE DATE APR 24 1998 SOFTWARE DATE 06 Aug 1996
+ * MODEL # R1121N1114 MODEL # B4121P1155
+ * HWDR P/N # 1 HDWR P/N # _
+ * SERIAL # R0010A SERIAL # SSG0226478
+ * MANUFACTUR DATE 6H07 MANUFACTUR DATE 7E02
+ * OPTIONS LIST IB
+ *
+ * --------------------------------------------------------------------------
+ * This code uses the two devices
+ * /dev/oncore.serial.n
+ * /dev/oncore.pps.n
+ * which may be linked to the same device.
+ * and can read initialization data from the file
+ * /etc/ntp.oncoreN (where n and N are the unit number, viz 127.127.30.N)
+ * or /etc/ntp.oncore
+ * --------------------------------------------------------------------------
+ * Reg.Clemens <reg@dwf.com> Sep98.
+ * Original code written for FreeBSD.
+ * With these mods it works on SunOS, Solaris (untested) and Linux
+ * (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + changes).
+ *
+ * Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the
+ * state machine state) are printed to CLOCKSTATS if that file is enabled
+ * in /etc/ntp.conf.
+ *
+ * --------------------------------------------------------------------------
+ */
+
+/*
+ * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13)
+ * doing an average of 10000 valid 2D and 3D fixes is what the automatic
+ * site survey mode does. Looking at the output from the receiver
+ * it seems like it is only using 3D fixes.
+ * When we do it ourselves, take 10000 3D fixes.
+ */
+
+#define POS_HOLD_AVERAGE 10000 /* nb, 10000s ~= 2h45m */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ONCORE)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_PPSAPI
+# ifdef HAVE_TIMEPPS_H
+# include <timepps.h>
+# else
+# ifdef HAVE_SYS_TIMEPPS_H
+# include <sys/timepps.h>
+# endif
+# endif
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef HAVE_SYS_TERMIOS_H
+#include <sys/termios.h>
+#endif
+
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+#ifndef HAVE_STRUCT_PPSCLOCKEV
+struct ppsclockev {
+# ifdef HAVE_TIMESPEC
+ struct timespec tv;
+# else
+ struct timeval tv;
+# endif
+ u_int serial;
+};
+#endif /* not HAVE_STRUCT_PPSCLOCKEV */
+
+enum receive_state {
+ ONCORE_NO_IDEA,
+ ONCORE_RESET_SENT,
+ ONCORE_TEST_SENT,
+ ONCORE_ID_SENT,
+ ONCORE_ALMANAC,
+ ONCORE_RUN
+};
+
+enum site_survey_state {
+ ONCORE_SS_UNKNOWN,
+ ONCORE_SS_HW,
+ ONCORE_SS_SW,
+ ONCORE_SS_DONE
+};
+
+struct instance {
+ int unit; /* 127.127.30.unit */
+ int ttyfd; /* TTY file descriptor */
+ int ppsfd; /* PPS file descriptor */
+#ifdef HAVE_PPSAPI
+ pps_handle_t pps_h;
+ pps_params_t pps_p;
+#endif
+ enum receive_state o_state; /* Receive state */
+
+ enum site_survey_state site_survey; /* Site Survey state */
+
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ int Bj_day;
+ int assert;
+
+ long delay; /* ns */
+ long offset; /* ns */
+
+ double ss_lat;
+ double ss_long;
+ double ss_ht;
+ int ss_count;
+ u_char ss_ht_type;
+ int posn_set;
+
+ int printed;
+ int pollcnt;
+ int polled;
+ u_int ev_serial;
+ int Rcvptr;
+ u_char Rcvbuf[500];
+ u_char Ea[77];
+ u_char En[70];
+ u_char Cj[300];
+ u_char As;
+ u_char Ay;
+ u_char Az;
+ u_char init_type;
+ s_char saw_tooth;
+};
+
+#define rcvbuf instance->Rcvbuf
+#define rcvptr instance->Rcvptr
+
+static void oncore_consume P((struct instance *));
+static void oncore_poll P((int, struct peer *));
+static void oncore_read_config P((struct instance *));
+static void oncore_receive P((struct recvbuf *));
+static void oncore_sendmsg P((int fd, u_char *, u_int));
+static void oncore_shutdown P((int, struct peer *));
+static int oncore_start P((int, struct peer *));
+static void oncore_stats P((struct instance *));
+
+static void oncore_msg_any P((struct instance *, u_char *, u_int, int));
+static void oncore_msg_As P((struct instance *, u_char *, u_int));
+static void oncore_msg_At P((struct instance *, u_char *, u_int));
+static void oncore_msg_Ay P((struct instance *, u_char *, u_int));
+static void oncore_msg_Az P((struct instance *, u_char *, u_int));
+static void oncore_msg_Bj P((struct instance *, u_char *, u_int));
+static void oncore_msg_Cf P((struct instance *, u_char *, u_int));
+static void oncore_msg_Cj P((struct instance *, u_char *, u_int));
+static void oncore_msg_Ea P((struct instance *, u_char *, u_int));
+static void oncore_msg_En P((struct instance *, u_char *, u_int));
+static void oncore_msg_Fa P((struct instance *, u_char *, u_int));
+
+struct refclock refclock_oncore = {
+ oncore_start, /* start up driver */
+ oncore_shutdown, /* shut down driver */
+ oncore_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * Understanding the next bit here is not easy unless you have a manual
+ * for the the UT or VP Oncore.
+ */
+
+static struct {
+ const char flag[3];
+ const int len;
+ void (*handler) P((struct instance *, u_char *, u_int));
+ const char *fmt;
+} oncore_messages[] = {
+ /* Ea and En first since they're most common */
+ { "Ea", 76, oncore_msg_Ea, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC" },
+ { "En", 69, oncore_msg_En, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC" },
+ { "Ab", 10, 0, "" },
+ { "Ac", 11, 0, "" },
+ { "Ad", 11, 0, "" },
+ { "Ae", 11, 0, "" },
+ { "Af", 15, 0, "" },
+ { "As", 20, oncore_msg_As, "" },
+ { "At", 8, oncore_msg_At, "" },
+ { "Aw", 8, 0, "" },
+ { "Ay", 11, oncore_msg_Ay, "" },
+ { "Az", 11, oncore_msg_Az, "" },
+ { "AB", 8, 0, "" },
+ { "Bb", 92, 0, "" },
+ { "Bj", 8, oncore_msg_Bj, "" },
+ { "Cb", 33, 0, "" },
+ { "Cf", 7, oncore_msg_Cf, "" },
+ { "Cg", 8, 0, "" },
+ { "Ch", 9, 0, "" },
+ { "Cj", 294, oncore_msg_Cj, "" },
+ { "Ek", 71, 0, "" },
+ { "Fa", 9, oncore_msg_Fa, "" },
+ { "Sz", 8, 0, "" },
+ { {0}, 7, 0, ""}
+};
+
+
+/*
+ * Position Set.
+ */
+u_char oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 };
+u_char oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 };
+u_char oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 };
+
+/*
+ * Position-Hold Mode
+ * Start automatic site survey
+ */
+static u_char oncore_cmd_At[] = { 'A', 't', 2 };
+
+/*
+ * Position-Hold Position
+ */
+u_char oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 };
+u_char oncore_cmd_Asx[]= { 'A', 's', 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff, 0xff };
+
+/*
+ * Set to UTC time (not GPS).
+ */
+u_char oncore_cmd_Aw[] = { 'A', 'w', 1 };
+
+/*
+ * Read back PPS Offset for Output
+ */
+u_char oncore_cmd_Ay[] = { 'A', 'y', 0, 0, 0, 0 };
+u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff };
+
+/*
+ * Read back Cable Delay for Output
+ */
+u_char oncore_cmd_Az[] = { 'A', 'z', 0, 0, 0, 0 };
+u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff };
+
+/*
+ * Application type = static.
+ */
+u_char oncore_cmd_AB[] = { 'A', 'B', 4 };
+
+/*
+ * Visible Satellite Status Msg.
+ */
+u_char oncore_cmd_Bb[] = { 'B', 'b', 0 }; /* just turn off */
+
+/*
+ * Leap Second Pending Message
+ * Request message once
+ */
+u_char oncore_cmd_Bj[] = { 'B', 'j', 0 };
+
+/*
+ * Set to Defaults
+ */
+static u_char oncore_cmd_Cf[] = { 'C', 'f' };
+
+/*
+ * Set to Position Fix mode (only needed on VP).
+ */
+u_char oncore_cmd_Cg[] = { 'C', 'g', 1 };
+
+/*
+ * Receiver Id
+ */
+static u_char oncore_cmd_Cj[] = { 'C', 'j' };
+
+/*
+ * Position/Status/Data message
+ * Send once per second
+ */
+static u_char oncore_cmd_Ea[] = { 'E', 'a', 1 };
+
+/*
+ * Position/Status Extension Msg
+ */
+u_char oncore_cmd_Ek[] = { 'E', 'k', 0 }; /* just turn off */
+
+/*
+ * Time Raim Setup & Status Message
+ * Send once per second
+ * Time-RAIM on
+ * Alarm limit 1us
+ * PPS on when we have the first sat
+ */
+static u_char oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/*
+ * Self-test
+ */
+static u_char oncore_cmd_Fa[] = { 'F', 'a' };
+
+#define DEVICE1 "/dev/oncore.serial.%d" /* name of serial device */
+#define DEVICE2 "/dev/oncore.pps.%d" /* name of pps device */
+#define INIT_FILE "/etc/ntp.oncore" /* optional init file */
+
+#define SPEED B9600 /* Oncore Binary speed (9600 bps) */
+
+/*
+ * Assemble and disassemble 32bit signed quantities from a buffer.
+ *
+ */
+
+ /* to buffer, int w, u_char *buf */
+#define w32_buf(buf,w) { unsigned int i_tmp; \
+ i_tmp = (w<0) ? (~(-w)+1) : (w); \
+ (buf)[0] = (i_tmp >> 24) & 0xff; \
+ (buf)[1] = (i_tmp >> 16) & 0xff; \
+ (buf)[2] = (i_tmp >> 8) & 0xff; \
+ (buf)[3] = (i_tmp ) & 0xff; \
+ }
+
+#define w32(buf) (((buf)[0]&0xff) << 24 | \
+ ((buf)[1]&0xff) << 16 | \
+ ((buf)[2]&0xff) << 8 | \
+ ((buf)[3]&0xff) )
+
+ /* from buffer, char *buf, result to an int */
+#define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf))
+
+/*
+ * oncore_start - initialize data for processing
+ */
+static int
+oncore_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct instance *instance;
+ struct refclockproc *pp;
+ int fd1, fd2, mode;
+ char device1[30], device2[30];
+ const char *cp;
+ struct stat stat1, stat2;
+
+ /* OPEN DEVICES */
+ /* opening different devices for fd1 and fd2 presents no problems */
+ /* opening the SAME device twice, seems to be OS dependent.
+ (a) on Linux (no streams) no problem
+ (b) on SunOS (and possibly Solaris, untested), (streams)
+ never see the line discipline.
+ Since things ALWAYS work if we only open the device once, we check
+ to see if the two devices are in fact the same, then proceed to
+ do one open or two.
+ */
+
+ (void)sprintf(device1, DEVICE1, unit);
+ (void)sprintf(device2, DEVICE2, unit);
+
+ if (stat(device1, &stat1)) {
+ perror("ONCORE: stat fd1");
+ exit(1);
+ }
+
+ if (stat(device2, &stat2)) {
+ perror("ONCORE: stat fd2");
+ exit(1);
+ }
+
+ if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) {
+ /* same device here */
+ if (!(fd1 = refclock_open(device1, SPEED, LDISC_RAW
+#ifdef HAVE_PPSAPI
+#else
+ | LDISC_PPS
+#endif
+ ))) {
+ perror("ONCORE: fd1");
+ exit(1);
+ }
+ fd2 = fd1;
+ } else { /* different devices here */
+ if (!(fd1=refclock_open(device1, SPEED, LDISC_RAW))) {
+ perror("ONCORE: fd1");
+ exit(1);
+ }
+ if ((fd2=open(device2, O_RDWR)) < 0) {
+ perror("ONCORE: fd2");
+ exit(1);
+ }
+ }
+
+ /* Devices now open, initialize instance structure */
+
+ if (!(instance = (struct instance *)emalloc(sizeof *instance))) {
+ perror("malloc");
+ close(fd1);
+ return (0);
+ }
+ memset((char *) instance, 0, sizeof *instance);
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)instance;
+ instance->unit = unit;
+ instance->ttyfd = fd1;
+ instance->ppsfd = fd2;
+
+ instance->Bj_day = -1;
+ instance->assert = 1;
+
+ /* go read any input data in /etc/ntp.oncoreX */
+
+ oncore_read_config(instance);
+
+#ifdef HAVE_PPSAPI
+ if (time_pps_create(fd2, &instance->pps_h) < 0) {
+ perror("time_pps_create");
+ exit(1);
+ }
+
+ if (instance->assert) {
+ instance->pps_p.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
+ instance->pps_p.assert_offset.tv_sec = 0;
+ instance->pps_p.assert_offset.tv_nsec = 0;
+ } else {
+ instance->pps_p.mode = PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
+ instance->pps_p.clear_offset.tv_sec = 0;
+ instance->pps_p.clear_offset.tv_nsec = 0;
+ }
+ instance->pps_p.mode |= PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) {
+ perror("time_pps_setparams");
+ exit(1);
+ }
+ if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS,
+ instance->pps_p.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR),
+ PPS_TSFMT_TSPEC) < 0) {
+ perror("time_pps_kcbind");
+ }
+#endif
+ instance->pp = pp;
+ instance->peer = peer;
+ instance->o_state = ONCORE_NO_IDEA;
+ cp = "state = ONCORE_NO_IDEA";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ /*
+ * Initialize miscellaneous variables
+ */
+
+ peer->precision = -26;
+ peer->minpoll = 4;
+ peer->maxpoll = 4;
+ pp->clockdesc = "Motorola UT/VP Oncore GPS Receiver";
+ memcpy((char *)&pp->refid, "GPS\0", 4);
+
+ pp->io.clock_recv = oncore_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd1;
+ if (!io_addclock(&pp->io)) {
+ perror("io_addclock");
+ (void) close(fd1);
+ free(instance);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)instance;
+
+ /*
+ * This will start the Oncore receiver.
+ * We send info from config to Oncore later.
+ */
+
+ mode = instance->init_type;
+ if (mode == 3 || mode == 4) {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cf, sizeof oncore_cmd_Cf);
+ instance->o_state = ONCORE_RESET_SENT;
+ cp = "state = ONCORE_RESET_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ } else {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof oncore_cmd_Fa);
+ instance->o_state = ONCORE_TEST_SENT;
+ cp = "state = ONCORE_TEST_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+
+ instance->pollcnt = 2;
+
+ return (1);
+}
+
+/*
+ * Read Input file if it exists.
+ */
+static void
+oncore_read_config(
+ struct instance *instance
+ )
+{
+/*
+ * First we try to open the configuration file /etc/ntp.oncoreN, where
+ * N is the unit number viz 127.127.30.N.
+ * If we don't find it, then we try the file /etc/ntp.oncore.
+ *
+ * If we find NEITHER then we don't have the cable delay or PPS offset
+ * and we choose MODE (4) below.
+ *
+ * Five Choices for MODE
+ * (0) ONCORE is preinitialized, don't do anything to change it.
+ * nb, DON'T set 0D mode, DON'T set Delay, position...
+ * (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode.
+ * (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position,
+ * lock this in, go to 0D mode.
+ * (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode.
+ * (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position,
+ * lock this in, go to 0D mode.
+ * NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY]
+ * then this position is set as the INITIAL position of the ONCORE.
+ * This can reduce the time to first fix.
+ * -------------------------------------------------------------------------------
+ * Note that an Oncore UT without a battery backup retains NO information if it is
+ * power cycled, with a Battery Backup it remembers the almanac, etc.
+ * For an Oncore VP, there is an eeprom that will contain this data, along with the
+ * option of Battery Backup.
+ * So a UT without Battery Backup is equivalent to doing a HARD RESET on each
+ * power cycle, since there is nowhere to store the data.
+ * -------------------------------------------------------------------------------
+ *
+ * If we open one or the other of the files, we read it looking for
+ * MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET
+ * then initialize using method MODE. For Mode = (1,3) all of (LAT, LON, HT) must
+ * be present or mode reverts to (2,4).
+ *
+ * Read input file.
+ *
+ * # is comment to end of line
+ * = allowed between 1st and 2nd fields.
+ *
+ * Expect to see one line with 'MODE' as first field, followed by an integer
+ * in the range 0-4 (default = 4).
+ *
+ * Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields.
+ * All numbers are floating point.
+ * DDD.ddd
+ * DDD MMM.mmm
+ * DDD MMM SSS.sss
+ *
+ * Expect to see one line with 'HT' (or 'HTMSL' or 'HTGPS') as first field.
+ * followed by 1-2 fields. First is a number, the second is 'FT' or 'M'.
+ * for feet or meters. HT is the same as HTGPS.
+ * HTMSL = HT above mean_sea_level,
+ * HTGPS = HT above GPS ellipse.
+ *
+ * There are two optional lines, starting with DELAY and OFFSET, followed
+ * by 1 or two fields. The first is a number (a time) the second is
+ * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
+ * DELAY is cable delay, typically a few tens of ns.
+ * OFFSET is the offset of the PPS pulse from 0. (only fully implemented
+ * with the PPSAPI, we need to be able to tell the Kernel about this
+ * offset if the Kernel PLL is in use, but can only do this presently
+ * when using the PPSAPI interface. If not using the Kernel PLL,
+ * then there is no problem.
+ *
+ * There is another optional line, with either ASSERT or CLEAR on it, which
+ * determine which transition of the PPS signal is used for timing by the
+ * PPSAPI. If neither is present, then ASSERT is assumed.
+ *
+ * So acceptable input would be
+ * # these are my coordinates (RWC)
+ * LON -106 34.610
+ * LAT 35 08.999
+ * HT 1589 # could equally well say HT 5215 FT
+ * DELAY 60 ns
+ */
+
+ FILE *fd;
+ char *cp, line[100], units[2], device[20];
+ int i, j, sign, lat_flg, long_flg, ht_flg, mode;
+ double f1, f2, f3;
+
+
+ sprintf(device, "%s%d", INIT_FILE, instance->unit);
+ if ((fd=fopen(device, "r")) == NULL)
+ if ((fd=fopen(INIT_FILE, "r")) == NULL) {
+ instance->init_type = 4;
+ return;
+ }
+
+ mode = 0;
+ lat_flg = long_flg = ht_flg = 0;
+ while (fgets(line, 100, fd)) {
+ if ((cp=strchr(line, '#')))
+ *cp = '\0';
+ i = strlen(line);
+ for (j=0; j<i; j++) /* just in case lower case */
+ if (islower((int)line[j]))
+ line[j] = toupper(line[j]);
+ for (j=0; j<i; j++) /* let them use `=' between terms */
+ if (line[j] == '=')
+ line[j] = ' ';
+ for (j=0; j<i; j++)
+ if (line[j] != ' ')
+ break;
+ if (!strncmp(&line[j], "LAT", 3)) {
+ j += 3;
+ f1 = f2 = f3 = 0;
+ sscanf(&line[j], "%lf %lf %lf", &f1, &f2, &f3);
+ sign = 1;
+ if (f1 < 0) {
+ f1 = -f1;
+ sign = -1;
+ }
+ instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
+ lat_flg++;
+ } else if (!strncmp(&line[j], "LON", 3)) {
+ j += 3;
+ f1 = f2 = f3 = 0;
+ sscanf(&line[j], "%lf %lf %lf", &f1, &f2, &f3);
+ sign = 1;
+ if (f1 < 0) {
+ f1 = -f1;
+ sign = -1;
+ }
+ instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
+ long_flg++;
+ } else if (!strncmp(&line[j], "HT", 2)) {
+ instance->ss_ht_type = 0;
+ if (!strncmp(&line[j], "HTGPS", 5)) {
+ instance->ss_ht_type = 0;
+ j +=3;
+ }
+ if (!strncmp(&line[j], "HTMSL", 5)) {
+ instance->ss_ht_type = 1;
+ j +=3;
+ }
+ j += 2;
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(&line[j], "%lf %1s", &f1, units);
+ if (units[0] == 'F')
+ f1 = 0.3048 * f1;
+ instance->ss_ht = 100 * f1; /* cm */
+ ht_flg++;
+ } else if (!strncmp(&line[j], "DELAY", 5)) {
+ j += 5;
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(&line[j], "%lf %1s", &f1, units);
+ if (units[0] == 'N')
+ ;
+ else if (units[0] == 'U')
+ f1 = 1000 * f1;
+ else if (units[0] == 'M')
+ f1 = 1000000 * f1;
+ else
+ f1 = 1000000000 * f1;
+ if (f1 < 0 || f1 > 1.e9)
+ f1 = 0;
+ instance->delay = f1; /* delay in ns */
+ } else if (!strncmp(&line[j], "OFFSET", 6)) {
+ j += 6;
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(&line[j], "%lf %1s", &f1, units);
+ if (units[0] == 'N')
+ ;
+ else if (units[0] == 'U')
+ f1 = 1000 * f1;
+ else if (units[0] == 'M')
+ f1 = 1000000 * f1;
+ else
+ f1 = 1000000000 * f1;
+ if (f1 < 0 || f1 > 1.e9)
+ f1 = 0;
+ instance->offset = f1; /* offset in ns */
+ } else if (!strncmp(&line[j], "MODE", 4)) {
+ j += 4;
+ sscanf(&line[j], "%d", &mode);
+ if (mode < 0 || mode > 4)
+ mode = 4;
+ instance->init_type = mode;
+ } else if (!strncmp(&line[j], "ASSERT", 6)) {
+ instance->assert = 1;
+ } else if (!strncmp(&line[j], "CLEAR", 5)) {
+ instance->assert = 0;
+ }
+ }
+ fclose(fd);
+
+ /*
+ * OK, have read all of data file, and extracted the good stuff.
+ * If lat/long/ht specified they ALL must be specified for mode = (1,3).
+ */
+
+ instance->posn_set = 1;
+ if ((lat_flg || long_flg || ht_flg) && !(lat_flg * long_flg * ht_flg)) {
+ printf("ONCORE: incomplete data on %s\n", INIT_FILE);
+ instance->posn_set = 0;
+ if (mode == 1 || mode == 3)
+ instance->init_type++;
+ }
+}
+
+
+
+/*
+ * oncore_shutdown - shut down the clock
+ */
+static void
+oncore_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct instance *instance;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ instance = (struct instance *) pp->unitptr;
+ free(instance);
+}
+
+
+
+/*
+ * oncore_poll - called by the transmit procedure
+ */
+static void
+oncore_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct instance *instance;
+
+ instance = (struct instance *) peer->procptr->unitptr;
+ if (!instance->pollcnt)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ instance->pollcnt--;
+ peer->procptr->polls++;
+ instance->polled = 1;
+}
+
+
+
+/*
+ * move dta from NTP to buffer (toss in unlikely case it wont fit)
+ */
+static void
+oncore_receive(
+ struct recvbuf *rbufp
+ )
+{
+ u_int i;
+ u_char *p;
+ struct peer *peer;
+ struct instance *instance;
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ instance = (struct instance *) peer->procptr->unitptr;
+ p = (u_char *) &rbufp->recv_space;
+
+#if 0
+ if (debug > 4) {
+ int i;
+ printf("ONCORE: >>>");
+ for(i=0; i<rbufp->recv_length; i++)
+ printf("%02x ", p[i]);
+ printf("\n");
+ printf("ONCORE: >>>");
+ for(i=0; i<rbufp->recv_length; i++)
+ printf("%03o ", p[i]);
+ printf("\n");
+ }
+#endif
+
+ i = rbufp->recv_length;
+ if (rcvbuf+rcvptr+i > &rcvbuf[sizeof rcvbuf])
+ i = sizeof(rcvbuf) - rcvptr; /* and some char will be lost */
+ memcpy(rcvbuf+rcvptr, p, i);
+ rcvptr += i;
+ oncore_consume(instance);
+}
+
+
+
+/*
+ * Deal with any complete messages
+ */
+static void
+oncore_consume(
+ struct instance *instance
+ )
+{
+ int i, l, j, m;
+
+ while (rcvptr >= 7) {
+ if (rcvbuf[0] != '@' || rcvbuf[1] != '@') {
+ /* We're not in sync, lets try to get there */
+ for (i=1; i < rcvptr-1; i++)
+ if (rcvbuf[i] == '@' && rcvbuf[i+1] == '@')
+ break;
+ if (debug > 4)
+ printf("ONCORE: >>> skipping %d chars\n", i);
+ if (i != rcvptr)
+ memcpy(rcvbuf, rcvbuf+i, (unsigned)(rcvptr-i));
+ rcvptr -= i;
+ }
+
+ /* Ok, we have a header now */
+ l = sizeof(oncore_messages)/sizeof(oncore_messages[0]) -1;
+ for(m=0; m<l; m++)
+ if (!strncmp(oncore_messages[m].flag, (char *)(rcvbuf+2), 2))
+ break;
+ l = oncore_messages[m].len;
+#if 0
+ if (debug > 3)
+ printf("ONCORE: GOT: %c%c %d of %d entry %d\n", rcvbuf[2], rcvbuf[3], rcvptr, l, m);
+#endif
+ /* Got the entire message ? */
+
+ if (rcvptr < l)
+ return;
+
+ /* Check the checksum */
+
+ j = 0;
+ for (i = 2; i < l-3; i++)
+ j ^= rcvbuf[i];
+ if (j == rcvbuf[l-3]) {
+ oncore_msg_any(instance, rcvbuf, (unsigned) (l-3), m);
+ if (oncore_messages[m].handler)
+ oncore_messages[m].handler(instance, rcvbuf, (unsigned) (l-3));
+ } else if (debug) {
+ printf("ONCORE: Checksum mismatch! calc %o is %o\n", j, rcvbuf[l-3]);
+ printf("ONCORE: @@%c%c ", rcvbuf[2], rcvbuf[3]);
+ for (i=4; i<l; i++)
+ printf("%03o ", rcvbuf[i]);
+ printf("\n");
+ }
+
+ if (l != rcvptr)
+ memcpy(rcvbuf, rcvbuf+l, (unsigned) (rcvptr-l));
+ rcvptr -= l;
+ }
+}
+
+
+
+/*
+ * write message to Oncore.
+ */
+static void
+oncore_sendmsg(
+ int fd,
+ u_char *ptr,
+ u_int len
+ )
+{
+ u_char cs = 0;
+
+ printf("ONCORE: Send @@%c%c %d\n", ptr[0], ptr[1], len);
+ write(fd, "@@", 2);
+ write(fd, ptr, len);
+ while (len--)
+ cs ^= *ptr++;
+ write(fd, &cs, 1);
+ write(fd, "\r\n", 2);
+}
+
+
+
+static void
+oncore_msg_any(
+ struct instance *instance,
+ u_char *buf,
+ u_int len,
+ int idx
+ )
+{
+ int i;
+ const char *fmt = oncore_messages[idx].fmt;
+ const char *p;
+ struct timeval tv;
+
+ if (debug > 3) {
+ GETTIMEOFDAY(&tv, 0);
+ printf("ONCORE: %ld.%06ld\n", (long) tv.tv_sec, (long) tv.tv_usec);
+
+ if (!*fmt) {
+ printf(">>@@%c%c ", buf[2], buf[3]);
+ for(i=2; i < len && i < 2400 ; i++)
+ printf("%02x", buf[i]);
+ printf("\n");
+ return;
+ } else {
+ printf("##");
+ for (p = fmt; *p; p++) {
+ putchar(*p);
+ putchar('_');
+ }
+ printf("\n%c%c", buf[2], buf[3]);
+ i = 4;
+ for (p = fmt; *p; p++) {
+ printf("%02x", buf[i++]);
+ }
+ printf("\n");
+ }
+ }
+}
+
+
+
+/*
+ * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup
+ * not so for VP (eeprom) or UT with battery
+ */
+static void
+oncore_msg_Cf(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ const char *cp;
+
+ if (instance->o_state == ONCORE_RESET_SENT) {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof oncore_cmd_Fa);
+ instance->o_state = ONCORE_TEST_SENT;
+ cp = "state = ONCORE_TEST_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+}
+
+
+
+static void
+oncore_msg_Fa(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ const char *cp;
+
+ if (instance->o_state == ONCORE_TEST_SENT) {
+ if (debug > 2)
+ printf("ONCORE: >>@@Fa %x %x\n", buf[4], buf[5]);
+ if (buf[4] || buf[5]) {
+ printf("ONCORE: SELF TEST FAILED\n");
+ exit(1);
+ }
+
+ /* sometimes the @@Cj request does not produce any output
+ PERHAPS the ONCORE is still busy from the selftest???
+ try a 2 second sleep here to see if it makes any difference
+ */
+
+ sleep(2);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof oncore_cmd_Cj);
+ instance->o_state = ONCORE_ID_SENT;
+ cp = "state = ONCORE_ID_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+}
+
+
+
+/*
+ * preliminaries out of the way, this is the REAL start of initialization
+ */
+static void
+oncore_msg_Cj(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ const char *cp;
+ int mode;
+
+ if (instance->o_state != ONCORE_ID_SENT)
+ return;
+
+ memcpy(instance->Cj, buf, len);
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof oncore_cmd_Cg); /* Set Posn Fix mode (not Idle (VP)) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bb, sizeof oncore_cmd_Bb); /* turn off */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ek, sizeof oncore_cmd_Ek); /* turn off */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Aw, sizeof oncore_cmd_Aw); /* UTC time */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_AB, sizeof oncore_cmd_AB); /* Appl type static */
+
+ mode = instance->init_type;
+ if (debug)
+ printf("ONCORE: INIT mode = %d\n", mode);
+
+ /* If there is Position input in the Config file
+ * and mode = (1,3) set it as posn hold posn, goto 0D mode.
+ * or mode = (2,4) set it as INITIAL position, and Site Survey.
+ */
+
+ switch (mode) {
+ case 0: /* NO initialization, don't change anything */
+ instance->site_survey = ONCORE_SS_DONE;
+ break;
+
+ case 1:
+ case 3:
+ w32_buf(&oncore_cmd_As[2], (int) instance->ss_lat);
+ w32_buf(&oncore_cmd_As[6], (int) instance->ss_long);
+ w32_buf(&oncore_cmd_As[10], (int) instance->ss_ht);
+ oncore_cmd_As[14] = instance->ss_ht_type;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_As, sizeof oncore_cmd_As);
+
+ instance->site_survey = ONCORE_SS_DONE;
+ oncore_cmd_At[2] = 1;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At);
+ record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
+ break;
+
+ case 2:
+ case 4:
+ if (instance->posn_set) {
+ w32_buf(&oncore_cmd_Ad[2], (int) instance->ss_lat);
+ w32_buf(&oncore_cmd_Ae[2], (int) instance->ss_long);
+ w32_buf(&oncore_cmd_Af[2], (int) instance->ss_ht);
+ oncore_cmd_Af[6] = instance->ss_ht_type;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ad, sizeof oncore_cmd_Ad);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ae, sizeof oncore_cmd_Ae);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Af, sizeof oncore_cmd_Af);
+ }
+ instance->site_survey = ONCORE_SS_UNKNOWN;
+ oncore_cmd_At[2] = 2;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At);
+ break;
+ }
+
+ if (mode != 0) {
+ /* cable delay in ns */
+ w32_buf(&oncore_cmd_Az[2], instance->delay);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Az, sizeof oncore_cmd_Az);
+
+ /* PPS offset in ns */
+ w32_buf(&oncore_cmd_Ay[2], instance->offset);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ay, sizeof oncore_cmd_Ay);
+ }
+
+ /* 8chan - Position/Status/Data Output Message, 1/s */
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea, sizeof oncore_cmd_Ea);
+
+ instance->o_state = ONCORE_ALMANAC;
+ cp = "state = ONCORE_ALMANAC";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+}
+
+
+
+static void
+oncore_msg_Ea(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ const char *cp;
+
+ if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN)
+ return;
+
+ memcpy(instance->Ea, buf, len);
+
+ /* When we have an almanac, start the En messages */
+
+ if (instance->o_state == ONCORE_ALMANAC) {
+ if ((instance->Ea[72] & 1)) {
+ if (debug)
+ printf("ONCORE: waiting for almanac\n");
+ return;
+ } else {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_En, sizeof oncore_cmd_En);
+ instance->o_state = ONCORE_RUN;
+ cp = "state = ONCORE_RUN";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+ }
+
+ /* must be ONCORE_RUN if we are here */
+ /* First check if Hardware SiteSurvey has Finished */
+
+ if ((instance->site_survey == ONCORE_SS_HW) && !(instance->Ea[37] & 0x20)) {
+ instance->site_survey = ONCORE_SS_DONE;
+ record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
+ }
+
+ if (!instance->printed && instance->site_survey == ONCORE_SS_DONE) { /* will print to clockstat when all */
+ instance->printed = 1; /* three messages respond */
+ /* Read back Position Hold Params */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Asx, sizeof oncore_cmd_Asx);
+ /* Read back PPS Offset for Output */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ayx, sizeof oncore_cmd_Ayx);
+ /* Read back Cable Delay for Output */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Azx, sizeof oncore_cmd_Azx);
+ }
+
+ /* Check the leap second status once per day */
+
+ /*
+ * The following additional check, checking for June/December, is a
+ * workaround for incorrect ONCORE firmware. The oncore starts
+ * reporting the leap second when the GPS satellite data message
+ * (page 18, subframe 4) is updated to a date in the future, which
+ * which can be several months before the leap second. WWV and other
+ * services seem to wait until the month of the event to turn
+ * on their indicators (which are usually a single bit).
+ */
+
+ if ((buf[4] == 6) || (buf[4] == 12)) {
+ if (instance->Bj_day != buf[5]) { /* do this 1/day */
+ instance->Bj_day = buf[5];
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof oncore_cmd_Bj);
+ }
+ }
+ instance->pp->year = buf[6]*256+buf[7];
+ instance->pp->day = ymd2yd(buf[6]*256+buf[7], buf[4], buf[5]);
+ instance->pp->hour = buf[8];
+ instance->pp->minute = buf[9];
+ instance->pp->second = buf[10];
+
+ if (instance->site_survey != ONCORE_SS_SW)
+ return;
+
+ /*
+ * We have to average our own position for the Position Hold Mode
+ */
+
+ /* We only take PDOP/3D fixes */
+
+ if (instance->Ea[37] & 1)
+ return;
+
+ /* Not if poor geometry or less than 3 sats */
+
+ if (instance->Ea[72] & 0x52)
+ return;
+
+ /* Only 3D fix */
+
+ if (!(instance->Ea[72] & 0x20))
+ return;
+
+ instance->ss_lat += buf_w32(&instance->Ea[15]);
+ instance->ss_long += buf_w32(&instance->Ea[19]);
+ instance->ss_ht += buf_w32(&instance->Ea[23]); /* GPS ellipse */
+ instance->ss_count++;
+
+ if (debug)
+ printf("ONCORE: AVG %d %d %d %d\n",
+ instance->ss_count,
+ (unsigned) (instance->ss_lat / instance->ss_count),
+ (unsigned) (instance->ss_long / instance->ss_count),
+ (unsigned) (instance->ss_ht / instance->ss_count)
+ );
+
+ if (instance->ss_count != POS_HOLD_AVERAGE)
+ return;
+
+ instance->ss_lat /= POS_HOLD_AVERAGE;
+ instance->ss_long /= POS_HOLD_AVERAGE;
+ instance->ss_ht /= POS_HOLD_AVERAGE;
+
+ w32_buf(&oncore_cmd_As[2], (int) instance->ss_lat);
+ w32_buf(&oncore_cmd_As[6], (int) instance->ss_long);
+ w32_buf(&oncore_cmd_As[10], (int) instance->ss_ht);
+ oncore_cmd_As[14] = 0;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_As, sizeof oncore_cmd_As);
+
+ oncore_cmd_At[2] = 1;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At);
+ record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
+ instance->site_survey = ONCORE_SS_DONE;
+}
+
+
+
+static void
+oncore_msg_En(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ int j;
+ l_fp ts, ts_tmp;
+ double dmy;
+#ifdef HAVE_TIMESPEC
+ struct timespec *tsp = 0;
+#else
+ struct timeval *tsp = 0;
+#endif
+#ifdef HAVE_PPSAPI
+ struct timespec timeout;
+ pps_info_t pps_i;
+#else /* ! HAVE_PPSAPI */
+#ifdef HAVE_CIOGETEV
+ struct ppsclockev ev;
+ int r = CIOGETEV;
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ struct ppsclockev ev;
+ int r = TIOCGPPSEV;
+#endif
+#endif /* ! HAVE_PPS_API */
+
+ if (instance->o_state != ONCORE_RUN)
+ return;
+
+ memcpy(instance->En, buf, len);
+
+ /* Don't do anything without an almanac to define the GPS->UTC delta */
+
+ if (instance->Ea[72] & 1)
+ return;
+
+ /* If Time RAIM doesn't like it, don't trust it */
+
+ if (instance->En[21])
+ return;
+
+#ifdef HAVE_PPSAPI
+ j = instance->ev_serial;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ if (time_pps_fetch(instance->pps_h, PPS_TSFMT_TSPEC, &pps_i,
+ &timeout) < 0) {
+ printf("ONCORE: time_pps_fetch failed\n");
+ return;
+ }
+
+ if (instance->assert) {
+ tsp = &pps_i.assert_timestamp;
+
+ if (debug > 2)
+ printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n",
+ pps_i.assert_sequence, j, tsp->tv_sec, tsp->tv_nsec);
+
+ if (pps_i.assert_sequence == j) {
+ printf("ONCORE: oncore_msg_En, error serial pps\n");
+ return;
+ }
+ instance->ev_serial = pps_i.assert_sequence;
+ } else {
+ tsp = &pps_i.clear_timestamp;
+
+ if (debug > 2)
+ printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n",
+ pps_i.clear_sequence, j, tsp->tv_sec, tsp->tv_nsec);
+
+ if (pps_i.clear_sequence == j) {
+ printf("ONCORE: oncore_msg_En, error serial pps\n");
+ return;
+ }
+ instance->ev_serial = pps_i.clear_sequence;
+ }
+
+ /* convert timespec -> ntp l_fp */
+
+ dmy = tsp->tv_nsec;
+ dmy /= 1e9;
+ ts.l_uf = dmy * 4294967296.0;
+ ts.l_ui = tsp->tv_sec;
+#if 0
+ alternate code for previous 4 lines is
+ dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */
+ DTOLFP(dmy, &ts);
+ dmy = tsp->tv_sec; /* integer part */
+ DTOLFP(dmy, &ts_tmp);
+ L_ADD(&ts, &ts_tmp);
+ or more simply
+ dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */
+ DTOLFP(dmy, &ts);
+ ts.l_ui = tsp->tv_sec;
+#endif /* 0 */
+#else /* ! HAVE_PPSAPI */
+ j = instance->ev_serial;
+ if (ioctl(instance->ppsfd, r, (caddr_t) &ev) < 0) {
+ perror("ONCORE: IOCTL:");
+ return;
+ }
+
+ tsp = &ev.tv;
+
+ if (debug > 2)
+ printf("ONCORE: serial/j (%d, %d) %ld.%06ld\n",
+ ev.serial, j, tsp->tv_sec, tsp->tv_usec);
+
+ if (ev.serial == j) {
+ printf("ONCORE: oncore_msg_En, error serial pps\n");
+ return;
+ }
+ instance->ev_serial = ev.serial;
+
+ /* convert timeval -> ntp l_fp */
+
+ TVTOTS(tsp, &ts);
+#endif
+ /* now have timestamp in ts */
+ /* add in saw_tooth and offset */
+
+ /* saw_tooth not really necessary if using TIMEVAL */
+ /* since its only precise to us, but do it anyway. */
+
+ /* offset in ns, and is positive (late), we subtract */
+ /* to put the PPS time transition back where it belongs */
+
+ j = instance->saw_tooth + instance->offset;
+ instance->saw_tooth = (s_char) buf[25]; /* update for next time */
+#ifdef HAVE_PPSAPI
+ /* must hand this offset off to the Kernel to do the addition */
+ /* so that the Kernel PLL sees the offset too */
+
+ if (instance->assert) {
+ instance->pps_p.assert_offset.tv_nsec =
+ -(instance->saw_tooth + instance->offset);
+ } else {
+ instance->pps_p.clear_offset.tv_nsec =
+ -(instance->saw_tooth + instance->offset);
+ }
+
+ if (time_pps_setparams(instance->pps_h, &instance->pps_p))
+ perror("time_pps_setparams");
+#else
+ /* if not PPSAPI, no way to inform kernel of OFFSET, just do it */
+
+ dmy = -1.0e-9*j;
+ DTOLFP(dmy, &ts_tmp);
+ L_ADD(&ts, &ts_tmp);
+#endif
+ /* have time from UNIX origin, convert to NTP origin. */
+
+ ts.l_ui += JAN_1970;
+ instance->pp->lastrec = ts;
+ instance->pp->msec = 0;
+
+ ts_tmp = ts;
+ ts_tmp.l_ui = 0; /* zero integer part */
+ LFPTOD(&ts_tmp, dmy); /* convert fractional part to a double */
+ j = 1.0e9*dmy; /* then to integer ns */
+ sprintf(instance->pp->a_lastcode,
+ "%u.%09u %d %d %2d %2d %2d %2ld rstat %02x dop %d nsat %2d,%d raim %d sigma %d neg-sawtooth %3d sat %d%d%d%d%d%d%d%d",
+ ts.l_ui, j,
+ instance->pp->year, instance->pp->day,
+ instance->pp->hour, instance->pp->minute, instance->pp->second,
+ (long) tsp->tv_sec % 60,
+
+ instance->Ea[72], instance->Ea[37], instance->Ea[38], instance->Ea[39], instance->En[21],
+ /*rstat dop nsat visible, nsat tracked, raim */
+ instance->En[23]*256+instance->En[24], (s_char) buf[25],
+ /* sigma neg-sawtooth */
+ /*sat*/ instance->Ea[41], instance->Ea[45], instance->Ea[49], instance->Ea[53],
+ instance->Ea[57], instance->Ea[61], instance->Ea[65], instance->Ea[69]
+ );
+
+ if (debug > 2) {
+ int i;
+ i = strlen(instance->pp->a_lastcode);
+ printf("ONCORE: len = %d %s\n", i, instance->pp->a_lastcode);
+ }
+
+ if (!refclock_process(instance->pp)) {
+ refclock_report(instance->peer, CEVNT_BADTIME);
+ return;
+ }
+
+ record_clock_stats(&(instance->peer->srcadr), instance->pp->a_lastcode);
+ instance->pollcnt = 2;
+
+ if (instance->polled) {
+ instance->polled = 0;
+/*
+ instance->pp->dispersion = instance->pp->skew = 0;
+*/
+ refclock_receive(instance->peer);
+ }
+}
+
+
+
+/*
+ * Try to use Oncore UT+ Auto Survey Feature
+ * If its not there (VP), set flag to do it ourselves.
+ */
+static void
+oncore_msg_At(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+
+ if (instance->site_survey != ONCORE_SS_UNKNOWN)
+ return;
+
+ if (buf[4] == 2)
+ instance->site_survey = ONCORE_SS_HW;
+ else {
+ instance->site_survey = ONCORE_SS_SW;
+
+ /*
+ * Probably a VP or an older UT which can't do site-survey.
+ * We will have to do it ourselves
+ */
+ oncore_cmd_At[2] = 0;
+ instance->ss_lat = instance->ss_long = instance->ss_ht = 0;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At);
+ }
+}
+
+
+
+/* get leap-second warning message */
+
+/*
+ * @@Bj does NOT behave as documented in current Oncore firmware.
+ * It turns on the LEAP indicator when the data is set, and does not,
+ * as documented, wait until the beginning of the month when the
+ * leap second will occur.
+ * Until this firmware bug is fixed, @@Bj is only called in June/December.
+ */
+
+static void
+oncore_msg_Bj(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ const char *cp;
+
+ switch(buf[4]) {
+ case 1:
+ instance->peer->leap = LEAP_ADDSECOND;
+ cp = "Set peer.leap to LEAP_ADDSECOND";
+ break;
+ case 2:
+ instance->peer->leap = LEAP_DELSECOND;
+ cp = "Set peer.leap to LEAP_DELSECOND";
+ break;
+ case 0:
+ default:
+ instance->peer->leap = LEAP_NOWARNING;
+ cp = "Set peer.leap to LEAP_NOWARNING";
+ break;
+ }
+ record_clock_stats(&(instance->peer->srcadr), cp);
+}
+
+
+
+/*
+ * get Position hold position
+ */
+static void
+oncore_msg_As(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ int lat, lon, ht;
+
+ if (!instance->printed || instance->As)
+ return;
+
+ instance->As = 1;
+
+ lat = buf_w32(&buf[4]);
+ instance->ss_lat = lat;
+
+ lon = buf_w32(&buf[8]);
+ instance->ss_long = lon;
+
+ ht = buf_w32(&buf[12]);
+ instance->ss_ht = ht;
+
+ instance->ss_ht_type = buf[16];
+
+ if (instance->Ay && instance->Az)
+ oncore_stats(instance);
+}
+
+
+
+/*
+ * get PPS Offset
+ */
+static void
+oncore_msg_Ay(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ if (!instance->printed || instance->Ay)
+ return;
+
+ instance->Ay = 1;
+
+ instance->offset = buf_w32(&buf[4]);
+
+ if (instance->As && instance->Az)
+ oncore_stats(instance);
+}
+
+
+
+/*
+ * get Cable Delay
+ */
+static void
+oncore_msg_Az(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ if (!instance->printed || instance->Az)
+ return;
+
+ instance->Az = 1;
+
+ instance->delay = buf_w32(&buf[4]);
+
+ if (instance->As && instance->Ay)
+ oncore_stats(instance);
+}
+
+
+
+/*
+ * print init data in ONCORE to clockstats file
+ */
+static void
+oncore_stats(
+ struct instance *instance
+ )
+{
+ char Msg[120], ew, ns, *cp, *cp1;
+ const char *Ht;
+ double xd, xm, xs, yd, ym, ys, hm, hft;
+ int idx, idy, is, imx, imy;
+ long lat, lon;
+
+ /* First, Receiver ID */
+
+ instance->Cj[294] = '\0';
+ for (cp=(char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) {
+ cp1 = strchr(cp, '\r');
+ if (!cp1)
+ cp1 = (char *)&instance->Cj[294];
+ *cp1 = '\0';
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ *cp1 = '\r';
+ cp = cp1+2;
+ }
+
+ /* Next Position */
+
+ record_clock_stats(&(instance->peer->srcadr), "Posn:");
+ ew = 'E';
+ lon = instance->ss_long;
+ if (lon < 0) {
+ ew = 'W';
+ lon = -lon;
+ }
+
+ ns = 'N';
+ lat = instance->ss_lat;
+ if (lat < 0) {
+ ns = 'S';
+ lat = -lat;
+ }
+
+ hm = instance->ss_ht/100.;
+ hft= hm/0.3048;
+ Ht = instance->ss_ht_type ? "MSL" : "GPS";
+
+ xd = lat/3600000.; /* lat, lon in int msec arc, ht in cm. */
+ yd = lon/3600000.;
+ sprintf(Msg, "Lat = %c %11.7fdeg, Long = %c %11.7fdeg, Alt = %5.2fm (%5.2fft) %s", ns, xd, ew, yd, hm, hft, Ht);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ idx = xd;
+ idy = yd;
+ imx = lat%3600000;
+ imy = lon%3600000;
+ xm = imx/60000.;
+ ym = imy/60000.;
+ sprintf(Msg, "Lat = %c %3ddeg %7.4fm, Long = %c %3ddeg %8.5fm, Alt = %5.2fm (%5.2fft) %s", ns, idx, xm, ew, idy, ym, hm, hft, Ht);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ imx = xm;
+ imy = ym;
+ is = lat%60000;
+ xs = is/1000.;
+ is = lon%60000;
+ ys = is/1000.;
+ sprintf(Msg, "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %5.2fm (%5.2fft) %s", ns, idx, imx, xs, ew, idy, imy, ys, hm, hft, Ht);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ /* finally, cable delay and PPS offset */
+
+ sprintf(Msg, "Cable delay is set to %ld ns", instance->delay);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ sprintf(Msg, "PPS Offset is set to %ld ns", instance->offset);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+#ifdef HAVE_PPSAPI
+ if (instance->assert)
+ cp = "Timing on Assert.";
+ else
+ cp = "Timing on Clear.";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+#endif
+}
+
+#else
+int refclock_oncore_bs;
+#endif /* REFCLOCK */
OpenPOWER on IntegriCloud