Deleted Added
full compact
refclock_nmea.c (182007) refclock_nmea.c (200576)
1/*
2 * refclock_nmea.c - clock driver for an NMEA GPS CLOCK
3 * Michael Petry Jun 20, 1994
4 * based on refclock_heathn.c
5 */
6#ifdef HAVE_CONFIG_H
7#include <config.h>
8#endif
9
1/*
2 * refclock_nmea.c - clock driver for an NMEA GPS CLOCK
3 * Michael Petry Jun 20, 1994
4 * based on refclock_heathn.c
5 */
6#ifdef HAVE_CONFIG_H
7#include <config.h>
8#endif
9
10#if defined(SYS_WINNT)
11#undef close
12#define close closesocket
13#endif
14
15#if defined(REFCLOCK) && defined(CLOCK_NMEA)
16
17#include <stdio.h>
18#include <ctype.h>
19
20#include "ntpd.h"
21#include "ntp_io.h"
22#include "ntp_unixtime.h"
23#include "ntp_refclock.h"
24#include "ntp_stdlib.h"
25
26#ifdef HAVE_PPSAPI
27# include "ppsapi_timepps.h"
28#endif /* HAVE_PPSAPI */
29
10#if defined(REFCLOCK) && defined(CLOCK_NMEA)
11
12#include <stdio.h>
13#include <ctype.h>
14
15#include "ntpd.h"
16#include "ntp_io.h"
17#include "ntp_unixtime.h"
18#include "ntp_refclock.h"
19#include "ntp_stdlib.h"
20
21#ifdef HAVE_PPSAPI
22# include "ppsapi_timepps.h"
23#endif /* HAVE_PPSAPI */
24
25#ifdef SYS_WINNT
26extern int async_write(int, const void *, unsigned int);
27#undef write
28#define write(fd, data, octets) async_write(fd, data, octets)
29#endif
30
30/*
31 * This driver supports the NMEA GPS Receiver with
32 *
33 * Protype was refclock_trak.c, Thanks a lot.
34 *
35 * The receiver used spits out the NMEA sentences for boat navigation.
36 * And you thought it was an information superhighway. Try a raging river
37 * filled with rapids and whirlpools that rip away your data and warp time.
38 *
39 * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in.
40 * On startup if initialization of the PPSAPI fails, it will fall back
41 * to the "normal" timestamps.
42 *
43 * The PPSAPI part of the driver understands fudge flag2 and flag3. If
44 * flag2 is set, it will use the clear edge of the pulse. If flag3 is
45 * set, kernel hardpps is enabled.
46 *
47 * GPS sentences other than RMC (the default) may be enabled by setting
48 * the relevent bits of 'mode' in the server configuration line
49 * server 127.127.20.x mode X
50 *
51 * bit 0 - enables RMC (1)
52 * bit 1 - enables GGA (2)
53 * bit 2 - enables GLL (4)
54 * multiple sentences may be selected
55 */
56
57/*
58 * Definitions
59 */
60#ifdef SYS_WINNT
61# define DEVICE "COM%d:" /* COM 1 - 3 supported */
62#else
63# define DEVICE "/dev/gps%d" /* name of radio device */
64#endif
65#define SPEED232 B4800 /* uart speed (4800 bps) */
66#define PRECISION (-9) /* precision assumed (about 2 ms) */
67#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
68#define REFID "GPS\0" /* reference id */
69#define DESCRIPTION "NMEA GPS Clock" /* who we are */
70#define NANOSECOND 1000000000 /* one second (ns) */
71#define RANGEGATE 500000 /* range gate (ns) */
72
73#define LENNMEA 75 /* min timecode length */
74
75/*
76 * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
77 * leap.
78 */
79static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
80static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
81
82/*
83 * Unit control structure
84 */
85struct nmeaunit {
86 int pollcnt; /* poll message counter */
87 int polled; /* Hand in a sample? */
88 l_fp tstamp; /* timestamp of last poll */
89#ifdef HAVE_PPSAPI
90 struct timespec ts; /* last timestamp */
91 pps_params_t pps_params; /* pps parameters */
92 pps_info_t pps_info; /* last pps data */
93 pps_handle_t handle; /* pps handlebars */
94#endif /* HAVE_PPSAPI */
95};
96
97/*
98 * Function prototypes
99 */
100static int nmea_start P((int, struct peer *));
101static void nmea_shutdown P((int, struct peer *));
102#ifdef HAVE_PPSAPI
103static void nmea_control P((int, struct refclockstat *, struct
104 refclockstat *, struct peer *));
105static int nmea_ppsapi P((struct peer *, int, int));
106static int nmea_pps P((struct nmeaunit *, l_fp *));
107#endif /* HAVE_PPSAPI */
108static void nmea_receive P((struct recvbuf *));
109static void nmea_poll P((int, struct peer *));
110static void gps_send P((int, const char *, struct peer *));
111static char *field_parse P((char *, int));
112
113/*
114 * Transfer vector
115 */
116struct refclock refclock_nmea = {
117 nmea_start, /* start up driver */
118 nmea_shutdown, /* shut down driver */
119 nmea_poll, /* transmit poll message */
120#ifdef HAVE_PPSAPI
121 nmea_control, /* fudge control */
122#else
123 noentry, /* fudge control */
124#endif /* HAVE_PPSAPI */
125 noentry, /* initialize driver */
126 noentry, /* buginfo */
127 NOFLAGS /* not used */
128};
129
130/*
131 * nmea_start - open the GPS devices and initialize data for processing
132 */
133static int
134nmea_start(
135 int unit,
136 struct peer *peer
137 )
138{
139 register struct nmeaunit *up;
140 struct refclockproc *pp;
141 int fd;
142 char device[20];
143
144 /*
145 * Open serial port. Use CLK line discipline, if available.
146 */
147 (void)sprintf(device, DEVICE, unit);
148
149 fd = refclock_open(device, SPEED232, LDISC_CLK);
150 if (fd <= 0) {
151#ifdef HAVE_READLINK
152 /* nmead support added by Jon Miner (cp_n18@yahoo.com)
153 *
154 * See http://home.hiwaay.net/~taylorc/gps/nmea-server/
155 * for information about nmead
156 *
157 * To use this, you need to create a link from /dev/gpsX to
158 * the server:port where nmead is running. Something like this:
159 *
160 * ln -s server:port /dev/gps1
161 */
162 char buffer[80];
163 char *nmea_host;
164 int nmea_port;
165 int len;
166 struct hostent *he;
167 struct protoent *p;
168 struct sockaddr_in so_addr;
169
170 if ((len = readlink(device,buffer,sizeof(buffer))) == -1)
171 return(0);
172 buffer[len] = 0;
173
174 if ((nmea_host = strtok(buffer,":")) == NULL)
175 return(0);
176
177 nmea_port = atoi(strtok(NULL,":"));
178
179 if ((he = gethostbyname(nmea_host)) == NULL)
180 return(0);
181 if ((p = getprotobyname("ip")) == NULL)
182 return(0);
183 so_addr.sin_family = AF_INET;
184 so_addr.sin_port = htons(nmea_port);
185 so_addr.sin_addr = *((struct in_addr *) he->h_addr);
186
187 if ((fd = socket(PF_INET,SOCK_STREAM,p->p_proto)) == -1)
188 return(0);
189 if (connect(fd,(struct sockaddr *)&so_addr,SOCKLEN(&so_addr)) == -1) {
190 close(fd);
191 return (0);
192 }
193#else
194 return (0);
195#endif
196 }
197
198 /*
199 * Allocate and initialize unit structure
200 */
201 up = (struct nmeaunit *)emalloc(sizeof(struct nmeaunit));
202 if (up == NULL) {
203 (void) close(fd);
204 return (0);
205 }
206 memset((char *)up, 0, sizeof(struct nmeaunit));
207 pp = peer->procptr;
208 pp->io.clock_recv = nmea_receive;
209 pp->io.srcclock = (caddr_t)peer;
210 pp->io.datalen = 0;
211 pp->io.fd = fd;
212 if (!io_addclock(&pp->io)) {
213 (void) close(fd);
214 free(up);
215 return (0);
216 }
217 pp->unitptr = (caddr_t)up;
218
219 /*
220 * Initialize miscellaneous variables
221 */
222 peer->precision = PRECISION;
223 pp->clockdesc = DESCRIPTION;
224 memcpy((char *)&pp->refid, REFID, 4);
225 up->pollcnt = 2;
226 gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
227
228#ifdef HAVE_PPSAPI
229 /*
230 * Start the PPSAPI interface if it is there. Default to use
231 * the assert edge and do not enable the kernel hardpps.
232 */
233 if (time_pps_create(fd, &up->handle) < 0) {
234 up->handle = 0;
235 msyslog(LOG_ERR,
236 "refclock_nmea: time_pps_create failed: %m");
237 return (1);
238 }
239 return(nmea_ppsapi(peer, 0, 0));
240#else
241 return (1);
242#endif /* HAVE_PPSAPI */
243}
244
245/*
246 * nmea_shutdown - shut down a GPS clock
247 */
248static void
249nmea_shutdown(
250 int unit,
251 struct peer *peer
252 )
253{
254 register struct nmeaunit *up;
255 struct refclockproc *pp;
256
257 pp = peer->procptr;
258 up = (struct nmeaunit *)pp->unitptr;
259#ifdef HAVE_PPSAPI
260 if (up->handle != 0)
261 time_pps_destroy(up->handle);
262#endif /* HAVE_PPSAPI */
263 io_closeclock(&pp->io);
264 free(up);
265}
266
267#ifdef HAVE_PPSAPI
268/*
269 * nmea_control - fudge control
270 */
271static void
272nmea_control(
273 int unit, /* unit (not used */
274 struct refclockstat *in, /* input parameters (not uded) */
275 struct refclockstat *out, /* output parameters (not used) */
276 struct peer *peer /* peer structure pointer */
277 )
278{
279 struct refclockproc *pp;
280
281 pp = peer->procptr;
282 nmea_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
283 pp->sloppyclockflag & CLK_FLAG3);
284}
285
286
287/*
288 * Initialize PPSAPI
289 */
290int
291nmea_ppsapi(
292 struct peer *peer, /* peer structure pointer */
293 int enb_clear, /* clear enable */
294 int enb_hardpps /* hardpps enable */
295 )
296{
297 struct refclockproc *pp;
298 struct nmeaunit *up;
299 int capability;
300
301 pp = peer->procptr;
302 up = (struct nmeaunit *)pp->unitptr;
303 if (time_pps_getcap(up->handle, &capability) < 0) {
304 msyslog(LOG_ERR,
305 "refclock_nmea: time_pps_getcap failed: %m");
306 return (0);
307 }
308 memset(&up->pps_params, 0, sizeof(pps_params_t));
309 if (enb_clear)
310 up->pps_params.mode = capability & PPS_CAPTURECLEAR;
311 else
312 up->pps_params.mode = capability & PPS_CAPTUREASSERT;
313 if (!up->pps_params.mode) {
314 msyslog(LOG_ERR,
315 "refclock_nmea: invalid capture edge %d",
316 !enb_clear);
317 return (0);
318 }
319 up->pps_params.mode |= PPS_TSFMT_TSPEC;
320 if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
321 msyslog(LOG_ERR,
322 "refclock_nmea: time_pps_setparams failed: %m");
323 return (0);
324 }
325 if (enb_hardpps) {
326 if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
327 up->pps_params.mode & ~PPS_TSFMT_TSPEC,
328 PPS_TSFMT_TSPEC) < 0) {
329 msyslog(LOG_ERR,
330 "refclock_nmea: time_pps_kcbind failed: %m");
331 return (0);
332 }
333 pps_enable = 1;
334 }
335 peer->precision = PPS_PRECISION;
336
337#if DEBUG
338 if (debug) {
339 time_pps_getparams(up->handle, &up->pps_params);
340 printf(
341 "refclock_ppsapi: capability 0x%x version %d mode 0x%x kern %d\n",
342 capability, up->pps_params.api_version,
343 up->pps_params.mode, enb_hardpps);
344 }
345#endif
346
347 return (1);
348}
349
350/*
351 * Get PPSAPI timestamps.
352 *
353 * Return 0 on failure and 1 on success.
354 */
355static int
356nmea_pps(
357 struct nmeaunit *up,
358 l_fp *tsptr
359 )
360{
361 pps_info_t pps_info;
362 struct timespec timeout, ts;
363 double dtemp;
364 l_fp tstmp;
365
366 /*
367 * Convert the timespec nanoseconds field to ntp l_fp units.
368 */
369 if (up->handle == 0)
370 return (0);
371 timeout.tv_sec = 0;
372 timeout.tv_nsec = 0;
373 memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
374 if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
375 &timeout) < 0)
376 return (0);
377 if (up->pps_params.mode & PPS_CAPTUREASSERT) {
378 if (pps_info.assert_sequence ==
379 up->pps_info.assert_sequence)
380 return (0);
381 ts = up->pps_info.assert_timestamp;
382 } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
383 if (pps_info.clear_sequence ==
384 up->pps_info.clear_sequence)
385 return (0);
386 ts = up->pps_info.clear_timestamp;
387 } else {
388 return (0);
389 }
390 if ((up->ts.tv_sec == ts.tv_sec) && (up->ts.tv_nsec == ts.tv_nsec))
391 return (0);
392 up->ts = ts;
393
394 tstmp.l_ui = ts.tv_sec + JAN_1970;
395 dtemp = ts.tv_nsec * FRAC / 1e9;
396 tstmp.l_uf = (u_int32)dtemp;
397 *tsptr = tstmp;
398 return (1);
399}
400#endif /* HAVE_PPSAPI */
401
402/*
403 * nmea_receive - receive data from the serial interface
404 */
405static void
406nmea_receive(
407 struct recvbuf *rbufp
408 )
409{
410 register struct nmeaunit *up;
411 struct refclockproc *pp;
412 struct peer *peer;
413 int month, day;
414 int i;
415 char *cp, *dp;
416 int cmdtype;
417 /* Use these variables to hold data until we decide its worth keeping */
418 char rd_lastcode[BMAX];
419 l_fp rd_tmp;
420 u_short rd_lencode;
421
422 /*
423 * Initialize pointers and read the timecode and timestamp
424 */
425 peer = (struct peer *)rbufp->recv_srcclock;
426 pp = peer->procptr;
427 up = (struct nmeaunit *)pp->unitptr;
428 rd_lencode = (u_short)refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
429
430 /*
431 * There is a case that a <CR><LF> gives back a "blank" line
432 */
433 if (rd_lencode == 0)
434 return;
435
436#ifdef DEBUG
437 if (debug)
438 printf("nmea: gpsread %d %s\n", rd_lencode,
439 rd_lastcode);
440#endif
441
442 /*
443 * We check the timecode format and decode its contents. The
444 * we only care about a few of them. The most important being
445 * the $GPRMC format
446 * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
447 * For Magellan (ColorTrak) GLL probably datum (order of sentences)
448 * also mode (0,1,2,3) select sentence ANY/ALL, RMC, GGA, GLL
449 * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21
450 * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F
451 * $GPRMB,...
452 * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77
453 * $GPAPB,...
454 * $GPGSA,...
455 * $GPGSV,...
456 * $GPGSV,...
457 */
458#define GPXXX 0
459#define GPRMC 1
460#define GPGGA 2
461#define GPGLL 4
462 cp = rd_lastcode;
463 cmdtype=0;
464 if(strncmp(cp,"$GPRMC",6)==0) {
465 cmdtype=GPRMC;
466 }
467 else if(strncmp(cp,"$GPGGA",6)==0) {
468 cmdtype=GPGGA;
469 }
470 else if(strncmp(cp,"$GPGLL",6)==0) {
471 cmdtype=GPGLL;
472 }
473 else if(strncmp(cp,"$GPXXX",6)==0) {
474 cmdtype=GPXXX;
475 }
476 else
477 return;
478
479
480 /* See if I want to process this message type */
481 if ( ((peer->ttl == 0) && (cmdtype != GPRMC))
482 || ((peer->ttl != 0) && !(cmdtype & peer->ttl)) )
483 return;
484
485 pp->lencode = rd_lencode;
486 strcpy(pp->a_lastcode,rd_lastcode);
487 cp = pp->a_lastcode;
488
489 pp->lastrec = up->tstamp = rd_tmp;
490 up->pollcnt = 2;
491
492#ifdef DEBUG
493 if (debug)
494 printf("nmea: timecode %d %s\n", pp->lencode,
495 pp->a_lastcode);
496#endif
497
498
499 /* Grab field depending on clock string type */
500 switch( cmdtype ) {
501 case GPRMC:
502 /*
503 * Test for synchronization. Check for quality byte.
504 */
505 dp = field_parse(cp,2);
506 if( dp[0] != 'A')
507 pp->leap = LEAP_NOTINSYNC;
508 else
509 pp->leap = LEAP_NOWARNING;
510
511 /* Now point at the time field */
512 dp = field_parse(cp,1);
513 break;
514
515
516 case GPGGA:
517 /*
518 * Test for synchronization. Check for quality byte.
519 */
520 dp = field_parse(cp,6);
521 if( dp[0] == '0')
522 pp->leap = LEAP_NOTINSYNC;
523 else
524 pp->leap = LEAP_NOWARNING;
525
526 /* Now point at the time field */
527 dp = field_parse(cp,1);
528 break;
529
530
531 case GPGLL:
532 /*
533 * Test for synchronization. Check for quality byte.
534 */
535 dp = field_parse(cp,6);
536 if( dp[0] != 'A')
537 pp->leap = LEAP_NOTINSYNC;
538 else
539 pp->leap = LEAP_NOWARNING;
540
541 /* Now point at the time field */
542 dp = field_parse(cp,5);
543 break;
544
545
546 case GPXXX:
547 return;
548 default:
549 return;
550
551 }
552
553 /*
554 * Check time code format of NMEA
555 */
556
557 if( !isdigit((int)dp[0]) ||
558 !isdigit((int)dp[1]) ||
559 !isdigit((int)dp[2]) ||
560 !isdigit((int)dp[3]) ||
561 !isdigit((int)dp[4]) ||
562 !isdigit((int)dp[5])
563 ) {
564 refclock_report(peer, CEVNT_BADREPLY);
565 return;
566 }
567
568
569 /*
570 * Convert time and check values.
571 */
572 pp->hour = ((dp[0] - '0') * 10) + dp[1] - '0';
573 pp->minute = ((dp[2] - '0') * 10) + dp[3] - '0';
574 pp->second = ((dp[4] - '0') * 10) + dp[5] - '0';
575 /* Default to 0 milliseconds, if decimal convert milliseconds in
576 one, two or three digits
577 */
578 pp->nsec = 0;
579 if (dp[6] == '.') {
580 if (isdigit((int)dp[7])) {
581 pp->nsec = (dp[7] - '0') * 100000000;
582 if (isdigit((int)dp[8])) {
583 pp->nsec += (dp[8] - '0') * 10000000;
584 if (isdigit((int)dp[9])) {
585 pp->nsec += (dp[9] - '0') * 1000000;
586 }
587 }
588 }
589 }
590
591 if (pp->hour > 23 || pp->minute > 59 || pp->second > 59
592 || pp->nsec > 1000000000) {
593 refclock_report(peer, CEVNT_BADTIME);
594 return;
595 }
596
597
598 /*
599 * Convert date and check values.
600 */
601 if (cmdtype==GPRMC) {
602 dp = field_parse(cp,9);
603 day = dp[0] - '0';
604 day = (day * 10) + dp[1] - '0';
605 month = dp[2] - '0';
606 month = (month * 10) + dp[3] - '0';
607 pp->year = dp[4] - '0';
608 pp->year = (pp->year * 10) + dp[5] - '0';
609 }
610 else {
611 /* only time */
612 time_t tt = time(NULL);
613 struct tm * t = gmtime(&tt);
614 day = t->tm_mday;
615 month = t->tm_mon + 1;
616 pp->year= t->tm_year;
617 }
618
619 if (month < 1 || month > 12 || day < 1) {
620 refclock_report(peer, CEVNT_BADTIME);
621 return;
622 }
623
624 /* Hmmmm this will be a nono for 2100,2200,2300 but I don't think I'll be here */
625 /* good thing that 2000 is a leap year */
626 /* pp->year will be 00-99 if read from GPS, 00-> (years since 1900) from tm_year */
627 if (pp->year % 4) {
628 if (day > day1tab[month - 1]) {
629 refclock_report(peer, CEVNT_BADTIME);
630 return;
631 }
632 for (i = 0; i < month - 1; i++)
633 day += day1tab[i];
634 } else {
635 if (day > day2tab[month - 1]) {
636 refclock_report(peer, CEVNT_BADTIME);
637 return;
638 }
639 for (i = 0; i < month - 1; i++)
640 day += day2tab[i];
641 }
642 pp->day = day;
643
644
645#ifdef HAVE_PPSAPI
646 /*
647 * If the PPSAPI is working, rather use its timestamps.
648 * assume that the PPS occurs on the second so blow any msec
649 */
650 if (nmea_pps(up, &rd_tmp) == 1) {
651 pp->lastrec = up->tstamp = rd_tmp;
652 pp->nsec = 0;
653 }
654#endif /* HAVE_PPSAPI */
655
656 /*
657 * Process the new sample in the median filter and determine the
658 * reference clock offset and dispersion. We use lastrec as both
659 * the reference time and receive time, in order to avoid being
660 * cute, like setting the reference time later than the receive
661 * time, which may cause a paranoid protocol module to chuck out
662 * the data.
663 */
664
665 if (!refclock_process(pp)) {
666 refclock_report(peer, CEVNT_BADTIME);
667 return;
668 }
669
670
671
672 /*
673 * Only go on if we had been polled.
674 */
675 if (!up->polled)
676 return;
677 up->polled = 0;
678 pp->lastref = pp->lastrec;
679 refclock_receive(peer);
680
681 /* If we get here - what we got from the clock is OK, so say so */
682 refclock_report(peer, CEVNT_NOMINAL);
683
684 record_clock_stats(&peer->srcadr, pp->a_lastcode);
685
686}
687
688/*
689 * nmea_poll - called by the transmit procedure
690 *
691 * We go to great pains to avoid changing state here, since there may be
692 * more than one eavesdropper receiving the same timecode.
693 */
694static void
695nmea_poll(
696 int unit,
697 struct peer *peer
698 )
699{
700 register struct nmeaunit *up;
701 struct refclockproc *pp;
702
703 pp = peer->procptr;
704 up = (struct nmeaunit *)pp->unitptr;
705 if (up->pollcnt == 0)
706 refclock_report(peer, CEVNT_TIMEOUT);
707 else
708 up->pollcnt--;
709 pp->polls++;
710 up->polled = 1;
711
712 /*
713 * usually nmea_receive can get a timestamp every second
714 */
715
716 gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
717}
718
719/*
720 *
721 * gps_send(fd,cmd, peer) Sends a command to the GPS receiver.
722 * as gps_send(fd,"rqts,u\r", peer);
723 *
724 * We don't currently send any data, but would like to send
725 * RTCM SC104 messages for differential positioning. It should
726 * also give us better time. Without a PPS output, we're
727 * Just fooling ourselves because of the serial code paths
728 *
729 */
730static void
731gps_send(
732 int fd,
733 const char *cmd,
734 struct peer *peer
735 )
736{
737
738 if (write(fd, cmd, strlen(cmd)) == -1) {
739 refclock_report(peer, CEVNT_FAULT);
740 }
741}
742
743static char *
744field_parse(
745 char *cp,
746 int fn
747 )
748{
749 char *tp;
750 int i = fn;
751
752 for (tp = cp; *tp != '\0'; tp++) {
753 if (*tp == ',')
754 i--;
755 if (i == 0)
756 break;
757 }
758 return (++tp);
759}
760#else
761int refclock_nmea_bs;
762#endif /* REFCLOCK */
31/*
32 * This driver supports the NMEA GPS Receiver with
33 *
34 * Protype was refclock_trak.c, Thanks a lot.
35 *
36 * The receiver used spits out the NMEA sentences for boat navigation.
37 * And you thought it was an information superhighway. Try a raging river
38 * filled with rapids and whirlpools that rip away your data and warp time.
39 *
40 * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in.
41 * On startup if initialization of the PPSAPI fails, it will fall back
42 * to the "normal" timestamps.
43 *
44 * The PPSAPI part of the driver understands fudge flag2 and flag3. If
45 * flag2 is set, it will use the clear edge of the pulse. If flag3 is
46 * set, kernel hardpps is enabled.
47 *
48 * GPS sentences other than RMC (the default) may be enabled by setting
49 * the relevent bits of 'mode' in the server configuration line
50 * server 127.127.20.x mode X
51 *
52 * bit 0 - enables RMC (1)
53 * bit 1 - enables GGA (2)
54 * bit 2 - enables GLL (4)
55 * multiple sentences may be selected
56 */
57
58/*
59 * Definitions
60 */
61#ifdef SYS_WINNT
62# define DEVICE "COM%d:" /* COM 1 - 3 supported */
63#else
64# define DEVICE "/dev/gps%d" /* name of radio device */
65#endif
66#define SPEED232 B4800 /* uart speed (4800 bps) */
67#define PRECISION (-9) /* precision assumed (about 2 ms) */
68#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
69#define REFID "GPS\0" /* reference id */
70#define DESCRIPTION "NMEA GPS Clock" /* who we are */
71#define NANOSECOND 1000000000 /* one second (ns) */
72#define RANGEGATE 500000 /* range gate (ns) */
73
74#define LENNMEA 75 /* min timecode length */
75
76/*
77 * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
78 * leap.
79 */
80static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
81static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
82
83/*
84 * Unit control structure
85 */
86struct nmeaunit {
87 int pollcnt; /* poll message counter */
88 int polled; /* Hand in a sample? */
89 l_fp tstamp; /* timestamp of last poll */
90#ifdef HAVE_PPSAPI
91 struct timespec ts; /* last timestamp */
92 pps_params_t pps_params; /* pps parameters */
93 pps_info_t pps_info; /* last pps data */
94 pps_handle_t handle; /* pps handlebars */
95#endif /* HAVE_PPSAPI */
96};
97
98/*
99 * Function prototypes
100 */
101static int nmea_start P((int, struct peer *));
102static void nmea_shutdown P((int, struct peer *));
103#ifdef HAVE_PPSAPI
104static void nmea_control P((int, struct refclockstat *, struct
105 refclockstat *, struct peer *));
106static int nmea_ppsapi P((struct peer *, int, int));
107static int nmea_pps P((struct nmeaunit *, l_fp *));
108#endif /* HAVE_PPSAPI */
109static void nmea_receive P((struct recvbuf *));
110static void nmea_poll P((int, struct peer *));
111static void gps_send P((int, const char *, struct peer *));
112static char *field_parse P((char *, int));
113
114/*
115 * Transfer vector
116 */
117struct refclock refclock_nmea = {
118 nmea_start, /* start up driver */
119 nmea_shutdown, /* shut down driver */
120 nmea_poll, /* transmit poll message */
121#ifdef HAVE_PPSAPI
122 nmea_control, /* fudge control */
123#else
124 noentry, /* fudge control */
125#endif /* HAVE_PPSAPI */
126 noentry, /* initialize driver */
127 noentry, /* buginfo */
128 NOFLAGS /* not used */
129};
130
131/*
132 * nmea_start - open the GPS devices and initialize data for processing
133 */
134static int
135nmea_start(
136 int unit,
137 struct peer *peer
138 )
139{
140 register struct nmeaunit *up;
141 struct refclockproc *pp;
142 int fd;
143 char device[20];
144
145 /*
146 * Open serial port. Use CLK line discipline, if available.
147 */
148 (void)sprintf(device, DEVICE, unit);
149
150 fd = refclock_open(device, SPEED232, LDISC_CLK);
151 if (fd <= 0) {
152#ifdef HAVE_READLINK
153 /* nmead support added by Jon Miner (cp_n18@yahoo.com)
154 *
155 * See http://home.hiwaay.net/~taylorc/gps/nmea-server/
156 * for information about nmead
157 *
158 * To use this, you need to create a link from /dev/gpsX to
159 * the server:port where nmead is running. Something like this:
160 *
161 * ln -s server:port /dev/gps1
162 */
163 char buffer[80];
164 char *nmea_host;
165 int nmea_port;
166 int len;
167 struct hostent *he;
168 struct protoent *p;
169 struct sockaddr_in so_addr;
170
171 if ((len = readlink(device,buffer,sizeof(buffer))) == -1)
172 return(0);
173 buffer[len] = 0;
174
175 if ((nmea_host = strtok(buffer,":")) == NULL)
176 return(0);
177
178 nmea_port = atoi(strtok(NULL,":"));
179
180 if ((he = gethostbyname(nmea_host)) == NULL)
181 return(0);
182 if ((p = getprotobyname("ip")) == NULL)
183 return(0);
184 so_addr.sin_family = AF_INET;
185 so_addr.sin_port = htons(nmea_port);
186 so_addr.sin_addr = *((struct in_addr *) he->h_addr);
187
188 if ((fd = socket(PF_INET,SOCK_STREAM,p->p_proto)) == -1)
189 return(0);
190 if (connect(fd,(struct sockaddr *)&so_addr,SOCKLEN(&so_addr)) == -1) {
191 close(fd);
192 return (0);
193 }
194#else
195 return (0);
196#endif
197 }
198
199 /*
200 * Allocate and initialize unit structure
201 */
202 up = (struct nmeaunit *)emalloc(sizeof(struct nmeaunit));
203 if (up == NULL) {
204 (void) close(fd);
205 return (0);
206 }
207 memset((char *)up, 0, sizeof(struct nmeaunit));
208 pp = peer->procptr;
209 pp->io.clock_recv = nmea_receive;
210 pp->io.srcclock = (caddr_t)peer;
211 pp->io.datalen = 0;
212 pp->io.fd = fd;
213 if (!io_addclock(&pp->io)) {
214 (void) close(fd);
215 free(up);
216 return (0);
217 }
218 pp->unitptr = (caddr_t)up;
219
220 /*
221 * Initialize miscellaneous variables
222 */
223 peer->precision = PRECISION;
224 pp->clockdesc = DESCRIPTION;
225 memcpy((char *)&pp->refid, REFID, 4);
226 up->pollcnt = 2;
227 gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
228
229#ifdef HAVE_PPSAPI
230 /*
231 * Start the PPSAPI interface if it is there. Default to use
232 * the assert edge and do not enable the kernel hardpps.
233 */
234 if (time_pps_create(fd, &up->handle) < 0) {
235 up->handle = 0;
236 msyslog(LOG_ERR,
237 "refclock_nmea: time_pps_create failed: %m");
238 return (1);
239 }
240 return(nmea_ppsapi(peer, 0, 0));
241#else
242 return (1);
243#endif /* HAVE_PPSAPI */
244}
245
246/*
247 * nmea_shutdown - shut down a GPS clock
248 */
249static void
250nmea_shutdown(
251 int unit,
252 struct peer *peer
253 )
254{
255 register struct nmeaunit *up;
256 struct refclockproc *pp;
257
258 pp = peer->procptr;
259 up = (struct nmeaunit *)pp->unitptr;
260#ifdef HAVE_PPSAPI
261 if (up->handle != 0)
262 time_pps_destroy(up->handle);
263#endif /* HAVE_PPSAPI */
264 io_closeclock(&pp->io);
265 free(up);
266}
267
268#ifdef HAVE_PPSAPI
269/*
270 * nmea_control - fudge control
271 */
272static void
273nmea_control(
274 int unit, /* unit (not used */
275 struct refclockstat *in, /* input parameters (not uded) */
276 struct refclockstat *out, /* output parameters (not used) */
277 struct peer *peer /* peer structure pointer */
278 )
279{
280 struct refclockproc *pp;
281
282 pp = peer->procptr;
283 nmea_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
284 pp->sloppyclockflag & CLK_FLAG3);
285}
286
287
288/*
289 * Initialize PPSAPI
290 */
291int
292nmea_ppsapi(
293 struct peer *peer, /* peer structure pointer */
294 int enb_clear, /* clear enable */
295 int enb_hardpps /* hardpps enable */
296 )
297{
298 struct refclockproc *pp;
299 struct nmeaunit *up;
300 int capability;
301
302 pp = peer->procptr;
303 up = (struct nmeaunit *)pp->unitptr;
304 if (time_pps_getcap(up->handle, &capability) < 0) {
305 msyslog(LOG_ERR,
306 "refclock_nmea: time_pps_getcap failed: %m");
307 return (0);
308 }
309 memset(&up->pps_params, 0, sizeof(pps_params_t));
310 if (enb_clear)
311 up->pps_params.mode = capability & PPS_CAPTURECLEAR;
312 else
313 up->pps_params.mode = capability & PPS_CAPTUREASSERT;
314 if (!up->pps_params.mode) {
315 msyslog(LOG_ERR,
316 "refclock_nmea: invalid capture edge %d",
317 !enb_clear);
318 return (0);
319 }
320 up->pps_params.mode |= PPS_TSFMT_TSPEC;
321 if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
322 msyslog(LOG_ERR,
323 "refclock_nmea: time_pps_setparams failed: %m");
324 return (0);
325 }
326 if (enb_hardpps) {
327 if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
328 up->pps_params.mode & ~PPS_TSFMT_TSPEC,
329 PPS_TSFMT_TSPEC) < 0) {
330 msyslog(LOG_ERR,
331 "refclock_nmea: time_pps_kcbind failed: %m");
332 return (0);
333 }
334 pps_enable = 1;
335 }
336 peer->precision = PPS_PRECISION;
337
338#if DEBUG
339 if (debug) {
340 time_pps_getparams(up->handle, &up->pps_params);
341 printf(
342 "refclock_ppsapi: capability 0x%x version %d mode 0x%x kern %d\n",
343 capability, up->pps_params.api_version,
344 up->pps_params.mode, enb_hardpps);
345 }
346#endif
347
348 return (1);
349}
350
351/*
352 * Get PPSAPI timestamps.
353 *
354 * Return 0 on failure and 1 on success.
355 */
356static int
357nmea_pps(
358 struct nmeaunit *up,
359 l_fp *tsptr
360 )
361{
362 pps_info_t pps_info;
363 struct timespec timeout, ts;
364 double dtemp;
365 l_fp tstmp;
366
367 /*
368 * Convert the timespec nanoseconds field to ntp l_fp units.
369 */
370 if (up->handle == 0)
371 return (0);
372 timeout.tv_sec = 0;
373 timeout.tv_nsec = 0;
374 memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
375 if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
376 &timeout) < 0)
377 return (0);
378 if (up->pps_params.mode & PPS_CAPTUREASSERT) {
379 if (pps_info.assert_sequence ==
380 up->pps_info.assert_sequence)
381 return (0);
382 ts = up->pps_info.assert_timestamp;
383 } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
384 if (pps_info.clear_sequence ==
385 up->pps_info.clear_sequence)
386 return (0);
387 ts = up->pps_info.clear_timestamp;
388 } else {
389 return (0);
390 }
391 if ((up->ts.tv_sec == ts.tv_sec) && (up->ts.tv_nsec == ts.tv_nsec))
392 return (0);
393 up->ts = ts;
394
395 tstmp.l_ui = ts.tv_sec + JAN_1970;
396 dtemp = ts.tv_nsec * FRAC / 1e9;
397 tstmp.l_uf = (u_int32)dtemp;
398 *tsptr = tstmp;
399 return (1);
400}
401#endif /* HAVE_PPSAPI */
402
403/*
404 * nmea_receive - receive data from the serial interface
405 */
406static void
407nmea_receive(
408 struct recvbuf *rbufp
409 )
410{
411 register struct nmeaunit *up;
412 struct refclockproc *pp;
413 struct peer *peer;
414 int month, day;
415 int i;
416 char *cp, *dp;
417 int cmdtype;
418 /* Use these variables to hold data until we decide its worth keeping */
419 char rd_lastcode[BMAX];
420 l_fp rd_tmp;
421 u_short rd_lencode;
422
423 /*
424 * Initialize pointers and read the timecode and timestamp
425 */
426 peer = (struct peer *)rbufp->recv_srcclock;
427 pp = peer->procptr;
428 up = (struct nmeaunit *)pp->unitptr;
429 rd_lencode = (u_short)refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
430
431 /*
432 * There is a case that a <CR><LF> gives back a "blank" line
433 */
434 if (rd_lencode == 0)
435 return;
436
437#ifdef DEBUG
438 if (debug)
439 printf("nmea: gpsread %d %s\n", rd_lencode,
440 rd_lastcode);
441#endif
442
443 /*
444 * We check the timecode format and decode its contents. The
445 * we only care about a few of them. The most important being
446 * the $GPRMC format
447 * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
448 * For Magellan (ColorTrak) GLL probably datum (order of sentences)
449 * also mode (0,1,2,3) select sentence ANY/ALL, RMC, GGA, GLL
450 * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21
451 * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F
452 * $GPRMB,...
453 * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77
454 * $GPAPB,...
455 * $GPGSA,...
456 * $GPGSV,...
457 * $GPGSV,...
458 */
459#define GPXXX 0
460#define GPRMC 1
461#define GPGGA 2
462#define GPGLL 4
463 cp = rd_lastcode;
464 cmdtype=0;
465 if(strncmp(cp,"$GPRMC",6)==0) {
466 cmdtype=GPRMC;
467 }
468 else if(strncmp(cp,"$GPGGA",6)==0) {
469 cmdtype=GPGGA;
470 }
471 else if(strncmp(cp,"$GPGLL",6)==0) {
472 cmdtype=GPGLL;
473 }
474 else if(strncmp(cp,"$GPXXX",6)==0) {
475 cmdtype=GPXXX;
476 }
477 else
478 return;
479
480
481 /* See if I want to process this message type */
482 if ( ((peer->ttl == 0) && (cmdtype != GPRMC))
483 || ((peer->ttl != 0) && !(cmdtype & peer->ttl)) )
484 return;
485
486 pp->lencode = rd_lencode;
487 strcpy(pp->a_lastcode,rd_lastcode);
488 cp = pp->a_lastcode;
489
490 pp->lastrec = up->tstamp = rd_tmp;
491 up->pollcnt = 2;
492
493#ifdef DEBUG
494 if (debug)
495 printf("nmea: timecode %d %s\n", pp->lencode,
496 pp->a_lastcode);
497#endif
498
499
500 /* Grab field depending on clock string type */
501 switch( cmdtype ) {
502 case GPRMC:
503 /*
504 * Test for synchronization. Check for quality byte.
505 */
506 dp = field_parse(cp,2);
507 if( dp[0] != 'A')
508 pp->leap = LEAP_NOTINSYNC;
509 else
510 pp->leap = LEAP_NOWARNING;
511
512 /* Now point at the time field */
513 dp = field_parse(cp,1);
514 break;
515
516
517 case GPGGA:
518 /*
519 * Test for synchronization. Check for quality byte.
520 */
521 dp = field_parse(cp,6);
522 if( dp[0] == '0')
523 pp->leap = LEAP_NOTINSYNC;
524 else
525 pp->leap = LEAP_NOWARNING;
526
527 /* Now point at the time field */
528 dp = field_parse(cp,1);
529 break;
530
531
532 case GPGLL:
533 /*
534 * Test for synchronization. Check for quality byte.
535 */
536 dp = field_parse(cp,6);
537 if( dp[0] != 'A')
538 pp->leap = LEAP_NOTINSYNC;
539 else
540 pp->leap = LEAP_NOWARNING;
541
542 /* Now point at the time field */
543 dp = field_parse(cp,5);
544 break;
545
546
547 case GPXXX:
548 return;
549 default:
550 return;
551
552 }
553
554 /*
555 * Check time code format of NMEA
556 */
557
558 if( !isdigit((int)dp[0]) ||
559 !isdigit((int)dp[1]) ||
560 !isdigit((int)dp[2]) ||
561 !isdigit((int)dp[3]) ||
562 !isdigit((int)dp[4]) ||
563 !isdigit((int)dp[5])
564 ) {
565 refclock_report(peer, CEVNT_BADREPLY);
566 return;
567 }
568
569
570 /*
571 * Convert time and check values.
572 */
573 pp->hour = ((dp[0] - '0') * 10) + dp[1] - '0';
574 pp->minute = ((dp[2] - '0') * 10) + dp[3] - '0';
575 pp->second = ((dp[4] - '0') * 10) + dp[5] - '0';
576 /* Default to 0 milliseconds, if decimal convert milliseconds in
577 one, two or three digits
578 */
579 pp->nsec = 0;
580 if (dp[6] == '.') {
581 if (isdigit((int)dp[7])) {
582 pp->nsec = (dp[7] - '0') * 100000000;
583 if (isdigit((int)dp[8])) {
584 pp->nsec += (dp[8] - '0') * 10000000;
585 if (isdigit((int)dp[9])) {
586 pp->nsec += (dp[9] - '0') * 1000000;
587 }
588 }
589 }
590 }
591
592 if (pp->hour > 23 || pp->minute > 59 || pp->second > 59
593 || pp->nsec > 1000000000) {
594 refclock_report(peer, CEVNT_BADTIME);
595 return;
596 }
597
598
599 /*
600 * Convert date and check values.
601 */
602 if (cmdtype==GPRMC) {
603 dp = field_parse(cp,9);
604 day = dp[0] - '0';
605 day = (day * 10) + dp[1] - '0';
606 month = dp[2] - '0';
607 month = (month * 10) + dp[3] - '0';
608 pp->year = dp[4] - '0';
609 pp->year = (pp->year * 10) + dp[5] - '0';
610 }
611 else {
612 /* only time */
613 time_t tt = time(NULL);
614 struct tm * t = gmtime(&tt);
615 day = t->tm_mday;
616 month = t->tm_mon + 1;
617 pp->year= t->tm_year;
618 }
619
620 if (month < 1 || month > 12 || day < 1) {
621 refclock_report(peer, CEVNT_BADTIME);
622 return;
623 }
624
625 /* Hmmmm this will be a nono for 2100,2200,2300 but I don't think I'll be here */
626 /* good thing that 2000 is a leap year */
627 /* pp->year will be 00-99 if read from GPS, 00-> (years since 1900) from tm_year */
628 if (pp->year % 4) {
629 if (day > day1tab[month - 1]) {
630 refclock_report(peer, CEVNT_BADTIME);
631 return;
632 }
633 for (i = 0; i < month - 1; i++)
634 day += day1tab[i];
635 } else {
636 if (day > day2tab[month - 1]) {
637 refclock_report(peer, CEVNT_BADTIME);
638 return;
639 }
640 for (i = 0; i < month - 1; i++)
641 day += day2tab[i];
642 }
643 pp->day = day;
644
645
646#ifdef HAVE_PPSAPI
647 /*
648 * If the PPSAPI is working, rather use its timestamps.
649 * assume that the PPS occurs on the second so blow any msec
650 */
651 if (nmea_pps(up, &rd_tmp) == 1) {
652 pp->lastrec = up->tstamp = rd_tmp;
653 pp->nsec = 0;
654 }
655#endif /* HAVE_PPSAPI */
656
657 /*
658 * Process the new sample in the median filter and determine the
659 * reference clock offset and dispersion. We use lastrec as both
660 * the reference time and receive time, in order to avoid being
661 * cute, like setting the reference time later than the receive
662 * time, which may cause a paranoid protocol module to chuck out
663 * the data.
664 */
665
666 if (!refclock_process(pp)) {
667 refclock_report(peer, CEVNT_BADTIME);
668 return;
669 }
670
671
672
673 /*
674 * Only go on if we had been polled.
675 */
676 if (!up->polled)
677 return;
678 up->polled = 0;
679 pp->lastref = pp->lastrec;
680 refclock_receive(peer);
681
682 /* If we get here - what we got from the clock is OK, so say so */
683 refclock_report(peer, CEVNT_NOMINAL);
684
685 record_clock_stats(&peer->srcadr, pp->a_lastcode);
686
687}
688
689/*
690 * nmea_poll - called by the transmit procedure
691 *
692 * We go to great pains to avoid changing state here, since there may be
693 * more than one eavesdropper receiving the same timecode.
694 */
695static void
696nmea_poll(
697 int unit,
698 struct peer *peer
699 )
700{
701 register struct nmeaunit *up;
702 struct refclockproc *pp;
703
704 pp = peer->procptr;
705 up = (struct nmeaunit *)pp->unitptr;
706 if (up->pollcnt == 0)
707 refclock_report(peer, CEVNT_TIMEOUT);
708 else
709 up->pollcnt--;
710 pp->polls++;
711 up->polled = 1;
712
713 /*
714 * usually nmea_receive can get a timestamp every second
715 */
716
717 gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
718}
719
720/*
721 *
722 * gps_send(fd,cmd, peer) Sends a command to the GPS receiver.
723 * as gps_send(fd,"rqts,u\r", peer);
724 *
725 * We don't currently send any data, but would like to send
726 * RTCM SC104 messages for differential positioning. It should
727 * also give us better time. Without a PPS output, we're
728 * Just fooling ourselves because of the serial code paths
729 *
730 */
731static void
732gps_send(
733 int fd,
734 const char *cmd,
735 struct peer *peer
736 )
737{
738
739 if (write(fd, cmd, strlen(cmd)) == -1) {
740 refclock_report(peer, CEVNT_FAULT);
741 }
742}
743
744static char *
745field_parse(
746 char *cp,
747 int fn
748 )
749{
750 char *tp;
751 int i = fn;
752
753 for (tp = cp; *tp != '\0'; tp++) {
754 if (*tp == ',')
755 i--;
756 if (i == 0)
757 break;
758 }
759 return (++tp);
760}
761#else
762int refclock_nmea_bs;
763#endif /* REFCLOCK */