/* * 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) * * 21/04/18: Added support for Resolution devices. * * 03/09/19: Added support for ACE III & Copernicus II. */ #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" #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 */ }; static int decode_date(struct refclockproc *pp, const char *cp); /* 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 */ #define CLK_RESOLUTION 5 /* Trimble Resolution Receivers */ #define CLK_ACE 6 /* Trimble ACE III */ #define CLK_COPERNICUS 7 /* Trimble Copernicus II */ 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 */ /* * 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; } /* * 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 *) emalloc(100); /* set UTC time */ sendsupercmd (&tx, 0x8E, 0xA2); sendbyte (&tx, 0x3); sendetx (&tx, fd); /* activate packets 0x8F-AB and 0x8F-AC */ sendsupercmd (&tx, 0x8E, 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 *) emalloc(100); sendsupercmd(&tx, 0x8E, 0xA5); sendbyte(&tx, 0x02); sendbyte(&tx, 0x00); sendbyte(&tx, 0x00); sendbyte(&tx, 0x00); sendetx(&tx, fd); free(tx.data); } /* * init_resolution - Prepares Resolution receiver to be used with NTP */ static void init_resolution ( int fd ) { struct packettx tx; tx.size = 0; tx.data = (u_char *) emalloc(100); /* set UTC time */ sendsupercmd (&tx, 0x8E, 0xA2); sendbyte (&tx, 0x3); sendetx (&tx, fd); /* squelch PPS output unless locked to at least one satellite */ sendsupercmd (&tx, 0x8E, 0x4E); sendbyte (&tx, 0x3); sendetx (&tx, fd); /* activate packets 0x8F-AB and 0x8F-AC */ sendsupercmd (&tx, 0x8E, 0xA5); sendint (&tx, 0x5); 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; u_int speed; snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit); /* * Open serial port. */ speed = (CLK_TYPE(peer) == CLK_COPERNICUS) ? SPEED232COP : SPEED232; fd = refclock_open(&peer->srcadr, gpsdev, speed, 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; case CLK_RESOLUTION: msyslog(LOG_NOTICE, "Palisade(%d) Resolution mode enabled" ,unit); tio.c_cflag = (CS8|CLOCAL|CREAD|PARENB|PARODD); break; case CLK_ACE: msyslog(LOG_NOTICE, "Palisade(%d) ACE III mode enabled" ,unit); tio.c_cflag = (CS8|CLOCAL|CREAD|PARENB|PARODD); break; case CLK_COPERNICUS: msyslog(LOG_NOTICE, "Palisade(%d) Copernicus II mode enabled" ,unit); /* Must use ORing/ANDing to set/clear c_cflag bits otherwise CBAUD gets set back to 0. This ought to be an issue for the other modes above but it seems that the baud rate defaults to 9600 if CBAUD gets set to 0. */ tio.c_cflag &= ~(PARENB|PARODD); 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); if (up->type == CLK_RESOLUTION) init_resolution(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 helpers */ static inline uint8_t get_u8( const char *cp) { return ((const u_char*)cp)[0]; } static inline uint16_t get_u16( const char *cp) { return ((uint16_t)get_u8(cp) << 8) | get_u8(cp + 1); } /* * unpack & fix date (the receiver provides a valid time for 1024 weeks * after 1997-12-14 and therefore folds back in 2017, 2037,...) * * Returns -1 on error, day-of-month + (month * 32) othertwise. */ int decode_date( struct refclockproc *pp, const char *cp) { static int32_t s_baseday = 0; struct calendar jd; int32_t rd; if (0 == s_baseday) { if (!ntpcal_get_build_date(&jd)) { jd.year = 2015; jd.month = 1; jd.monthday = 1; } s_baseday = ntpcal_date_to_rd(&jd); } /* get date fields and convert to RDN */ jd.monthday = get_u8 ( cp ); jd.month = get_u8 (cp + 1); jd.year = get_u16(cp + 2); rd = ntpcal_date_to_rd(&jd); /* for the paranoid: do reverse calculation and cross-check */ ntpcal_rd_to_date(&jd, rd); if ((jd.monthday != get_u8 ( cp )) || (jd.month != get_u8 (cp + 1)) || (jd.year != get_u16(cp + 2)) ) return - 1; /* calculate cycle shift to base day and calculate re-folded * date * * One could do a proper modulo calculation here, but a counting * loop is probably faster for the next few rollovers... */ while (rd < s_baseday) rd += 7*1024; ntpcal_rd_to_date(&jd, rd); /* fill refclock structure & indicate success */ pp->day = jd.yearday; pp->year = jd.year; return ((int)jd.month << 5) | jd.monthday; } /* * 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; int mmday; long tow; uint16_t wn; int GPS_UTC_Offset; 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 ) && (up->type != CLK_RESOLUTION ) && (up->type != CLK_ACE ) && (up->type != CLK_COPERNICUS ) ) { 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 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) { 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; mmday = decode_date(pp, &mb(11)); if (mmday < 0) break; #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, (mmday >> 5), (mmday & 31), 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; } mmday = decode_date(pp, &mb(14)); if (mmday < 0) break; up->month = (mmday >> 5); /* Save for LEAP check */ 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); 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.%09ld %02d/%02d/%04d UTC %02x %s\n", up->unit, mb(0) & 0xff, event, pp->hour, pp->minute, pp->second, pp->nsec, (mmday >> 5), (mmday & 31), 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 mmday = decode_date(pp, &mb(13)); if (mmday < 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", (mmday & 31)); } #endif 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, (mmday >> 5), (mmday & 31), 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 Offset: %ld\n", (long)getlong((u_char *) &mb(6))); #endif return 0; } /* GPS time packet for ACE III or Copernicus II receiver */ else if ((up->rpt_buf[0] == PACKET_41) && ((up->type == CLK_ACE) || (up->type == CLK_COPERNICUS))) { #ifdef DEBUG if ((debug > 1) && (up->type == CLK_ACE)) printf("TSIP_decode: Packet 0x41 seen in ACE III mode\n"); if ((debug > 1) && (up->type == CLK_COPERNICUS)) printf("TSIP_decode: Packet 0x41 seen in Copernicus II mode\n"); #endif if (up->rpt_cnt != LENCODE_41) { /* check length */ refclock_report(peer, CEVNT_BADREPLY); up->polled = -1; #ifdef DEBUG printf("TSIP_decode: unit %d: bad packet %02x len %d\n", up->unit, up->rpt_buf[0] & 0xff, up->rpt_cnt); #endif return 0; } if (up->polled <= 0) return 0; tow = (long)getsingle((u_char *) &mb(0)); wn = (uint16_t)getint((u_char *) &mb(4)); GPS_UTC_Offset = (int)getsingle((u_char *) &mb(6)); if (GPS_UTC_Offset == 0){ /* Check UTC Offset */ #ifdef DEBUG printf("TSIP_decode: UTC Offset Unknown\n"); #endif refclock_report(peer, CEVNT_BADREPLY); up->polled = -1; return 0; } /* Get date & time from WN & ToW minus offset */ { TCivilDate cd; TGpsDatum wd; l_fp ugo; /* UTC-GPS offset, negative number */ ugo.Ul_i.Xl_i = (int32_t)-GPS_UTC_Offset; ugo.l_uf = 0; wd = gpscal_from_gpsweek((wn % 1024), (int32_t)tow, ugo); gpscal_to_calendar(&cd, &wd); pp->year = cd.year; pp->day = cd.yearday; pp->hour = cd.hour; pp->minute = cd.minute; pp->second = cd.second; pp->nsec = 0; pp->leap = LEAP_NOWARNING; #ifdef DEBUG if (debug > 1) { printf("GPS TOW: %ld\n", tow); printf("GPS WN: %d\n", wn); printf("GPS UTC-GPS Offset: %d\n", GPS_UTC_Offset); 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, cd.month, cd.monthday, pp->year); } #endif } return 1; } /* 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 not 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; } } /* Health Status for Copernicus II Receiver */ else if ((up->rpt_buf[0] == PACKET_46) && (up->type == CLK_COPERNICUS)) { #ifdef DEBUG if (debug > 1) /* Status Codes */ switch (mb(0)) { case 0x00: printf ("Doing Position Fixes\n"); break; case 0x01: printf ("Do not have GPS time yet\n"); break; case 0x03: printf ("PDOP is too high\n"); break; case 0x04: printf("The Chosen satellite is unusable\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; } #endif /* Error Codes */ if ((mb(1) & 0x3E) != 0) { /* Don't regard bits 0 and 6 as errors */ refclock_report(peer, CEVNT_BADTIME); up->polled = -1; #ifdef DEBUG if (debug > 1) { if ((mb(1) & 0x18) == 0x08) printf ("Antenna feed line fault (open)\n"); if ((mb(1) & 0x18) == 0x18) printf ("Antenna feed line fault (short)\n"); } #endif } return 0; } /* Other packets output by ACE III & Copernicus II Receivers, dropped silently */ else if (((up->rpt_buf[0] == (char) 0x4A) || (up->rpt_buf[0] == (char) 0x4B) || (up->rpt_buf[0] == (char) 0x56) || (up->rpt_buf[0] == (char) 0x5F) || (up->rpt_buf[0] == (char) 0x6D) || (up->rpt_buf[0] == (char) 0x82) || (up->rpt_buf[0] == (char) 0x84)) && ((up->type == CLK_ACE) || (up->type == CLK_COPERNICUS))) { #ifdef DEBUG if ((debug > 1) && (up->type == CLK_ACE)) printf("TSIP_decode: Packet 0x%2x seen in ACE III mode\n", (up->rpt_buf[0] & 0XFF)); if ((debug > 1) && (up->type == CLK_COPERNICUS)) printf("TSIP_decode: Packet 0x%2x seen in Copernicus II mode\n", (up->rpt_buf[0] & 0XFF)); #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; if (p + rbufp->recv_length >= sizeof buf) { struct palisade_unit *up; up = pp->unitptr; /* * We COULD see if there is a \r\n in the incoming * buffer before it overflows, and then process the * current line. * * Similarly, if we already have a hunk of data that * we're now flushing, that will cause the line of * data we're in the process of collecting to be garbage. * * Since we now check for this overflow and log when it * happens, we're now in a better place to easily see * what's going on and perhaps better choices can be made. */ /* Do we need to log the size of the overflow? */ msyslog(LOG_ERR, "Palisade(%d) praecis_parse(): input buffer overflow", up->unit); p = 0; praecis_msg = 0; refclock_report(peer, CEVNT_BADREPLY); return; } memcpy(buf+p, rbufp->recv_buffer, rbufp->recv_length); p += rbufp->recv_length; if ( p >= 2 && 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); } } return; } 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; struct packettx tx; up = pp->unitptr; if (up->type == CLK_ACE) { /* Poll by sending a 0x21 command */ tx.size = 0; tx.data = (u_char *) emalloc(100); sendcmd (&tx, 0x21); sendetx (&tx, pp->io.fd); free(tx.data); } else { /* 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) if (write (pp->io.fd, "", 1) != 1) msyslog(LOG_WARNING, "Palisade(%d) HW_poll: failed to send trigger: %m", up->unit); 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 */ } /* (up->type != CLK_ACE) */ /* poll timestamp */ get_systime(&pp->lastrec); if (up->type != CLK_ACE) { 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); } /* * copy/swap a big-endian 32-bit single-precision floating point into a host 32-bit int */ static int32 getsingle( u_char *bp ) { u_int32 mantissa; int8_t exponent; uint8_t sign, exp_field; int32 res; memcpy(&mantissa, bp, sizeof(mantissa)); mantissa = ((u_int32)ntohl(mantissa) & 0x7FFFFF) | 0x800000; exp_field = ((uint8_t)bp[0] << 1) + ((uint8_t)bp[1] >> 7); exponent = (int8_t)exp_field - 127; sign = ((uint8_t)bp[0] >> 7); if (exponent > 23) res = (int32)(mantissa << (exponent - 23)); else res = (int32)(mantissa >> (23 - exponent)); return sign ? -res : res; } #else /* REFCLOCK && CLOCK_PALISADE*/ int refclock_palisade_c_notempty; #endif