1818eba70c
Reviewed by: delphij (suggested MFC) Approved by: roberto Security: CVE-2015-1798, CVE-2015-1799 Security: VuXML ebd84c96-dd7e-11e4-854e-3c970e169bc2 MFC after: 1 month
1260 lines
28 KiB
C
1260 lines
28 KiB
C
/*
|
|
* This software was developed by the Software and Component Technologies
|
|
* group of Trimble Navigation, Ltd.
|
|
*
|
|
* Copyright (c) 1997, 1998, 1999, 2000 Trimble Navigation Ltd.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Trimble Navigation, Ltd.
|
|
* 4. The name of Trimble Navigation Ltd. may not be used to endorse or
|
|
* promote products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY TRIMBLE NAVIGATION LTD. ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL TRIMBLE NAVIGATION LTD. BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* refclock_palisade - clock driver for the Trimble Palisade GPS
|
|
* timing receiver
|
|
*
|
|
* For detailed information on this program, please refer to the html
|
|
* Refclock 29 page accompanying the NTP distribution.
|
|
*
|
|
* for questions / bugs / comments, contact:
|
|
* sven_dietrich@trimble.com
|
|
*
|
|
* Sven-Thorsten Dietrich
|
|
* 645 North Mary Avenue
|
|
* Post Office Box 3642
|
|
* Sunnyvale, CA 94088-3642
|
|
*
|
|
* Version 2.45; July 14, 1999
|
|
*
|
|
*
|
|
*
|
|
* 31/03/06: Added support for Thunderbolt GPS Disciplined Clock.
|
|
* Contact: Fernando Pablo Hauscarriaga
|
|
* E-mail: fernandoph@iar.unlp.edu.ar
|
|
* Home page: www.iar.unlp.edu.ar/~fernandoph
|
|
* Instituto Argentino de Radioastronomia
|
|
* www.iar.unlp.edu.ar
|
|
*
|
|
* 14/01/07: Conditinal compilation for Thunderbolt support no longer needed
|
|
* now we use mode 2 for decode thunderbolt packets.
|
|
* Fernando P. Hauscarriaga
|
|
*
|
|
* 30/08/09: Added support for Trimble Acutime Gold Receiver.
|
|
* Fernando P. Hauscarriaga (fernandoph@iar.unlp.edu.ar)
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#if defined(REFCLOCK) && defined(CLOCK_PALISADE)
|
|
|
|
#ifdef SYS_WINNT
|
|
extern int async_write(int, const void *, unsigned int);
|
|
#undef write
|
|
#define write(fd, data, octets) async_write(fd, data, octets)
|
|
#endif
|
|
|
|
#include "refclock_palisade.h"
|
|
/* Table to get from month to day of the year */
|
|
const int days_of_year [12] = {
|
|
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
|
|
};
|
|
|
|
#ifdef DEBUG
|
|
const char * Tracking_Status[15][15] = {
|
|
{ "Doing Fixes\0" }, { "Good 1SV\0" }, { "Approx. 1SV\0" },
|
|
{"Need Time\0" }, { "Need INIT\0" }, { "PDOP too High\0" },
|
|
{ "Bad 1SV\0" }, { "0SV Usable\0" }, { "1SV Usable\0" },
|
|
{ "2SV Usable\0" }, { "3SV Usable\0" }, { "No Integrity\0" },
|
|
{ "Diff Corr\0" }, { "Overdet Clock\0" }, { "Invalid\0" } };
|
|
#endif
|
|
|
|
/*
|
|
* Transfer vector
|
|
*/
|
|
struct refclock refclock_palisade = {
|
|
palisade_start, /* start up driver */
|
|
palisade_shutdown, /* shut down driver */
|
|
palisade_poll, /* transmit poll message */
|
|
noentry, /* not used */
|
|
noentry, /* initialize driver (not used) */
|
|
noentry, /* not used */
|
|
NOFLAGS /* not used */
|
|
};
|
|
|
|
int day_of_year (char *dt);
|
|
|
|
/* Extract the clock type from the mode setting */
|
|
#define CLK_TYPE(x) ((int)(((x)->ttl) & 0x7F))
|
|
|
|
/* Supported clock types */
|
|
#define CLK_TRIMBLE 0 /* Trimble Palisade */
|
|
#define CLK_PRAECIS 1 /* Endrun Technologies Praecis */
|
|
#define CLK_THUNDERBOLT 2 /* Trimble Thunderbolt GPS Receiver */
|
|
#define CLK_ACUTIME 3 /* Trimble Acutime Gold */
|
|
#define CLK_ACUTIMEB 4 /* Trimble Actutime Gold Port B */
|
|
|
|
int praecis_msg;
|
|
static void praecis_parse(struct recvbuf *rbufp, struct peer *peer);
|
|
|
|
/* These routines are for sending packets to the Thunderbolt receiver
|
|
* They are taken from Markus Prosch
|
|
*/
|
|
|
|
#ifdef PALISADE_SENDCMD_RESURRECTED
|
|
/*
|
|
* sendcmd - Build data packet for sending
|
|
*/
|
|
static void
|
|
sendcmd (
|
|
struct packettx *buffer,
|
|
int c
|
|
)
|
|
{
|
|
*buffer->data = DLE;
|
|
*(buffer->data + 1) = (unsigned char)c;
|
|
buffer->size = 2;
|
|
}
|
|
#endif /* PALISADE_SENDCMD_RESURRECTED */
|
|
|
|
/*
|
|
* sendsupercmd - Build super data packet for sending
|
|
*/
|
|
static void
|
|
sendsupercmd (
|
|
struct packettx *buffer,
|
|
int c1,
|
|
int c2
|
|
)
|
|
{
|
|
*buffer->data = DLE;
|
|
*(buffer->data + 1) = (unsigned char)c1;
|
|
*(buffer->data + 2) = (unsigned char)c2;
|
|
buffer->size = 3;
|
|
}
|
|
|
|
/*
|
|
* sendbyte -
|
|
*/
|
|
static void
|
|
sendbyte (
|
|
struct packettx *buffer,
|
|
int b
|
|
)
|
|
{
|
|
if (b == DLE)
|
|
*(buffer->data+buffer->size++) = DLE;
|
|
*(buffer->data+buffer->size++) = (unsigned char)b;
|
|
}
|
|
|
|
/*
|
|
* sendint -
|
|
*/
|
|
static void
|
|
sendint (
|
|
struct packettx *buffer,
|
|
int a
|
|
)
|
|
{
|
|
sendbyte(buffer, (unsigned char)((a>>8) & 0xff));
|
|
sendbyte(buffer, (unsigned char)(a & 0xff));
|
|
}
|
|
|
|
/*
|
|
* sendetx - Send packet or super packet to the device
|
|
*/
|
|
static int
|
|
sendetx (
|
|
struct packettx *buffer,
|
|
int fd
|
|
)
|
|
{
|
|
int result;
|
|
|
|
*(buffer->data+buffer->size++) = DLE;
|
|
*(buffer->data+buffer->size++) = ETX;
|
|
result = write(fd, buffer->data, (unsigned long)buffer->size);
|
|
|
|
if (result != -1)
|
|
return (result);
|
|
else
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* init_thunderbolt - Prepares Thunderbolt receiver to be used with
|
|
* NTP (also taken from Markus Prosch).
|
|
*/
|
|
static void
|
|
init_thunderbolt (
|
|
int fd
|
|
)
|
|
{
|
|
struct packettx tx;
|
|
|
|
tx.size = 0;
|
|
tx.data = (u_char *) malloc(100);
|
|
|
|
/* set UTC time */
|
|
sendsupercmd (&tx, 0x8E, 0xA2);
|
|
sendbyte (&tx, 0x3);
|
|
sendetx (&tx, fd);
|
|
|
|
/* activate packets 0x8F-AB and 0x8F-AC */
|
|
sendsupercmd (&tx, 0x8F, 0xA5);
|
|
sendint (&tx, 0x5);
|
|
sendetx (&tx, fd);
|
|
|
|
free(tx.data);
|
|
}
|
|
|
|
/*
|
|
* init_acutime - Prepares Acutime Receiver to be used with NTP
|
|
*/
|
|
static void
|
|
init_acutime (
|
|
int fd
|
|
)
|
|
{
|
|
/* Disable all outputs, Enable Event-Polling on PortA so
|
|
we can ask for time packets */
|
|
struct packettx tx;
|
|
|
|
tx.size = 0;
|
|
tx.data = (u_char *) malloc(100);
|
|
|
|
sendsupercmd(&tx, 0x8E, 0xA5);
|
|
sendbyte(&tx, 0x02);
|
|
sendbyte(&tx, 0x00);
|
|
sendbyte(&tx, 0x00);
|
|
sendbyte(&tx, 0x00);
|
|
sendetx(&tx, fd);
|
|
|
|
free(tx.data);
|
|
}
|
|
|
|
/*
|
|
* palisade_start - open the devices and initialize data for processing
|
|
*/
|
|
static int
|
|
palisade_start (
|
|
int unit,
|
|
struct peer *peer
|
|
)
|
|
{
|
|
struct palisade_unit *up;
|
|
struct refclockproc *pp;
|
|
int fd;
|
|
char gpsdev[20];
|
|
struct termios tio;
|
|
|
|
snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
|
|
|
|
/*
|
|
* Open serial port.
|
|
*/
|
|
fd = refclock_open(gpsdev, SPEED232, LDISC_RAW);
|
|
if (fd <= 0) {
|
|
#ifdef DEBUG
|
|
printf("Palisade(%d) start: open %s failed\n", unit, gpsdev);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
msyslog(LOG_NOTICE, "Palisade(%d) fd: %d dev: %s", unit, fd,
|
|
gpsdev);
|
|
|
|
if (tcgetattr(fd, &tio) < 0) {
|
|
msyslog(LOG_ERR,
|
|
"Palisade(%d) tcgetattr(fd, &tio): %m",unit);
|
|
#ifdef DEBUG
|
|
printf("Palisade(%d) tcgetattr(fd, &tio)\n",unit);
|
|
#endif
|
|
close(fd);
|
|
return (0);
|
|
}
|
|
|
|
tio.c_cflag |= (PARENB|PARODD);
|
|
tio.c_iflag &= ~ICRNL;
|
|
|
|
/*
|
|
* Allocate and initialize unit structure
|
|
*/
|
|
up = emalloc_zero(sizeof(*up));
|
|
|
|
up->type = CLK_TYPE(peer);
|
|
switch (up->type) {
|
|
case CLK_TRIMBLE:
|
|
/* Normal mode, do nothing */
|
|
break;
|
|
case CLK_PRAECIS:
|
|
msyslog(LOG_NOTICE, "Palisade(%d) Praecis mode enabled"
|
|
,unit);
|
|
break;
|
|
case CLK_THUNDERBOLT:
|
|
msyslog(LOG_NOTICE, "Palisade(%d) Thunderbolt mode enabled"
|
|
,unit);
|
|
tio.c_cflag = (CS8|CLOCAL|CREAD);
|
|
break;
|
|
case CLK_ACUTIME:
|
|
msyslog(LOG_NOTICE, "Palisade(%d) Acutime Gold mode enabled"
|
|
,unit);
|
|
break;
|
|
default:
|
|
msyslog(LOG_NOTICE, "Palisade(%d) mode unknown",unit);
|
|
break;
|
|
}
|
|
if (tcsetattr(fd, TCSANOW, &tio) == -1) {
|
|
msyslog(LOG_ERR, "Palisade(%d) tcsetattr(fd, &tio): %m",unit);
|
|
#ifdef DEBUG
|
|
printf("Palisade(%d) tcsetattr(fd, &tio)\n",unit);
|
|
#endif
|
|
close(fd);
|
|
free(up);
|
|
return 0;
|
|
}
|
|
|
|
pp = peer->procptr;
|
|
pp->io.clock_recv = palisade_io;
|
|
pp->io.srcclock = peer;
|
|
pp->io.datalen = 0;
|
|
pp->io.fd = fd;
|
|
if (!io_addclock(&pp->io)) {
|
|
#ifdef DEBUG
|
|
printf("Palisade(%d) io_addclock\n",unit);
|
|
#endif
|
|
close(fd);
|
|
pp->io.fd = -1;
|
|
free(up);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Initialize miscellaneous variables
|
|
*/
|
|
pp->unitptr = up;
|
|
pp->clockdesc = DESCRIPTION;
|
|
|
|
peer->precision = PRECISION;
|
|
peer->sstclktype = CTL_SST_TS_UHF;
|
|
peer->minpoll = TRMB_MINPOLL;
|
|
peer->maxpoll = TRMB_MAXPOLL;
|
|
memcpy((char *)&pp->refid, REFID, 4);
|
|
|
|
up->leap_status = 0;
|
|
up->unit = (short) unit;
|
|
up->rpt_status = TSIP_PARSED_EMPTY;
|
|
up->rpt_cnt = 0;
|
|
|
|
if (up->type == CLK_THUNDERBOLT)
|
|
init_thunderbolt(fd);
|
|
if (up->type == CLK_ACUTIME)
|
|
init_acutime(fd);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* palisade_shutdown - shut down the clock
|
|
*/
|
|
static void
|
|
palisade_shutdown (
|
|
int unit,
|
|
struct peer *peer
|
|
)
|
|
{
|
|
struct palisade_unit *up;
|
|
struct refclockproc *pp;
|
|
pp = peer->procptr;
|
|
up = pp->unitptr;
|
|
if (-1 != pp->io.fd)
|
|
io_closeclock(&pp->io);
|
|
if (NULL != up)
|
|
free(up);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* unpack_date - get day and year from date
|
|
*/
|
|
int
|
|
day_of_year (
|
|
char * dt
|
|
)
|
|
{
|
|
int day, mon, year;
|
|
|
|
mon = dt[1];
|
|
/* Check month is inside array bounds */
|
|
if ((mon < 1) || (mon > 12))
|
|
return -1;
|
|
|
|
day = dt[0] + days_of_year[mon - 1];
|
|
year = getint((u_char *) (dt + 2));
|
|
|
|
if ( !(year % 4) && ((year % 100) ||
|
|
(!(year % 100) && !(year%400)))
|
|
&&(mon > 2))
|
|
day ++; /* leap year and March or later */
|
|
|
|
return day;
|
|
}
|
|
|
|
|
|
/*
|
|
* TSIP_decode - decode the TSIP data packets
|
|
*/
|
|
int
|
|
TSIP_decode (
|
|
struct peer *peer
|
|
)
|
|
{
|
|
int st;
|
|
long secint;
|
|
double secs;
|
|
double secfrac;
|
|
unsigned short event = 0;
|
|
|
|
struct palisade_unit *up;
|
|
struct refclockproc *pp;
|
|
|
|
pp = peer->procptr;
|
|
up = pp->unitptr;
|
|
|
|
/*
|
|
* Check the time packet, decode its contents.
|
|
* If the timecode has invalid length or is not in
|
|
* proper format, declare bad format and exit.
|
|
*/
|
|
|
|
if ((up->type != CLK_THUNDERBOLT) & (up->type != CLK_ACUTIME)){
|
|
if ((up->rpt_buf[0] == (char) 0x41) ||
|
|
(up->rpt_buf[0] == (char) 0x46) ||
|
|
(up->rpt_buf[0] == (char) 0x54) ||
|
|
(up->rpt_buf[0] == (char) 0x4B) ||
|
|
(up->rpt_buf[0] == (char) 0x6D)) {
|
|
|
|
/* standard time packet - GPS time and GPS week number */
|
|
#ifdef DEBUG
|
|
printf("Palisade Port B packets detected. Connect to Port A\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We cast both to u_char to as 0x8f uses the sign bit on a char
|
|
*/
|
|
if ((u_char) up->rpt_buf[0] == (u_char) 0x8f) {
|
|
/*
|
|
* Superpackets
|
|
*/
|
|
event = (unsigned short) (getint((u_char *) &mb(1)) & 0xffff);
|
|
if (!((pp->sloppyclockflag & CLK_FLAG2) || event))
|
|
/* Ignore Packet */
|
|
return 0;
|
|
|
|
switch (mb(0) & 0xff) {
|
|
int GPS_UTC_Offset;
|
|
long tow;
|
|
|
|
case PACKET_8F0B:
|
|
|
|
if (up->polled <= 0)
|
|
return 0;
|
|
|
|
if (up->rpt_cnt != LENCODE_8F0B) /* check length */
|
|
break;
|
|
|
|
#ifdef DEBUG
|
|
if (debug > 1) {
|
|
int ts;
|
|
double lat, lon, alt;
|
|
lat = getdbl((u_char *) &mb(42)) * R2D;
|
|
lon = getdbl((u_char *) &mb(50)) * R2D;
|
|
alt = getdbl((u_char *) &mb(58));
|
|
|
|
printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n",
|
|
up->unit, lat,lon,alt);
|
|
printf("TSIP_decode: unit %d: Sats:",
|
|
up->unit);
|
|
for (st = 66, ts = 0; st <= 73; st++)
|
|
if (mb(st)) {
|
|
if (mb(st) > 0) ts++;
|
|
printf(" %02d", mb(st));
|
|
}
|
|
printf(" : Tracking %d\n", ts);
|
|
}
|
|
#endif
|
|
|
|
GPS_UTC_Offset = getint((u_char *) &mb(16));
|
|
if (GPS_UTC_Offset == 0) { /* Check UTC offset */
|
|
#ifdef DEBUG
|
|
printf("TSIP_decode: UTC Offset Unknown\n");
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
secs = getdbl((u_char *) &mb(3));
|
|
secint = (long) secs;
|
|
secfrac = secs - secint; /* 0.0 <= secfrac < 1.0 */
|
|
|
|
pp->nsec = (long) (secfrac * 1000000000);
|
|
|
|
secint %= 86400; /* Only care about today */
|
|
pp->hour = secint / 3600;
|
|
secint %= 3600;
|
|
pp->minute = secint / 60;
|
|
secint %= 60;
|
|
pp->second = secint % 60;
|
|
|
|
if ((pp->day = day_of_year(&mb(11))) < 0) break;
|
|
|
|
pp->year = getint((u_char *) &mb(13));
|
|
|
|
#ifdef DEBUG
|
|
if (debug > 1)
|
|
printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d UTC %02d\n",
|
|
up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
|
|
pp->second, pp->nsec, mb(12), mb(11), pp->year, GPS_UTC_Offset);
|
|
#endif
|
|
/* Only use this packet when no
|
|
* 8F-AD's are being received
|
|
*/
|
|
|
|
if (up->leap_status) {
|
|
up->leap_status = 0;
|
|
return 0;
|
|
}
|
|
|
|
return 2;
|
|
break;
|
|
|
|
case PACKET_NTP:
|
|
/* Palisade-NTP Packet */
|
|
|
|
if (up->rpt_cnt != LENCODE_NTP) /* check length */
|
|
break;
|
|
|
|
up->leap_status = mb(19);
|
|
|
|
if (up->polled <= 0)
|
|
return 0;
|
|
|
|
/* Check Tracking Status */
|
|
st = mb(18);
|
|
if (st < 0 || st > 14)
|
|
st = 14;
|
|
if ((st >= 2 && st <= 7) || st == 11 || st == 12) {
|
|
#ifdef DEBUG
|
|
printf("TSIP_decode: Not Tracking Sats : %s\n",
|
|
*Tracking_Status[st]);
|
|
#endif
|
|
refclock_report(peer, CEVNT_BADTIME);
|
|
up->polled = -1;
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
up->month = mb(15);
|
|
if ( (up->leap_status & PALISADE_LEAP_PENDING) &&
|
|
/* Avoid early announce: https://bugs.ntp.org/2773 */
|
|
(6 == up->month || 12 == up->month) ) {
|
|
if (up->leap_status & PALISADE_UTC_TIME)
|
|
pp->leap = LEAP_ADDSECOND;
|
|
else
|
|
pp->leap = LEAP_DELSECOND;
|
|
}
|
|
else if (up->leap_status)
|
|
pp->leap = LEAP_NOWARNING;
|
|
|
|
else { /* UTC flag is not set:
|
|
* Receiver may have been reset, and lost
|
|
* its UTC almanac data */
|
|
pp->leap = LEAP_NOTINSYNC;
|
|
#ifdef DEBUG
|
|
printf("TSIP_decode: UTC Almanac unavailable: %d\n",
|
|
mb(19));
|
|
#endif
|
|
refclock_report(peer, CEVNT_BADTIME);
|
|
up->polled = -1;
|
|
return 0;
|
|
}
|
|
|
|
pp->nsec = (long) (getdbl((u_char *) &mb(3))
|
|
* 1000000000);
|
|
|
|
if ((pp->day = day_of_year(&mb(14))) < 0)
|
|
break;
|
|
pp->year = getint((u_char *) &mb(16));
|
|
pp->hour = mb(11);
|
|
pp->minute = mb(12);
|
|
pp->second = mb(13);
|
|
up->month = mb(14); /* Save for LEAP check */
|
|
|
|
#ifdef DEBUG
|
|
if (debug > 1)
|
|
printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d UTC %02x %s\n",
|
|
up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
|
|
pp->second, pp->nsec, mb(15), mb(14), pp->year,
|
|
mb(19), *Tracking_Status[st]);
|
|
#endif
|
|
return 1;
|
|
break;
|
|
|
|
case PACKET_8FAC:
|
|
if (up->polled <= 0)
|
|
return 0;
|
|
|
|
if (up->rpt_cnt != LENCODE_8FAC)/* check length */
|
|
break;
|
|
|
|
#ifdef DEBUG
|
|
if (debug > 1) {
|
|
double lat, lon, alt;
|
|
lat = getdbl((u_char *) &mb(36)) * R2D;
|
|
lon = getdbl((u_char *) &mb(44)) * R2D;
|
|
alt = getdbl((u_char *) &mb(52));
|
|
|
|
printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n",
|
|
up->unit, lat,lon,alt);
|
|
printf("TSIP_decode: unit %d\n", up->unit);
|
|
}
|
|
#endif
|
|
if ( (getint((u_char *) &mb(10)) & 0x80) &&
|
|
/* Avoid early announce: https://bugs.ntp.org/2773 */
|
|
(6 == up->month || 12 == up->month) )
|
|
pp->leap = LEAP_ADDSECOND; /* we ASSUME addsecond */
|
|
else
|
|
pp->leap = LEAP_NOWARNING;
|
|
|
|
#ifdef DEBUG
|
|
if (debug > 1)
|
|
printf("TSIP_decode: unit %d: 0x%02x leap %d\n",
|
|
up->unit, mb(0) & 0xff, pp->leap);
|
|
if (debug > 1) {
|
|
printf("Receiver MODE: 0x%02X\n", (u_char)mb(1));
|
|
if (mb(1) == 0x00)
|
|
printf(" AUTOMATIC\n");
|
|
if (mb(1) == 0x01)
|
|
printf(" SINGLE SATELLITE\n");
|
|
if (mb(1) == 0x03)
|
|
printf(" HORIZONTAL(2D)\n");
|
|
if (mb(1) == 0x04)
|
|
printf(" FULL POSITION(3D)\n");
|
|
if (mb(1) == 0x05)
|
|
printf(" DGPR REFERENCE\n");
|
|
if (mb(1) == 0x06)
|
|
printf(" CLOCK HOLD(2D)\n");
|
|
if (mb(1) == 0x07)
|
|
printf(" OVERDETERMINED CLOCK\n");
|
|
|
|
printf("\n** Disciplining MODE 0x%02X:\n", (u_char)mb(2));
|
|
if (mb(2) == 0x00)
|
|
printf(" NORMAL\n");
|
|
if (mb(2) == 0x01)
|
|
printf(" POWER-UP\n");
|
|
if (mb(2) == 0x02)
|
|
printf(" AUTO HOLDOVER\n");
|
|
if (mb(2) == 0x03)
|
|
printf(" MANUAL HOLDOVER\n");
|
|
if (mb(2) == 0x04)
|
|
printf(" RECOVERY\n");
|
|
if (mb(2) == 0x06)
|
|
printf(" DISCIPLINING DISABLED\n");
|
|
}
|
|
#endif
|
|
return 0;
|
|
break;
|
|
|
|
case PACKET_8FAB:
|
|
/* Thunderbolt Primary Timing Packet */
|
|
|
|
if (up->rpt_cnt != LENCODE_8FAB) /* check length */
|
|
break;
|
|
|
|
if (up->polled <= 0)
|
|
return 0;
|
|
|
|
GPS_UTC_Offset = getint((u_char *) &mb(7));
|
|
|
|
if (GPS_UTC_Offset == 0){ /* Check UTC Offset */
|
|
#ifdef DEBUG
|
|
printf("TSIP_decode: UTC Offset Unknown\n");
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
|
|
if ((mb(9) & 0x1d) == 0x0) {
|
|
/* if we know the GPS time and the UTC offset,
|
|
we expect UTC timing information !!! */
|
|
|
|
pp->leap = LEAP_NOTINSYNC;
|
|
refclock_report(peer, CEVNT_BADTIME);
|
|
up->polled = -1;
|
|
return 0;
|
|
}
|
|
|
|
pp->nsec = 0;
|
|
#ifdef DEBUG
|
|
printf("\nTiming Flags are:\n");
|
|
printf("Timing flag value is: 0x%X\n", mb(9));
|
|
if ((mb(9) & 0x01) != 0)
|
|
printf (" Getting UTC time\n");
|
|
else
|
|
printf (" Getting GPS time\n");
|
|
if ((mb(9) & 0x02) != 0)
|
|
printf (" PPS is from UTC\n");
|
|
else
|
|
printf (" PPS is from GPS\n");
|
|
if ((mb(9) & 0x04) != 0)
|
|
printf (" Time is not Set\n");
|
|
else
|
|
printf (" Time is Set\n");
|
|
if ((mb(9) & 0x08) != 0)
|
|
printf(" I dont have UTC info\n");
|
|
else
|
|
printf (" I have UTC info\n");
|
|
if ((mb(9) & 0x10) != 0)
|
|
printf (" Time is from USER\n\n");
|
|
else
|
|
printf (" Time is from GPS\n\n");
|
|
#endif
|
|
|
|
if ((pp->day = day_of_year(&mb(13))) < 0)
|
|
break;
|
|
tow = getlong((u_char *) &mb(1));
|
|
#ifdef DEBUG
|
|
if (debug > 1) {
|
|
printf("pp->day: %d\n", pp->day);
|
|
printf("TOW: %ld\n", tow);
|
|
printf("DAY: %d\n", mb(13));
|
|
}
|
|
#endif
|
|
pp->year = getint((u_char *) &mb(15));
|
|
pp->hour = mb(12);
|
|
pp->minute = mb(11);
|
|
pp->second = mb(10);
|
|
|
|
|
|
#ifdef DEBUG
|
|
if (debug > 1)
|
|
printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d ",up->unit, mb(0) & 0xff, event, pp->hour, pp->minute, pp->second, pp->nsec, mb(14), mb(13), pp->year);
|
|
#endif
|
|
return 1;
|
|
break;
|
|
|
|
default:
|
|
/* Ignore Packet */
|
|
return 0;
|
|
} /* switch */
|
|
} /* if 8F packets */
|
|
|
|
else if (up->rpt_buf[0] == (u_char)0x42) {
|
|
printf("0x42\n");
|
|
return 0;
|
|
}
|
|
else if (up->rpt_buf[0] == (u_char)0x43) {
|
|
printf("0x43\n");
|
|
return 0;
|
|
}
|
|
else if ((up->rpt_buf[0] == PACKET_41) & (up->type == CLK_THUNDERBOLT)){
|
|
printf("Undocumented 0x41 packet on Thunderbolt\n");
|
|
return 0;
|
|
}
|
|
else if ((up->rpt_buf[0] == PACKET_41A) & (up->type == CLK_ACUTIME)) {
|
|
#ifdef DEBUG
|
|
printf("GPS TOW: %ld\n", (long)getlong((u_char *) &mb(0)));
|
|
printf("GPS WN: %d\n", getint((u_char *) &mb(4)));
|
|
printf("GPS UTC-GPS Offser: %ld\n", (long)getlong((u_char *) &mb(6)));
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* Health Status for Acutime Receiver */
|
|
else if ((up->rpt_buf[0] == PACKET_46) & (up->type == CLK_ACUTIME)) {
|
|
#ifdef DEBUG
|
|
if (debug > 1)
|
|
/* Status Codes */
|
|
switch (mb(0)) {
|
|
case 0x00:
|
|
printf ("Doing Position Fixes\n");
|
|
break;
|
|
case 0x01:
|
|
printf ("Do no have GPS time yet\n");
|
|
break;
|
|
case 0x03:
|
|
printf ("PDOP is too high\n");
|
|
break;
|
|
case 0x08:
|
|
printf ("No usable satellites\n");
|
|
break;
|
|
case 0x09:
|
|
printf ("Only 1 usable satellite\n");
|
|
break;
|
|
case 0x0A:
|
|
printf ("Only 2 usable satellites\n");
|
|
break;
|
|
case 0x0B:
|
|
printf ("Only 3 usable satellites\n");
|
|
break;
|
|
case 0x0C:
|
|
printf("The Chosen satellite is unusable\n");
|
|
break;
|
|
}
|
|
#endif
|
|
/* Error Codes */
|
|
if (mb(1) != 0) {
|
|
|
|
refclock_report(peer, CEVNT_BADTIME);
|
|
up->polled = -1;
|
|
#ifdef DEBUG
|
|
if (debug > 1) {
|
|
if (mb(1) & 0x01)
|
|
printf ("Signal Processor Error, reset unit.\n");
|
|
if (mb(1) & 0x02)
|
|
printf ("Alignment error, channel or chip 1, reset unit.\n");
|
|
if (mb(1) & 0x03)
|
|
printf ("Alignment error, channel or chip 2, reset unit.\n");
|
|
if (mb(1) & 0x04)
|
|
printf ("Antenna feed line fault (open or short)\n");
|
|
if (mb(1) & 0x05)
|
|
printf ("Excessive reference frequency error, refer to packet 0x2D and packet 0x4D documentation for further information\n");
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
else if (up->rpt_buf[0] == 0x54)
|
|
return 0;
|
|
|
|
else if (up->rpt_buf[0] == PACKET_6D) {
|
|
#ifdef DEBUG
|
|
int sats;
|
|
|
|
if ((mb(0) & 0x01) && (mb(0) & 0x02))
|
|
printf("2d Fix Dimension\n");
|
|
if (mb(0) & 0x04)
|
|
printf("3d Fix Dimension\n");
|
|
|
|
if (mb(0) & 0x08)
|
|
printf("Fix Mode is MANUAL\n");
|
|
else
|
|
printf("Fix Mode is AUTO\n");
|
|
|
|
sats = mb(0) & 0xF0;
|
|
sats = sats >> 4;
|
|
printf("Tracking %d Satellites\n", sats);
|
|
#endif
|
|
return 0;
|
|
} /* else if not super packet */
|
|
refclock_report(peer, CEVNT_BADREPLY);
|
|
up->polled = -1;
|
|
#ifdef DEBUG
|
|
printf("TSIP_decode: unit %d: bad packet %02x-%02x event %d len %d\n",
|
|
up->unit, up->rpt_buf[0] & 0xff, mb(0) & 0xff,
|
|
event, up->rpt_cnt);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* palisade__receive - receive data from the serial interface
|
|
*/
|
|
|
|
static void
|
|
palisade_receive (
|
|
struct peer * peer
|
|
)
|
|
{
|
|
struct palisade_unit *up;
|
|
struct refclockproc *pp;
|
|
|
|
/*
|
|
* Initialize pointers and read the timecode and timestamp.
|
|
*/
|
|
pp = peer->procptr;
|
|
up = pp->unitptr;
|
|
|
|
if (! TSIP_decode(peer)) return;
|
|
|
|
if (up->polled <= 0)
|
|
return; /* no poll pending, already received or timeout */
|
|
|
|
up->polled = 0; /* Poll reply received */
|
|
pp->lencode = 0; /* clear time code */
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
printf(
|
|
"palisade_receive: unit %d: %4d %03d %02d:%02d:%02d.%09ld\n",
|
|
up->unit, pp->year, pp->day, pp->hour, pp->minute,
|
|
pp->second, pp->nsec);
|
|
#endif
|
|
|
|
/*
|
|
* Process the sample
|
|
* Generate timecode: YYYY DoY HH:MM:SS.microsec
|
|
* report and process
|
|
*/
|
|
|
|
snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
|
|
"%4d %03d %02d:%02d:%02d.%09ld",
|
|
pp->year, pp->day,
|
|
pp->hour,pp->minute, pp->second, pp->nsec);
|
|
pp->lencode = 24;
|
|
|
|
if (!refclock_process(pp)) {
|
|
refclock_report(peer, CEVNT_BADTIME);
|
|
|
|
#ifdef DEBUG
|
|
printf("palisade_receive: unit %d: refclock_process failed!\n",
|
|
up->unit);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
record_clock_stats(&peer->srcadr, pp->a_lastcode);
|
|
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
printf("palisade_receive: unit %d: %s\n",
|
|
up->unit, prettydate(&pp->lastrec));
|
|
#endif
|
|
pp->lastref = pp->lastrec;
|
|
refclock_receive(peer);
|
|
}
|
|
|
|
|
|
/*
|
|
* palisade_poll - called by the transmit procedure
|
|
*
|
|
*/
|
|
static void
|
|
palisade_poll (
|
|
int unit,
|
|
struct peer *peer
|
|
)
|
|
{
|
|
struct palisade_unit *up;
|
|
struct refclockproc *pp;
|
|
|
|
pp = peer->procptr;
|
|
up = pp->unitptr;
|
|
|
|
pp->polls++;
|
|
if (up->polled > 0) /* last reply never arrived or error */
|
|
refclock_report(peer, CEVNT_TIMEOUT);
|
|
|
|
up->polled = 2; /* synchronous packet + 1 event */
|
|
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
printf("palisade_poll: unit %d: polling %s\n", unit,
|
|
(pp->sloppyclockflag & CLK_FLAG2) ?
|
|
"synchronous packet" : "event");
|
|
#endif
|
|
|
|
if (pp->sloppyclockflag & CLK_FLAG2)
|
|
return; /* using synchronous packet input */
|
|
|
|
if(up->type == CLK_PRAECIS) {
|
|
if(write(peer->procptr->io.fd,"SPSTAT\r\n",8) < 0)
|
|
msyslog(LOG_ERR, "Palisade(%d) write: %m:",unit);
|
|
else {
|
|
praecis_msg = 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (HW_poll(pp) < 0)
|
|
refclock_report(peer, CEVNT_FAULT);
|
|
}
|
|
|
|
static void
|
|
praecis_parse (
|
|
struct recvbuf *rbufp,
|
|
struct peer *peer
|
|
)
|
|
{
|
|
static char buf[100];
|
|
static int p = 0;
|
|
struct refclockproc *pp;
|
|
|
|
pp = peer->procptr;
|
|
|
|
memcpy(buf+p,rbufp->recv_space.X_recv_buffer, rbufp->recv_length);
|
|
p += rbufp->recv_length;
|
|
|
|
if(buf[p-2] == '\r' && buf[p-1] == '\n') {
|
|
buf[p-2] = '\0';
|
|
record_clock_stats(&peer->srcadr, buf);
|
|
|
|
p = 0;
|
|
praecis_msg = 0;
|
|
|
|
if (HW_poll(pp) < 0)
|
|
refclock_report(peer, CEVNT_FAULT);
|
|
|
|
}
|
|
}
|
|
|
|
static void
|
|
palisade_io (
|
|
struct recvbuf *rbufp
|
|
)
|
|
{
|
|
/*
|
|
* Initialize pointers and read the timecode and timestamp.
|
|
*/
|
|
struct palisade_unit *up;
|
|
struct refclockproc *pp;
|
|
struct peer *peer;
|
|
|
|
char * c, * d;
|
|
|
|
peer = rbufp->recv_peer;
|
|
pp = peer->procptr;
|
|
up = pp->unitptr;
|
|
|
|
if(up->type == CLK_PRAECIS) {
|
|
if(praecis_msg) {
|
|
praecis_parse(rbufp,peer);
|
|
return;
|
|
}
|
|
}
|
|
|
|
c = (char *) &rbufp->recv_space;
|
|
d = c + rbufp->recv_length;
|
|
|
|
while (c != d) {
|
|
|
|
/* Build time packet */
|
|
switch (up->rpt_status) {
|
|
|
|
case TSIP_PARSED_DLE_1:
|
|
switch (*c)
|
|
{
|
|
case 0:
|
|
case DLE:
|
|
case ETX:
|
|
up->rpt_status = TSIP_PARSED_EMPTY;
|
|
break;
|
|
|
|
default:
|
|
up->rpt_status = TSIP_PARSED_DATA;
|
|
/* save packet ID */
|
|
up->rpt_buf[0] = *c;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case TSIP_PARSED_DATA:
|
|
if (*c == DLE)
|
|
up->rpt_status = TSIP_PARSED_DLE_2;
|
|
else
|
|
mb(up->rpt_cnt++) = *c;
|
|
break;
|
|
|
|
case TSIP_PARSED_DLE_2:
|
|
if (*c == DLE) {
|
|
up->rpt_status = TSIP_PARSED_DATA;
|
|
mb(up->rpt_cnt++) =
|
|
*c;
|
|
}
|
|
else if (*c == ETX)
|
|
up->rpt_status = TSIP_PARSED_FULL;
|
|
else {
|
|
/* error: start new report packet */
|
|
up->rpt_status = TSIP_PARSED_DLE_1;
|
|
up->rpt_buf[0] = *c;
|
|
}
|
|
break;
|
|
|
|
case TSIP_PARSED_FULL:
|
|
case TSIP_PARSED_EMPTY:
|
|
default:
|
|
if ( *c != DLE)
|
|
up->rpt_status = TSIP_PARSED_EMPTY;
|
|
else
|
|
up->rpt_status = TSIP_PARSED_DLE_1;
|
|
break;
|
|
}
|
|
|
|
c++;
|
|
|
|
if (up->rpt_status == TSIP_PARSED_DLE_1) {
|
|
up->rpt_cnt = 0;
|
|
if (pp->sloppyclockflag & CLK_FLAG2)
|
|
/* stamp it */
|
|
get_systime(&pp->lastrec);
|
|
}
|
|
else if (up->rpt_status == TSIP_PARSED_EMPTY)
|
|
up->rpt_cnt = 0;
|
|
|
|
else if (up->rpt_cnt > BMAX)
|
|
up->rpt_status =TSIP_PARSED_EMPTY;
|
|
|
|
if (up->rpt_status == TSIP_PARSED_FULL)
|
|
palisade_receive(peer);
|
|
|
|
} /* while chars in buffer */
|
|
}
|
|
|
|
|
|
/*
|
|
* Trigger the Palisade's event input, which is driven off the RTS
|
|
*
|
|
* Take a system time stamp to match the GPS time stamp.
|
|
*
|
|
*/
|
|
long
|
|
HW_poll (
|
|
struct refclockproc * pp /* pointer to unit structure */
|
|
)
|
|
{
|
|
int x; /* state before & after RTS set */
|
|
struct palisade_unit *up;
|
|
|
|
up = pp->unitptr;
|
|
|
|
/* read the current status, so we put things back right */
|
|
if (ioctl(pp->io.fd, TIOCMGET, &x) < 0) {
|
|
DPRINTF(1, ("Palisade HW_poll: unit %d: GET %m\n",
|
|
up->unit));
|
|
msyslog(LOG_ERR, "Palisade(%d) HW_poll: ioctl(fd,GET): %m",
|
|
up->unit);
|
|
return -1;
|
|
}
|
|
|
|
x |= TIOCM_RTS; /* turn on RTS */
|
|
|
|
/* Edge trigger */
|
|
if (up->type == CLK_ACUTIME)
|
|
write (pp->io.fd, "", 1);
|
|
|
|
if (ioctl(pp->io.fd, TIOCMSET, &x) < 0) {
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
printf("Palisade HW_poll: unit %d: SET \n", up->unit);
|
|
#endif
|
|
msyslog(LOG_ERR,
|
|
"Palisade(%d) HW_poll: ioctl(fd, SET, RTS_on): %m",
|
|
up->unit);
|
|
return -1;
|
|
}
|
|
|
|
x &= ~TIOCM_RTS; /* turn off RTS */
|
|
|
|
/* poll timestamp */
|
|
get_systime(&pp->lastrec);
|
|
|
|
if (ioctl(pp->io.fd, TIOCMSET, &x) == -1) {
|
|
#ifdef DEBUG
|
|
if (debug)
|
|
printf("Palisade HW_poll: unit %d: UNSET \n", up->unit);
|
|
#endif
|
|
msyslog(LOG_ERR,
|
|
"Palisade(%d) HW_poll: ioctl(fd, UNSET, RTS_off): %m",
|
|
up->unit);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* copy/swap a big-endian palisade double into a host double
|
|
*/
|
|
static double
|
|
getdbl (
|
|
u_char *bp
|
|
)
|
|
{
|
|
#ifdef WORDS_BIGENDIAN
|
|
double out;
|
|
|
|
memcpy(&out, bp, sizeof(out));
|
|
return out;
|
|
#else
|
|
union {
|
|
u_char ch[8];
|
|
u_int32 u32[2];
|
|
} ui;
|
|
|
|
union {
|
|
double out;
|
|
u_int32 u32[2];
|
|
} uo;
|
|
|
|
memcpy(ui.ch, bp, sizeof(ui.ch));
|
|
/* least-significant 32 bits of double from swapped bp[4] to bp[7] */
|
|
uo.u32[0] = ntohl(ui.u32[1]);
|
|
/* most-significant 32 bits from swapped bp[0] to bp[3] */
|
|
uo.u32[1] = ntohl(ui.u32[0]);
|
|
|
|
return uo.out;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* copy/swap a big-endian palisade short into a host short
|
|
*/
|
|
static short
|
|
getint (
|
|
u_char *bp
|
|
)
|
|
{
|
|
u_short us;
|
|
|
|
memcpy(&us, bp, sizeof(us));
|
|
return (short)ntohs(us);
|
|
}
|
|
|
|
/*
|
|
* copy/swap a big-endian palisade 32-bit int into a host 32-bit int
|
|
*/
|
|
static int32
|
|
getlong(
|
|
u_char *bp
|
|
)
|
|
{
|
|
u_int32 u32;
|
|
|
|
memcpy(&u32, bp, sizeof(u32));
|
|
return (int32)(u_int32)ntohl(u32);
|
|
}
|
|
|
|
#else /* REFCLOCK && CLOCK_PALISADE*/
|
|
int refclock_palisade_c_notempty;
|
|
#endif
|