NetBSD/dist/ntp/ntpd/refclock_palisade.c
2000-03-29 12:38:44 +00:00

884 lines
19 KiB
C

/* $NetBSD: refclock_palisade.c,v 1.1.1.1 2000/03/29 12:38:53 simonb Exp $ */
/*
* 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
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined(REFCLOCK) && (defined(PALISADE) || defined(CLOCK_PALISADE))
#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 P((char *dt));
/*
* palisade_start - open the devices and initialize data for processing
*/
static int
palisade_start (
#ifdef PALISADE
unit, peer
)
int unit;
struct peer *peer;
#else /* ANSI */
int unit,
struct peer *peer
)
#endif
{
struct palisade_unit *up;
struct refclockproc *pp;
int fd;
char gpsdev[20];
struct termios tio;
#ifdef SYS_WINNT
(void) sprintf(gpsdev, "COM%d:", unit);
#else
(void) sprintf(gpsdev, DEVICE, unit);
#endif
/*
* Open serial port.
*/
#if defined PALISADE
fd = open(gpsdev, O_RDWR
#ifdef O_NONBLOCK
| O_NONBLOCK
#endif
);
#else /* NTP 4.x */
fd = refclock_open(gpsdev, SPEED232, LDISC_RAW);
#endif
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 defined PALISADE
tio.c_cflag = (CS8|CLOCAL|CREAD|PARENB|PARODD);
tio.c_iflag = (IGNBRK);
tio.c_oflag = (0);
tio.c_lflag = (0);
if (cfsetispeed(&tio, SPEED232) == -1) {
msyslog(LOG_ERR,"Palisade(%d) cfsetispeed(fd, &tio): %m",unit);
#ifdef DEBUG
printf("Palisade(%d) cfsetispeed(fd, &tio)\n",unit);
#endif
return 0;
}
if (cfsetospeed(&tio, SPEED232) == -1) {
#ifdef DEBUG
printf("Palisade(%d) cfsetospeed(fd, &tio)\n",unit);
#endif
msyslog(LOG_ERR,"Palisade(%d) cfsetospeed(fd, &tio): %m",unit);
return 0;
}
#else /* NTP 4.x */
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
return (0);
}
tio.c_cflag |= (PARENB|PARODD);
tio.c_iflag &= ~ICRNL;
#endif /* NTP 4.x */
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
return 0;
}
/*
* Allocate and initialize unit structure
*/
up = (struct palisade_unit *) emalloc(sizeof(struct palisade_unit));
if (!(up)) {
msyslog(LOG_ERR, "Palisade(%d) emalloc: %m",unit);
#ifdef DEBUG
printf("Palisade(%d) emalloc\n",unit);
#endif
(void) close(fd);
return (0);
}
memset((char *)up, 0, sizeof(struct palisade_unit));
pp = peer->procptr;
pp->io.clock_recv = palisade_io;
pp->io.srcclock = (caddr_t)peer;
pp->io.datalen = 0;
pp->io.fd = fd;
if (!io_addclock(&pp->io)) {
#ifdef DEBUG
printf("Palisade(%d) io_addclock\n",unit);
#endif
(void) close(fd);
free(up);
return (0);
}
/*
* Initialize miscellaneous variables
*/
pp->unitptr = (caddr_t)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;
return 1;
}
/*
* palisade_shutdown - shut down the clock
*/
static void
palisade_shutdown (
#ifdef PALISADE
unit, peer
)
int unit;
struct peer *peer;
#else /* ANSI */
int unit,
struct peer *peer
)
#endif
{
struct palisade_unit *up;
struct refclockproc *pp;
pp = peer->procptr;
up = (struct palisade_unit *)pp->unitptr;
io_closeclock(&pp->io);
free(up);
}
/*
* unpack_date - get day and year from date
*/
int
day_of_year (
#ifdef PALISADE
dt
)
char * dt;
#else
char * dt
)
#endif
{
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 (
#ifdef PALISADE
peer
)
struct peer *peer;
#else
struct peer *peer
)
#endif
{
int st;
long secint;
double secs;
double secfrac;
unsigned short event = 0;
struct palisade_unit *up;
struct refclockproc *pp;
pp = peer->procptr;
up = (struct palisade_unit *)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->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;
}
if (up->rpt_buf[0] == (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;
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->usec = (long) (secfrac * 1000000);
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.%06ld %02d/%02d/%04d UTC %02d\n",
up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
pp->second, pp->usec, 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;
}
if (up->leap_status & PALISADE_LEAP_PENDING) {
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->usec = (long) (getdbl((u_char *) &mb(3)) * 1000000);
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);
#ifdef DEBUG
if (debug > 1)
printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%06ld %02d/%02d/%04d UTC %02x %s\n",
up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
pp->second, pp->usec, mb(15), mb(14), pp->year,
mb(19), *Tracking_Status[st]);
#endif
return 1;
break;
default:
/* Ignore Packet */
return 0;
} /* switch */
}/* if 8F packets */
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 (
#ifdef PALISADE
peer
)
struct peer * peer;
#else /* ANSI */
struct peer * peer
)
#endif
{
struct palisade_unit *up;
struct refclockproc *pp;
/*
* Initialize pointers and read the timecode and timestamp.
*/
pp = peer->procptr;
up = (struct palisade_unit *)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.%06ld\n",
up->unit, pp->year, pp->day, pp->hour, pp->minute,
pp->second, pp->usec);
#endif
/*
* Process the sample
* Generate timecode: YYYY DoY HH:MM:SS.microsec
* report and process
*/
(void) sprintf(pp->a_lastcode,"%4d %03d %02d:%02d:%02d.%06ld",
pp->year,pp->day,pp->hour,pp->minute, pp->second,pp->usec);
pp->lencode = 24;
#ifdef PALISADE
pp->lasttime = current_time;
#endif
if (!refclock_process(pp
#ifdef PALISADE
, PALISADE_SAMPLES, PALISADE_SAMPLES * 3 / 5
#endif
)) {
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
refclock_receive(peer
#ifdef PALISADE
, &pp->offset, 0, pp->dispersion,
&pp->lastrec, &pp->lastrec, pp->leap
#endif
);
}
/*
* palisade_poll - called by the transmit procedure
*
*/
static void
palisade_poll (
#ifdef PALISADE
unit, peer
)
int unit;
struct peer *peer;
#else
int unit,
struct peer *peer
)
#endif
{
struct palisade_unit *up;
struct refclockproc *pp;
pp = peer->procptr;
up = (struct palisade_unit *)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 (HW_poll(pp) < 0)
refclock_report(peer, CEVNT_FAULT);
}
static void
palisade_io (
#ifdef PALISADE
rbufp
)
struct recvbuf *rbufp;
#else /* ANSI */
struct recvbuf *rbufp
)
#endif
{
/*
* Initialize pointers and read the timecode and timestamp.
*/
struct palisade_unit *up;
struct refclockproc *pp;
struct peer *peer;
char * c, * d;
peer = (struct peer *)rbufp->recv_srcclock;
pp = peer->procptr;
up = (struct palisade_unit *)pp->unitptr;
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 (
#ifdef PALISADE
pp /* pointer to unit structure */
)
struct refclockproc * pp; /* pointer to unit structure */
#else
struct refclockproc * pp /* pointer to unit structure */
)
#endif
{
int x; /* state before & after RTS set */
struct palisade_unit *up;
up = (struct palisade_unit *) pp->unitptr;
/* read the current status, so we put things back right */
if (ioctl(pp->io.fd, TIOCMGET, &x) < 0) {
#ifdef DEBUG
if (debug)
printf("Palisade HW_poll: unit %d: GET %s\n", up->unit, strerror(errno));
#endif
msyslog(LOG_ERR, "Palisade(%d) HW_poll: ioctl(fd,GET): %m",
up->unit);
return -1;
}
x |= TIOCM_RTS; /* turn on RTS */
/* Edge trigger */
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;
}
#if 0 /* unused */
/*
* this 'casts' a character array into a float
*/
float
getfloat (
#ifdef PALISADE
bp
)
u_char *bp;
#else
u_char *bp
)
#endif
{
float sval;
#ifdef WORDS_BIGENDIAN
((char *) &sval)[0] = *bp++;
((char *) &sval)[1] = *bp++;
((char *) &sval)[2] = *bp++;
((char *) &sval)[3] = *bp++;
#else
((char *) &sval)[3] = *bp++;
((char *) &sval)[2] = *bp++;
((char *) &sval)[1] = *bp++;
((char *) &sval)[0] = *bp;
#endif /* ! XNTP_BIG_ENDIAN */
return sval;
}
#endif
/*
* this 'casts' a character array into a double
*/
double
getdbl (
#ifdef PALISADE
bp
)
u_char *bp;
#else
u_char *bp
)
#endif
{
double dval;
#ifdef WORDS_BIGENDIAN
((char *) &dval)[0] = *bp++;
((char *) &dval)[1] = *bp++;
((char *) &dval)[2] = *bp++;
((char *) &dval)[3] = *bp++;
((char *) &dval)[4] = *bp++;
((char *) &dval)[5] = *bp++;
((char *) &dval)[6] = *bp++;
((char *) &dval)[7] = *bp;
#else
((char *) &dval)[7] = *bp++;
((char *) &dval)[6] = *bp++;
((char *) &dval)[5] = *bp++;
((char *) &dval)[4] = *bp++;
((char *) &dval)[3] = *bp++;
((char *) &dval)[2] = *bp++;
((char *) &dval)[1] = *bp++;
((char *) &dval)[0] = *bp;
#endif /* ! XNTP_BIG_ENDIAN */
return dval;
}
/*
* cast a 16 bit character array into a short (16 bit) int
*/
short
getint (
#ifdef PALISADE
bp
)
u_char *bp;
#else
u_char *bp
)
#endif
{
return (short) (bp[1] + (bp[0] << 8));
}
#endif /* REFCLOCK */