2 * This software was developed by the Software and Component Technologies
3 * group of Trimble Navigation, Ltd.
5 * Copyright (c) 1997, 1998, 1999, 2000 Trimble Navigation Ltd.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by Trimble Navigation, Ltd.
19 * 4. The name of Trimble Navigation Ltd. may not be used to endorse or
20 * promote products derived from this software without specific prior
23 * THIS SOFTWARE IS PROVIDED BY TRIMBLE NAVIGATION LTD. ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL TRIMBLE NAVIGATION LTD. BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * refclock_palisade - clock driver for the Trimble Palisade GPS
40 * For detailed information on this program, please refer to the html
41 * Refclock 29 page accompanying the NTP distribution.
43 * for questions / bugs / comments, contact:
44 * sven_dietrich@trimble.com
46 * Sven-Thorsten Dietrich
47 * 645 North Mary Avenue
48 * Post Office Box 3642
49 * Sunnyvale, CA 94088-3642
51 * Version 2.45; July 14, 1999
59 #if defined(REFCLOCK) && (defined(PALISADE) || defined(CLOCK_PALISADE))
62 extern int async_write(int, const void *, unsigned int);
64 #define write(fd, data, octets) async_write(fd, data, octets)
67 #include "refclock_palisade.h"
68 /* Table to get from month to day of the year */
69 const int days_of_year [12] = {
70 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
74 const char * Tracking_Status[15][15] = {
75 { "Doing Fixes\0" }, { "Good 1SV\0" }, { "Approx. 1SV\0" },
76 {"Need Time\0" }, { "Need INIT\0" }, { "PDOP too High\0" },
77 { "Bad 1SV\0" }, { "0SV Usable\0" }, { "1SV Usable\0" },
78 { "2SV Usable\0" }, { "3SV Usable\0" }, { "No Integrity\0" },
79 { "Diff Corr\0" }, { "Overdet Clock\0" }, { "Invalid\0" } };
85 struct refclock refclock_palisade = {
86 palisade_start, /* start up driver */
87 palisade_shutdown, /* shut down driver */
88 palisade_poll, /* transmit poll message */
89 noentry, /* not used */
90 noentry, /* initialize driver (not used) */
91 noentry, /* not used */
92 NOFLAGS /* not used */
95 int day_of_year P((char *dt));
97 /* Extract the clock type from the mode setting */
98 #define CLK_TYPE(x) ((int)(((x)->ttl) & 0x7F))
100 /* Supported clock types */
101 #define CLK_TRIMBLE 0 /* Trimble Palisade */
102 #define CLK_PRAECIS 1 /* Endrun Technologies Praecis */
105 static void praecis_parse(struct recvbuf *rbufp, struct peer *peer);
108 * palisade_start - open the devices and initialize data for processing
123 struct palisade_unit *up;
124 struct refclockproc *pp;
130 (void) sprintf(gpsdev, "COM%d:", unit);
132 (void) sprintf(gpsdev, DEVICE, unit);
138 fd = open(gpsdev, O_RDWR
144 fd = refclock_open(gpsdev, SPEED232, LDISC_RAW);
148 printf("Palisade(%d) start: open %s failed\n", unit, gpsdev);
153 msyslog(LOG_NOTICE, "Palisade(%d) fd: %d dev: %s", unit, fd,
157 tio.c_cflag = (CS8|CLOCAL|CREAD|PARENB|PARODD);
158 tio.c_iflag = (IGNBRK);
162 if (cfsetispeed(&tio, SPEED232) == -1) {
163 msyslog(LOG_ERR,"Palisade(%d) cfsetispeed(fd, &tio): %m",unit);
165 printf("Palisade(%d) cfsetispeed(fd, &tio)\n",unit);
169 if (cfsetospeed(&tio, SPEED232) == -1) {
171 printf("Palisade(%d) cfsetospeed(fd, &tio)\n",unit);
173 msyslog(LOG_ERR,"Palisade(%d) cfsetospeed(fd, &tio): %m",unit);
177 if (tcgetattr(fd, &tio) < 0) {
179 "Palisade(%d) tcgetattr(fd, &tio): %m",unit);
181 printf("Palisade(%d) tcgetattr(fd, &tio)\n",unit);
186 tio.c_cflag |= (PARENB|PARODD);
187 tio.c_iflag &= ~ICRNL;
190 if (tcsetattr(fd, TCSANOW, &tio) == -1) {
191 msyslog(LOG_ERR, "Palisade(%d) tcsetattr(fd, &tio): %m",unit);
193 printf("Palisade(%d) tcsetattr(fd, &tio)\n",unit);
199 * Allocate and initialize unit structure
201 up = (struct palisade_unit *) emalloc(sizeof(struct palisade_unit));
204 msyslog(LOG_ERR, "Palisade(%d) emalloc: %m",unit);
206 printf("Palisade(%d) emalloc\n",unit);
212 memset((char *)up, 0, sizeof(struct palisade_unit));
214 up->type = CLK_TYPE(peer);
217 /* Normal mode, do nothing */
220 msyslog(LOG_NOTICE, "Palisade(%d) Praecis mode enabled\n",unit);
223 msyslog(LOG_NOTICE, "Palisade(%d) mode unknown\n",unit);
228 pp->io.clock_recv = palisade_io;
229 pp->io.srcclock = (caddr_t)peer;
232 if (!io_addclock(&pp->io)) {
234 printf("Palisade(%d) io_addclock\n",unit);
242 * Initialize miscellaneous variables
244 pp->unitptr = (caddr_t)up;
245 pp->clockdesc = DESCRIPTION;
247 peer->precision = PRECISION;
248 peer->sstclktype = CTL_SST_TS_UHF;
249 peer->minpoll = TRMB_MINPOLL;
250 peer->maxpoll = TRMB_MAXPOLL;
251 memcpy((char *)&pp->refid, REFID, 4);
254 up->unit = (short) unit;
255 up->rpt_status = TSIP_PARSED_EMPTY;
263 * palisade_shutdown - shut down the clock
278 struct palisade_unit *up;
279 struct refclockproc *pp;
281 up = (struct palisade_unit *)pp->unitptr;
282 io_closeclock(&pp->io);
289 * unpack_date - get day and year from date
305 /* Check month is inside array bounds */
306 if ((mon < 1) || (mon > 12))
309 day = dt[0] + days_of_year[mon - 1];
310 year = getint((u_char *) (dt + 2));
312 if ( !(year % 4) && ((year % 100) ||
313 (!(year % 100) && !(year%400)))
315 day ++; /* leap year and March or later */
322 * TSIP_decode - decode the TSIP data packets
339 unsigned short event = 0;
341 struct palisade_unit *up;
342 struct refclockproc *pp;
345 up = (struct palisade_unit *)pp->unitptr;
348 * Check the time packet, decode its contents.
349 * If the timecode has invalid length or is not in
350 * proper format, declare bad format and exit.
353 if ((up->rpt_buf[0] == (char) 0x41) ||
354 (up->rpt_buf[0] == (char) 0x46) ||
355 (up->rpt_buf[0] == (char) 0x54) ||
356 (up->rpt_buf[0] == (char) 0x4B) ||
357 (up->rpt_buf[0] == (char) 0x6D)) {
359 /* standard time packet - GPS time and GPS week number */
361 printf("Palisade Port B packets detected. Connect to Port A\n");
368 * We cast both to u_char to as 0x8f uses the sign bit on a char
370 if ((u_char) up->rpt_buf[0] == (u_char) 0x8f) {
374 event = (unsigned short) (getint((u_char *) &mb(1)) & 0xffff);
375 if (!((pp->sloppyclockflag & CLK_FLAG2) || event))
379 switch (mb(0) & 0xff) {
386 if (up->rpt_cnt != LENCODE_8F0B) /* check length */
392 double lat, lon, alt;
393 lat = getdbl((u_char *) &mb(42)) * R2D;
394 lon = getdbl((u_char *) &mb(50)) * R2D;
395 alt = getdbl((u_char *) &mb(58));
397 printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n",
398 up->unit, lat,lon,alt);
399 printf("TSIP_decode: unit %d: Sats:", up->unit);
400 for (st = 66, ts = 0; st <= 73; st++) if (mb(st)) {
401 if (mb(st) > 0) ts++;
402 printf(" %02d", mb(st));
404 printf(" : Tracking %d\n", ts);
408 GPS_UTC_Offset = getint((u_char *) &mb(16));
409 if (GPS_UTC_Offset == 0) { /* Check UTC offset */
411 printf("TSIP_decode: UTC Offset Unknown\n");
416 secs = getdbl((u_char *) &mb(3));
417 secint = (long) secs;
418 secfrac = secs - secint; /* 0.0 <= secfrac < 1.0 */
420 pp->nsec = (long) (secfrac * 1000000000);
422 secint %= 86400; /* Only care about today */
423 pp->hour = secint / 3600;
425 pp->minute = secint / 60;
427 pp->second = secint % 60;
429 if ((pp->day = day_of_year(&mb(11))) < 0) break;
431 pp->year = getint((u_char *) &mb(13));
435 printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%06ld %02d/%02d/%04d UTC %02d\n",
436 up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
437 pp->second, pp->nsec, mb(12), mb(11), pp->year, GPS_UTC_Offset);
439 /* Only use this packet when no
440 * 8F-AD's are being received
443 if (up->leap_status) {
452 /* Palisade-NTP Packet */
454 if (up->rpt_cnt != LENCODE_NTP) /* check length */
457 up->leap_status = mb(19);
462 /* Check Tracking Status */
464 if (st < 0 || st > 14) st = 14;
465 if ((st >= 2 && st <= 7) || st == 11 || st == 12) {
467 printf("TSIP_decode: Not Tracking Sats : %s\n",
468 *Tracking_Status[st]);
470 refclock_report(peer, CEVNT_BADTIME);
476 if (up->leap_status & PALISADE_LEAP_PENDING) {
477 if (up->leap_status & PALISADE_UTC_TIME)
478 pp->leap = LEAP_ADDSECOND;
480 pp->leap = LEAP_DELSECOND;
482 else if (up->leap_status)
483 pp->leap = LEAP_NOWARNING;
485 else { /* UTC flag is not set:
486 * Receiver may have been reset, and lost
487 * its UTC almanac data */
488 pp->leap = LEAP_NOTINSYNC;
490 printf("TSIP_decode: UTC Almanac unavailable: %d\n",
493 refclock_report(peer, CEVNT_BADTIME);
498 pp->nsec = (long) (getdbl((u_char *) &mb(3)) * 1000000000);
500 if ((pp->day = day_of_year(&mb(14))) < 0)
502 pp->year = getint((u_char *) &mb(16));
509 printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%06ld %02d/%02d/%04d UTC %02x %s\n",
510 up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
511 pp->second, pp->nsec, mb(15), mb(14), pp->year,
512 mb(19), *Tracking_Status[st]);
523 refclock_report(peer, CEVNT_BADREPLY);
526 printf("TSIP_decode: unit %d: bad packet %02x-%02x event %d len %d\n",
527 up->unit, up->rpt_buf[0] & 0xff, mb(0) & 0xff,
534 * palisade__receive - receive data from the serial interface
548 struct palisade_unit *up;
549 struct refclockproc *pp;
552 * Initialize pointers and read the timecode and timestamp.
555 up = (struct palisade_unit *)pp->unitptr;
557 if (! TSIP_decode(peer)) return;
560 return; /* no poll pending, already received or timeout */
562 up->polled = 0; /* Poll reply received */
563 pp->lencode = 0; /* clear time code */
567 "palisade_receive: unit %d: %4d %03d %02d:%02d:%02d.%06ld\n",
568 up->unit, pp->year, pp->day, pp->hour, pp->minute,
569 pp->second, pp->nsec);
574 * Generate timecode: YYYY DoY HH:MM:SS.microsec
578 (void) sprintf(pp->a_lastcode,"%4d %03d %02d:%02d:%02d.%06ld",
579 pp->year,pp->day,pp->hour,pp->minute, pp->second,pp->nsec);
583 pp->lasttime = current_time;
585 if (!refclock_process(pp
587 , PALISADE_SAMPLES, PALISADE_SAMPLES * 3 / 5
590 refclock_report(peer, CEVNT_BADTIME);
593 printf("palisade_receive: unit %d: refclock_process failed!\n",
599 record_clock_stats(&peer->srcadr, pp->a_lastcode);
603 printf("palisade_receive: unit %d: %s\n",
604 up->unit, prettydate(&pp->lastrec));
606 pp->lastref = pp->lastrec;
607 refclock_receive(peer
609 , &pp->offset, 0, pp->dispersion,
610 &pp->lastrec, &pp->lastrec, pp->leap
617 * palisade_poll - called by the transmit procedure
633 struct palisade_unit *up;
634 struct refclockproc *pp;
637 up = (struct palisade_unit *)pp->unitptr;
640 if (up->polled > 0) /* last reply never arrived or error */
641 refclock_report(peer, CEVNT_TIMEOUT);
643 up->polled = 2; /* synchronous packet + 1 event */
647 printf("palisade_poll: unit %d: polling %s\n", unit,
648 (pp->sloppyclockflag & CLK_FLAG2) ?
649 "synchronous packet" : "event");
652 if (pp->sloppyclockflag & CLK_FLAG2)
653 return; /* using synchronous packet input */
655 if(up->type == CLK_PRAECIS) {
656 if(write(peer->procptr->io.fd,"SPSTAT\r\n",8) < 0)
657 msyslog(LOG_ERR, "Palisade(%d) write: %m:",unit);
665 refclock_report(peer, CEVNT_FAULT);
669 praecis_parse(struct recvbuf *rbufp, struct peer *peer)
671 static char buf[100];
673 struct refclockproc *pp;
677 memcpy(buf+p,rbufp->recv_space.X_recv_buffer, rbufp->recv_length);
678 p += rbufp->recv_length;
680 if(buf[p-2] == '\r' && buf[p-1] == '\n') {
682 record_clock_stats(&peer->srcadr, buf);
688 refclock_report(peer, CEVNT_FAULT);
698 struct recvbuf *rbufp;
700 struct recvbuf *rbufp
705 * Initialize pointers and read the timecode and timestamp.
707 struct palisade_unit *up;
708 struct refclockproc *pp;
713 peer = (struct peer *)rbufp->recv_srcclock;
715 up = (struct palisade_unit *)pp->unitptr;
717 if(up->type == CLK_PRAECIS) {
719 praecis_parse(rbufp,peer);
724 c = (char *) &rbufp->recv_space;
725 d = c + rbufp->recv_length;
729 /* Build time packet */
730 switch (up->rpt_status) {
732 case TSIP_PARSED_DLE_1:
738 up->rpt_status = TSIP_PARSED_EMPTY;
742 up->rpt_status = TSIP_PARSED_DATA;
749 case TSIP_PARSED_DATA:
751 up->rpt_status = TSIP_PARSED_DLE_2;
753 mb(up->rpt_cnt++) = *c;
756 case TSIP_PARSED_DLE_2:
758 up->rpt_status = TSIP_PARSED_DATA;
763 up->rpt_status = TSIP_PARSED_FULL;
765 /* error: start new report packet */
766 up->rpt_status = TSIP_PARSED_DLE_1;
771 case TSIP_PARSED_FULL:
772 case TSIP_PARSED_EMPTY:
775 up->rpt_status = TSIP_PARSED_EMPTY;
777 up->rpt_status = TSIP_PARSED_DLE_1;
783 if (up->rpt_status == TSIP_PARSED_DLE_1) {
785 if (pp->sloppyclockflag & CLK_FLAG2)
787 get_systime(&pp->lastrec);
789 else if (up->rpt_status == TSIP_PARSED_EMPTY)
792 else if (up->rpt_cnt > BMAX)
793 up->rpt_status =TSIP_PARSED_EMPTY;
795 if (up->rpt_status == TSIP_PARSED_FULL)
796 palisade_receive(peer);
798 } /* while chars in buffer */
803 * Trigger the Palisade's event input, which is driven off the RTS
805 * Take a system time stamp to match the GPS time stamp.
811 pp /* pointer to unit structure */
813 struct refclockproc * pp; /* pointer to unit structure */
815 struct refclockproc * pp /* pointer to unit structure */
819 int x; /* state before & after RTS set */
820 struct palisade_unit *up;
822 up = (struct palisade_unit *) pp->unitptr;
824 /* read the current status, so we put things back right */
825 if (ioctl(pp->io.fd, TIOCMGET, &x) < 0) {
828 printf("Palisade HW_poll: unit %d: GET %s\n", up->unit, strerror(errno));
830 msyslog(LOG_ERR, "Palisade(%d) HW_poll: ioctl(fd,GET): %m",
835 x |= TIOCM_RTS; /* turn on RTS */
838 if (ioctl(pp->io.fd, TIOCMSET, &x) < 0) {
841 printf("Palisade HW_poll: unit %d: SET \n", up->unit);
844 "Palisade(%d) HW_poll: ioctl(fd, SET, RTS_on): %m",
849 x &= ~TIOCM_RTS; /* turn off RTS */
852 get_systime(&pp->lastrec);
854 if (ioctl(pp->io.fd, TIOCMSET, &x) == -1) {
857 printf("Palisade HW_poll: unit %d: UNSET \n", up->unit);
860 "Palisade(%d) HW_poll: ioctl(fd, UNSET, RTS_off): %m",
870 * this 'casts' a character array into a float
884 #ifdef WORDS_BIGENDIAN
885 ((char *) &sval)[0] = *bp++;
886 ((char *) &sval)[1] = *bp++;
887 ((char *) &sval)[2] = *bp++;
888 ((char *) &sval)[3] = *bp++;
890 ((char *) &sval)[3] = *bp++;
891 ((char *) &sval)[2] = *bp++;
892 ((char *) &sval)[1] = *bp++;
893 ((char *) &sval)[0] = *bp;
894 #endif /* ! XNTP_BIG_ENDIAN */
900 * this 'casts' a character array into a double
914 #ifdef WORDS_BIGENDIAN
915 ((char *) &dval)[0] = *bp++;
916 ((char *) &dval)[1] = *bp++;
917 ((char *) &dval)[2] = *bp++;
918 ((char *) &dval)[3] = *bp++;
919 ((char *) &dval)[4] = *bp++;
920 ((char *) &dval)[5] = *bp++;
921 ((char *) &dval)[6] = *bp++;
922 ((char *) &dval)[7] = *bp;
924 ((char *) &dval)[7] = *bp++;
925 ((char *) &dval)[6] = *bp++;
926 ((char *) &dval)[5] = *bp++;
927 ((char *) &dval)[4] = *bp++;
928 ((char *) &dval)[3] = *bp++;
929 ((char *) &dval)[2] = *bp++;
930 ((char *) &dval)[1] = *bp++;
931 ((char *) &dval)[0] = *bp;
932 #endif /* ! XNTP_BIG_ENDIAN */
937 * cast a 16 bit character array into a short (16 bit) int
950 return (short) (bp[1] + (bp[0] << 8));
954 int refclock_palisade_bs;
955 #endif /* REFCLOCK */