154359Sroberto/* 2280849Scy * /src/NTP/REPOSITORY/ntp4-dev/libparse/clk_trimtsip.c,v 4.19 2009/11/01 10:47:49 kardel RELEASE_20091101_A 354359Sroberto * 4280849Scy * clk_trimtsip.c,v 4.19 2009/11/01 10:47:49 kardel RELEASE_20091101_A 554359Sroberto * 6182007Sroberto * Trimble TSIP support 7182007Sroberto * Thanks to Sven Dietrich for providing test hardware 8182007Sroberto * 9280849Scy * Copyright (c) 1995-2009 by Frank Kardel <kardel <AT> ntp.org> 10282408Scy * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany 11182007Sroberto * 12182007Sroberto * Redistribution and use in source and binary forms, with or without 13182007Sroberto * modification, are permitted provided that the following conditions 14182007Sroberto * are met: 15182007Sroberto * 1. Redistributions of source code must retain the above copyright 16182007Sroberto * notice, this list of conditions and the following disclaimer. 17182007Sroberto * 2. Redistributions in binary form must reproduce the above copyright 18182007Sroberto * notice, this list of conditions and the following disclaimer in the 19182007Sroberto * documentation and/or other materials provided with the distribution. 20182007Sroberto * 3. Neither the name of the author nor the names of its contributors 21182007Sroberto * may be used to endorse or promote products derived from this software 22182007Sroberto * without specific prior written permission. 23182007Sroberto * 24182007Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25182007Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26182007Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27182007Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28182007Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29182007Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30182007Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31182007Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32182007Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33182007Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34182007Sroberto * SUCH DAMAGE. 35182007Sroberto * 3654359Sroberto */ 3754359Sroberto 3854359Sroberto#ifdef HAVE_CONFIG_H 3954359Sroberto# include <config.h> 4054359Sroberto#endif 4154359Sroberto 4254359Sroberto#if defined(REFCLOCK) && defined(CLOCK_PARSE) && defined(CLOCK_TRIMTSIP) 4354359Sroberto 4454359Sroberto#include "ntp_syslog.h" 4554359Sroberto#include "ntp_types.h" 4654359Sroberto#include "ntp_fp.h" 47280849Scy#include "timevalops.h" 4854359Sroberto#include "ntp_calendar.h" 4954359Sroberto#include "ntp_machine.h" 5054359Sroberto#include "ntp_stdlib.h" 5154359Sroberto 5254359Sroberto#include "parse.h" 5354359Sroberto 5454359Sroberto#ifndef PARSESTREAM 5582498Sroberto# include <stdio.h> 5654359Sroberto#else 5782498Sroberto# include "sys/parsestreams.h" 5882498Sroberto#endif 5954359Sroberto 6054359Sroberto#include "ascii.h" 6154359Sroberto#include "binio.h" 6254359Sroberto#include "ieee754io.h" 6354359Sroberto#include "trimble.h" 6454359Sroberto 6554359Sroberto/* 6654359Sroberto * Trimble low level TSIP parser / time converter 6754359Sroberto * 6854359Sroberto * The receiver uses a serial message protocol called Trimble Standard 6954359Sroberto * Interface Protocol (it can support others but this driver only supports 7054359Sroberto * TSIP). Messages in this protocol have the following form: 7154359Sroberto * 7254359Sroberto * <DLE><id> ... <data> ... <DLE><ETX> 7354359Sroberto * 7454359Sroberto * Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled 7554359Sroberto * on transmission and compressed back to one on reception. Otherwise 7654359Sroberto * the values of data bytes can be anything. The serial interface is RS-422 7754359Sroberto * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits 7854359Sroberto * in total!), and 1 stop bit. The protocol supports byte, integer, single, 7954359Sroberto * and double datatypes. Integers are two bytes, sent most significant first. 8054359Sroberto * Singles are IEEE754 single precision floating point numbers (4 byte) sent 8154359Sroberto * sign & exponent first. Doubles are IEEE754 double precision floating point 8254359Sroberto * numbers (8 byte) sent sign & exponent first. 8354359Sroberto * The receiver supports a large set of messages, only a very small subset of 8454359Sroberto * which is used here. 8554359Sroberto * 8654359Sroberto * From this module the following are recognised: 8754359Sroberto * 8854359Sroberto * ID Description 8954359Sroberto * 9054359Sroberto * 41 GPS Time 9154359Sroberto * 46 Receiver health 9254359Sroberto * 4F UTC correction data (used to get leap second warnings) 9354359Sroberto * 9454359Sroberto * All others are accepted but ignored for time conversion - they are passed up to higher layers. 9554359Sroberto * 9654359Sroberto */ 9754359Sroberto 9854359Srobertostatic offsets_t trim_offsets = { 0, 1, 2, 3, 4, 5, 6, 7 }; 9954359Sroberto 10054359Srobertostruct trimble 10154359Sroberto{ 10254359Sroberto u_char t_in_pkt; /* first DLE received */ 10354359Sroberto u_char t_dle; /* subsequent DLE received */ 10454359Sroberto u_short t_week; /* GPS week */ 10554359Sroberto u_short t_weekleap; /* GPS week of next/last week */ 10654359Sroberto u_short t_dayleap; /* day in week */ 10754359Sroberto u_short t_gpsutc; /* GPS - UTC offset */ 10854359Sroberto u_short t_gpsutcleap; /* offset at next/last leap */ 10954359Sroberto u_char t_operable; /* receiver feels OK */ 11054359Sroberto u_char t_mode; /* actual operating mode */ 11154359Sroberto u_char t_leap; /* possible leap warning */ 11254359Sroberto u_char t_utcknown; /* utc offset known */ 11354359Sroberto}; 11454359Sroberto 11554359Sroberto#define STATUS_BAD 0 /* BAD or UNINITIALIZED receiver status */ 11654359Sroberto#define STATUS_UNSAFE 1 /* not enough receivers for full precision */ 11754359Sroberto#define STATUS_SYNC 2 /* enough information for good operation */ 11854359Sroberto 119282408Scystatic unsigned long inp_tsip (parse_t *, char, timestamp_t *); 120280849Scystatic unsigned long cvt_trimtsip (unsigned char *, int, struct format *, clocktime_t *, void *); 12154359Sroberto 12254359Srobertostruct clockformat clock_trimtsip = 12354359Sroberto{ 12454359Sroberto inp_tsip, /* Trimble TSIP input handler */ 12554359Sroberto cvt_trimtsip, /* Trimble TSIP conversion */ 12654359Sroberto pps_one, /* easy PPS monitoring */ 12754359Sroberto 0, /* no configuration data */ 12854359Sroberto "Trimble TSIP", 12954359Sroberto 400, /* input buffer */ 13054359Sroberto sizeof(struct trimble) /* private data */ 13154359Sroberto}; 13254359Sroberto 13354359Sroberto#define ADDSECOND 0x01 13454359Sroberto#define DELSECOND 0x02 13554359Sroberto 13654359Srobertostatic unsigned long 13754359Srobertoinp_tsip( 13854359Sroberto parse_t *parseio, 139282408Scy char ch, 14054359Sroberto timestamp_t *tstamp 14154359Sroberto ) 14254359Sroberto{ 14354359Sroberto struct trimble *t = (struct trimble *)parseio->parse_pdata; 14454359Sroberto 14554359Sroberto if (!t) 14654359Sroberto return PARSE_INP_SKIP; /* local data not allocated - sigh! */ 14754359Sroberto 14854359Sroberto if (!t->t_in_pkt && ch != DLE) { 14954359Sroberto /* wait for start of packet */ 15054359Sroberto return PARSE_INP_SKIP; 15154359Sroberto } 15254359Sroberto 15354359Sroberto if ((parseio->parse_index >= (parseio->parse_dsize - 2)) || 15454359Sroberto (parseio->parse_dtime.parse_msglen >= (sizeof(parseio->parse_dtime.parse_msg) - 2))) 15554359Sroberto { /* OVERFLOW - DROP! */ 15654359Sroberto t->t_in_pkt = t->t_dle = 0; 15754359Sroberto parseio->parse_index = 0; 15854359Sroberto parseio->parse_dtime.parse_msglen = 0; 15954359Sroberto return PARSE_INP_SKIP; 16054359Sroberto } 16154359Sroberto 16254359Sroberto switch (ch) { 16354359Sroberto case DLE: 16454359Sroberto if (!t->t_in_pkt) { 16554359Sroberto t->t_dle = 0; 16654359Sroberto t->t_in_pkt = 1; 16754359Sroberto parseio->parse_index = 0; 16854359Sroberto parseio->parse_data[parseio->parse_index++] = ch; 16954359Sroberto parseio->parse_dtime.parse_msglen = 0; 17054359Sroberto parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch; 17154359Sroberto parseio->parse_dtime.parse_stime = *tstamp; /* pick up time stamp at packet start */ 17254359Sroberto } else if (t->t_dle) { 17354359Sroberto /* Double DLE -> insert a DLE */ 17454359Sroberto t->t_dle = 0; 17554359Sroberto parseio->parse_data[parseio->parse_index++] = DLE; 17654359Sroberto parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = DLE; 17754359Sroberto } else 17854359Sroberto t->t_dle = 1; 17954359Sroberto break; 18054359Sroberto 18154359Sroberto case ETX: 18254359Sroberto if (t->t_dle) { 18354359Sroberto /* DLE,ETX -> end of packet */ 18454359Sroberto parseio->parse_data[parseio->parse_index++] = DLE; 18554359Sroberto parseio->parse_data[parseio->parse_index] = ch; 186282408Scy parseio->parse_ldsize = (u_short) (parseio->parse_index + 1); 18754359Sroberto memcpy(parseio->parse_ldata, parseio->parse_data, parseio->parse_ldsize); 18854359Sroberto parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = DLE; 18954359Sroberto parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch; 19054359Sroberto t->t_in_pkt = t->t_dle = 0; 19154359Sroberto return PARSE_INP_TIME|PARSE_INP_DATA; 19254359Sroberto } 193280849Scy /*FALLTHROUGH*/ 19454359Sroberto 19554359Sroberto default: /* collect data */ 19654359Sroberto t->t_dle = 0; 19754359Sroberto parseio->parse_data[parseio->parse_index++] = ch; 19854359Sroberto parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch; 19954359Sroberto } 20054359Sroberto 20154359Sroberto return PARSE_INP_SKIP; 20254359Sroberto} 203282408Scy 204282408Scystatic short 20554359Srobertogetshort( 20654359Sroberto unsigned char *p 20754359Sroberto ) 20854359Sroberto{ 209282408Scy return (short) get_msb_short(&p); 21054359Sroberto} 21154359Sroberto 21254359Sroberto/* 21354359Sroberto * cvt_trimtsip 21454359Sroberto * 21554359Sroberto * convert TSIP type format 21654359Sroberto */ 21754359Srobertostatic unsigned long 21854359Srobertocvt_trimtsip( 21954359Sroberto unsigned char *buffer, 22054359Sroberto int size, 22154359Sroberto struct format *format, 22254359Sroberto clocktime_t *clock_time, 22354359Sroberto void *local 22454359Sroberto ) 22554359Sroberto{ 22654359Sroberto register struct trimble *t = (struct trimble *)local; /* get local data space */ 22754359Sroberto#define mb(_X_) (buffer[2+(_X_)]) /* shortcut for buffer access */ 22854359Sroberto register u_char cmd; 22954359Sroberto 23054359Sroberto clock_time->flags = 0; 23154359Sroberto 23254359Sroberto if (!t) { 23354359Sroberto return CVT_NONE; /* local data not allocated - sigh! */ 23454359Sroberto } 23554359Sroberto 23654359Sroberto if ((size < 4) || 23754359Sroberto (buffer[0] != DLE) || 23854359Sroberto (buffer[size-1] != ETX) || 23954359Sroberto (buffer[size-2] != DLE)) 24054359Sroberto { 24154359Sroberto printf("TRIMBLE BAD packet, size %d:\n", size); 24254359Sroberto return CVT_NONE; 24354359Sroberto } 24454359Sroberto else 24554359Sroberto { 24654359Sroberto unsigned char *bp; 24754359Sroberto cmd = buffer[1]; 248282408Scy 24954359Sroberto switch(cmd) 25054359Sroberto { 25154359Sroberto case CMD_RCURTIME: 25254359Sroberto { /* GPS time */ 253316068Sdelphij l_fp secs; 254316068Sdelphij u_int week = getshort((unsigned char *)&mb(4)); 255316068Sdelphij l_fp utcoffset; 256316068Sdelphij l_fp gpstime; 25754359Sroberto 25854359Sroberto bp = &mb(0); 25954359Sroberto if (fetch_ieee754(&bp, IEEE_SINGLE, &secs, trim_offsets) != IEEE_OK) 26054359Sroberto return CVT_FAIL|CVT_BADFMT; 261282408Scy 26254359Sroberto if ((secs.l_i <= 0) || 26354359Sroberto (t->t_utcknown == 0)) 26454359Sroberto { 26554359Sroberto clock_time->flags = PARSEB_POWERUP; 26654359Sroberto return CVT_OK; 26754359Sroberto } 268344884Scy week = basedate_expand_gpsweek(week); 26954359Sroberto 27054359Sroberto /* time OK */ 27154359Sroberto 27254359Sroberto /* fetch UTC offset */ 27354359Sroberto bp = &mb(6); 27454359Sroberto if (fetch_ieee754(&bp, IEEE_SINGLE, &utcoffset, trim_offsets) != IEEE_OK) 27554359Sroberto return CVT_FAIL|CVT_BADFMT; 276282408Scy 27754359Sroberto L_SUB(&secs, &utcoffset); /* adjust GPS time to UTC time */ 27854359Sroberto 27954359Sroberto gpstolfp((unsigned short)week, (unsigned short)0, 28054359Sroberto secs.l_ui, &gpstime); 28154359Sroberto 28254359Sroberto gpstime.l_uf = secs.l_uf; 28354359Sroberto 28454359Sroberto clock_time->utctime = gpstime.l_ui - JAN_1970; 28554359Sroberto 28654359Sroberto TSFTOTVU(gpstime.l_uf, clock_time->usecond); 28754359Sroberto 28854359Sroberto if (t->t_leap == ADDSECOND) 28954359Sroberto clock_time->flags |= PARSEB_LEAPADD; 290282408Scy 29154359Sroberto if (t->t_leap == DELSECOND) 29254359Sroberto clock_time->flags |= PARSEB_LEAPDEL; 293282408Scy 29454359Sroberto switch (t->t_operable) 29554359Sroberto { 29654359Sroberto case STATUS_SYNC: 29754359Sroberto clock_time->flags &= ~(PARSEB_POWERUP|PARSEB_NOSYNC); 29854359Sroberto break; 29954359Sroberto 30054359Sroberto case STATUS_UNSAFE: 30154359Sroberto clock_time->flags |= PARSEB_NOSYNC; 30254359Sroberto break; 30354359Sroberto 30454359Sroberto case STATUS_BAD: 30554359Sroberto clock_time->flags |= PARSEB_NOSYNC|PARSEB_POWERUP; 30654359Sroberto break; 30754359Sroberto } 308282408Scy 30954359Sroberto if (t->t_mode == 0) 31054359Sroberto clock_time->flags |= PARSEB_POSITION; 311282408Scy 31254359Sroberto clock_time->flags |= PARSEB_S_LEAP|PARSEB_S_POSITION; 313282408Scy 31454359Sroberto return CVT_OK; 31554359Sroberto 31654359Sroberto } /* case 0x41 */ 31754359Sroberto 31854359Sroberto case CMD_RRECVHEALTH: 31954359Sroberto { 32054359Sroberto /* TRIMBLE health */ 32154359Sroberto u_char status = mb(0); 32254359Sroberto 32354359Sroberto switch (status) 32454359Sroberto { 32554359Sroberto case 0x00: /* position fixes */ 32654359Sroberto t->t_operable = STATUS_SYNC; 32754359Sroberto break; 32854359Sroberto 32954359Sroberto case 0x09: /* 1 satellite */ 33054359Sroberto case 0x0A: /* 2 satellites */ 33154359Sroberto case 0x0B: /* 3 satellites */ 33254359Sroberto t->t_operable = STATUS_UNSAFE; 33354359Sroberto break; 33454359Sroberto 33554359Sroberto default: 33654359Sroberto t->t_operable = STATUS_BAD; 33754359Sroberto break; 33854359Sroberto } 33954359Sroberto t->t_mode = status; 34054359Sroberto } 34154359Sroberto break; 34254359Sroberto 34354359Sroberto case CMD_RUTCPARAM: 34454359Sroberto { 34554359Sroberto l_fp t0t; 34654359Sroberto unsigned char *lbp; 347282408Scy 34854359Sroberto /* UTC correction data - derive a leap warning */ 349282408Scy int tls = t->t_gpsutc = (u_short) getshort((unsigned char *)&mb(12)); /* current leap correction (GPS-UTC) */ 350282408Scy int tlsf = t->t_gpsutcleap = (u_short) getshort((unsigned char *)&mb(24)); /* new leap correction */ 35154359Sroberto 352344884Scy t->t_weekleap = basedate_expand_gpsweek( 353344884Scy (u_short) getshort((unsigned char *)&mb(20))); /* week no of leap correction */ 35454359Sroberto 355282408Scy t->t_dayleap = (u_short) getshort((unsigned char *)&mb(22)); /* day in week of leap correction */ 356344884Scy t->t_week = basedate_expand_gpsweek( 357344884Scy (u_short) getshort((unsigned char *)&mb(18))); /* current week no */ 358282408Scy 35954359Sroberto lbp = (unsigned char *)&mb(14); /* last update time */ 36054359Sroberto if (fetch_ieee754(&lbp, IEEE_SINGLE, &t0t, trim_offsets) != IEEE_OK) 36154359Sroberto return CVT_FAIL|CVT_BADFMT; 36254359Sroberto 36354359Sroberto t->t_utcknown = t0t.l_ui != 0; 364282408Scy 36554359Sroberto if ((t->t_utcknown) && /* got UTC information */ 36654359Sroberto (tlsf != tls) && /* something will change */ 36754359Sroberto ((t->t_weekleap - t->t_week) < 5)) /* and close in the future */ 36854359Sroberto { 36954359Sroberto /* generate a leap warning */ 37054359Sroberto if (tlsf > tls) 37154359Sroberto t->t_leap = ADDSECOND; 37254359Sroberto else 37354359Sroberto t->t_leap = DELSECOND; 37454359Sroberto } 37554359Sroberto else 37654359Sroberto { 37754359Sroberto t->t_leap = 0; 37854359Sroberto } 37954359Sroberto } 38054359Sroberto break; 38154359Sroberto 38254359Sroberto default: 38354359Sroberto /* it's validly formed, but we don't care about it! */ 38454359Sroberto break; 38554359Sroberto } 38654359Sroberto } 38754359Sroberto return CVT_SKIP; 38854359Sroberto} 38954359Sroberto 39054359Sroberto#else /* not (REFCLOCK && CLOCK_PARSE && CLOCK_TRIMTSIP && !PARSESTREAM) */ 39154359Srobertoint clk_trimtsip_bs; 39254359Sroberto#endif /* not (REFCLOCK && CLOCK_PARSE && CLOCK_TRIMTSIP && !PARSESTREAM) */ 39354359Sroberto 39454359Sroberto/* 39554359Sroberto * History: 39654359Sroberto * 39754359Sroberto * clk_trimtsip.c,v 398280849Scy * Revision 4.19 2009/11/01 10:47:49 kardel 399280849Scy * de-P() 400280849Scy * 401280849Scy * Revision 4.18 2009/11/01 08:46:46 kardel 402280849Scy * clarify case FALLTHROUGH 403280849Scy * 404182007Sroberto * Revision 4.17 2005/04/16 17:32:10 kardel 405182007Sroberto * update copyright 406182007Sroberto * 407182007Sroberto * Revision 4.16 2004/11/14 15:29:41 kardel 408182007Sroberto * support PPSAPI, upgrade Copyright to Berkeley style 409182007Sroberto * 41056746Sroberto * Revision 4.13 1999/11/28 09:13:51 kardel 41156746Sroberto * RECON_4_0_98F 41256746Sroberto * 41354359Sroberto * Revision 4.12 1999/02/28 13:00:08 kardel 41454359Sroberto * *** empty log message *** 41554359Sroberto * 41654359Sroberto * Revision 4.11 1999/02/28 11:47:54 kardel 41754359Sroberto * (struct trimble): new member t_utcknown 41854359Sroberto * (cvt_trimtsip): fixed status monitoring, bad receiver states are 41954359Sroberto * now recognized 42054359Sroberto * 42154359Sroberto * Revision 4.10 1999/02/27 15:57:15 kardel 42254359Sroberto * use mmemcpy instead of bcopy 42354359Sroberto * 42454359Sroberto * Revision 4.9 1999/02/21 12:17:42 kardel 42554359Sroberto * 4.91f reconcilation 42654359Sroberto * 42754359Sroberto * Revision 4.8 1998/11/15 20:27:58 kardel 42854359Sroberto * Release 4.0.73e13 reconcilation 42954359Sroberto * 43054359Sroberto * Revision 4.7 1998/08/16 18:49:20 kardel 43154359Sroberto * (cvt_trimtsip): initial kernel capable version (no more floats) 43254359Sroberto * (clock_trimtsip =): new format name 43354359Sroberto * 43454359Sroberto * Revision 4.6 1998/08/09 22:26:05 kardel 43554359Sroberto * Trimble TSIP support 43654359Sroberto * 43754359Sroberto * Revision 4.5 1998/08/02 10:37:05 kardel 43854359Sroberto * working TSIP parser 43954359Sroberto * 44054359Sroberto * Revision 4.4 1998/06/28 16:50:40 kardel 44154359Sroberto * (getflt): fixed ENDIAN issue 44254359Sroberto * (getdbl): fixed ENDIAN issue 44354359Sroberto * (getint): use get_msb_short() 44454359Sroberto * (cvt_trimtsip): use gpstolfp() for conversion 44554359Sroberto * 44654359Sroberto * Revision 4.3 1998/06/13 12:07:31 kardel 44754359Sroberto * fix SYSV clock name clash 44854359Sroberto * 44954359Sroberto * Revision 4.2 1998/06/12 15:22:30 kardel 45054359Sroberto * fix prototypes 45154359Sroberto * 45254359Sroberto * Revision 4.1 1998/05/24 09:39:54 kardel 45354359Sroberto * implementation of the new IO handling model 45454359Sroberto * 45554359Sroberto * Revision 4.0 1998/04/10 19:45:32 kardel 45654359Sroberto * Start 4.0 release version numbering 45754359Sroberto * 45854359Sroberto * from V3 1.8 loginfo deleted 1998/04/11 kardel 45954359Sroberto */ 460