aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/xntpd/parse/clk_trimtsip.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/xntpd/parse/clk_trimtsip.c')
-rw-r--r--usr.sbin/xntpd/parse/clk_trimtsip.c471
1 files changed, 471 insertions, 0 deletions
diff --git a/usr.sbin/xntpd/parse/clk_trimtsip.c b/usr.sbin/xntpd/parse/clk_trimtsip.c
new file mode 100644
index 000000000000..f622beac08db
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_trimtsip.c
@@ -0,0 +1,471 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_TRIMTSIP)
+/*
+ * $Header: /src/NTP/REPOSITORY/v3/parse/clk_trimble.c,v 3.9 1994/02/02 17:45:27 kardel Exp $
+ *
+ * Trimble TSIP support - CURRENTLY VERY MUCH UNDER CONSTRUCTION
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+
+#include "ntp_syslog.h"
+#include "ntp_types.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+/*
+ * Trimble TSIP parser
+ *
+ * The receiver uses a serial message protocol called Trimble Standard
+ * Interface Protocol (it can support others but this driver only supports
+ * TSIP). Messages in this protocol have the following form:
+ *
+ * <DLE><id> ... <data> ... <DLE><ETX>
+ *
+ * Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled
+ * on transmission and compressed back to one on reception. Otherwise
+ * the values of data bytes can be anything. The serial interface is RS-422
+ * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits
+ * in total!), and 1 stop bit. The protocol supports byte, integer, single,
+ * and double datatypes. Integers are two bytes, sent most significant first.
+ * Singles are IEEE754 single precision floating point numbers (4 byte) sent
+ * sign & exponent first. Doubles are IEEE754 double precision floating point
+ * numbers (8 byte) sent sign & exponent first.
+ * The receiver supports a large set of messages, only a small subset of
+ * which are used here. From driver to receiver the following are used:
+ *
+ * ID Description
+ *
+ * 21 Request current time
+ * 22 Mode Select
+ * 2C Set/Request operating parameters
+ * 2F Request UTC info
+ * 35 Set/Request I/O options
+ *
+ * From receiver to driver the following are recognised:
+ *
+ * ID Description
+ *
+ * 41 GPS Time
+ * 44 Satellite selection, PDOP, mode
+ * 46 Receiver health
+ * 4B Machine code/status
+ * 4C Report operating parameters (debug only)
+ * 4F UTC correction data (used to get leap second warnings)
+ * 55 I/O options (debug only)
+ *
+ * All others are accepted but ignored.
+ *
+ */
+
+extern int debug;
+
+struct trimble
+{
+ u_char t_in_pkt; /* first DLE received */
+ u_char t_dle; /* subsequent DLE received */
+ u_char t_status; /* last status */
+ u_char t_error; /* last error */
+ u_short t_week; /* GPS week */
+ u_short t_weekleap; /* GPS week of next/last week */
+ u_short t_dayleap; /* day in week */
+ u_short t_gpsutc; /* GPS - UTC offset */
+ u_short t_gpsutcleap; /* offset at next/last leap */
+ u_char t_operable; /* receiver feels OK */
+ u_char t_leap; /* possible leap warning */
+};
+
+static unsigned LONG inp_tsip();
+static unsigned LONG cvt_trimtsip();
+
+struct clockformat clock_trimtsip =
+{
+ inp_tsip, /* Trimble TSIP input handler */
+ cvt_trimtsip, /* Trimble TSIP conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (unsigned LONG (*)())0, /* no time code synthesizer monitoring */
+ (void *)0, /* no configuration data */
+ "Trimble SV6/TSIP",
+ 128, /* input buffer */
+ CVT_FIXEDONLY, /* we do our own input handling */
+ sizeof(struct trimble), /* no private data */
+ { 0, 0},
+ '\0',
+ '\0',
+ '\0'
+};
+
+#define ADDSECOND 0x01
+#define DELSECOND 0x02
+
+#define DLE 0x10
+#define ETX 0x03
+
+static unsigned LONG inp_tsip(parseio, ch, ctime)
+ register parse_t *parseio;
+ register unsigned char ch;
+ register timestamp_t *ctime;
+{
+ register struct trimble *t = (struct trimble *)parseio->parse_pdata;
+
+ if (!t)
+ return 0; /* local data not allocated - sigh! */
+
+ if (!t->t_in_pkt && ch != DLE) {
+ /* wait for start of packet */
+#ifdef DEBUG
+ if (debug > 2)
+ printf("sv6+ discarding %2.2x\n", ch);
+#endif
+ return 0;
+ }
+
+ switch (ch) {
+ case DLE:
+ if (!t->t_in_pkt) {
+ t->t_dle = 0;
+ t->t_in_pkt = 1;
+ parseio->parse_index = 0;
+ parseio->parse_data[parseio->parse_index++] = ch;
+ parseio->parse_dtime.parse_stime = *ctime; /* pick up time stamp at packet start */
+ } else if (t->t_dle) {
+ /* Double DLE -> insert a DLE */
+ t->t_dle = 0;
+ parseio->parse_data[parseio->parse_index++] = DLE;
+ } else
+ t->t_dle = 1;
+ break;
+ case ETX:
+ if (t->t_dle) {
+ /* DLE,ETX -> end of packet */
+ parseio->parse_data[parseio->parse_index++] = DLE;
+ parseio->parse_data[parseio->parse_index++] = ch;
+ parseio->parse_data[parseio->parse_index] = '\0';
+ parseio->parse_ldsize = parseio->parse_index+1;
+ bcopy(parseio->parse_data, parseio->parse_ldata, parseio->parse_ldsize);
+ t->t_in_pkt = t->t_dle = 0;
+ return 1;
+ }
+ /* fall into ... */
+ default:
+ t->t_dle = 0;
+ parseio->parse_data[parseio->parse_index++] = ch;
+ }
+
+ return (parseio->parse_index == parseio->parse_dsize-1); /* buffer full - attempt to parse (likely to fail) */
+}
+
+#define GPSORIGIN 2524953600 /* NTP origin - GPS origin in seconds */
+#define SECSPERWEEK 604800 /* seconds per week - GPS tells us about weeks */
+#define L_UF_SCALE 4294967296.0 /* scale a float fraction to l_uf units */
+
+/*
+ * mapping union for ints, floats, doubles for both input & output to the
+ * receiver
+ *
+ * CAVEAT: must disappear - non portable
+ */
+
+union {
+ u_char bd[8];
+ int iv;
+ float fv;
+ double dv;
+} uval;
+
+static float getflt P((u_char *));
+static double getdbl P((u_char *));
+static int getint P((u_char *));
+
+/*
+ * cvt_trimtsip
+ *
+ * convert TSIP type format
+ */
+static unsigned LONG
+cvt_trimtsip(buffer, size, format, clock, t)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+ register struct trimble *t;
+{
+#define mb(_X_) (buffer[2+(_X_)]) /* shortcut for buffer access */
+ register u_char cmd;
+
+ if (!t) {
+#ifdef DEBUG
+ if (debug) printf("sv6+ BAD call (t=0)\n");
+#endif
+ return CVT_NONE; /* local data not allocated - sigh! */
+ }
+
+ if ((size < 4) ||
+ (buffer[0] != DLE) ||
+ (buffer[size-1] != ETX) ||
+ (buffer[size-2] != DLE))
+ {
+#ifdef DEBUG
+ if (debug > 2) {
+ int i;
+
+ printf("sv6+ BAD packet, size %d:\n ", size);
+ for (i = 0; i < size; i++) {
+ printf ("%2.2x, ", buffer[i]&0xff);
+ if (i%16 == 15) printf("\n\t");
+ }
+ printf("\n");
+ }
+#endif
+ return CVT_NONE;
+ }
+ else
+ {
+ cmd = buffer[1];
+
+#ifdef DEBUG
+ if (debug > 1)
+ switch(cmd)
+ {
+ case 0x41:
+ printf("sv6+ gps time: %f, %d, %f\n",
+ getflt(&mb(0)), getint(&mb(4)), getflt(&mb(6)));
+ break;
+
+ case 0x44:
+ printf("sv6+ sats: %2x, %2d %2d %2d %2d, %.2f\n",
+ mb(0), mb(1), mb(2), mb(3), mb(4), getflt(&mb(5)));
+ break;
+
+ case 0x45:
+ printf("sv6+ software: %d.%d (19%d/%d/%d)\n",
+ mb(0)&0xff, mb(1)&0xff, mb(4)&0xff, mb(2)&0xff, mb(3)&0xff);
+ break;
+
+ case 0x46:
+ printf("sv6+ health: %2x %2x\n",
+ mb(0), mb(1));
+ break;
+
+ case 0x48:
+ printf("sv6+ gps message: '%.22s'\n", &mb(0));
+ break;
+
+ case 0x4b:
+ printf("sv6+ status: %2d %2x\n",
+ mb(0), mb(1));
+ break;
+
+ case 0x4c:
+ printf("sv6+ op params: %2x %.1f %.1f %.1f %.1f\n",
+ mb(0), getflt(&mb(1)), getflt(&mb(5)),
+ getflt(&mb(9)), getflt(&mb(13)));
+ break;
+
+ case 0x4f:
+ printf("sv6+ utc data: %.3e %.3e %d %d %d %d %d\n",
+ getdbl(&mb(0)), getflt(&mb(8)), getint(&mb(18)),
+ getint(&mb(12)), getint(&mb(20)), getint(&mb(22)), getint(&mb(24)));
+ break;
+
+ case 0x54:
+ /*printf("sv6+ bias and rate: %.1fm %.2fm/s at %.1fs\n",
+ getflt(&mb(0)), getflt(&mb(4)), getflt(&mb(8))); ignore it*/
+ break;
+
+ case 0x55:
+ printf("sv6+ io opts: %2x %2x %2x %2x\n",
+ mb(0), mb(1), mb(2), mb(3));
+ break;
+
+ case 0x8f:
+ {
+#define RTOD (180.0 / 3.1415926535898)
+ double lat = getdbl(&mb(2));
+ double lng = getdbl(&mb(10));
+ printf("sv6+ last fix: %2.2x %d lat %f %c, long %f %c, alt %.2fm\n",
+ mb(1)&0xff, mb(40)&0xff,
+ ((lat < 0) ? (-lat) : (lat))*RTOD, (lat < 0 ? 'S' : 'N'),
+ ((lng < 0) ? (-lng) : (lng))*RTOD, (lng < 0 ? 'W' : 'E'),
+ getdbl(&mb(18)));
+ }
+ break;
+
+ case 0x40:
+ case 0x5b:
+ case 0x6d:
+ /* Ignore */
+ break;
+
+ default:
+ printf("sv6+ cmd ignored: %2x, length: %d\n",
+ cmd, size);
+ break;
+ }
+#endif /* DEBUG */
+
+ switch(cmd)
+ {
+ case 0x41:
+ { /* GPS time */
+ float secs = getflt(&mb(0));
+ int week = getint(&mb(4));
+ int secint;
+ float secfrac;
+ l_fp gpstime, off;
+
+ if (secs <= 0)
+ {
+#ifdef DEBUG
+ if (debug)
+ printf("sv6+ seconds <= 0 (%e), setting POWERUP\n");
+#endif
+ clock->flags = PARSEB_POWERUP;
+ return CVT_OK;
+ }
+
+ /* time OK */
+ secint = secs; /* integer part, hopefully */
+ secfrac = secs - secint; /* 0.0 <= secfrac < 1.0 */
+ secint -= getflt(&mb(6)); /* UTC offset */
+ gpstime.l_ui = week*SECSPERWEEK + secint + GPSORIGIN; /* convert to NTP time */
+ gpstime.l_uf = secfrac*L_UF_SCALE;
+
+ clock->utctime = gpstime.l_ui - JAN_1970;
+ TSFTOTVU(gpstime.l_uf, clock->usecond);
+
+ if (t->t_leap == ADDSECOND)
+ clock->flags |= PARSEB_LEAPADD;
+
+ if (t->t_leap == DELSECOND)
+ clock->flags |= PARSEB_LEAPDEL;
+
+ if (t->t_operable)
+ clock->flags &= ~(PARSEB_NOSYNC|PARSEB_POWERUP);
+ else
+ clock->flags |= PARSEB_NOSYNC;
+ return CVT_OK;
+
+ } /* case 0x41 */
+ break;
+
+ case 0x46:
+ {
+ /* sv6+ health */
+ u_char status = t->t_status = mb(0);
+ u_char error = t->t_error = mb(1);
+
+ if (status == 0 || status == 9 || status == 10 || status == 11)
+ {
+ if (!t->t_operable)
+ syslog(LOG_ERR, "Trimble clock synced");
+ t->t_operable = 1;
+ }
+ else
+ {
+ if (t->t_operable)
+ syslog(LOG_ERR, "Trimble clock unsynced");
+ t->t_operable = 0;
+ }
+ }
+ break;
+
+ case 0x4f:
+ {
+ /* UTC correction data - derive a leap warning */
+ int tls = t->t_gpsutc = getint(&mb(12)); /* current leap correction (GPS-UTC) */
+ int wnlsf = t->t_weekleap = getint(&mb(20)); /* week no of leap correction */
+ int dn = t->t_dayleap = getint(&mb(22)); /* day in week of leap correction */
+ int tlsf = t->t_gpsutcleap = getint(&mb(24)); /* new leap correction */
+ U_LONG now, leaptime;
+
+ t->t_week = getint(&mb(18)); /* current week no */
+
+ /* this stuff hasn't been tested yet... */
+ now = clock->utctime + JAN_1970; /* now in GPS seconds */
+ leaptime = (wnlsf*7 + dn)*86400; /* time of leap in GPS seconds */
+ if ((leaptime > now) && ((leaptime-now) < 86400*28))
+ {
+ /* generate a leap warning */
+ if (tlsf > tls)
+ t->t_leap = ADDSECOND;
+ else
+ t->t_leap = DELSECOND;
+ }
+ else
+ {
+ t->t_leap = 0;
+ }
+ }
+ break;
+
+ default:
+ /* it's validly formed, but we don't care about it! */
+ break;
+ }
+ }
+ return CVT_SKIP;
+}
+
+/*
+ * getflt, getdbl, getint convert fields in the incoming data into the
+ * appropriate type of item
+ *
+ * CAVEAT: these routines are currently definitely byte order dependent
+ * and assume Representation(float) == IEEE754
+ * These functions MUST be converted to portable versions (especially
+ * converting the float representation into ntp_fp formats in order
+ * to avoid floating point operations at all!
+ */
+
+static float
+getflt(bp)
+ u_char *bp;
+{
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp;
+ return uval.fv;
+}
+
+static double
+getdbl(bp)
+ u_char *bp;
+{
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp++;
+ uval.bd[4] = *bp++;
+ uval.bd[5] = *bp++;
+ uval.bd[6] = *bp++;
+ uval.bd[7] = *bp;
+ return uval.dv;
+}
+
+static int
+getint(bp)
+ u_char *bp;
+{
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp;
+ if (uval.bd[2] & 0x80) /* sign-extend */
+ uval.bd[0] = uval.bd[1] = 0xff;
+ else
+ uval.bd[0] = uval.bd[1] = 0;
+ return uval.iv;
+}
+
+#endif /* defined(PARSE) && defined(CLOCK_TRIMTSIP) */
+
+/*
+ * History:
+ *
+ * $Log: clk_trimble.c,v $
+ */