1 /* $NetBSD: refclock_nmea.c,v 1.4 2007/01/06 19:45:23 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
12 #if defined(REFCLOCK) && defined(CLOCK_NMEA)
16 #include <sys/socket.h>
20 #include "ntp_unixtime.h"
21 #include "ntp_refclock.h"
22 #include "ntp_stdlib.h"
25 # include "ppsapi_timepps.h"
26 #endif /* HAVE_PPSAPI */
29 extern int async_write(int, const void *, unsigned int);
31 #define write(fd, data, octets) async_write(fd, data, octets)
35 * This driver supports the NMEA GPS Receiver with
37 * Protype was refclock_trak.c, Thanks a lot.
39 * The receiver used spits out the NMEA sentences for boat navigation.
40 * And you thought it was an information superhighway. Try a raging river
41 * filled with rapids and whirlpools that rip away your data and warp time.
43 * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in.
44 * On startup if initialization of the PPSAPI fails, it will fall back
45 * to the "normal" timestamps.
47 * The PPSAPI part of the driver understands fudge flag2 and flag3. If
48 * flag2 is set, it will use the clear edge of the pulse. If flag3 is
49 * set, kernel hardpps is enabled.
51 * GPS sentences other than RMC (the default) may be enabled by setting
52 * the relevent bits of 'mode' in the server configuration line
53 * server 127.127.20.x mode X
55 * bit 0 - enables RMC (1)
56 * bit 1 - enables GGA (2)
57 * bit 2 - enables GLL (4)
58 * multiple sentences may be selected
65 # define DEVICE "COM%d:" /* COM 1 - 3 supported */
67 # define DEVICE "/dev/gps%d" /* name of radio device */
69 #define SPEED232 B4800 /* uart speed (4800 bps) */
70 #define PRECISION (-9) /* precision assumed (about 2 ms) */
71 #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
72 #define REFID "GPS\0" /* reference id */
73 #define DESCRIPTION "NMEA GPS Clock" /* who we are */
74 #define NANOSECOND 1000000000 /* one second (ns) */
75 #define RANGEGATE 500000 /* range gate (ns) */
77 #define LENNMEA 75 /* min timecode length */
80 * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
83 static int day1tab
[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
84 static int day2tab
[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
87 * Unit control structure
90 int pollcnt
; /* poll message counter */
91 int polled
; /* Hand in a sample? */
92 l_fp tstamp
; /* timestamp of last poll */
94 struct timespec ts
; /* last timestamp */
95 pps_params_t pps_params
; /* pps parameters */
96 pps_info_t pps_info
; /* last pps data */
97 pps_handle_t handle
; /* pps handlebars */
98 #endif /* HAVE_PPSAPI */
102 * Function prototypes
104 static int nmea_start
P((int, struct peer
*));
105 static void nmea_shutdown
P((int, struct peer
*));
107 static void nmea_control
P((int, struct refclockstat
*, struct
108 refclockstat
*, struct peer
*));
109 static int nmea_ppsapi
P((struct peer
*, int, int));
110 static int nmea_pps
P((struct nmeaunit
*, l_fp
*));
111 #endif /* HAVE_PPSAPI */
112 static void nmea_receive
P((struct recvbuf
*));
113 static void nmea_poll
P((int, struct peer
*));
114 static void gps_send
P((int, const char *, struct peer
*));
115 static char *field_parse
P((char *, int));
120 struct refclock refclock_nmea
= {
121 nmea_start
, /* start up driver */
122 nmea_shutdown
, /* shut down driver */
123 nmea_poll
, /* transmit poll message */
125 nmea_control
, /* fudge control */
127 noentry
, /* fudge control */
128 #endif /* HAVE_PPSAPI */
129 noentry
, /* initialize driver */
130 noentry
, /* buginfo */
131 NOFLAGS
/* not used */
135 * nmea_start - open the GPS devices and initialize data for processing
143 register struct nmeaunit
*up
;
144 struct refclockproc
*pp
;
149 * Open serial port. Use CLK line discipline, if available.
151 (void)sprintf(device
, DEVICE
, unit
);
153 fd
= refclock_open(device
, SPEED232
, LDISC_CLK
);
156 /* nmead support added by Jon Miner (cp_n18@yahoo.com)
158 * See http://home.hiwaay.net/~taylorc/gps/nmea-server/
159 * for information about nmead
161 * To use this, you need to create a link from /dev/gpsX to
162 * the server:port where nmead is running. Something like this:
164 * ln -s server:port /dev/gps1
172 struct sockaddr_in so_addr
;
174 if ((len
= readlink(device
,buffer
,sizeof(buffer
))) == -1)
178 if ((nmea_host
= strtok(buffer
,":")) == NULL
)
181 nmea_port
= atoi(strtok(NULL
,":"));
183 if ((he
= gethostbyname(nmea_host
)) == NULL
)
185 if ((p
= getprotobyname("ip")) == NULL
)
187 so_addr
.sin_family
= AF_INET
;
188 so_addr
.sin_port
= htons(nmea_port
);
189 so_addr
.sin_addr
= *((struct in_addr
*) he
->h_addr
);
191 if ((fd
= socket(PF_INET
,SOCK_STREAM
,p
->p_proto
)) == -1)
193 if (connect(fd
,(struct sockaddr
*)&so_addr
,SOCKLEN(&so_addr
)) == -1) {
203 * Allocate and initialize unit structure
205 up
= (struct nmeaunit
*)emalloc(sizeof(struct nmeaunit
));
210 memset((char *)up
, 0, sizeof(struct nmeaunit
));
212 pp
->io
.clock_recv
= nmea_receive
;
213 pp
->io
.srcclock
= (caddr_t
)peer
;
216 if (!io_addclock(&pp
->io
)) {
221 pp
->unitptr
= (caddr_t
)up
;
224 * Initialize miscellaneous variables
226 peer
->precision
= PRECISION
;
227 pp
->clockdesc
= DESCRIPTION
;
228 memcpy((char *)&pp
->refid
, REFID
, 4);
230 gps_send(pp
->io
.fd
,"$PMOTG,RMC,0000*1D\r\n", peer
);
234 * Start the PPSAPI interface if it is there. Default to use
235 * the assert edge and do not enable the kernel hardpps.
237 if (time_pps_create(fd
, &up
->handle
) < 0) {
240 "refclock_nmea: time_pps_create failed: %m");
243 return(nmea_ppsapi(peer
, 0, 0));
246 #endif /* HAVE_PPSAPI */
250 * nmea_shutdown - shut down a GPS clock
258 register struct nmeaunit
*up
;
259 struct refclockproc
*pp
;
262 up
= (struct nmeaunit
*)pp
->unitptr
;
265 time_pps_destroy(up
->handle
);
266 #endif /* HAVE_PPSAPI */
267 io_closeclock(&pp
->io
);
273 * nmea_control - fudge control
277 int unit
, /* unit (not used */
278 struct refclockstat
*in
, /* input parameters (not uded) */
279 struct refclockstat
*out
, /* output parameters (not used) */
280 struct peer
*peer
/* peer structure pointer */
283 struct refclockproc
*pp
;
286 nmea_ppsapi(peer
, pp
->sloppyclockflag
& CLK_FLAG2
,
287 pp
->sloppyclockflag
& CLK_FLAG3
);
296 struct peer
*peer
, /* peer structure pointer */
297 int enb_clear
, /* clear enable */
298 int enb_hardpps
/* hardpps enable */
301 struct refclockproc
*pp
;
306 up
= (struct nmeaunit
*)pp
->unitptr
;
307 if (time_pps_getcap(up
->handle
, &capability
) < 0) {
309 "refclock_nmea: time_pps_getcap failed: %m");
312 memset(&up
->pps_params
, 0, sizeof(pps_params_t
));
314 up
->pps_params
.mode
= capability
& PPS_CAPTURECLEAR
;
316 up
->pps_params
.mode
= capability
& PPS_CAPTUREASSERT
;
317 if (!up
->pps_params
.mode
) {
319 "refclock_nmea: invalid capture edge %d",
323 up
->pps_params
.mode
|= PPS_TSFMT_TSPEC
;
324 if (time_pps_setparams(up
->handle
, &up
->pps_params
) < 0) {
326 "refclock_nmea: time_pps_setparams failed: %m");
330 if (time_pps_kcbind(up
->handle
, PPS_KC_HARDPPS
,
331 up
->pps_params
.mode
& ~PPS_TSFMT_TSPEC
,
332 PPS_TSFMT_TSPEC
) < 0) {
334 "refclock_nmea: time_pps_kcbind failed: %m");
339 peer
->precision
= PPS_PRECISION
;
343 time_pps_getparams(up
->handle
, &up
->pps_params
);
345 "refclock_ppsapi: capability 0x%x version %d mode 0x%x kern %d\n",
346 capability
, up
->pps_params
.api_version
,
347 up
->pps_params
.mode
, enb_hardpps
);
355 * Get PPSAPI timestamps.
357 * Return 0 on failure and 1 on success.
366 struct timespec timeout
, ts
;
371 * Convert the timespec nanoseconds field to ntp l_fp units.
377 memcpy(&pps_info
, &up
->pps_info
, sizeof(pps_info_t
));
378 if (time_pps_fetch(up
->handle
, PPS_TSFMT_TSPEC
, &up
->pps_info
,
381 if (up
->pps_params
.mode
& PPS_CAPTUREASSERT
) {
382 if (pps_info
.assert_sequence
==
383 up
->pps_info
.assert_sequence
)
385 ts
= up
->pps_info
.assert_timestamp
;
386 } else if (up
->pps_params
.mode
& PPS_CAPTURECLEAR
) {
387 if (pps_info
.clear_sequence
==
388 up
->pps_info
.clear_sequence
)
390 ts
= up
->pps_info
.clear_timestamp
;
394 if ((up
->ts
.tv_sec
== ts
.tv_sec
) && (up
->ts
.tv_nsec
== ts
.tv_nsec
))
398 tstmp
.l_ui
= ts
.tv_sec
+ JAN_1970
;
399 dtemp
= ts
.tv_nsec
* FRAC
/ 1e9
;
400 tstmp
.l_uf
= (u_int32
)dtemp
;
404 #endif /* HAVE_PPSAPI */
407 * nmea_receive - receive data from the serial interface
411 struct recvbuf
*rbufp
414 register struct nmeaunit
*up
;
415 struct refclockproc
*pp
;
421 /* Use these variables to hold data until we decide its worth keeping */
422 char rd_lastcode
[BMAX
];
427 * Initialize pointers and read the timecode and timestamp
429 peer
= (struct peer
*)rbufp
->recv_srcclock
;
431 up
= (struct nmeaunit
*)pp
->unitptr
;
432 rd_lencode
= (u_short
)refclock_gtlin(rbufp
, rd_lastcode
, BMAX
, &rd_tmp
);
435 * There is a case that a <CR><LF> gives back a "blank" line
442 printf("nmea: gpsread %d %s\n", rd_lencode
,
447 * We check the timecode format and decode its contents. The
448 * we only care about a few of them. The most important being
450 * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
451 * For Magellan (ColorTrak) GLL probably datum (order of sentences)
452 * also mode (0,1,2,3) select sentence ANY/ALL, RMC, GGA, GLL
453 * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21
454 * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F
456 * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77
468 if(strncmp(cp
,"$GPRMC",6)==0) {
471 else if(strncmp(cp
,"$GPGGA",6)==0) {
474 else if(strncmp(cp
,"$GPGLL",6)==0) {
477 else if(strncmp(cp
,"$GPXXX",6)==0) {
484 /* See if I want to process this message type */
485 if ( ((peer
->ttl
== 0) && (cmdtype
!= GPRMC
))
486 || ((peer
->ttl
!= 0) && !(cmdtype
& peer
->ttl
)) )
489 pp
->lencode
= rd_lencode
;
490 strcpy(pp
->a_lastcode
,rd_lastcode
);
493 pp
->lastrec
= up
->tstamp
= rd_tmp
;
498 printf("nmea: timecode %d %s\n", pp
->lencode
,
503 /* Grab field depending on clock string type */
507 * Test for synchronization. Check for quality byte.
509 dp
= field_parse(cp
,2);
511 pp
->leap
= LEAP_NOTINSYNC
;
513 pp
->leap
= LEAP_NOWARNING
;
515 /* Now point at the time field */
516 dp
= field_parse(cp
,1);
522 * Test for synchronization. Check for quality byte.
524 dp
= field_parse(cp
,6);
526 pp
->leap
= LEAP_NOTINSYNC
;
528 pp
->leap
= LEAP_NOWARNING
;
530 /* Now point at the time field */
531 dp
= field_parse(cp
,1);
537 * Test for synchronization. Check for quality byte.
539 dp
= field_parse(cp
,6);
541 pp
->leap
= LEAP_NOTINSYNC
;
543 pp
->leap
= LEAP_NOWARNING
;
545 /* Now point at the time field */
546 dp
= field_parse(cp
,5);
558 * Check time code format of NMEA
561 if( !isdigit((int)dp
[0]) ||
562 !isdigit((int)dp
[1]) ||
563 !isdigit((int)dp
[2]) ||
564 !isdigit((int)dp
[3]) ||
565 !isdigit((int)dp
[4]) ||
568 refclock_report(peer
, CEVNT_BADREPLY
);
574 * Convert time and check values.
576 pp
->hour
= ((dp
[0] - '0') * 10) + dp
[1] - '0';
577 pp
->minute
= ((dp
[2] - '0') * 10) + dp
[3] - '0';
578 pp
->second
= ((dp
[4] - '0') * 10) + dp
[5] - '0';
579 /* Default to 0 milliseconds, if decimal convert milliseconds in
580 one, two or three digits
584 if (isdigit((int)dp
[7])) {
585 pp
->nsec
= (dp
[7] - '0') * 100000000;
586 if (isdigit((int)dp
[8])) {
587 pp
->nsec
+= (dp
[8] - '0') * 10000000;
588 if (isdigit((int)dp
[9])) {
589 pp
->nsec
+= (dp
[9] - '0') * 1000000;
595 if (pp
->hour
> 23 || pp
->minute
> 59 || pp
->second
> 59
596 || pp
->nsec
> 1000000000) {
597 refclock_report(peer
, CEVNT_BADTIME
);
603 * Convert date and check values.
605 if (cmdtype
==GPRMC
) {
606 dp
= field_parse(cp
,9);
608 day
= (day
* 10) + dp
[1] - '0';
610 month
= (month
* 10) + dp
[3] - '0';
611 pp
->year
= dp
[4] - '0';
612 pp
->year
= (pp
->year
* 10) + dp
[5] - '0';
616 time_t tt
= time(NULL
);
617 struct tm
* t
= gmtime(&tt
);
619 month
= t
->tm_mon
+ 1;
620 pp
->year
= t
->tm_year
;
623 if (month
< 1 || month
> 12 || day
< 1) {
624 refclock_report(peer
, CEVNT_BADTIME
);
628 /* Hmmmm this will be a nono for 2100,2200,2300 but I don't think I'll be here */
629 /* good thing that 2000 is a leap year */
630 /* pp->year will be 00-99 if read from GPS, 00-> (years since 1900) from tm_year */
632 if (day
> day1tab
[month
- 1]) {
633 refclock_report(peer
, CEVNT_BADTIME
);
636 for (i
= 0; i
< month
- 1; i
++)
639 if (day
> day2tab
[month
- 1]) {
640 refclock_report(peer
, CEVNT_BADTIME
);
643 for (i
= 0; i
< month
- 1; i
++)
651 * If the PPSAPI is working, rather use its timestamps.
652 * assume that the PPS occurs on the second so blow any msec
654 if (nmea_pps(up
, &rd_tmp
) == 1) {
655 pp
->lastrec
= up
->tstamp
= rd_tmp
;
658 #endif /* HAVE_PPSAPI */
661 * Process the new sample in the median filter and determine the
662 * reference clock offset and dispersion. We use lastrec as both
663 * the reference time and receive time, in order to avoid being
664 * cute, like setting the reference time later than the receive
665 * time, which may cause a paranoid protocol module to chuck out
669 if (!refclock_process(pp
)) {
670 refclock_report(peer
, CEVNT_BADTIME
);
677 * Only go on if we had been polled.
682 pp
->lastref
= pp
->lastrec
;
683 refclock_receive(peer
);
685 /* If we get here - what we got from the clock is OK, so say so */
686 refclock_report(peer
, CEVNT_NOMINAL
);
688 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
693 * nmea_poll - called by the transmit procedure
695 * We go to great pains to avoid changing state here, since there may be
696 * more than one eavesdropper receiving the same timecode.
704 register struct nmeaunit
*up
;
705 struct refclockproc
*pp
;
708 up
= (struct nmeaunit
*)pp
->unitptr
;
709 if (up
->pollcnt
== 0)
710 refclock_report(peer
, CEVNT_TIMEOUT
);
717 * usually nmea_receive can get a timestamp every second
720 gps_send(pp
->io
.fd
,"$PMOTG,RMC,0000*1D\r\n", peer
);
725 * gps_send(fd,cmd, peer) Sends a command to the GPS receiver.
726 * as gps_send(fd,"rqts,u\r", peer);
728 * We don't currently send any data, but would like to send
729 * RTCM SC104 messages for differential positioning. It should
730 * also give us better time. Without a PPS output, we're
731 * Just fooling ourselves because of the serial code paths
742 if (write(fd
, cmd
, strlen(cmd
)) == -1) {
743 refclock_report(peer
, CEVNT_FAULT
);
756 for (tp
= cp
; *tp
!= '\0'; tp
++) {
765 int refclock_nmea_bs
;
766 #endif /* REFCLOCK */