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(SYS_WINNT)
61 #define close closesocket
64 #if defined(REFCLOCK) && (defined(PALISADE) || defined(CLOCK_PALISADE))
66 #include "refclock_palisade.h"
67 /* Table to get from month to day of the year */
68 const int days_of_year
[12] = {
69 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
73 const char * Tracking_Status
[15][15] = {
74 { "Doing Fixes\0" }, { "Good 1SV\0" }, { "Approx. 1SV\0" },
75 {"Need Time\0" }, { "Need INIT\0" }, { "PDOP too High\0" },
76 { "Bad 1SV\0" }, { "0SV Usable\0" }, { "1SV Usable\0" },
77 { "2SV Usable\0" }, { "3SV Usable\0" }, { "No Integrity\0" },
78 { "Diff Corr\0" }, { "Overdet Clock\0" }, { "Invalid\0" } };
84 struct refclock refclock_palisade
= {
85 palisade_start
, /* start up driver */
86 palisade_shutdown
, /* shut down driver */
87 palisade_poll
, /* transmit poll message */
88 noentry
, /* not used */
89 noentry
, /* initialize driver (not used) */
90 noentry
, /* not used */
91 NOFLAGS
/* not used */
94 int day_of_year
P((char *dt
));
96 /* Extract the clock type from the mode setting */
97 #define CLK_TYPE(x) ((int)(((x)->ttl) & 0x7F))
99 /* Supported clock types */
100 #define CLK_TRIMBLE 0 /* Trimble Palisade */
101 #define CLK_PRAECIS 1 /* Endrun Technologies Praecis */
104 static void praecis_parse(struct recvbuf
*rbufp
, struct peer
*peer
);
107 * palisade_start - open the devices and initialize data for processing
122 struct palisade_unit
*up
;
123 struct refclockproc
*pp
;
129 (void) sprintf(gpsdev
, "COM%d:", unit
);
131 (void) sprintf(gpsdev
, DEVICE
, unit
);
137 fd
= open(gpsdev
, O_RDWR
143 fd
= refclock_open(gpsdev
, SPEED232
, LDISC_RAW
);
147 printf("Palisade(%d) start: open %s failed\n", unit
, gpsdev
);
152 msyslog(LOG_NOTICE
, "Palisade(%d) fd: %d dev: %s", unit
, fd
,
156 tio
.c_cflag
= (CS8
|CLOCAL
|CREAD
|PARENB
|PARODD
);
157 tio
.c_iflag
= (IGNBRK
);
161 if (cfsetispeed(&tio
, SPEED232
) == -1) {
162 msyslog(LOG_ERR
,"Palisade(%d) cfsetispeed(fd, &tio): %m",unit
);
164 printf("Palisade(%d) cfsetispeed(fd, &tio)\n",unit
);
168 if (cfsetospeed(&tio
, SPEED232
) == -1) {
170 printf("Palisade(%d) cfsetospeed(fd, &tio)\n",unit
);
172 msyslog(LOG_ERR
,"Palisade(%d) cfsetospeed(fd, &tio): %m",unit
);
176 if (tcgetattr(fd
, &tio
) < 0) {
178 "Palisade(%d) tcgetattr(fd, &tio): %m",unit
);
180 printf("Palisade(%d) tcgetattr(fd, &tio)\n",unit
);
185 tio
.c_cflag
|= (PARENB
|PARODD
);
186 tio
.c_iflag
&= ~ICRNL
;
189 if (tcsetattr(fd
, TCSANOW
, &tio
) == -1) {
190 msyslog(LOG_ERR
, "Palisade(%d) tcsetattr(fd, &tio): %m",unit
);
192 printf("Palisade(%d) tcsetattr(fd, &tio)\n",unit
);
198 * Allocate and initialize unit structure
200 up
= (struct palisade_unit
*) emalloc(sizeof(struct palisade_unit
));
203 msyslog(LOG_ERR
, "Palisade(%d) emalloc: %m",unit
);
205 printf("Palisade(%d) emalloc\n",unit
);
211 memset((char *)up
, 0, sizeof(struct palisade_unit
));
213 up
->type
= CLK_TYPE(peer
);
216 /* Normal mode, do nothing */
219 msyslog(LOG_NOTICE
, "Palisade(%d) Praecis mode enabled\n",unit
);
222 msyslog(LOG_NOTICE
, "Palisade(%d) mode unknown\n",unit
);
227 pp
->io
.clock_recv
= palisade_io
;
228 pp
->io
.srcclock
= (caddr_t
)peer
;
231 if (!io_addclock(&pp
->io
)) {
233 printf("Palisade(%d) io_addclock\n",unit
);
241 * Initialize miscellaneous variables
243 pp
->unitptr
= (caddr_t
)up
;
244 pp
->clockdesc
= DESCRIPTION
;
246 peer
->precision
= PRECISION
;
247 peer
->sstclktype
= CTL_SST_TS_UHF
;
248 peer
->minpoll
= TRMB_MINPOLL
;
249 peer
->maxpoll
= TRMB_MAXPOLL
;
250 memcpy((char *)&pp
->refid
, REFID
, 4);
253 up
->unit
= (short) unit
;
254 up
->rpt_status
= TSIP_PARSED_EMPTY
;
262 * palisade_shutdown - shut down the clock
277 struct palisade_unit
*up
;
278 struct refclockproc
*pp
;
280 up
= (struct palisade_unit
*)pp
->unitptr
;
281 io_closeclock(&pp
->io
);
288 * unpack_date - get day and year from date
304 /* Check month is inside array bounds */
305 if ((mon
< 1) || (mon
> 12))
308 day
= dt
[0] + days_of_year
[mon
- 1];
309 year
= getint((u_char
*) (dt
+ 2));
311 if ( !(year
% 4) && ((year
% 100) ||
312 (!(year
% 100) && !(year
%400)))
314 day
++; /* leap year and March or later */
321 * TSIP_decode - decode the TSIP data packets
338 unsigned short event
= 0;
340 struct palisade_unit
*up
;
341 struct refclockproc
*pp
;
344 up
= (struct palisade_unit
*)pp
->unitptr
;
347 * Check the time packet, decode its contents.
348 * If the timecode has invalid length or is not in
349 * proper format, declare bad format and exit.
352 if ((up
->rpt_buf
[0] == (char) 0x41) ||
353 (up
->rpt_buf
[0] == (char) 0x46) ||
354 (up
->rpt_buf
[0] == (char) 0x54) ||
355 (up
->rpt_buf
[0] == (char) 0x4B) ||
356 (up
->rpt_buf
[0] == (char) 0x6D)) {
358 /* standard time packet - GPS time and GPS week number */
360 printf("Palisade Port B packets detected. Connect to Port A\n");
367 * We cast both to u_char to as 0x8f uses the sign bit on a char
369 if ((u_char
) up
->rpt_buf
[0] == (u_char
) 0x8f) {
373 event
= (unsigned short) (getint((u_char
*) &mb(1)) & 0xffff);
374 if (!((pp
->sloppyclockflag
& CLK_FLAG2
) || event
))
378 switch (mb(0) & 0xff) {
385 if (up
->rpt_cnt
!= LENCODE_8F0B
) /* check length */
391 double lat
, lon
, alt
;
392 lat
= getdbl((u_char
*) &mb(42)) * R2D
;
393 lon
= getdbl((u_char
*) &mb(50)) * R2D
;
394 alt
= getdbl((u_char
*) &mb(58));
396 printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n",
397 up
->unit
, lat
,lon
,alt
);
398 printf("TSIP_decode: unit %d: Sats:", up
->unit
);
399 for (st
= 66, ts
= 0; st
<= 73; st
++) if (mb(st
)) {
400 if (mb(st
) > 0) ts
++;
401 printf(" %02d", mb(st
));
403 printf(" : Tracking %d\n", ts
);
407 GPS_UTC_Offset
= getint((u_char
*) &mb(16));
408 if (GPS_UTC_Offset
== 0) { /* Check UTC offset */
410 printf("TSIP_decode: UTC Offset Unknown\n");
415 secs
= getdbl((u_char
*) &mb(3));
416 secint
= (long) secs
;
417 secfrac
= secs
- secint
; /* 0.0 <= secfrac < 1.0 */
419 pp
->nsec
= (long) (secfrac
* 1000000000);
421 secint
%= 86400; /* Only care about today */
422 pp
->hour
= secint
/ 3600;
424 pp
->minute
= secint
/ 60;
426 pp
->second
= secint
% 60;
428 if ((pp
->day
= day_of_year(&mb(11))) < 0) break;
430 pp
->year
= getint((u_char
*) &mb(13));
434 printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%06ld %02d/%02d/%04d UTC %02d\n",
435 up
->unit
, mb(0) & 0xff, event
, pp
->hour
, pp
->minute
,
436 pp
->second
, pp
->nsec
, mb(12), mb(11), pp
->year
, GPS_UTC_Offset
);
438 /* Only use this packet when no
439 * 8F-AD's are being received
442 if (up
->leap_status
) {
451 /* Palisade-NTP Packet */
453 if (up
->rpt_cnt
!= LENCODE_NTP
) /* check length */
456 up
->leap_status
= mb(19);
461 /* Check Tracking Status */
463 if (st
< 0 || st
> 14) st
= 14;
464 if ((st
>= 2 && st
<= 7) || st
== 11 || st
== 12) {
466 printf("TSIP_decode: Not Tracking Sats : %s\n",
467 *Tracking_Status
[st
]);
469 refclock_report(peer
, CEVNT_BADTIME
);
475 if (up
->leap_status
& PALISADE_LEAP_PENDING
) {
476 if (up
->leap_status
& PALISADE_UTC_TIME
)
477 pp
->leap
= LEAP_ADDSECOND
;
479 pp
->leap
= LEAP_DELSECOND
;
481 else if (up
->leap_status
)
482 pp
->leap
= LEAP_NOWARNING
;
484 else { /* UTC flag is not set:
485 * Receiver may have been reset, and lost
486 * its UTC almanac data */
487 pp
->leap
= LEAP_NOTINSYNC
;
489 printf("TSIP_decode: UTC Almanac unavailable: %d\n",
492 refclock_report(peer
, CEVNT_BADTIME
);
497 pp
->nsec
= (long) (getdbl((u_char
*) &mb(3)) * 1000000000);
499 if ((pp
->day
= day_of_year(&mb(14))) < 0)
501 pp
->year
= getint((u_char
*) &mb(16));
508 printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%06ld %02d/%02d/%04d UTC %02x %s\n",
509 up
->unit
, mb(0) & 0xff, event
, pp
->hour
, pp
->minute
,
510 pp
->second
, pp
->nsec
, mb(15), mb(14), pp
->year
,
511 mb(19), *Tracking_Status
[st
]);
522 refclock_report(peer
, CEVNT_BADREPLY
);
525 printf("TSIP_decode: unit %d: bad packet %02x-%02x event %d len %d\n",
526 up
->unit
, up
->rpt_buf
[0] & 0xff, mb(0) & 0xff,
533 * palisade__receive - receive data from the serial interface
547 struct palisade_unit
*up
;
548 struct refclockproc
*pp
;
551 * Initialize pointers and read the timecode and timestamp.
554 up
= (struct palisade_unit
*)pp
->unitptr
;
556 if (! TSIP_decode(peer
)) return;
559 return; /* no poll pending, already received or timeout */
561 up
->polled
= 0; /* Poll reply received */
562 pp
->lencode
= 0; /* clear time code */
566 "palisade_receive: unit %d: %4d %03d %02d:%02d:%02d.%06ld\n",
567 up
->unit
, pp
->year
, pp
->day
, pp
->hour
, pp
->minute
,
568 pp
->second
, pp
->nsec
);
573 * Generate timecode: YYYY DoY HH:MM:SS.microsec
577 (void) sprintf(pp
->a_lastcode
,"%4d %03d %02d:%02d:%02d.%06ld",
578 pp
->year
,pp
->day
,pp
->hour
,pp
->minute
, pp
->second
,pp
->nsec
);
582 pp
->lasttime
= current_time
;
584 if (!refclock_process(pp
586 , PALISADE_SAMPLES
, PALISADE_SAMPLES
* 3 / 5
589 refclock_report(peer
, CEVNT_BADTIME
);
592 printf("palisade_receive: unit %d: refclock_process failed!\n",
598 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
602 printf("palisade_receive: unit %d: %s\n",
603 up
->unit
, prettydate(&pp
->lastrec
));
605 pp
->lastref
= pp
->lastrec
;
606 refclock_receive(peer
608 , &pp
->offset
, 0, pp
->dispersion
,
609 &pp
->lastrec
, &pp
->lastrec
, pp
->leap
616 * palisade_poll - called by the transmit procedure
632 struct palisade_unit
*up
;
633 struct refclockproc
*pp
;
636 up
= (struct palisade_unit
*)pp
->unitptr
;
639 if (up
->polled
> 0) /* last reply never arrived or error */
640 refclock_report(peer
, CEVNT_TIMEOUT
);
642 up
->polled
= 2; /* synchronous packet + 1 event */
646 printf("palisade_poll: unit %d: polling %s\n", unit
,
647 (pp
->sloppyclockflag
& CLK_FLAG2
) ?
648 "synchronous packet" : "event");
651 if (pp
->sloppyclockflag
& CLK_FLAG2
)
652 return; /* using synchronous packet input */
654 if(up
->type
== CLK_PRAECIS
) {
655 if(write(peer
->procptr
->io
.fd
,"SPSTAT\r\n",8) < 0)
656 msyslog(LOG_ERR
, "Palisade(%d) write: %m:",unit
);
664 refclock_report(peer
, CEVNT_FAULT
);
668 praecis_parse(struct recvbuf
*rbufp
, struct peer
*peer
)
670 static char buf
[100];
672 struct refclockproc
*pp
;
676 memcpy(buf
+p
,rbufp
->recv_space
.X_recv_buffer
, rbufp
->recv_length
);
677 p
+= rbufp
->recv_length
;
679 if(buf
[p
-2] == '\r' && buf
[p
-1] == '\n') {
681 record_clock_stats(&peer
->srcadr
, buf
);
687 refclock_report(peer
, CEVNT_FAULT
);
697 struct recvbuf
*rbufp
;
699 struct recvbuf
*rbufp
704 * Initialize pointers and read the timecode and timestamp.
706 struct palisade_unit
*up
;
707 struct refclockproc
*pp
;
712 peer
= (struct peer
*)rbufp
->recv_srcclock
;
714 up
= (struct palisade_unit
*)pp
->unitptr
;
716 if(up
->type
== CLK_PRAECIS
) {
718 praecis_parse(rbufp
,peer
);
723 c
= (char *) &rbufp
->recv_space
;
724 d
= c
+ rbufp
->recv_length
;
728 /* Build time packet */
729 switch (up
->rpt_status
) {
731 case TSIP_PARSED_DLE_1
:
737 up
->rpt_status
= TSIP_PARSED_EMPTY
;
741 up
->rpt_status
= TSIP_PARSED_DATA
;
748 case TSIP_PARSED_DATA
:
750 up
->rpt_status
= TSIP_PARSED_DLE_2
;
752 mb(up
->rpt_cnt
++) = *c
;
755 case TSIP_PARSED_DLE_2
:
757 up
->rpt_status
= TSIP_PARSED_DATA
;
762 up
->rpt_status
= TSIP_PARSED_FULL
;
764 /* error: start new report packet */
765 up
->rpt_status
= TSIP_PARSED_DLE_1
;
770 case TSIP_PARSED_FULL
:
771 case TSIP_PARSED_EMPTY
:
774 up
->rpt_status
= TSIP_PARSED_EMPTY
;
776 up
->rpt_status
= TSIP_PARSED_DLE_1
;
782 if (up
->rpt_status
== TSIP_PARSED_DLE_1
) {
784 if (pp
->sloppyclockflag
& CLK_FLAG2
)
786 get_systime(&pp
->lastrec
);
788 else if (up
->rpt_status
== TSIP_PARSED_EMPTY
)
791 else if (up
->rpt_cnt
> BMAX
)
792 up
->rpt_status
=TSIP_PARSED_EMPTY
;
794 if (up
->rpt_status
== TSIP_PARSED_FULL
)
795 palisade_receive(peer
);
797 } /* while chars in buffer */
802 * Trigger the Palisade's event input, which is driven off the RTS
804 * Take a system time stamp to match the GPS time stamp.
810 pp
/* pointer to unit structure */
812 struct refclockproc
* pp
; /* pointer to unit structure */
814 struct refclockproc
* pp
/* pointer to unit structure */
818 int x
; /* state before & after RTS set */
819 struct palisade_unit
*up
;
821 up
= (struct palisade_unit
*) pp
->unitptr
;
823 /* read the current status, so we put things back right */
824 if (ioctl(pp
->io
.fd
, TIOCMGET
, &x
) < 0) {
827 printf("Palisade HW_poll: unit %d: GET %s\n", up
->unit
, strerror(errno
));
829 msyslog(LOG_ERR
, "Palisade(%d) HW_poll: ioctl(fd,GET): %m",
834 x
|= TIOCM_RTS
; /* turn on RTS */
837 if (ioctl(pp
->io
.fd
, TIOCMSET
, &x
) < 0) {
840 printf("Palisade HW_poll: unit %d: SET \n", up
->unit
);
843 "Palisade(%d) HW_poll: ioctl(fd, SET, RTS_on): %m",
848 x
&= ~TIOCM_RTS
; /* turn off RTS */
851 get_systime(&pp
->lastrec
);
853 if (ioctl(pp
->io
.fd
, TIOCMSET
, &x
) == -1) {
856 printf("Palisade HW_poll: unit %d: UNSET \n", up
->unit
);
859 "Palisade(%d) HW_poll: ioctl(fd, UNSET, RTS_off): %m",
869 * this 'casts' a character array into a float
883 #ifdef WORDS_BIGENDIAN
884 ((char *) &sval
)[0] = *bp
++;
885 ((char *) &sval
)[1] = *bp
++;
886 ((char *) &sval
)[2] = *bp
++;
887 ((char *) &sval
)[3] = *bp
++;
889 ((char *) &sval
)[3] = *bp
++;
890 ((char *) &sval
)[2] = *bp
++;
891 ((char *) &sval
)[1] = *bp
++;
892 ((char *) &sval
)[0] = *bp
;
893 #endif /* ! XNTP_BIG_ENDIAN */
899 * this 'casts' a character array into a double
913 #ifdef WORDS_BIGENDIAN
914 ((char *) &dval
)[0] = *bp
++;
915 ((char *) &dval
)[1] = *bp
++;
916 ((char *) &dval
)[2] = *bp
++;
917 ((char *) &dval
)[3] = *bp
++;
918 ((char *) &dval
)[4] = *bp
++;
919 ((char *) &dval
)[5] = *bp
++;
920 ((char *) &dval
)[6] = *bp
++;
921 ((char *) &dval
)[7] = *bp
;
923 ((char *) &dval
)[7] = *bp
++;
924 ((char *) &dval
)[6] = *bp
++;
925 ((char *) &dval
)[5] = *bp
++;
926 ((char *) &dval
)[4] = *bp
++;
927 ((char *) &dval
)[3] = *bp
++;
928 ((char *) &dval
)[2] = *bp
++;
929 ((char *) &dval
)[1] = *bp
++;
930 ((char *) &dval
)[0] = *bp
;
931 #endif /* ! XNTP_BIG_ENDIAN */
936 * cast a 16 bit character array into a short (16 bit) int
949 return (short) (bp
[1] + (bp
[0] << 8));
953 int refclock_palisade_bs
;
954 #endif /* REFCLOCK */