diff options
Diffstat (limited to 'contrib/ntp/ntpd/refclock_oncore.c')
-rw-r--r-- | contrib/ntp/ntpd/refclock_oncore.c | 1660 |
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 */ |