1 /* $NetBSD: refclock_palisade.c,v 1.3 2005/05/21 01:47:09 riz Exp $ */
4 * This software was developed by the Software and Component Technologies
5 * group of Trimble Navigation, Ltd.
7 * Copyright (c) 1997, 1998, 1999, 2000 Trimble Navigation Ltd.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Trimble Navigation, Ltd.
21 * 4. The name of Trimble Navigation Ltd. may not be used to endorse or
22 * promote products derived from this software without specific prior
25 * THIS SOFTWARE IS PROVIDED BY TRIMBLE NAVIGATION LTD. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL TRIMBLE NAVIGATION LTD. BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * refclock_palisade - clock driver for the Trimble Palisade GPS
42 * For detailed information on this program, please refer to the html
43 * Refclock 29 page accompanying the NTP distribution.
45 * for questions / bugs / comments, contact:
46 * sven_dietrich@trimble.com
48 * Sven-Thorsten Dietrich
49 * 645 North Mary Avenue
50 * Post Office Box 3642
51 * Sunnyvale, CA 94088-3642
53 * Version 2.45; July 14, 1999
61 #if defined(REFCLOCK) && (defined(PALISADE) || defined(CLOCK_PALISADE))
64 extern int async_write(int, const void *, unsigned int);
66 #define write(fd, data, octets) async_write(fd, data, octets)
69 #include "refclock_palisade.h"
70 /* Table to get from month to day of the year */
71 const int days_of_year
[12] = {
72 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
76 const char * Tracking_Status
[15][15] = {
77 { "Doing Fixes\0" }, { "Good 1SV\0" }, { "Approx. 1SV\0" },
78 {"Need Time\0" }, { "Need INIT\0" }, { "PDOP too High\0" },
79 { "Bad 1SV\0" }, { "0SV Usable\0" }, { "1SV Usable\0" },
80 { "2SV Usable\0" }, { "3SV Usable\0" }, { "No Integrity\0" },
81 { "Diff Corr\0" }, { "Overdet Clock\0" }, { "Invalid\0" } };
87 struct refclock refclock_palisade
= {
88 palisade_start
, /* start up driver */
89 palisade_shutdown
, /* shut down driver */
90 palisade_poll
, /* transmit poll message */
91 noentry
, /* not used */
92 noentry
, /* initialize driver (not used) */
93 noentry
, /* not used */
94 NOFLAGS
/* not used */
97 int day_of_year
P((char *dt
));
99 /* Extract the clock type from the mode setting */
100 #define CLK_TYPE(x) ((int)(((x)->ttl) & 0x7F))
102 /* Supported clock types */
103 #define CLK_TRIMBLE 0 /* Trimble Palisade */
104 #define CLK_PRAECIS 1 /* Endrun Technologies Praecis */
107 static void praecis_parse(struct recvbuf
*rbufp
, struct peer
*peer
);
110 * palisade_start - open the devices and initialize data for processing
125 struct palisade_unit
*up
;
126 struct refclockproc
*pp
;
132 (void) sprintf(gpsdev
, "COM%d:", unit
);
134 (void) sprintf(gpsdev
, DEVICE
, unit
);
140 fd
= open(gpsdev
, O_RDWR
146 fd
= refclock_open(gpsdev
, SPEED232
, LDISC_RAW
);
150 printf("Palisade(%d) start: open %s failed\n", unit
, gpsdev
);
155 msyslog(LOG_NOTICE
, "Palisade(%d) fd: %d dev: %s", unit
, fd
,
159 tio
.c_cflag
= (CS8
|CLOCAL
|CREAD
|PARENB
|PARODD
);
160 tio
.c_iflag
= (IGNBRK
);
164 if (cfsetispeed(&tio
, SPEED232
) == -1) {
165 msyslog(LOG_ERR
,"Palisade(%d) cfsetispeed(fd, &tio): %m",unit
);
167 printf("Palisade(%d) cfsetispeed(fd, &tio)\n",unit
);
171 if (cfsetospeed(&tio
, SPEED232
) == -1) {
173 printf("Palisade(%d) cfsetospeed(fd, &tio)\n",unit
);
175 msyslog(LOG_ERR
,"Palisade(%d) cfsetospeed(fd, &tio): %m",unit
);
179 if (tcgetattr(fd
, &tio
) < 0) {
181 "Palisade(%d) tcgetattr(fd, &tio): %m",unit
);
183 printf("Palisade(%d) tcgetattr(fd, &tio)\n",unit
);
188 tio
.c_cflag
|= (PARENB
|PARODD
);
189 tio
.c_iflag
&= ~ICRNL
;
192 if (tcsetattr(fd
, TCSANOW
, &tio
) == -1) {
193 msyslog(LOG_ERR
, "Palisade(%d) tcsetattr(fd, &tio): %m",unit
);
195 printf("Palisade(%d) tcsetattr(fd, &tio)\n",unit
);
201 * Allocate and initialize unit structure
203 up
= (struct palisade_unit
*) emalloc(sizeof(struct palisade_unit
));
206 msyslog(LOG_ERR
, "Palisade(%d) emalloc: %m",unit
);
208 printf("Palisade(%d) emalloc\n",unit
);
214 memset((char *)up
, 0, sizeof(struct palisade_unit
));
216 up
->type
= CLK_TYPE(peer
);
219 /* Normal mode, do nothing */
222 msyslog(LOG_NOTICE
, "Palisade(%d) Praecis mode enabled\n",unit
);
225 msyslog(LOG_NOTICE
, "Palisade(%d) mode unknown\n",unit
);
230 pp
->io
.clock_recv
= palisade_io
;
231 pp
->io
.srcclock
= (caddr_t
)peer
;
234 if (!io_addclock(&pp
->io
)) {
236 printf("Palisade(%d) io_addclock\n",unit
);
244 * Initialize miscellaneous variables
246 pp
->unitptr
= (caddr_t
)up
;
247 pp
->clockdesc
= DESCRIPTION
;
249 peer
->precision
= PRECISION
;
250 peer
->sstclktype
= CTL_SST_TS_UHF
;
251 peer
->minpoll
= TRMB_MINPOLL
;
252 peer
->maxpoll
= TRMB_MAXPOLL
;
253 memcpy((char *)&pp
->refid
, REFID
, 4);
256 up
->unit
= (short) unit
;
257 up
->rpt_status
= TSIP_PARSED_EMPTY
;
265 * palisade_shutdown - shut down the clock
280 struct palisade_unit
*up
;
281 struct refclockproc
*pp
;
283 up
= (struct palisade_unit
*)pp
->unitptr
;
284 io_closeclock(&pp
->io
);
291 * unpack_date - get day and year from date
307 /* Check month is inside array bounds */
308 if ((mon
< 1) || (mon
> 12))
311 day
= dt
[0] + days_of_year
[mon
- 1];
312 year
= getint((u_char
*) (dt
+ 2));
314 if ( !(year
% 4) && ((year
% 100) ||
315 (!(year
% 100) && !(year
%400)))
317 day
++; /* leap year and March or later */
324 * TSIP_decode - decode the TSIP data packets
341 unsigned short event
= 0;
343 struct palisade_unit
*up
;
344 struct refclockproc
*pp
;
347 up
= (struct palisade_unit
*)pp
->unitptr
;
350 * Check the time packet, decode its contents.
351 * If the timecode has invalid length or is not in
352 * proper format, declare bad format and exit.
355 if ((up
->rpt_buf
[0] == (char) 0x41) ||
356 (up
->rpt_buf
[0] == (char) 0x46) ||
357 (up
->rpt_buf
[0] == (char) 0x54) ||
358 (up
->rpt_buf
[0] == (char) 0x4B) ||
359 (up
->rpt_buf
[0] == (char) 0x6D)) {
361 /* standard time packet - GPS time and GPS week number */
363 printf("Palisade Port B packets detected. Connect to Port A\n");
370 * We cast both to u_char to as 0x8f uses the sign bit on a char
372 if ((u_char
) up
->rpt_buf
[0] == (u_char
) 0x8f) {
376 event
= (unsigned short) (getint((u_char
*) &mb(1)) & 0xffff);
377 if (!((pp
->sloppyclockflag
& CLK_FLAG2
) || event
))
381 switch (mb(0) & 0xff) {
388 if (up
->rpt_cnt
!= LENCODE_8F0B
) /* check length */
394 double lat
, lon
, alt
;
395 lat
= getdbl((u_char
*) &mb(42)) * R2D
;
396 lon
= getdbl((u_char
*) &mb(50)) * R2D
;
397 alt
= getdbl((u_char
*) &mb(58));
399 printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n",
400 up
->unit
, lat
,lon
,alt
);
401 printf("TSIP_decode: unit %d: Sats:", up
->unit
);
402 for (st
= 66, ts
= 0; st
<= 73; st
++) if (mb(st
)) {
403 if (mb(st
) > 0) ts
++;
404 printf(" %02d", mb(st
));
406 printf(" : Tracking %d\n", ts
);
410 GPS_UTC_Offset
= getint((u_char
*) &mb(16));
411 if (GPS_UTC_Offset
== 0) { /* Check UTC offset */
413 printf("TSIP_decode: UTC Offset Unknown\n");
418 secs
= getdbl((u_char
*) &mb(3));
419 secint
= (long) secs
;
420 secfrac
= secs
- secint
; /* 0.0 <= secfrac < 1.0 */
422 pp
->nsec
= (long) (secfrac
* 1000000000);
424 secint
%= 86400; /* Only care about today */
425 pp
->hour
= secint
/ 3600;
427 pp
->minute
= secint
/ 60;
429 pp
->second
= secint
% 60;
431 if ((pp
->day
= day_of_year(&mb(11))) < 0) break;
433 pp
->year
= getint((u_char
*) &mb(13));
437 printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%06ld %02d/%02d/%04d UTC %02d\n",
438 up
->unit
, mb(0) & 0xff, event
, pp
->hour
, pp
->minute
,
439 pp
->second
, pp
->nsec
, mb(12), mb(11), pp
->year
, GPS_UTC_Offset
);
441 /* Only use this packet when no
442 * 8F-AD's are being received
445 if (up
->leap_status
) {
454 /* Palisade-NTP Packet */
456 if (up
->rpt_cnt
!= LENCODE_NTP
) /* check length */
459 up
->leap_status
= mb(19);
464 /* Check Tracking Status */
466 if (st
< 0 || st
> 14) st
= 14;
467 if ((st
>= 2 && st
<= 7) || st
== 11 || st
== 12) {
469 printf("TSIP_decode: Not Tracking Sats : %s\n",
470 *Tracking_Status
[st
]);
472 refclock_report(peer
, CEVNT_BADTIME
);
478 if (up
->leap_status
& PALISADE_LEAP_PENDING
) {
479 if (up
->leap_status
& PALISADE_UTC_TIME
)
480 pp
->leap
= LEAP_ADDSECOND
;
482 pp
->leap
= LEAP_DELSECOND
;
484 else if (up
->leap_status
)
485 pp
->leap
= LEAP_NOWARNING
;
487 else { /* UTC flag is not set:
488 * Receiver may have been reset, and lost
489 * its UTC almanac data */
490 pp
->leap
= LEAP_NOTINSYNC
;
492 printf("TSIP_decode: UTC Almanac unavailable: %d\n",
495 refclock_report(peer
, CEVNT_BADTIME
);
500 pp
->nsec
= (long) (getdbl((u_char
*) &mb(3)) * 1000000000);
502 if ((pp
->day
= day_of_year(&mb(14))) < 0)
504 pp
->year
= getint((u_char
*) &mb(16));
511 printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%06ld %02d/%02d/%04d UTC %02x %s\n",
512 up
->unit
, mb(0) & 0xff, event
, pp
->hour
, pp
->minute
,
513 pp
->second
, pp
->nsec
, mb(15), mb(14), pp
->year
,
514 mb(19), *Tracking_Status
[st
]);
525 refclock_report(peer
, CEVNT_BADREPLY
);
528 printf("TSIP_decode: unit %d: bad packet %02x-%02x event %d len %d\n",
529 up
->unit
, up
->rpt_buf
[0] & 0xff, mb(0) & 0xff,
536 * palisade__receive - receive data from the serial interface
550 struct palisade_unit
*up
;
551 struct refclockproc
*pp
;
554 * Initialize pointers and read the timecode and timestamp.
557 up
= (struct palisade_unit
*)pp
->unitptr
;
559 if (! TSIP_decode(peer
)) return;
562 return; /* no poll pending, already received or timeout */
564 up
->polled
= 0; /* Poll reply received */
565 pp
->lencode
= 0; /* clear time code */
569 "palisade_receive: unit %d: %4d %03d %02d:%02d:%02d.%06ld\n",
570 up
->unit
, pp
->year
, pp
->day
, pp
->hour
, pp
->minute
,
571 pp
->second
, pp
->nsec
);
576 * Generate timecode: YYYY DoY HH:MM:SS.microsec
580 (void) sprintf(pp
->a_lastcode
,"%4d %03d %02d:%02d:%02d.%06ld",
581 pp
->year
,pp
->day
,pp
->hour
,pp
->minute
, pp
->second
,pp
->nsec
);
585 pp
->lasttime
= current_time
;
587 if (!refclock_process(pp
589 , PALISADE_SAMPLES
, PALISADE_SAMPLES
* 3 / 5
592 refclock_report(peer
, CEVNT_BADTIME
);
595 printf("palisade_receive: unit %d: refclock_process failed!\n",
601 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
605 printf("palisade_receive: unit %d: %s\n",
606 up
->unit
, prettydate(&pp
->lastrec
));
608 pp
->lastref
= pp
->lastrec
;
609 refclock_receive(peer
611 , &pp
->offset
, 0, pp
->dispersion
,
612 &pp
->lastrec
, &pp
->lastrec
, pp
->leap
619 * palisade_poll - called by the transmit procedure
635 struct palisade_unit
*up
;
636 struct refclockproc
*pp
;
639 up
= (struct palisade_unit
*)pp
->unitptr
;
642 if (up
->polled
> 0) /* last reply never arrived or error */
643 refclock_report(peer
, CEVNT_TIMEOUT
);
645 up
->polled
= 2; /* synchronous packet + 1 event */
649 printf("palisade_poll: unit %d: polling %s\n", unit
,
650 (pp
->sloppyclockflag
& CLK_FLAG2
) ?
651 "synchronous packet" : "event");
654 if (pp
->sloppyclockflag
& CLK_FLAG2
)
655 return; /* using synchronous packet input */
657 if(up
->type
== CLK_PRAECIS
) {
658 if(write(peer
->procptr
->io
.fd
,"SPSTAT\r\n",8) < 0)
659 msyslog(LOG_ERR
, "Palisade(%d) write: %m:",unit
);
667 refclock_report(peer
, CEVNT_FAULT
);
671 praecis_parse(struct recvbuf
*rbufp
, struct peer
*peer
)
673 static char buf
[100];
675 struct refclockproc
*pp
;
679 memcpy(buf
+p
,rbufp
->recv_space
.X_recv_buffer
, rbufp
->recv_length
);
680 p
+= rbufp
->recv_length
;
682 if(buf
[p
-2] == '\r' && buf
[p
-1] == '\n') {
684 record_clock_stats(&peer
->srcadr
, buf
);
690 refclock_report(peer
, CEVNT_FAULT
);
700 struct recvbuf
*rbufp
;
702 struct recvbuf
*rbufp
707 * Initialize pointers and read the timecode and timestamp.
709 struct palisade_unit
*up
;
710 struct refclockproc
*pp
;
715 peer
= (struct peer
*)rbufp
->recv_srcclock
;
717 up
= (struct palisade_unit
*)pp
->unitptr
;
719 if(up
->type
== CLK_PRAECIS
) {
721 praecis_parse(rbufp
,peer
);
726 c
= (char *) &rbufp
->recv_space
;
727 d
= c
+ rbufp
->recv_length
;
731 /* Build time packet */
732 switch (up
->rpt_status
) {
734 case TSIP_PARSED_DLE_1
:
740 up
->rpt_status
= TSIP_PARSED_EMPTY
;
744 up
->rpt_status
= TSIP_PARSED_DATA
;
751 case TSIP_PARSED_DATA
:
753 up
->rpt_status
= TSIP_PARSED_DLE_2
;
755 mb(up
->rpt_cnt
++) = *c
;
758 case TSIP_PARSED_DLE_2
:
760 up
->rpt_status
= TSIP_PARSED_DATA
;
765 up
->rpt_status
= TSIP_PARSED_FULL
;
767 /* error: start new report packet */
768 up
->rpt_status
= TSIP_PARSED_DLE_1
;
773 case TSIP_PARSED_FULL
:
774 case TSIP_PARSED_EMPTY
:
777 up
->rpt_status
= TSIP_PARSED_EMPTY
;
779 up
->rpt_status
= TSIP_PARSED_DLE_1
;
785 if (up
->rpt_status
== TSIP_PARSED_DLE_1
) {
787 if (pp
->sloppyclockflag
& CLK_FLAG2
)
789 get_systime(&pp
->lastrec
);
791 else if (up
->rpt_status
== TSIP_PARSED_EMPTY
)
794 else if (up
->rpt_cnt
> BMAX
)
795 up
->rpt_status
=TSIP_PARSED_EMPTY
;
797 if (up
->rpt_status
== TSIP_PARSED_FULL
)
798 palisade_receive(peer
);
800 } /* while chars in buffer */
805 * Trigger the Palisade's event input, which is driven off the RTS
807 * Take a system time stamp to match the GPS time stamp.
813 pp
/* pointer to unit structure */
815 struct refclockproc
* pp
; /* pointer to unit structure */
817 struct refclockproc
* pp
/* pointer to unit structure */
821 int x
; /* state before & after RTS set */
822 struct palisade_unit
*up
;
824 up
= (struct palisade_unit
*) pp
->unitptr
;
826 /* read the current status, so we put things back right */
827 if (ioctl(pp
->io
.fd
, TIOCMGET
, &x
) < 0) {
830 printf("Palisade HW_poll: unit %d: GET %s\n", up
->unit
, strerror(errno
));
832 msyslog(LOG_ERR
, "Palisade(%d) HW_poll: ioctl(fd,GET): %m",
837 x
|= TIOCM_RTS
; /* turn on RTS */
840 if (ioctl(pp
->io
.fd
, TIOCMSET
, &x
) < 0) {
843 printf("Palisade HW_poll: unit %d: SET \n", up
->unit
);
846 "Palisade(%d) HW_poll: ioctl(fd, SET, RTS_on): %m",
851 x
&= ~TIOCM_RTS
; /* turn off RTS */
854 get_systime(&pp
->lastrec
);
856 if (ioctl(pp
->io
.fd
, TIOCMSET
, &x
) == -1) {
859 printf("Palisade HW_poll: unit %d: UNSET \n", up
->unit
);
862 "Palisade(%d) HW_poll: ioctl(fd, UNSET, RTS_off): %m",
872 * this 'casts' a character array into a float
886 #ifdef WORDS_BIGENDIAN
887 ((char *) &sval
)[0] = *bp
++;
888 ((char *) &sval
)[1] = *bp
++;
889 ((char *) &sval
)[2] = *bp
++;
890 ((char *) &sval
)[3] = *bp
++;
892 ((char *) &sval
)[3] = *bp
++;
893 ((char *) &sval
)[2] = *bp
++;
894 ((char *) &sval
)[1] = *bp
++;
895 ((char *) &sval
)[0] = *bp
;
896 #endif /* ! XNTP_BIG_ENDIAN */
902 * this 'casts' a character array into a double
916 #ifdef WORDS_BIGENDIAN
917 ((char *) &dval
)[0] = *bp
++;
918 ((char *) &dval
)[1] = *bp
++;
919 ((char *) &dval
)[2] = *bp
++;
920 ((char *) &dval
)[3] = *bp
++;
921 ((char *) &dval
)[4] = *bp
++;
922 ((char *) &dval
)[5] = *bp
++;
923 ((char *) &dval
)[6] = *bp
++;
924 ((char *) &dval
)[7] = *bp
;
926 ((char *) &dval
)[7] = *bp
++;
927 ((char *) &dval
)[6] = *bp
++;
928 ((char *) &dval
)[5] = *bp
++;
929 ((char *) &dval
)[4] = *bp
++;
930 ((char *) &dval
)[3] = *bp
++;
931 ((char *) &dval
)[2] = *bp
++;
932 ((char *) &dval
)[1] = *bp
++;
933 ((char *) &dval
)[0] = *bp
;
934 #endif /* ! XNTP_BIG_ENDIAN */
939 * cast a 16 bit character array into a short (16 bit) int
952 return (short) (bp
[1] + (bp
[0] << 8));
956 int refclock_palisade_bs
;
957 #endif /* REFCLOCK */