/contrib/ntp/ntpd/refclock_oncore.c
https://bitbucket.org/freebsd/freebsd-head/ · C · 3732 lines · 2485 code · 595 blank · 652 comment · 705 complexity · c5431bce34bce5c87b214f45f81694fc MD5 · raw file
Large files are truncated click here to view the full file
- /*
- * ----------------------------------------------------------------------------
- * "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.
- * should work with Basic, PVT6, VP, UT, UT+, GT, GT+, SL, M12, M12+T
- * The receivers with TRAIM (VP, UT, UT+, M12+T), will be more accurate
- * than the others.
- * The receivers without position hold (GT, GT+) will be less accurate.
- *
- * 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
- *
- * (Basic) (M12)
- * COPYRIGHT 1991-1994 MOTOROLA INC. COPYRIGHT 1991-2000 MOTOROLA INC.
- * SFTW P/N # 98-P39949M SFTW P/N # 61-G10002A
- * SOFTWARE VER # 5 SOFTWARE VER # 1
- * SOFTWARE REV # 0 SOFTWARE REV # 3
- * SOFTWARE DATE 20 JAN 1994 SOFTWARE DATE Mar 13 2000
- * MODEL # A11121P116 MODEL # P143T12NR1
- * HDWR P/N # _ HWDR P/N # 1
- * SERIAL # SSG0049809 SERIAL # P003UD
- * MANUFACTUR DATE 417AMA199 MANUFACTUR DATE 0C27
- * OPTIONS LIST AB
- *
- * (M12+T) (M12+T later version)
- * COPYRIGHT 1991-2002 MOTOROLA INC. COPYRIGHT 1991-2003 MOTOROLA INC.
- * SFTW P/N # 61-G10268A SFTW P/N # 61-G10268A
- * SOFTWARE VER # 2 SOFTWARE VER # 2
- * SOFTWARE REV # 0 SOFTWARE REV # 1
- * SOFTWARE DATE AUG 14 2002 SOFTWARE DATE APR 16 2003
- * MODEL # P283T12T11 MODEL # P273T12T12
- * HWDR P/N # 2 HWDR P/N # 2
- * SERIAL # P04DC2 SERIAL # P05Z7Z
- * MANUFACTUR DATE 2J17 MANUFACTUR DATE 3G15
- *
- * --------------------------------------------------------------------------
- * Reg Clemens (Feb 2006)
- * Fix some gcc4 compiler complaints
- * Fix possible segfault in oncore_init_shmem
- * change all (possible) fprintf(stderr, to record_clock_stats
- * Apply patch from Russell J. Yount <rjy@cmu.edu> Fixed (new) MT12+T UTC not correct
- * immediately after new Almanac Read.
- * Apply patch for new PPS implementation by Rodolfo Giometti <giometti@linux.it>
- * now code can use old Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de> or
- * the new one. Compiles depending on timepps.h seen.
- * --------------------------------------------------------------------------
- * Luis Batanero Guerrero <luisba@rao.es> (Dec 2005) Patch for leap seconds
- * (the oncore driver was setting the wrong ntpd variable)
- * --------------------------------------------------------------------------
- * Reg.Clemens (Mar 2004)
- * Support for interfaces other than PPSAPI removed, for Solaris, SunOS,
- * SCO, you now need to use one of the timepps.h files in the root dir.
- * this driver will 'grab' it for you if you dont have one in /usr/include
- * --------------------------------------------------------------------------
- * 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, /etc/ntp.oncore.N, or /etc/ntp.oncore, where
- * n or N are the unit number, viz 127.127.30.N.
- * --------------------------------------------------------------------------
- * Reg.Clemens <reg@dwf.com> Sep98.
- * Original code written for FreeBSD.
- * With these mods it works on FreeBSD, SunOS, Solaris and Linux
- * (SunOS 4.1.3 + ppsclock)
- * (Solaris7 + MU4)
- * (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + or later).
- *
- * 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 */
- /*
- * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a
- * "STATUS" line in the oncore config file, which contains the most recent
- * copy of all types of messages we recognize. This file can be mmap(2)'ed
- * by monitoring and statistics programs.
- *
- * See separate HTML documentation for this option.
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #if defined(REFCLOCK) && defined(CLOCK_ONCORE)
- #include "ntpd.h"
- #include "ntp_io.h"
- #include "ntp_unixtime.h"
- #include "ntp_refclock.h"
- #include "ntp_stdlib.h"
- #include <stdio.h>
- #include <ctype.h>
- #include <sys/stat.h>
- #ifdef ONCORE_SHMEM_STATUS
- # ifdef HAVE_SYS_MMAN_H
- # include <sys/mman.h>
- # ifndef MAP_FAILED
- # define MAP_FAILED ((u_char *) -1)
- # endif /* MAP_FAILED */
- # endif /* HAVE_SYS_MMAN_H */
- #endif /* ONCORE_SHMEM_STATUS */
- #ifdef HAVE_PPSAPI
- # include "ppsapi_timepps.h"
- #endif
- #ifdef HAVE_SYS_SIO_H
- # include <sys/sio.h>
- #endif
- enum receive_state {
- ONCORE_NO_IDEA,
- ONCORE_CHECK_ID,
- ONCORE_CHECK_CHAN,
- ONCORE_HAVE_CHAN,
- ONCORE_RESET_SENT,
- ONCORE_TEST_SENT,
- ONCORE_INIT,
- ONCORE_ALMANAC,
- ONCORE_RUN
- };
- enum site_survey_state {
- ONCORE_SS_UNKNOWN,
- ONCORE_SS_TESTING,
- ONCORE_SS_HW,
- ONCORE_SS_SW,
- ONCORE_SS_DONE
- };
- enum antenna_state {
- ONCORE_ANTENNA_UNKNOWN = -1,
- ONCORE_ANTENNA_OK = 0,
- ONCORE_ANTENNA_OC = 1,
- ONCORE_ANTENNA_UC = 2,
- ONCORE_ANTENNA_NV = 3
- };
- /* Model Name, derived from the @@Cj message.
- * Used to initialize some variables.
- */
- enum oncore_model {
- ONCORE_BASIC,
- ONCORE_PVT6,
- ONCORE_VP,
- ONCORE_UT,
- ONCORE_UTPLUS,
- ONCORE_GT,
- ONCORE_GTPLUS,
- ONCORE_SL,
- ONCORE_M12,
- ONCORE_UNKNOWN
- };
- /* the bits that describe these properties are in the same place
- * on the VP/UT, but have moved on the M12. As such we extract
- * them, and use them from this struct.
- *
- */
- struct RSM {
- u_char posn0D;
- u_char posn2D;
- u_char posn3D;
- u_char bad_almanac;
- u_char bad_fix;
- };
- /* It is possible to test the VP/UT each cycle (@@Ea or equivalent) to
- * see what mode it is in. The bits on the M12 are multiplexed with
- * other messages, so we have to 'keep' the last known mode here.
- */
- enum posn_mode {
- MODE_UNKNOWN,
- MODE_0D,
- MODE_2D,
- MODE_3D
- };
- struct instance {
- int unit; /* 127.127.30.unit */
- struct refclockproc *pp;
- struct peer *peer;
- int ttyfd; /* TTY file descriptor */
- int ppsfd; /* PPS file descriptor */
- int shmemfd; /* Status shm descriptor */
- pps_handle_t pps_h;
- pps_params_t pps_p;
- enum receive_state o_state; /* Receive state */
- enum posn_mode mode; /* 0D, 2D, 3D */
- enum site_survey_state site_survey; /* Site Survey state */
- enum antenna_state ant_state; /* antenna state */
- int Bj_day;
- u_long delay; /* ns */
- long offset; /* ns */
- u_char *shmem;
- char *shmem_fname;
- u_int shmem_Cb;
- u_int shmem_Ba;
- u_int shmem_Ea;
- u_int shmem_Ha;
- u_char shmem_reset;
- u_char shmem_Posn;
- u_char shmem_bad_Ea;
- u_char almanac_from_shmem;
- double ss_lat;
- double ss_long;
- double ss_ht;
- double dH;
- int ss_count;
- u_char posn_set;
- enum oncore_model model;
- u_int version;
- u_int revision;
- u_char chan; /* 6 for PVT6 or BASIC, 8 for UT/VP, 12 for m12, 0 if unknown */
- s_char traim; /* do we have traim? yes UT/VP, M12+T, no BASIC, GT, M12, -1 unknown, 0 no, +1 yes */
- /* the following 7 are all timing counters */
- u_char traim_delay; /* seconds counter, waiting for reply */
- u_char count; /* cycles thru Ea before starting */
- u_char count1; /* cycles thru Ea after SS_TESTING, waiting for SS_HW */
- u_char count2; /* cycles thru Ea after count, to check for @@Ea */
- u_char count3; /* cycles thru Ea checking for # channels */
- u_char count4; /* cycles thru leap after Gj to issue Bj */
- u_char count5; /* cycles thru get_timestamp waiting for valid UTC correction */
- u_char count5_set; /* only set count5 once */
- u_char pollcnt;
- u_char timeout; /* count to retry Cj after Fa self-test */
- struct RSM rsm; /* bits extracted from Receiver Status Msg in @@Ea */
- u_char printed;
- u_char polled;
- u_long ev_serial;
- int Rcvptr;
- u_char Rcvbuf[500];
- u_char BEHa[160]; /* Ba, Ea or Ha */
- u_char BEHn[80]; /* Bn , En , or Hn */
- u_char Cj[300];
- u_char Ag; /* Satellite mask angle */
- u_char saw_At;
- u_char saw_Ay;
- u_char saw_Az;
- s_char saw_Gj;
- u_char have_dH;
- u_char init_type;
- s_char saw_tooth;
- s_char chan_in; /* chan number from INPUT, will always use it */
- u_char chan_id; /* chan number determined from part number */
- u_char chan_ck; /* chan number determined by sending commands to hardware */
- s_char traim_in; /* TRAIM from INPUT, will always use ON/OFF specified */
- s_char traim_id; /* TRAIM determined from part number */
- u_char traim_ck; /* TRAIM determined by sending commands to hardware */
- u_char once; /* one pass code at top of BaEaHa */
- s_char assert;
- u_char hardpps;
- };
- #define rcvbuf instance->Rcvbuf
- #define rcvptr instance->Rcvptr
- static int oncore_start P((int, struct peer *));
- static void oncore_poll P((int, struct peer *));
- static void oncore_shutdown P((int, struct peer *));
- static void oncore_consume P((struct instance *));
- static void oncore_read_config P((struct instance *));
- static void oncore_receive P((struct recvbuf *));
- static int oncore_ppsapi P((struct instance *));
- static void oncore_get_timestamp P((struct instance *, long, long));
- static void oncore_init_shmem P((struct instance *));
- static void oncore_antenna_report P((struct instance *, enum antenna_state));
- static void oncore_chan_test P((struct instance *));
- static void oncore_check_almanac P((struct instance *));
- static void oncore_check_antenna P((struct instance *));
- static void oncore_check_leap_sec P((struct instance *));
- static int oncore_checksum_ok P((u_char *, int));
- static void oncore_compute_dH P((struct instance *));
- static void oncore_load_almanac P((struct instance *));
- static void oncore_print_Cb P((struct instance *, u_char *));
- /* static void oncore_print_array P((u_char *, int)); */
- static void oncore_print_posn P((struct instance *));
- static void oncore_sendmsg P((int, u_char *, size_t));
- static void oncore_set_posn P((struct instance *));
- static void oncore_set_traim P((struct instance *));
- static void oncore_shmem_get_3D P((struct instance *));
- static void oncore_ss P((struct instance *));
- static int oncore_wait_almanac P((struct instance *));
- static void oncore_msg_any P((struct instance *, u_char *, size_t, int));
- static void oncore_msg_Adef P((struct instance *, u_char *, size_t));
- static void oncore_msg_Ag P((struct instance *, u_char *, size_t));
- static void oncore_msg_As P((struct instance *, u_char *, size_t));
- static void oncore_msg_At P((struct instance *, u_char *, size_t));
- static void oncore_msg_Ay P((struct instance *, u_char *, size_t));
- static void oncore_msg_Az P((struct instance *, u_char *, size_t));
- static void oncore_msg_BaEaHa P((struct instance *, u_char *, size_t));
- static void oncore_msg_Bd P((struct instance *, u_char *, size_t));
- static void oncore_msg_Bj P((struct instance *, u_char *, size_t));
- static void oncore_msg_BnEnHn P((struct instance *, u_char *, size_t));
- static void oncore_msg_CaFaIa P((struct instance *, u_char *, size_t));
- static void oncore_msg_Cb P((struct instance *, u_char *, size_t));
- static void oncore_msg_Cf P((struct instance *, u_char *, size_t));
- static void oncore_msg_Cj P((struct instance *, u_char *, size_t));
- static void oncore_msg_Cj_id P((struct instance *, u_char *, size_t));
- static void oncore_msg_Cj_init P((struct instance *, u_char *, size_t));
- static void oncore_msg_Ga P((struct instance *, u_char *, size_t));
- static void oncore_msg_Gb P((struct instance *, u_char *, size_t));
- static void oncore_msg_Gj P((struct instance *, u_char *, size_t));
- static void oncore_msg_Sz P((struct instance *, u_char *, size_t));
- 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 various Oncore Models.
- */
- static struct msg_desc {
- const char flag[3];
- const int len;
- void (*handler) P((struct instance *, u_char *, size_t));
- const char *fmt;
- int shmem;
- } oncore_messages[] = {
- /* Ea and En first since they're most common */
- { "Ea", 76, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC" },
- { "Ba", 68, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC" },
- { "Ha", 154, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC" },
- { "Bn", 59, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC" },
- { "En", 69, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC" },
- { "Hn", 78, oncore_msg_BnEnHn, "" },
- { "Ab", 10, 0, "" },
- { "Ac", 11, 0, "" },
- { "Ad", 11, oncore_msg_Adef, "" },
- { "Ae", 11, oncore_msg_Adef, "" },
- { "Af", 15, oncore_msg_Adef, "" },
- { "Ag", 8, oncore_msg_Ag, "" }, /* Satellite mask angle */
- { "As", 20, oncore_msg_As, "" },
- { "At", 8, oncore_msg_At, "" },
- { "Au", 12, 0, "" },
- { "Av", 8, 0, "" },
- { "Aw", 8, 0, "" },
- { "Ay", 11, oncore_msg_Ay, "" },
- { "Az", 11, oncore_msg_Az, "" },
- { "AB", 8, 0, "" },
- { "Bb", 92, 0, "" },
- { "Bd", 23, oncore_msg_Bd, "" },
- { "Bj", 8, oncore_msg_Bj, "" },
- { "Ca", 9, oncore_msg_CaFaIa, "" },
- { "Cb", 33, oncore_msg_Cb, "" },
- { "Cf", 7, oncore_msg_Cf, "" },
- { "Cg", 8, 0, "" },
- { "Ch", 9, 0, "" },
- { "Cj", 294, oncore_msg_Cj, "" },
- { "Ek", 71, 0, "" },
- { "Fa", 9, oncore_msg_CaFaIa, "" },
- { "Ga", 20, oncore_msg_Ga, "" },
- { "Gb", 17, oncore_msg_Gb, "" },
- { "Gc", 8, 0, "" },
- { "Gd", 8, 0, "" },
- { "Ge", 8, 0, "" },
- { "Gj", 21, oncore_msg_Gj, "" },
- { "Ia", 10, oncore_msg_CaFaIa, "" },
- { "Sz", 8, oncore_msg_Sz, "" },
- { {0}, 7, 0, "" }
- };
- static u_char oncore_cmd_Aa[] = { 'A', 'a', 0, 0, 0 }; /* 6/8 Time of Day */
- static u_char oncore_cmd_Ab[] = { 'A', 'b', 0, 0, 0 }; /* 6/8 GMT Correction */
- static u_char oncore_cmd_AB[] = { 'A', 'B', 4 }; /* VP Application Type: Static */
- static u_char oncore_cmd_Ac[] = { 'A', 'c', 0, 0, 0, 0 }; /* 6/8 Date */
- static u_char oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 }; /* 6/8 Latitude */
- static u_char oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 }; /* 6/8 Longitude */
- static u_char oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 }; /* 6/8 Height */
- static u_char oncore_cmd_Ag[] = { 'A', 'g', 0 }; /* 6/8/12 Satellite Mask Angle */
- static u_char oncore_cmd_Agx[] = { 'A', 'g', 0xff }; /* 6/8/12 Satellite Mask Angle: read */
- static u_char oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 6/8/12 Posn Hold Parameters */
- static u_char oncore_cmd_Asx[] = { 'A', 's', 0x7f,0xff,0xff,0xff, /* 6/8/12 Posn Hold Readback */
- 0x7f,0xff,0xff,0xff, /* on UT+ this doesnt work with 0xff */
- 0x7f,0xff,0xff,0xff, 0xff }; /* but does work with 0x7f (sigh). */
- static u_char oncore_cmd_At0[] = { 'A', 't', 0 }; /* 6/8 Posn Hold: off */
- static u_char oncore_cmd_At1[] = { 'A', 't', 1 }; /* 6/8 Posn Hold: on */
- static u_char oncore_cmd_At2[] = { 'A', 't', 2 }; /* 6/8 Posn Hold: Start Site Survey */
- static u_char oncore_cmd_Atx[] = { 'A', 't', 0xff }; /* 6/8 Posn Hold: Read Back */
- static u_char oncore_cmd_Au[] = { 'A', 'u', 0,0,0,0, 0 }; /* GT/M12 Altitude Hold Ht. */
- static u_char oncore_cmd_Av0[] = { 'A', 'v', 0 }; /* VP/GT Altitude Hold: off */
- static u_char oncore_cmd_Av1[] = { 'A', 'v', 1 }; /* VP/GT Altitude Hold: on */
- static u_char oncore_cmd_Aw[] = { 'A', 'w', 1 }; /* 6/8/12 UTC/GPS time selection */
- static u_char oncore_cmd_Ay[] = { 'A', 'y', 0, 0, 0, 0 }; /* Timing 1PPS time offset: set */
- static u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff }; /* Timing 1PPS time offset: Read */
- static u_char oncore_cmd_Az[] = { 'A', 'z', 0, 0, 0, 0 }; /* 6/8UT/12 1PPS Cable Delay: set */
- static u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff }; /* 6/8UT/12 1PPS Cable Delay: Read */
- static u_char oncore_cmd_Ba0[] = { 'B', 'a', 0 }; /* 6 Position/Data/Status: off */
- static u_char oncore_cmd_Ba[] = { 'B', 'a', 1 }; /* 6 Position/Data/Status: on */
- static u_char oncore_cmd_Bb[] = { 'B', 'b', 1 }; /* 6/8/12 Visible Satellites */
- static u_char oncore_cmd_Bd[] = { 'B', 'd', 1 }; /* 6/8/12? Almanac Status Msg. */
- static u_char oncore_cmd_Be[] = { 'B', 'e', 1 }; /* 6/8/12 Request Almanac Data */
- static u_char oncore_cmd_Bj[] = { 'B', 'j', 0 }; /* 6/8 Leap Second Pending */
- static u_char oncore_cmd_Bn0[] = { 'B', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim on */
- static u_char oncore_cmd_Bn[] = { 'B', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg on, traim on */
- static u_char oncore_cmd_Bnx[] = { 'B', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim off */
- static u_char oncore_cmd_Ca[] = { 'C', 'a' }; /* 6 Self Test */
- static u_char oncore_cmd_Cf[] = { 'C', 'f' }; /* 6/8/12 Set to Defaults */
- static u_char oncore_cmd_Cg[] = { 'C', 'g', 1 }; /* VP Posn Fix/Idle Mode */
- static u_char oncore_cmd_Cj[] = { 'C', 'j' }; /* 6/8/12 Receiver ID */
- static u_char oncore_cmd_Ea0[] = { 'E', 'a', 0 }; /* 8 Position/Data/Status: off */
- static u_char oncore_cmd_Ea[] = { 'E', 'a', 1 }; /* 8 Position/Data/Status: on */
- static u_char oncore_cmd_Ek[] = { 'E', 'k', 0 }; /* just turn off */ /* 8 Posn/Status/Data - extension */
- static u_char oncore_cmd_En0[] = { 'E', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim on */
- static u_char oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg on, traim on */
- static u_char oncore_cmd_Enx[] = { 'E', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim off */
- static u_char oncore_cmd_Fa[] = { 'F', 'a' }; /* 8 Self Test */
- static u_char oncore_cmd_Ga[] = { 'G', 'a', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 12 Position Set */
- static u_char oncore_cmd_Gax[] = { 'G', 'a', 0xff, 0xff, 0xff, 0xff, /* 12 Position Set: Read */
- 0xff, 0xff, 0xff, 0xff, /* */
- 0xff, 0xff, 0xff, 0xff, 0xff }; /* */
- static u_char oncore_cmd_Gb[] = { 'G', 'b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* 12 set Date/Time */
- static u_char oncore_cmd_Gc[] = { 'G', 'c', 1 }; /* 12 PPS Control: On Cont */
- static u_char oncore_cmd_Gd0[] = { 'G', 'd', 0 }; /* 12 Position Control: 3D (no hold) */
- static u_char oncore_cmd_Gd1[] = { 'G', 'd', 1 }; /* 12 Position Control: 0D (3D hold) */
- static u_char oncore_cmd_Gd2[] = { 'G', 'd', 2 }; /* 12 Position Control: 2D (Alt Hold) */
- static u_char oncore_cmd_Gd3[] = { 'G', 'd', 3 }; /* 12 Position Coltrol: Start Site Survey */
- static u_char oncore_cmd_Ge0[] = { 'G', 'e', 0 }; /* M12+T TRAIM: off */
- static u_char oncore_cmd_Ge[] = { 'G', 'e', 1 }; /* M12+T TRAIM: on */
- static u_char oncore_cmd_Gj[] = { 'G', 'j' }; /* 8?/12 Leap Second Pending */
- static u_char oncore_cmd_Ha0[] = { 'H', 'a', 0 }; /* 12 Position/Data/Status: off */
- static u_char oncore_cmd_Ha[] = { 'H', 'a', 1 }; /* 12 Position/Data/Status: on */
- static u_char oncore_cmd_Hn0[] = { 'H', 'n', 0 }; /* 12 TRAIM Status: off */
- static u_char oncore_cmd_Hn[] = { 'H', 'n', 1 }; /* 12 TRAIM Status: on */
- static u_char oncore_cmd_Ia[] = { 'I', 'a' }; /* 12 Self Test */
- /* it appears that as of 1997/1998, the UT had As,At, but not Au,Av
- * the GT had Au,Av, but not As,At
- * This was as of v2.0 of both firmware sets. possibly 1.3 for UT.
- * Bj in UT at v1.3
- * dont see Bd in UT/GT thru 1999
- * Gj in UT as of 3.0, 1999 , Bj as of 1.3
- */
- static char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jly",
- "Aug", "Sep", "Oct", "Nov", "Dec" };
- #define DEVICE1 "/dev/oncore.serial.%d" /* name of serial device */
- #define DEVICE2 "/dev/oncore.pps.%d" /* name of pps device */
- #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) { u_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
- )
- {
- #define STRING_LEN 32
- register struct instance *instance;
- struct refclockproc *pp;
- int fd1, fd2, num;
- char device1[STRING_LEN], device2[STRING_LEN], Msg[160];
- const char *cp;
- struct stat stat1, stat2;
- /* create instance structure for this unit */
- if (!(instance = (struct instance *) malloc(sizeof *instance))) {
- perror("malloc");
- return (0);
- }
- memset((char *) instance, 0, sizeof *instance);
- /* initialize miscellaneous variables */
- pp = peer->procptr;
- pp->unitptr = (caddr_t) instance;
- instance->pp = pp;
- instance->unit = unit;
- instance->peer = peer;
- instance->assert = 1;
- instance->once = 1;
- instance->Bj_day = -1;
- instance->traim = -1;
- instance->traim_in = -1;
- instance->chan_in = -1;
- instance->model = ONCORE_UNKNOWN;
- instance->mode = MODE_UNKNOWN;
- instance->site_survey = ONCORE_SS_UNKNOWN;
- instance->Ag = 0xff; /* Satellite mask angle, unset by user */
- instance->ant_state = ONCORE_ANTENNA_UNKNOWN;
- peer->precision = -26;
- peer->minpoll = 4;
- peer->maxpoll = 4;
- pp->clockdesc = "Motorola Oncore GPS Receiver";
- memcpy((char *)&pp->refid, "GPS\0", (size_t) 4);
- cp = "ONCORE DRIVER -- CONFIGURING";
- record_clock_stats(&(instance->peer->srcadr), cp);
- instance->o_state = ONCORE_NO_IDEA;
- cp = "state = ONCORE_NO_IDEA";
- record_clock_stats(&(instance->peer->srcadr), cp);
- /* Now open files.
- * This is a bit complicated, a we dont want to open the same file twice
- * (its a problem on some OS), and device2 may not exist for the new PPS
- */
- (void)sprintf(device1, DEVICE1, unit);
- (void)sprintf(device2, DEVICE2, unit);
- /* 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.
- */
- if (stat(device1, &stat1)) {
- sprintf(Msg, "Can't stat fd1 (%s)\n", device1);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- exit(1);
- }
- if (stat(device2, &stat2)) {
- sprintf(Msg, "Can't stat fd2 (%s)\n", device2);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- exit(1);
- }
- if (!(fd1 = refclock_open(device1, SPEED, LDISC_RAW))) {
- sprintf(Msg, "Can't open fd1 (%s)\n", device1);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- exit(1);
- }
- if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) /* same device here */
- fd2 = fd1;
- else { /* different devices here */
- if ((fd2=open(device2, O_RDWR)) < 0) {
- sprintf(Msg, "Can't open fd2 (%s)\n", device2);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- exit(1);
- }
- }
- num = fd2;
- /* open ppsapi soure */
- if (time_pps_create(num, &instance->pps_h) < 0) {
- record_clock_stats(&(instance->peer->srcadr), "PPSAPI not found in kernel");
- return(0);
- }
- /* continue initialization */
- instance->ttyfd = fd1;
- instance->ppsfd = fd2;
- /* go read any input data in /etc/ntp.oncoreX or /etc/ntp/oncore.X */
- oncore_read_config(instance);
- if (!oncore_ppsapi(instance))
- return(0);
- 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)) {
- record_clock_stats(&(instance->peer->srcadr), "ONCORE: io_addclock");
- (void) close(fd1);
- free(instance);
- return (0);
- }
- #ifdef ONCORE_SHMEM_STATUS
- /*
- * Before starting ONCORE, lets setup SHMEM
- * This will include merging an old SHMEM into the new one if
- * an old one is found.
- */
- oncore_init_shmem(instance);
- #endif
- /*
- * This will return the Model of the Oncore receiver.
- * and start the Initialization loop in oncore_msg_Cj.
- */
- instance->o_state = ONCORE_CHECK_ID;
- cp = "state = ONCORE_CHECK_ID";
- record_clock_stats(&(instance->peer->srcadr), cp);
- instance->timeout = 4;
- oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */
- oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
- instance->pollcnt = 2;
- return (1);
- }
- /*
- * 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;
- io_closeclock(&pp->io);
- time_pps_destroy (instance->pps_h);
- close(instance->ttyfd);
- if ((instance->ppsfd != -1) && (instance->ppsfd != instance->ttyfd))
- close(instance->ppsfd);
- if (instance->shmemfd)
- close(instance->shmemfd);
- 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->timeout) {
- char *cp;
- instance->timeout--;
- if (instance->timeout == 0) {
- cp = "Oncore: No response from @@Cj, shutting down driver";
- record_clock_stats(&(instance->peer->srcadr), cp);
- oncore_shutdown(unit, peer);
- } else {
- oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
- cp = "Oncore: Resend @@Cj";
- record_clock_stats(&(instance->peer->srcadr), cp);
- }
- return;
- }
- if (!instance->pollcnt)
- refclock_report(peer, CEVNT_TIMEOUT);
- else
- instance->pollcnt--;
- peer->procptr->polls++;
- instance->polled = 1;
- }
- /*
- * Initialize PPSAPI
- */
- static int
- oncore_ppsapi(
- struct instance *instance
- )
- {
- int cap, mode, mode1;
- char *cp, Msg[160];
- if (time_pps_getcap(instance->pps_h, &cap) < 0) {
- msyslog(LOG_ERR, "time_pps_getcap failed: %m");
- return (0);
- }
- if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) {
- msyslog(LOG_ERR, "time_pps_getparams failed: %m");
- return (0);
- }
- /* nb. only turn things on, if someone else has turned something
- * on before we get here, leave it alone!
- */
- if (instance->assert) {
- cp = "Assert.";
- mode = PPS_CAPTUREASSERT;
- mode1 = PPS_OFFSETASSERT;
- } else {
- cp = "Clear.";
- mode = PPS_CAPTURECLEAR;
- mode1 = PPS_OFFSETCLEAR;
- }
- sprintf(Msg, "Initializing timeing to %s.", cp);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- if (!(mode & cap)) {
- sprintf(Msg, "Can't set timeing to %s, exiting...", cp);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- return(0);
- }
- if (!(mode1 & cap)) {
- sprintf(Msg, "Can't set PPS_%sCLEAR, this will increase jitter.", cp);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- mode1 = 0;
- }
- /* only set what is legal */
- instance->pps_p.mode = (mode | mode1 | PPS_TSFMT_TSPEC) & cap;
- if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) {
- record_clock_stats(&(instance->peer->srcadr), "ONCORE: time_pps_setparams fails");
- exit(1);
- }
- /* If HARDPPS is on, we tell kernel */
- if (instance->hardpps) {
- int i;
- record_clock_stats(&(instance->peer->srcadr), "HARDPPS Set.");
- if (instance->assert)
- i = PPS_CAPTUREASSERT;
- else
- i = PPS_CAPTURECLEAR;
- /* we know that 'i' is legal from above */
- if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i,
- PPS_TSFMT_TSPEC) < 0) {
- msyslog(LOG_ERR, "time_pps_kcbind failed: %m");
- record_clock_stats(&(instance->peer->srcadr), "HARDPPS failed, abort...");
- return (0);
- }
- pps_enable = 1;
- }
- return(1);
- }
- #ifdef ONCORE_SHMEM_STATUS
- static void
- oncore_init_shmem(
- struct instance *instance
- )
- {
- int i, l, n, fd, shmem_old_size, n1;
- char Msg[160];
- u_char *cp, *cp1, *buf, *shmem_old;
- struct msg_desc *mp;
- struct stat sbuf;
- size_t shmem_length;
- /*
- * The first thing we do is see if there is an instance->shmem_fname file (still)
- * out there from a previous run. If so, we copy it in and use it to initialize
- * shmem (so we won't lose our almanac if we need it).
- */
- shmem_old = 0;
- shmem_old_size = 0;
- if ((fd = open(instance->shmem_fname, O_RDONLY)) < 0)
- record_clock_stats(&(instance->peer->srcadr), "ONCORE: Can't open SHMEM file");
- else {
- fstat(fd, &sbuf);
- shmem_old_size = sbuf.st_size;
- if (shmem_old_size != 0) {
- shmem_old = (u_char *) malloc((unsigned) sbuf.st_size);
- if (shmem_old == NULL)
- record_clock_stats(&(instance->peer->srcadr), "ONCORE: Can't malloc buffer for shmem_old");
- else
- read(fd, shmem_old, shmem_old_size);
- }
- close(fd);
- }
- /* OK, we now create the NEW SHMEM. */
- if ((instance->shmemfd = open(instance->shmem_fname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) {
- record_clock_stats(&(instance->peer->srcadr), "ONCORE: Can't open shmem");
- if (shmem_old)
- free(shmem_old);
- return;
- }
- /* see how big it needs to be */
- n = 1;
- for (mp=oncore_messages; mp->flag[0]; mp++) {
- mp->shmem = n;
- /* Allocate space for multiplexed almanac, and 0D/2D/3D @@Ea records */
- if (!strcmp(mp->flag, "Cb")) {
- instance->shmem_Cb = n;
- n += (mp->len + 3) * 34;
- }
- if (!strcmp(mp->flag, "Ba")) {
- instance->shmem_Ba = n;
- n += (mp->len + 3) * 3;
- }
- if (!strcmp(mp->flag, "Ea")) {
- instance->shmem_Ea = n;
- n += (mp->len + 3) * 3;
- }
- if (!strcmp(mp->flag, "Ha")) {
- instance->shmem_Ha = n;
- n += (mp->len + 3) * 3;
- }
- n += (mp->len + 3);
- }
- shmem_length = n + 2;
- buf = malloc(shmem_length);
- if (buf == NULL) {
- record_clock_stats(&(instance->peer->srcadr), "ONCORE: Can't malloc buffer for shmem");
- close(instance->shmemfd);
- if (shmem_old)
- free(shmem_old);
- return;
- }
- memset(buf, 0, shmem_length);
- /* next build the new SHMEM buffer in memory */
- for (mp=oncore_messages; mp->flag[0]; mp++) {
- l = mp->shmem;
- buf[l + 0] = mp->len >> 8;
- buf[l + 1] = mp->len & 0xff;
- buf[l + 2] = 0;
- buf[l + 3] = '@';
- buf[l + 4] = '@';
- buf[l + 5] = mp->flag[0];
- buf[l + 6] = mp->flag[1];
- if (!strcmp(mp->flag, "Cb") || !strcmp(mp->flag, "Ba") || !strcmp(mp->flag, "Ea") || !strcmp(mp->flag, "Ha")) {
- if (!strcmp(mp->flag, "Cb"))
- n = 35;
- else
- n = 4;
- for (i=1; i<n; i++) {
- buf[l + i * (mp->len+3) + 0] = mp->len >> 8;
- buf[l + i * (mp->len+3) + 1] = mp->len & 0xff;
- buf[l + i * (mp->len+3) + 2] = 0;
- buf[l + i * (mp->len+3) + 3] = '@';
- buf[l + i * (mp->len+3) + 4] = '@';
- buf[l + i * (mp->len+3) + 5] = mp->flag[0];
- buf[l + i * (mp->len+3) + 6] = mp->flag[1];
- }
- }
- }
- /* we now walk thru the two buffers (shmem_old and buf, soon to become shmem)
- * copying the data in shmem_old to buf.
- * When we are done we write it out and free both buffers.
- * If the structure sizes dont agree, I will not copy.
- * This could be due to an addition/deletion or a problem with the disk file.
- */
- if (shmem_old) {
- if (shmem_old_size == shmem_length) {
- for (cp=buf+4, cp1=shmem_old+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3), cp1+=(n+3)) {
- n1 = 256*(*(cp1-3)) + *(cp1-2);
- if (n == 0 || n1 != n || strncmp((char *) cp, (char *) cp1, 4))
- break;
- memcpy(cp, cp1, (size_t) n);
- }
- }
- free(shmem_old);
- }
- i = write(instance->shmemfd, buf, shmem_length);
- free(buf);
- if (i != shmem_length) {
- record_clock_stats(&(instance->peer->srcadr), "ONCORE: error writing shmem");
- close(instance->shmemfd);
- return;
- }
- instance->shmem = (u_char *) mmap(0, shmem_length,
- PROT_READ | PROT_WRITE,
- #ifdef MAP_HASSEMAPHORE
- MAP_HASSEMAPHORE |
- #endif
- MAP_SHARED, instance->shmemfd, (off_t)0);
- if (instance->shmem == (u_char *)MAP_FAILED) {
- instance->shmem = 0;
- close(instance->shmemfd);
- return;
- }
- sprintf(Msg, "SHMEM (size = %ld) is CONFIGURED and available as %s",
- (u_long) shmem_length, instance->shmem_fname);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- }
- #endif /* ONCORE_SHMEM_STATUS */
- /*
- * Read Input file if it exists.
- */
- static void
- oncore_read_config(
- struct instance *instance
- )
- {
- /*
- * First we try to open the configuration file
- * /etc/oncoreN
- * where N is the unit number viz 127.127.30.N.
- * If we don't find it we try
- * /etc/ntp.oncore.N
- * and then
- * /etc/ntp.oncore
- *
- * If we don't find any 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, ASSERT, CLEAR, HARDPPS,
- * STATUS, POSN3D, POSN2D, CHAN, TRAIM
- * 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' 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 height above the GPS ellipsoid.
- * If the receiver reports height in both GPS and MSL, then we will report
- * the difference GPS-MSL on the clockstats file.
- *
- * There is an optional line, starting with DELAY, 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.
- *
- * There is an optional line, starting with 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.
- * 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 an 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.
- * ASSERT/CLEAR can also be set with FLAG2 of the ntp.conf input.
- * For Flag2, ASSERT=0, and hence is default.
- *
- * There is an optional line, with HARDPPS on it. Including this line causes
- * the PPS signal to control the kernel PLL.
- * HARDPPS can also be set with FLAG3 of the ntp.conf input.
- * For Flag3, 0 is disabled, and the default.
- *
- * There are three options that have to do with using the shared memory option.
- * First, to enable the option there must be a SHMEM line with a file name.
- * The file name is the file associated with the shared memory.
- *
- * In shared memory, there is one 'record' for each returned variable.
- * For the @@Ea data there are three 'records' containing position data.
- * There will always be data in the record corresponding to the '0D' @@Ea record,
- * and the user has a choice of filling the '3D' record by specifying POSN3D,
- * or the '2D' record by specifying POSN2D. In either case the '2D' or '3D'
- * record is filled once every 15s.
- *
- * Two additional variables that can be set are CHAN and TRAIM. These should be
- * set correctly by the code examining the @@Cj record, but we bring them out here
- * to allow the user to override either the # of channels, or the existence of TRAIM.
- * CHAN expects to be followed by in integer: 6, 8, or 12. TRAIM expects to be
- * followed by YES or NO.
- *
- * There is an optional line with MASK on it followed by one integer field in the
- * range 0 to 89. This sets the satellite mask angle and will determine the minimum
- * elevation angle for satellites to be tracked by the receiver. The default value
- * is 10 deg for the VP and 0 deg for all other receivers.
- *
- * 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, *cc, *ca, line[100], units[2], device[20], Msg[160], **cpp;
- char *dirs[] = { "/etc/ntp", "/etc", 0 };
- int i, sign, lat_flg, long_flg, ht_flg, mode, mask;
- double f1, f2, f3;
- fd = NULL; /* just to shutup gcc complaint */
- for (cpp=dirs; *cpp; cpp++) {
- cp = *cpp;
- sprintf(device, "%s/ntp.oncore.%d", cp, instance->unit); /* try "ntp.oncore.0 */
- if ((fd=fopen(device, "r")))
- break;
- sprintf(device, "%s/ntp.oncore%d", cp, instance->unit); /* try "ntp.oncore0" */
- if ((fd=fopen(device, "r")))
- break;
- sprintf(device, "%s/ntp.oncore", cp); /* and finally "ntp.oncore" */
- if ((fd=fopen(device, "r")))
- break;
- }
- if (!fd) { /* no inputfile, default to the works ... */
- instance->init_type = 4;
- return;
- }
- mode = mask = 0;
- lat_flg = long_flg = ht_flg = 0;
- while (fgets(line, 100, fd)) {
- /* Remove comments */
- if ((cp = strchr(line, '#')))
- *cp = '\0';
- /* Remove trailing space */
- for (i = strlen(line);
- i > 0 && isascii((int)line[i - 1]) && isspace((int)line[i - 1]);
- )
- line[--i] = '\0';
- /* Remove leading space */
- for (cc = line; *cc && isascii((int)*cc) && isspace((int)*cc); cc++)
- continue;
- /* Stop if nothing left */
- if (!*cc)
- continue;
- /* Uppercase the command and find the arg */
- for (ca = cc; *ca; ca++) {
- if (isascii((int)*ca)) {
- if (islower((int)*ca)) {
- *ca = toupper(*ca);
- } else if (isspace((int)*ca) || (*ca == '='))
- break;
- }
- }
- /* Remove space (and possible =) leading the arg */
- for (; *ca && isascii((int)*ca) && (isspace((int)*ca) || (*ca == '=')); ca++)
- continue;
- if (!strncmp(cc, "STATUS", (size_t) 6) || !strncmp(cc, "SHMEM", (size_t) 5)) {
- i = strlen(ca);
- instance->shmem_fname = (char *) malloc((unsigned) (i+1));
- strcpy(instance->shmem_fname, ca);
- continue;
- }
- /* Uppercase argument as well */
- for (cp = ca; *cp; cp++)
- if (isascii((int)*cp) && islower((int)*cp))
- *cp = toupper(*cp);
- if (!strncmp(cc, "LAT", (size_t) 3)) {
- f1 = f2 = f3 = 0;
- sscanf(ca, "%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(cc, "LON", (size_t) 3)) {
- f1 = f2 = f3 = 0;
- sscanf(ca, "%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(cc, "HT", (size_t) 2)) {
- f1 = 0;
- units[0] = '\0';
- sscanf(ca, "%lf %1s", &f1, units);
- if (units[0] == 'F')
- f1 = 0.3048 * f1;
- instance->ss_ht = 100 * f1; /* cm */
- ht_flg++;
- } else if (!strncmp(cc, "DELAY", (size_t) 5)) {
- f1 = 0;
- units[0] = '\0';
- sscanf(ca, "%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;
- if (f1 < 0 || f1 > 999999) {
- sprintf(Msg, "PPS Cable delay of %fns out of Range, ignored", f1);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- } else
- instance->delay = f1; /* delay in ns */
- } else if (!strncmp(cc, "OFFSET", (size_t) 6)) {
- f1 = 0;
- units[0] = '\0';
- sscanf(ca, "%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;
- if (f1 < 0 || f1 > 999999999.) {
- sprintf(Msg, "PPS Offset of %fns out of Range, ignored", f1);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- } else
- instance->offset = f1; /* offset in ns */
- } else if (!strncmp(cc, "MODE", (size_t) 4)) {
- sscanf(ca, "%d", &mode);
- if (mode < 0 || mode > 4)
- mode = 4;
- } else if (!strncmp(cc, "ASSERT", (size_t) 6)) {
- instance->assert = 1;
- } else if (!strncmp(cc, "CLEAR", (size_t) 5)) {
- instance->assert = 0;
- } else if (!strncmp(cc, "HARDPPS", (size_t) 7)) {
- instance->hardpps = 1;
- } else if (!strncmp(cc, "POSN2D", (size_t) 6)) {
- instance->shmem_Posn = 2;
- } else if (!strncmp(cc, "POSN3D", (size_t) 6)) {
- instance->shmem_Posn = 3;
- } else if (!strncmp(cc, "CHAN", (size_t) 4)) {
- sscanf(ca, "%d", &i);
- if ((i == 6) || (i == 8) || (i == 12))
- instance->chan_in = i;
- } else if (!strncmp(cc, "TRAIM", (size_t) 5)) {
- instance->traim_in = 1; /* so TRAIM alone is YES */
- if (!strcmp(ca, "NO") || !strcmp(ca, "OFF")) /* Yes/No, On/Off */
- instance->traim_in = 0;
- } else if (!strncmp(cc, "MASK", (size_t) 4)) {
- sscanf(ca, "%d", &mask);
- if (mask > -1 && mask < 90)
- instance->Ag = mask; /* Satellite mask angle */
- }
- }
- 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 )) {
- printf("ONCORE: incomplete data on %s\n", device);
- instance->posn_set = 0;
- if (mode == 1 || mode == 3) {
- sprintf(Msg, "Input Mode = %d, but no/incomplete position, mode set to %d", mode, mode+1);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- mode++;
- }
- }
- instance->init_type = mode;
- sprintf(Msg, "Input mode = %d", mode);
- record_clock_stats(&(instance->peer->srcadr), Msg);
- }
- /*
- * move data from NTP to buffer (toss the extra in the unlikely case it won't fit)
- */
- static void
- oncore_receive(
- struct recvbuf *rbufp
- )
- {
- size_t 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, m;
- unsigned l;
- 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;
- #ifdef DEBUG
- if (debug > 4)
- printf("ONCORE[%d]: >>> skipping %d chars\n", instance->unit, i);
- #endif
- if (i != rcvptr)
- memcpy(rcvbuf, rcvbuf+i, (size_t)(rcvptr-i));
- rcvptr -= i;
- continue;
- }
- /* 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), (size_t) 2))
- break;
- if (m == l) {
- #ifdef DEBUG
- if (debug > 4)
- printf("ONCORE[%d]: >>> Unknown MSG, skipping 4 (%c%c)\n", instance->unit, rcvbuf[2], rcvbuf[3]);
- #endif
- memcpy(rcvbuf, rcvbuf+4, (size_t) 4);
- rcvptr -= 4;
- continue;
- }
- l = oncore_messages[m].len;
- #if 0
- if (debug > 3)
- printf("ONCORE[%d]: GOT: %c%c %d of %d entry %d\n", instance->unit, rcvbuf[2], rcvbuf[3], rcvptr, l, m);
- #endif
- /* Got the entire message ? */
- if (rcvptr < l)
- return;
- /* are we at the end of message? should be <Cksum><CR><LF> */
- if (rcvbuf[l-2] != '\r' || rcvbuf[l-1] != '\n') {
- #ifdef DEBUG
- if (debug)
- printf("ONCORE[%d]: NO <CR><LF> at end of message\n", instance->unit);
- #endif
- } else { /* check the CheckSum */
- if (oncore_checksum_ok(rcvbuf, l)) {
- if (instance->shmem != NULL) {
- instance->shmem[oncore_messages[m].shmem + 2]++;
- memcpy(instance->shmem + oncore_messages[m].shmem + 3,
- rcvbuf, (size_t) l);
- }
- oncore_msg_any(instance, rcvbuf, (size_t) (l-3), m);
- if (oncore_messages[m].handler)
- oncore_messages[m].handler(instance, rcvbuf, (size_t) (l-3));
- }
- #ifdef DEBUG
- else if (debug) {
- printf("ONCORE[%d]: Checksum mismatch!\n", instance->unit);
- printf("ONCORE[%d]: @@%c%c ", instance->unit, rcvbuf[2], rcvbuf[3]);
- for (i=4; i<l; i++)
- printf("%03o ", rcvbuf[i]);
- printf("\n");
- }
- #endif
- }
- if (l != rcvptr)
- memcpy(rcvbuf, rcvbuf+l, (size_t) (rcvptr-l));
- rcvptr -= l;
- }
- }
- static void
- oncore_get_timestamp(
- struct instance *instance,
- long dt1, /* tick offset THIS time step */
- long dt2 /* tick offset NEXT time step */
- )
- {
- int Rsm;
- u_long j;
- l_fp ts, ts_tmp;
- double dmy;
- #ifdef HAVE_STRUCT_TIMESPEC
- struct timespec *tsp = 0;
- #else
- struct timeval *tsp = 0;
- #endif
- int current_mode;
- u_long i;
- pps_params_t current_params;
- struct timespec timeout;
- pps_info_t pps_i;
- #if 1
- /* If we are in SiteSurvey mode, then we are in 3D mode, and we fall thru.
- * If we have Finished the SiteSurvey, then we fall thru for the 14/15
- * times we get here in 0D mode (the 1/15 is in 3D for SHMEM).
- * This gives good time, which gets better when the SS is done.
- */
- if ((instance->site_survey == ONCORE_SS_DONE) && (instance->mode != MODE_0D))
- #else
- /* old check, only fall thru for SS_DONE and 0D mode, 2h45m wait for ticks */
- if ((instance->site_survey != ONCORE_SS_DONE) || (instance->mode != MODE_0D))
- #endif
- return;
- /* Don't do anything without an almanac to define the GPS->UTC delta */
- if (instance->rsm.bad_almanac)
- return;
- /* Once the Almanac is valid, the M12+T does not produce valid UTC
- * immediately.
- * Wait for UTC offset decode valid, then wait one message more
- * so we are not off by 13 seconds after reset.
- */
- if (instance->count5) {
- instance->count5--;
- return;
- }
- j = instance->ev_serial;
- timeout.tv_sec = 0;
- timeout.tv_ns…