1 /* $NetBSD: refclock_nmea.c,v 1.1.1.1 2009/12/13 16:55:55 kardel Exp $ */
4 * refclock_nmea.c - clock driver for an NMEA GPS CLOCK
5 * Michael Petry Jun 20, 1994
6 * based on refclock_heathn.c
8 * Updated to add support for Accord GPS Clock
9 * Venu Gopal Dec 05, 2007
10 * neo.venu@gmail.com, venugopal_d@pgad.gov.in
12 * Updated to process 'time1' fudge factor
13 * Venu Gopal May 05, 2008
15 * Converted to common PPSAPI code, separate PPS fudge time1
16 * from serial timecode fudge time2.
17 * Dave Hart July 1, 2009
18 * hart@ntp.org, davehart@davehart.com
24 #if defined(REFCLOCK) && defined(CLOCK_NMEA)
29 #include <sys/socket.h>
33 #include "ntp_unixtime.h"
34 #include "ntp_refclock.h"
35 #include "ntp_stdlib.h"
38 # include "ppsapi_timepps.h"
39 #include "refclock_atom.h"
40 #endif /* HAVE_PPSAPI */
43 #undef write /* ports/winnt/include/config.h: #define write _write */
44 extern int async_write(int, const void *, unsigned int);
45 #define write(fd, data, octets) async_write(fd, data, octets)
49 * This driver supports NMEA-compatible GPS receivers
51 * Prototype was refclock_trak.c, Thanks a lot.
53 * The receiver used spits out the NMEA sentences for boat navigation.
54 * And you thought it was an information superhighway. Try a raging river
55 * filled with rapids and whirlpools that rip away your data and warp time.
57 * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in.
58 * On startup if initialization of the PPSAPI fails, it will fall back
59 * to the "normal" timestamps.
61 * The PPSAPI part of the driver understands fudge flag2 and flag3. If
62 * flag2 is set, it will use the clear edge of the pulse. If flag3 is
63 * set, kernel hardpps is enabled.
65 * GPS sentences other than RMC (the default) may be enabled by setting
66 * the relevent bits of 'mode' in the server configuration line
67 * server 127.127.20.x mode X
69 * bit 0 - enables RMC (1)
70 * bit 1 - enables GGA (2)
71 * bit 2 - enables GLL (4)
72 * bit 3 - enables ZDA (8) - Standard Time & Date
73 * bit 3 - enables ZDG (8) - Accord GPS Clock's custom sentence with GPS time
74 * very close to standard ZDA
76 * Multiple sentences may be selected except when ZDG/ZDA is selected.
78 * bit 4/5/6 - selects the baudrate for serial port :
79 * 0 for 4800 (default)
86 #define NMEA_MESSAGE_MASK_OLD 0x07
87 #define NMEA_MESSAGE_MASK_SINGLE 0x08
88 #define NMEA_MESSAGE_MASK (NMEA_MESSAGE_MASK_OLD | NMEA_MESSAGE_MASK_SINGLE)
90 #define NMEA_BAUDRATE_MASK 0x70
91 #define NMEA_BAUDRATE_SHIFT 4
96 #define DEVICE "/dev/gps%d" /* GPS serial device */
97 #define PPSDEV "/dev/gpspps%d" /* PPSAPI device override */
98 #define SPEED232 B4800 /* uart speed (4800 bps) */
99 #define PRECISION (-9) /* precision assumed (about 2 ms) */
100 #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
101 #define REFID "GPS\0" /* reference id */
102 #define DESCRIPTION "NMEA GPS Clock" /* who we are */
103 #define NANOSECOND 1000000000 /* one second (ns) */
104 #define RANGEGATE 500000 /* range gate (ns) */
108 #define M_NOCTTY O_NOCTTY
113 #define M_NONBLOCK O_NONBLOCK
115 #define PPSOPENMODE (O_RDWR | M_NOCTTY | M_NONBLOCK)
118 * Unit control structure
122 struct refclock_atom atom
; /* PPSAPI structure */
123 int ppsapi_tried
; /* attempt PPSAPI once */
124 int ppsapi_lit
; /* time_pps_create() worked */
125 int ppsapi_fd
; /* fd used with PPSAPI */
126 int tcount
; /* timecode sample counter */
127 int pcount
; /* PPS sample counter */
128 #endif /* HAVE_PPSAPI */
129 l_fp tstamp
; /* timestamp of last poll */
130 int gps_time
; /* 0 UTC, 1 GPS time */
134 * Function prototypes
136 static int nmea_start (int, struct peer
*);
137 static void nmea_shutdown (int, struct peer
*);
138 static void nmea_receive (struct recvbuf
*);
139 static void nmea_poll (int, struct peer
*);
141 static void nmea_control (int, struct refclockstat
*,
142 struct refclockstat
*, struct peer
*);
143 static void nmea_timer (int, struct peer
*);
144 #define NMEA_CONTROL nmea_control
145 #define NMEA_TIMER nmea_timer
147 #define NMEA_CONTROL noentry
148 #define NMEA_TIMER noentry
149 #endif /* HAVE_PPSAPI */
150 static void gps_send (int, const char *, struct peer
*);
151 static char * field_parse (char *, int);
152 static int nmea_checksum_ok(const char *);
157 struct refclock refclock_nmea
= {
158 nmea_start
, /* start up driver */
159 nmea_shutdown
, /* shut down driver */
160 nmea_poll
, /* transmit poll message */
161 NMEA_CONTROL
, /* fudge control */
162 noentry
, /* initialize driver */
163 noentry
, /* buginfo */
164 NMEA_TIMER
/* called once per second */
168 * nmea_start - open the GPS devices and initialize data for processing
176 register struct nmeaunit
*up
;
177 struct refclockproc
*pp
;
186 * Open serial port. Use CLK line discipline, if available.
188 snprintf(device
, sizeof(device
), DEVICE
, unit
);
191 * Opening the serial port with appropriate baudrate
192 * based on the value of bit 4/5/6
194 switch ((peer
->ttl
& NMEA_BAUDRATE_MASK
) >> NMEA_BAUDRATE_SHIFT
) {
228 fd
= refclock_open(device
, baudrate
, LDISC_CLK
);
232 /* nmead support added by Jon Miner (cp_n18@yahoo.com)
234 * See http://home.hiwaay.net/~taylorc/gps/nmea-server/
235 * for information about nmead
237 * To use this, you need to create a link from /dev/gpsX to
238 * the server:port where nmead is running. Something like this:
240 * ln -s server:port /dev/gps1
243 char *nmea_host
, *nmea_tail
;
248 struct sockaddr_in so_addr
;
250 if ((len
= readlink(device
,buffer
,sizeof(buffer
))) == -1)
254 if ((nmea_host
= strtok(buffer
,":")) == NULL
)
256 if ((nmea_tail
= strtok(NULL
,":")) == NULL
)
259 nmea_port
= atoi(nmea_tail
);
261 if ((he
= gethostbyname(nmea_host
)) == NULL
)
263 if ((p
= getprotobyname("ip")) == NULL
)
265 memset(&so_addr
, 0, sizeof(so_addr
));
266 so_addr
.sin_family
= AF_INET
;
267 so_addr
.sin_port
= htons(nmea_port
);
268 so_addr
.sin_addr
= *((struct in_addr
*) he
->h_addr
);
270 if ((fd
= socket(PF_INET
,SOCK_STREAM
,p
->p_proto
)) == -1)
272 if (connect(fd
,(struct sockaddr
*)&so_addr
, sizeof(so_addr
)) == -1) {
282 msyslog(LOG_NOTICE
, "%s serial %s open at %s bps",
283 refnumtoa(&peer
->srcadr
), device
, baudtext
);
286 * Allocate and initialize unit structure
288 up
= emalloc(sizeof(*up
));
289 memset(up
, 0, sizeof(*up
));
290 pp
->io
.clock_recv
= nmea_receive
;
291 pp
->io
.srcclock
= (caddr_t
)peer
;
294 if (!io_addclock(&pp
->io
)) {
300 pp
->unitptr
= (caddr_t
)up
;
303 * Initialize miscellaneous variables
305 peer
->precision
= PRECISION
;
306 pp
->clockdesc
= DESCRIPTION
;
307 memcpy(&pp
->refid
, REFID
, 4);
309 gps_send(fd
,"$PMOTG,RMC,0000*1D\r\n", peer
);
316 * nmea_shutdown - shut down a GPS clock
318 * NOTE this routine is called after nmea_start() returns failure,
319 * as well as during a normal shutdown due to ntpq :config unpeer.
327 register struct nmeaunit
*up
;
328 struct refclockproc
*pp
;
333 up
= (struct nmeaunit
*)pp
->unitptr
;
336 if (up
->ppsapi_lit
) {
337 time_pps_destroy(up
->atom
.handle
);
338 if (up
->ppsapi_fd
!= pp
->io
.fd
)
339 close(up
->ppsapi_fd
);
345 io_closeclock(&pp
->io
);
349 * nmea_control - configure fudge params
355 struct refclockstat
*in_st
,
356 struct refclockstat
*out_st
,
361 register struct nmeaunit
*up
;
362 struct refclockproc
*pp
;
369 up
= (struct nmeaunit
*)pp
->unitptr
;
371 if (!(CLK_FLAG1
& pp
->sloppyclockflag
)) {
372 if (!up
->ppsapi_tried
)
374 up
->ppsapi_tried
= 0;
377 peer
->flags
&= ~FLAG_PPS
;
378 peer
->precision
= PRECISION
;
379 time_pps_destroy(up
->atom
.handle
);
380 if (up
->ppsapi_fd
!= pp
->io
.fd
)
381 close(up
->ppsapi_fd
);
388 if (up
->ppsapi_tried
)
391 * Light up the PPSAPI interface.
393 up
->ppsapi_tried
= 1;
396 * if /dev/gpspps$UNIT can be opened that will be used for
397 * PPSAPI. Otherwise, the GPS serial device /dev/gps$UNIT
398 * already opened is used for PPSAPI as well.
400 snprintf(device
, sizeof(device
), PPSDEV
, unit
);
402 pps_fd
= open(device
, PPSOPENMODE
, S_IRUSR
| S_IWUSR
);
407 if (refclock_ppsapi(pps_fd
, &up
->atom
)) {
409 up
->ppsapi_fd
= pps_fd
;
414 msyslog(LOG_WARNING
, "%s flag1 1 but PPSAPI fails",
415 refnumtoa(&peer
->srcadr
));
417 #endif /* HAVE_PPSAPI */
421 * nmea_timer - called once per second, fetches PPS
422 * timestamp and stuffs in median filter.
432 struct refclockproc
*pp
;
437 up
= (struct nmeaunit
*)pp
->unitptr
;
439 if (up
->ppsapi_lit
&&
440 refclock_pps(peer
, &up
->atom
, pp
->sloppyclockflag
) > 0) {
442 peer
->flags
|= FLAG_PPS
;
443 peer
->precision
= PPS_PRECISION
;
446 #endif /* HAVE_PPSAPI */
450 * nmea_receive - receive data from the serial interface
454 struct recvbuf
*rbufp
457 register struct nmeaunit
*up
;
458 struct refclockproc
*pp
;
464 /* Use these variables to hold data until we decide its worth keeping */
465 char rd_lastcode
[BMAX
];
470 * Initialize pointers and read the timecode and timestamp
472 peer
= rbufp
->recv_peer
;
474 up
= (struct nmeaunit
*)pp
->unitptr
;
476 rd_lencode
= refclock_gtlin(
483 * There is a case that a <CR><LF> gives back a "blank" line
488 DPRINTF(1, ("nmea: gpsread %d %s\n", rd_lencode
, rd_lastcode
));
491 * We check the timecode format and decode its contents. The
492 * we only care about a few of them. The most important being
494 * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
495 * mode (0,1,2,3) selects sentence ANY/ALL, RMC, GGA, GLL, ZDA
496 * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21
497 * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F
498 * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77
500 * Defining GPZDA to support Standard Time & Date
501 * sentence. The sentence has the following format
503 * $--ZDA,HHMMSS.SS,DD,MM,YYYY,TH,TM,*CS<CR><LF>
505 * Apart from the familiar fields,
506 * 'TH' Time zone Hours
507 * 'TM' Time zone Minutes
509 * Defining GPZDG to support Accord GPS Clock's custom NMEA
510 * sentence. The sentence has the following format
512 * $GPZDG,HHMMSS.S,DD,MM,YYYY,AA.BB,V*CS<CR><LF>
514 * It contains the GPS timestamp valid for next PPS pulse.
515 * Apart from the familiar fields,
516 * 'AA.BB' denotes the signal strength( should be < 05.00 )
517 * 'V' denotes the GPS sync status :
518 * '0' indicates INVALID time,
519 * '1' indicates accuracy of +/-20 ms
520 * '2' indicates accuracy of +/-100 ns
522 #define GPXXX 0 /* any/all */
531 /* Allow for GLGGA and GPGGA etc. */
534 if (strncmp(msg
, "RMC", 3) == 0)
536 else if (strncmp(msg
, "GGA", 3) == 0)
538 else if (strncmp(msg
, "GLL", 3) == 0)
540 else if (strncmp(msg
, "ZD", 2) == 0) {
544 else if ('A' != msg
[2])
551 /* See if I want to process this message type */
552 if (peer
->ttl
&& !(cmdtype
& (peer
->ttl
& NMEA_MESSAGE_MASK
)))
556 * $GPZDG provides GPS time not UTC, and the two mix poorly.
557 * Once have processed a $GPZDG, do not process any further
558 * UTC sentences (all but $GPZDG currently).
560 if (up
->gps_time
&& !cmdtypezdg
)
563 /* make sure it came in clean */
564 if (!nmea_checksum_ok(rd_lastcode
)) {
565 refclock_report(peer
, CEVNT_BADREPLY
);
569 pp
->lencode
= (u_short
) rd_lencode
;
570 memcpy(pp
->a_lastcode
, rd_lastcode
, pp
->lencode
+ 1);
573 up
->tstamp
= rd_timestamp
;
574 pp
->lastrec
= up
->tstamp
;
576 DPRINTF(1, ("nmea: timecode %d %s\n", pp
->lencode
, pp
->a_lastcode
));
578 /* Grab field depending on clock string type */
583 * Test for synchronization. Check for quality byte.
585 dp
= field_parse(cp
, 2);
587 pp
->leap
= LEAP_NOTINSYNC
;
589 pp
->leap
= LEAP_NOWARNING
;
591 /* Now point at the time field */
592 dp
= field_parse(cp
, 1);
597 * Test for synchronization. Check for quality byte.
599 dp
= field_parse(cp
, 6);
601 pp
->leap
= LEAP_NOTINSYNC
;
603 pp
->leap
= LEAP_NOWARNING
;
605 /* Now point at the time field */
606 dp
= field_parse(cp
, 1);
611 * Test for synchronization. Check for quality byte.
613 dp
= field_parse(cp
, 6);
615 pp
->leap
= LEAP_NOTINSYNC
;
617 pp
->leap
= LEAP_NOWARNING
;
619 /* Now point at the time field */
620 dp
= field_parse(cp
, 5);
625 * Test for synchronization. For $GPZDG check for validity of GPS time.
628 dp
= field_parse(cp
, 6);
630 pp
->leap
= LEAP_NOTINSYNC
;
632 pp
->leap
= LEAP_NOWARNING
;
634 pp
->leap
= LEAP_NOWARNING
;
636 /* Now point at the time field */
637 dp
= field_parse(cp
, 1);
645 * Check time code format of NMEA
647 if (!isdigit((unsigned char)dp
[0]) ||
648 !isdigit((unsigned char)dp
[1]) ||
649 !isdigit((unsigned char)dp
[2]) ||
650 !isdigit((unsigned char)dp
[3]) ||
651 !isdigit((unsigned char)dp
[4]) ||
652 !isdigit((unsigned char)dp
[5])) {
654 DPRINTF(1, ("NMEA time code %c%c%c%c%c%c non-numeric",
655 dp
[0], dp
[1], dp
[2], dp
[3], dp
[4], dp
[5]));
656 refclock_report(peer
, CEVNT_BADTIME
);
661 * Convert time and check values.
663 pp
->hour
= ((dp
[0] - '0') * 10) + dp
[1] - '0';
664 pp
->minute
= ((dp
[2] - '0') * 10) + dp
[3] - '0';
665 pp
->second
= ((dp
[4] - '0') * 10) + dp
[5] - '0';
667 * Default to 0 milliseconds, if decimal convert milliseconds in
668 * one, two or three digits
672 if (isdigit((unsigned char)dp
[7])) {
673 pp
->nsec
= (dp
[7] - '0') * 100000000;
674 if (isdigit((unsigned char)dp
[8])) {
675 pp
->nsec
+= (dp
[8] - '0') * 10000000;
676 if (isdigit((unsigned char)dp
[9])) {
677 pp
->nsec
+= (dp
[9] - '0') * 1000000;
684 * Manipulating GPS timestamp in GPZDG as the seconds field
685 * is valid for next PPS tick. Just rolling back the second,
686 * minute and hour fields appopriately
689 if (pp
->second
== 0) {
691 if (pp
->minute
== 0) {
700 if (pp
->hour
> 23 || pp
->minute
> 59 ||
701 pp
->second
> 59 || pp
->nsec
> 1000000000) {
703 DPRINTF(1, ("NMEA hour/min/sec/nsec range %02d:%02d:%02d.%09ld\n",
704 pp
->hour
, pp
->minute
, pp
->second
, pp
->nsec
));
705 refclock_report(peer
, CEVNT_BADTIME
);
710 * Convert date and check values.
712 if (GPRMC
== cmdtype
) {
714 dp
= field_parse(cp
,9);
716 day
= (day
* 10) + dp
[1] - '0';
718 month
= (month
* 10) + dp
[3] - '0';
719 pp
->year
= dp
[4] - '0';
720 pp
->year
= (pp
->year
* 10) + dp
[5] - '0';
722 } else if (GPZDG_ZDA
== cmdtype
) {
724 dp
= field_parse(cp
, 2);
725 day
= 10 * (dp
[0] - '0') + (dp
[1] - '0');
726 dp
= field_parse(cp
, 3);
727 month
= 10 * (dp
[0] - '0') + (dp
[1] - '0');
728 dp
= field_parse(cp
, 4);
729 pp
->year
= /* 1000 * (dp[0] - '0') + 100 * (dp[1] - '0') + */ 10 * (dp
[2] - '0') + (dp
[3] - '0');
733 time_t tt
= time(NULL
);
734 struct tm
* t
= gmtime(&tt
);
736 month
= t
->tm_mon
+ 1;
737 pp
->year
= t
->tm_year
+ 1900;
740 if (month
< 1 || month
> 12 || day
< 1) {
741 refclock_report(peer
, CEVNT_BADDATE
);
745 /* pp->year will be 2 or 4 digits if read from GPS, 4 from gmtime */
746 if (pp
->year
< 100) {
747 if (pp
->year
< 9) /* year of our line of code is 2009 */
753 /* pp->year now 4 digits as ymd2yd requires */
754 day
= ymd2yd(pp
->year
, month
, day
);
756 refclock_report(peer
, CEVNT_BADDATE
);
762 * If "fudge 127.127.20.__ flag4 1" is configured in ntp.conf,
763 * remove the location and checksum from the NMEA sentence
764 * recorded as the last timecode and visible to remote users
767 * ntpq -c clockvar <server>
769 * Note that this also removes the location from the clockstats
770 * log (if it is enabled). Some NTP operators monitor their
771 * NMEA GPS using the change in location in clockstats over
772 * time as as a proxy for the quality of GPS reception and
773 * thereby time reported.
775 if (CLK_FLAG4
& pp
->sloppyclockflag
) {
777 * Start by pointing cp and dp at the fields with
778 * longitude and latitude in the last timecode.
783 cp
= field_parse(pp
->a_lastcode
, 1);
784 dp
= field_parse(cp
, 2);
788 cp
= field_parse(pp
->a_lastcode
, 2);
789 dp
= field_parse(cp
, 2);
793 cp
= field_parse(pp
->a_lastcode
, 3);
794 dp
= field_parse(cp
, 2);
802 /* Blank the entire latitude & longitude. */
810 /* Longitude at cp then latitude at dp */
817 /* Blank the checksum, the last two characters */
819 cp
= pp
->a_lastcode
+ pp
->lencode
- 2;
827 * Note if we're only using GPS timescale from now on.
829 if (cmdtypezdg
&& !up
->gps_time
) {
832 msyslog(LOG_INFO
, "%s using only $GPZDG",
833 refnumtoa(&peer
->srcadr
));
837 * Process the new sample in the median filter and determine the
838 * timecode timestamp, but only if the PPS is not in control.
842 if (peer
->flags
& FLAG_PPS
)
844 #endif /* HAVE_PPSAPI */
845 if (!refclock_process_f(pp
, pp
->fudgetime2
))
846 refclock_report(peer
, CEVNT_BADTIME
);
851 * nmea_poll - called by the transmit procedure
853 * We go to great pains to avoid changing state here, since there may be
854 * more than one eavesdropper receiving the same timecode.
862 register struct nmeaunit
*up
;
863 struct refclockproc
*pp
;
866 up
= (struct nmeaunit
*)pp
->unitptr
;
869 * Process median filter samples. If none received, declare a
870 * timeout and keep going.
873 if (up
->pcount
== 0) {
874 peer
->flags
&= ~FLAG_PPS
;
875 peer
->precision
= PRECISION
;
877 if (up
->tcount
== 0) {
878 pp
->coderecv
= pp
->codeproc
;
879 refclock_report(peer
, CEVNT_TIMEOUT
);
882 up
->pcount
= up
->tcount
= 0;
883 #else /* HAVE_PPSAPI */
884 if (pp
->coderecv
== pp
->codeproc
) {
885 refclock_report(peer
, CEVNT_TIMEOUT
);
888 #endif /* HAVE_PPSAPI */
891 pp
->lastref
= pp
->lastrec
;
892 refclock_receive(peer
);
893 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
896 * usually nmea_receive can get a timestamp every second,
897 * but at least one Motorola unit needs prompting each
901 gps_send(pp
->io
.fd
,"$PMOTG,RMC,0000*1D\r\n", peer
);
907 * gps_send(fd,cmd, peer) Sends a command to the GPS receiver.
908 * as gps_send(fd,"rqts,u\r", peer);
910 * We don't currently send any data, but would like to send
911 * RTCM SC104 messages for differential positioning. It should
912 * also give us better time. Without a PPS output, we're
913 * Just fooling ourselves because of the serial code paths
923 if (write(fd
, cmd
, strlen(cmd
)) == -1) {
924 refclock_report(peer
, CEVNT_FAULT
);
938 for (tp
= cp
; i
&& *tp
; tp
++)
947 * nmea_checksum_ok verifies 8-bit XOR checksum is correct then returns 1
949 * format is $XXXXX,1,2,3,4*ML
951 * 8-bit XOR of characters between $ and * noninclusive is transmitted
952 * in last two chars M and L holding most and least significant nibbles
953 * in hex representation such as:
955 * $GPGLL,5057.970,N,00146.110,E,142451,A*27
956 * $GPVTG,089.0,T,,,15.2,N,,*7F
973 for ( ; *p
&& '*' != *p
; p
++) {
981 if (0 == p
[0] || 0 == p
[1] || 0 != p
[2])
984 if (0 == hextoint(p
, &input_cs
))
987 if (my_cs
!= input_cs
)
993 int refclock_nmea_bs
;
994 #endif /* REFCLOCK && CLOCK_NMEA */