Annotation of embedaddon/ntp/ntpd/refclock_nmea.c, revision 1.1.1.1
1.1 misho 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: * Updated to add support for Accord GPS Clock
7: * Venu Gopal Dec 05, 2007
8: * neo.venu@gmail.com, venugopal_d@pgad.gov.in
9: *
10: * Updated to process 'time1' fudge factor
11: * Venu Gopal May 05, 2008
12: *
13: * Converted to common PPSAPI code, separate PPS fudge time1
14: * from serial timecode fudge time2.
15: * Dave Hart July 1, 2009
16: * hart@ntp.org, davehart@davehart.com
17: */
18:
19: #ifdef HAVE_CONFIG_H
20: #include <config.h>
21: #endif
22:
23: #if defined(REFCLOCK) && defined(CLOCK_NMEA)
24:
25: #include <sys/stat.h>
26: #include <stdio.h>
27: #include <ctype.h>
28:
29: #include "ntpd.h"
30: #include "ntp_io.h"
31: #include "ntp_unixtime.h"
32: #include "ntp_refclock.h"
33: #include "ntp_stdlib.h"
34: #include "ntp_calendar.h"
35:
36: #ifdef HAVE_PPSAPI
37: # include "ppsapi_timepps.h"
38: # include "refclock_atom.h"
39: #endif /* HAVE_PPSAPI */
40:
41: #ifdef SYS_WINNT
42: #undef write /* ports/winnt/include/config.h: #define write _write */
43: extern int async_write(int, const void *, unsigned int);
44: #define write(fd, data, octets) async_write(fd, data, octets)
45: #endif
46:
47: #ifndef TIMESPECTOTS
48: #define TIMESPECTOTS(ptspec, pts) \
49: do { \
50: DTOLFP((ptspec)->tv_nsec * 1.0e-9, pts); \
51: (pts)->l_ui += (u_int32)((ptspec)->tv_sec) + JAN_1970; \
52: } while (0)
53: #endif
54:
55:
56: /*
57: * This driver supports NMEA-compatible GPS receivers
58: *
59: * Prototype was refclock_trak.c, Thanks a lot.
60: *
61: * The receiver used spits out the NMEA sentences for boat navigation.
62: * And you thought it was an information superhighway. Try a raging river
63: * filled with rapids and whirlpools that rip away your data and warp time.
64: *
65: * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in.
66: * On startup if initialization of the PPSAPI fails, it will fall back
67: * to the "normal" timestamps.
68: *
69: * The PPSAPI part of the driver understands fudge flag2 and flag3. If
70: * flag2 is set, it will use the clear edge of the pulse. If flag3 is
71: * set, kernel hardpps is enabled.
72: *
73: * GPS sentences other than RMC (the default) may be enabled by setting
74: * the relevent bits of 'mode' in the server configuration line
75: * server 127.127.20.x mode X
76: *
77: * bit 0 - enables RMC (1)
78: * bit 1 - enables GGA (2)
79: * bit 2 - enables GLL (4)
80: * bit 3 - enables ZDA (8) - Standard Time & Date
81: * bit 3 - enables ZDG (8) - Accord GPS Clock's custom sentence with GPS time
82: * very close to standard ZDA
83: *
84: * Multiple sentences may be selected except when ZDG/ZDA is selected.
85: *
86: * bit 4/5/6 - selects the baudrate for serial port :
87: * 0 for 4800 (default)
88: * 1 for 9600
89: * 2 for 19200
90: * 3 for 38400
91: * 4 for 57600
92: * 5 for 115200
93: */
94: #define NMEA_MESSAGE_MASK_OLD 0x07
95: #define NMEA_MESSAGE_MASK_SINGLE 0x08
96: #define NMEA_MESSAGE_MASK (NMEA_MESSAGE_MASK_OLD | NMEA_MESSAGE_MASK_SINGLE)
97:
98: #define NMEA_BAUDRATE_MASK 0x70
99: #define NMEA_BAUDRATE_SHIFT 4
100:
101: /*
102: * Definitions
103: */
104: #define DEVICE "/dev/gps%d" /* GPS serial device */
105: #define PPSDEV "/dev/gpspps%d" /* PPSAPI device override */
106: #define SPEED232 B4800 /* uart speed (4800 bps) */
107: #define PRECISION (-9) /* precision assumed (about 2 ms) */
108: #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
109: #define REFID "GPS\0" /* reference id */
110: #define DESCRIPTION "NMEA GPS Clock" /* who we are */
111: #ifndef O_NOCTTY
112: #define M_NOCTTY 0
113: #else
114: #define M_NOCTTY O_NOCTTY
115: #endif
116: #ifndef O_NONBLOCK
117: #define M_NONBLOCK 0
118: #else
119: #define M_NONBLOCK O_NONBLOCK
120: #endif
121: #define PPSOPENMODE (O_RDWR | M_NOCTTY | M_NONBLOCK)
122:
123: /* NMEA sentence array indexes for those we use */
124: #define NMEA_GPRMC 0 /* recommended min. nav. */
125: #define NMEA_GPGGA 1 /* fix and quality */
126: #define NMEA_GPGLL 2 /* geo. lat/long */
127: #define NMEA_GPZDA 3 /* date/time */
128: /*
129: * $GPZDG is a proprietary sentence that violates the spec, by not
130: * using $P and an assigned company identifier to prefix the sentence
131: * identifier. When used with this driver, the system needs to be
132: * isolated from other NTP networks, as it operates in GPS time, not
133: * UTC as is much more common. GPS time is >15 seconds different from
134: * UTC due to not respecting leap seconds since 1970 or so. Other
135: * than the different timebase, $GPZDG is similar to $GPZDA.
136: */
137: #define NMEA_GPZDG 4
138: #define NMEA_ARRAY_SIZE (NMEA_GPZDG + 1)
139:
140: /*
141: * Sentence selection mode bits
142: */
143: #define USE_ALL 0 /* any/all */
144: #define USE_GPRMC 1
145: #define USE_GPGGA 2
146: #define USE_GPGLL 4
147: #define USE_GPZDA_ZDG 8 /* affects both */
148:
149: /* mapping from sentence index to controlling mode bit */
150: u_char sentence_mode[NMEA_ARRAY_SIZE] =
151: {
152: USE_GPRMC,
153: USE_GPGGA,
154: USE_GPGLL,
155: USE_GPZDA_ZDG,
156: USE_GPZDA_ZDG
157: };
158:
159: /*
160: * Unit control structure
161: */
162: struct nmeaunit {
163: #ifdef HAVE_PPSAPI
164: struct refclock_atom atom; /* PPSAPI structure */
165: int ppsapi_tried; /* attempt PPSAPI once */
166: int ppsapi_lit; /* time_pps_create() worked */
167: int ppsapi_fd; /* fd used with PPSAPI */
168: int ppsapi_gate; /* allow edge detection processing */
169: int tcount; /* timecode sample counter */
170: int pcount; /* PPS sample counter */
171: #endif /* HAVE_PPSAPI */
172: l_fp tstamp; /* timestamp of last poll */
173: int gps_time; /* 0 UTC, 1 GPS time */
174: /* per sentence checksum seen flag */
175: struct calendar used; /* hh:mm:ss of used sentence */
176: u_char cksum_seen[NMEA_ARRAY_SIZE];
177: };
178:
179: /*
180: * Function prototypes
181: */
182: static int nmea_start (int, struct peer *);
183: static void nmea_shutdown (int, struct peer *);
184: static void nmea_receive (struct recvbuf *);
185: static void nmea_poll (int, struct peer *);
186: #ifdef HAVE_PPSAPI
187: static void nmea_control (int, struct refclockstat *,
188: struct refclockstat *, struct peer *);
189: static void nmea_timer (int, struct peer *);
190: #define NMEA_CONTROL nmea_control
191: #define NMEA_TIMER nmea_timer
192: #else
193: #define NMEA_CONTROL noentry
194: #define NMEA_TIMER noentry
195: #endif /* HAVE_PPSAPI */
196: static void gps_send (int, const char *, struct peer *);
197: static char * field_parse (char *, int);
198: static int nmea_checksum_ok(const char *);
199: static void nmea_day_unfold(struct calendar*);
200: static void nmea_century_unfold(struct calendar*);
201:
202: /*
203: * Transfer vector
204: */
205: struct refclock refclock_nmea = {
206: nmea_start, /* start up driver */
207: nmea_shutdown, /* shut down driver */
208: nmea_poll, /* transmit poll message */
209: NMEA_CONTROL, /* fudge control */
210: noentry, /* initialize driver */
211: noentry, /* buginfo */
212: NMEA_TIMER /* called once per second */
213: };
214:
215: /*
216: * nmea_start - open the GPS devices and initialize data for processing
217: */
218: static int
219: nmea_start(
220: int unit,
221: struct peer *peer
222: )
223: {
224: register struct nmeaunit *up;
225: struct refclockproc *pp;
226: int fd;
227: char device[20];
228: int baudrate;
229: char *baudtext;
230:
231: pp = peer->procptr;
232:
233: /*
234: * Open serial port. Use CLK line discipline, if available.
235: */
236: snprintf(device, sizeof(device), DEVICE, unit);
237:
238: /*
239: * Opening the serial port with appropriate baudrate
240: * based on the value of bit 4/5/6
241: */
242: switch ((peer->ttl & NMEA_BAUDRATE_MASK) >> NMEA_BAUDRATE_SHIFT) {
243: case 0:
244: case 6:
245: case 7:
246: default:
247: baudrate = SPEED232;
248: baudtext = "4800";
249: break;
250: case 1:
251: baudrate = B9600;
252: baudtext = "9600";
253: break;
254: case 2:
255: baudrate = B19200;
256: baudtext = "19200";
257: break;
258: case 3:
259: baudrate = B38400;
260: baudtext = "38400";
261: break;
262: #ifdef B57600
263: case 4:
264: baudrate = B57600;
265: baudtext = "57600";
266: break;
267: #endif
268: #ifdef B115200
269: case 5:
270: baudrate = B115200;
271: baudtext = "115200";
272: break;
273: #endif
274: }
275:
276: fd = refclock_open(device, baudrate, LDISC_CLK);
277:
278: if (fd <= 0) {
279: #ifdef HAVE_READLINK
280: /* nmead support added by Jon Miner (cp_n18@yahoo.com)
281: *
282: * See http://home.hiwaay.net/~taylorc/gps/nmea-server/
283: * for information about nmead
284: *
285: * To use this, you need to create a link from /dev/gpsX
286: * to the server:port where nmead is running. Something
287: * like this:
288: *
289: * ln -s server:port /dev/gps1
290: */
291: char buffer[80];
292: char *nmea_host, *nmea_tail;
293: int nmea_port;
294: int len;
295: struct hostent *he;
296: struct protoent *p;
297: struct sockaddr_in so_addr;
298:
299: if ((len = readlink(device,buffer,sizeof(buffer))) == -1)
300: return(0);
301: buffer[len] = 0;
302:
303: if ((nmea_host = strtok(buffer,":")) == NULL)
304: return(0);
305: if ((nmea_tail = strtok(NULL,":")) == NULL)
306: return(0);
307:
308: nmea_port = atoi(nmea_tail);
309:
310: if ((he = gethostbyname(nmea_host)) == NULL)
311: return(0);
312: if ((p = getprotobyname("tcp")) == NULL)
313: return(0);
314: memset(&so_addr, 0, sizeof(so_addr));
315: so_addr.sin_family = AF_INET;
316: so_addr.sin_port = htons(nmea_port);
317: so_addr.sin_addr = *((struct in_addr *) he->h_addr);
318:
319: if ((fd = socket(PF_INET,SOCK_STREAM,p->p_proto)) == -1)
320: return(0);
321: if (connect(fd,(struct sockaddr *)&so_addr, sizeof(so_addr)) == -1) {
322: close(fd);
323: return (0);
324: }
325: #else
326: pp->io.fd = -1;
327: return (0);
328: #endif
329: }
330:
331: msyslog(LOG_NOTICE, "%s serial %s open at %s bps",
332: refnumtoa(&peer->srcadr), device, baudtext);
333:
334: /*
335: * Allocate and initialize unit structure
336: */
337: up = emalloc(sizeof(*up));
338: memset(up, 0, sizeof(*up));
339: pp->io.clock_recv = nmea_receive;
340: pp->io.srcclock = (caddr_t)peer;
341: pp->io.datalen = 0;
342: pp->io.fd = fd;
343: if (!io_addclock(&pp->io)) {
344: pp->io.fd = -1;
345: close(fd);
346: free(up);
347: return (0);
348: }
349: pp->unitptr = (caddr_t)up;
350:
351: /*
352: * Initialize miscellaneous variables
353: */
354: peer->precision = PRECISION;
355: pp->clockdesc = DESCRIPTION;
356: memcpy(&pp->refid, REFID, 4);
357:
358: gps_send(fd,"$PMOTG,RMC,0000*1D\r\n", peer);
359:
360: return (1);
361: }
362:
363:
364: /*
365: * nmea_shutdown - shut down a GPS clock
366: *
367: * NOTE this routine is called after nmea_start() returns failure,
368: * as well as during a normal shutdown due to ntpq :config unpeer.
369: */
370: static void
371: nmea_shutdown(
372: int unit,
373: struct peer *peer
374: )
375: {
376: register struct nmeaunit *up;
377: struct refclockproc *pp;
378:
379: UNUSED_ARG(unit);
380:
381: pp = peer->procptr;
382: up = (struct nmeaunit *)pp->unitptr;
383: if (up != NULL) {
384: #ifdef HAVE_PPSAPI
385: if (up->ppsapi_lit) {
386: time_pps_destroy(up->atom.handle);
387: if (up->ppsapi_fd != pp->io.fd)
388: close(up->ppsapi_fd);
389: }
390: #endif
391: free(up);
392: }
393: if (-1 != pp->io.fd)
394: io_closeclock(&pp->io);
395: }
396:
397: /*
398: * nmea_control - configure fudge params
399: */
400: #ifdef HAVE_PPSAPI
401: static void
402: nmea_control(
403: int unit,
404: struct refclockstat *in_st,
405: struct refclockstat *out_st,
406: struct peer *peer
407: )
408: {
409: char device[32];
410: register struct nmeaunit *up;
411: struct refclockproc *pp;
412: int pps_fd;
413:
414: UNUSED_ARG(in_st);
415: UNUSED_ARG(out_st);
416:
417: pp = peer->procptr;
418: up = (struct nmeaunit *)pp->unitptr;
419:
420: if (!(CLK_FLAG1 & pp->sloppyclockflag)) {
421: if (!up->ppsapi_tried)
422: return;
423: up->ppsapi_tried = 0;
424: if (!up->ppsapi_lit)
425: return;
426: peer->flags &= ~FLAG_PPS;
427: peer->precision = PRECISION;
428: time_pps_destroy(up->atom.handle);
429: if (up->ppsapi_fd != pp->io.fd)
430: close(up->ppsapi_fd);
431: up->atom.handle = 0;
432: up->ppsapi_lit = 0;
433: up->ppsapi_fd = -1;
434: return;
435: }
436:
437: if (up->ppsapi_tried)
438: return;
439: /*
440: * Light up the PPSAPI interface.
441: */
442: up->ppsapi_tried = 1;
443:
444: /*
445: * if /dev/gpspps$UNIT can be opened that will be used for
446: * PPSAPI. Otherwise, the GPS serial device /dev/gps$UNIT
447: * already opened is used for PPSAPI as well.
448: */
449: snprintf(device, sizeof(device), PPSDEV, unit);
450:
451: pps_fd = open(device, PPSOPENMODE, S_IRUSR | S_IWUSR);
452:
453: if (-1 == pps_fd)
454: pps_fd = pp->io.fd;
455:
456: if (refclock_ppsapi(pps_fd, &up->atom)) {
457: up->ppsapi_lit = 1;
458: up->ppsapi_fd = pps_fd;
459: /* prepare to use the PPS API for our own purposes now. */
460: refclock_params(pp->sloppyclockflag, &up->atom);
461: return;
462: }
463:
464: NLOG(NLOG_CLOCKINFO)
465: msyslog(LOG_WARNING, "%s flag1 1 but PPSAPI fails",
466: refnumtoa(&peer->srcadr));
467: }
468: #endif /* HAVE_PPSAPI */
469:
470:
471: /*
472: * nmea_timer - called once per second, fetches PPS
473: * timestamp and stuffs in median filter.
474: */
475: #ifdef HAVE_PPSAPI
476: static void
477: nmea_timer(
478: int unit,
479: struct peer * peer
480: )
481: {
482: struct nmeaunit *up;
483: struct refclockproc *pp;
484:
485: UNUSED_ARG(unit);
486:
487: pp = peer->procptr;
488: up = (struct nmeaunit *)pp->unitptr;
489:
490: if (up->ppsapi_lit && up->ppsapi_gate &&
491: refclock_pps(peer, &up->atom, pp->sloppyclockflag) > 0) {
492: up->pcount++,
493: peer->flags |= FLAG_PPS;
494: peer->precision = PPS_PRECISION;
495: }
496: }
497: #endif /* HAVE_PPSAPI */
498:
499: #ifdef HAVE_PPSAPI
500: /*
501: * This function is used to correlate a receive time stamp and a
502: * reference time with a PPS edge time stamp. It applies the necessary
503: * fudges (fudge1 for PPS, fudge2 for receive time) and then tries to
504: * move the receive time stamp to the corresponding edge. This can
505: * warp into future, if a transmission delay of more than 500ms is not
506: * compensated with a corresponding fudge time2 value, because then
507: * the next PPS edge is nearer than the last. (Similiar to what the
508: * PPS ATOM driver does, but we deal with full time stamps here, not
509: * just phase shift information.) Likewise, a negative fudge time2
510: * value must be used if the reference time stamp correlates with the
511: * *following* PPS pulse.
512: *
513: * Note that the receive time fudge value only needs to move the receive
514: * stamp near a PPS edge but that close proximity is not required;
515: * +/-100ms precision should be enough. But since the fudge value will
516: * probably also be used to compensate the transmission delay when no PPS
517: * edge can be related to the time stamp, it's best to get it as close
518: * as possible.
519: *
520: * It should also be noted that the typical use case is matching to
521: * the preceeding edge, as most units relate their sentences to the
522: * current second.
523: *
524: * The function returns PPS_RELATE_NONE (0) if no PPS edge correlation
525: * can be fixed; PPS_RELATE_EDGE (1) when a PPS edge could be fixed, but
526: * the distance to the reference time stamp is too big (exceeds +/-400ms)
527: * and the ATOM driver PLL cannot be used to fix the phase; and
528: * PPS_RELATE_PHASE (2) when the ATOM driver PLL code can be used.
529: *
530: * On output, the receive time stamp is replaced with the
531: * corresponding PPS edge time if a fix could be made; the PPS fudge
532: * is updated to reflect the proper fudge time to apply. (This implies
533: * that 'refclock_process_f()' must be used!)
534: */
535: #define PPS_RELATE_NONE 0 /* no pps correlation possible */
536: #define PPS_RELATE_EDGE 1 /* recv time fixed, no phase lock */
537: #define PPS_RELATE_PHASE 2 /* recv time fixed, phase lock ok */
538:
539: static int
540: refclock_ppsrelate(
541: const struct refclockproc *pp , /* for sanity */
542: const struct refclock_atom *ap , /* for PPS io */
543: const l_fp *reftime ,
544: l_fp *rd_stamp, /* i/o read stamp */
545: double pp_fudge, /* pps fudge */
546: double *rd_fudge) /* i/o read fudge */
547: {
548: pps_info_t pps_info;
549: struct timespec timeout;
550: l_fp pp_stamp, pp_delta;
551: double delta, idelta;
552:
553: if (pp->leap == LEAP_NOTINSYNC)
554: return PPS_RELATE_NONE; /* clock is insane, no chance */
555:
556: memset(&timeout, 0, sizeof(timeout));
557: memset(&pps_info, 0, sizeof(pps_info_t));
558:
559: if (time_pps_fetch(ap->handle, PPS_TSFMT_TSPEC,
560: &pps_info, &timeout) < 0)
561: return PPS_RELATE_NONE;
562:
563: /* get last active PPS edge before receive */
564: if (ap->pps_params.mode & PPS_CAPTUREASSERT)
565: timeout = pps_info.assert_timestamp;
566: else if (ap->pps_params.mode & PPS_CAPTURECLEAR)
567: timeout = pps_info.clear_timestamp;
568: else
569: return PPS_RELATE_NONE;
570:
571: /* get delta between receive time and PPS time */
572: TIMESPECTOTS(&timeout, &pp_stamp);
573: pp_delta = *rd_stamp;
574: L_SUB(&pp_delta, &pp_stamp);
575: LFPTOD(&pp_delta, delta);
576: delta += pp_fudge - *rd_fudge;
577: if (fabs(delta) > 1.5)
578: return PPS_RELATE_NONE; /* PPS timeout control */
579:
580: /* eventually warp edges, check phase */
581: idelta = floor(delta + 0.5);
582: pp_fudge -= idelta;
583: delta -= idelta;
584: if (fabs(delta) > 0.45)
585: return PPS_RELATE_NONE; /* dead band control */
586:
587: /* we actually have a PPS edge to relate with! */
588: *rd_stamp = pp_stamp;
589: *rd_fudge = pp_fudge;
590:
591: /* if whole system out-of-sync, do not try to PLL */
592: if (sys_leap == LEAP_NOTINSYNC)
593: return PPS_RELATE_EDGE; /* cannot PLL with atom code */
594:
595: /* check against reftime if ATOM PLL can be used */
596: pp_delta = *reftime;
597: L_SUB(&pp_delta, &pp_stamp);
598: LFPTOD(&pp_delta, delta);
599: delta += pp_fudge;
600: if (fabs(delta) > 0.45)
601: return PPS_RELATE_EDGE; /* cannot PLL with atom code */
602:
603: /* all checks passed, gets an AAA rating here! */
604: return PPS_RELATE_PHASE; /* can PLL with atom code */
605: }
606: #endif /* HAVE_PPSAPI */
607:
608: /*
609: * nmea_receive - receive data from the serial interface
610: */
611: static void
612: nmea_receive(
613: struct recvbuf *rbufp
614: )
615: {
616: register struct nmeaunit *up;
617: struct refclockproc *pp;
618: struct peer *peer;
619: char *cp, *dp, *msg;
620: u_char sentence;
621: /* Use these variables to hold data until we decide its worth
622: * keeping */
623: char rd_lastcode[BMAX];
624: l_fp rd_timestamp, reftime;
625: int rd_lencode;
626: double rd_fudge;
627: struct calendar date;
628:
629: /*
630: * Initialize pointers and read the timecode and timestamp
631: */
632: peer = rbufp->recv_peer;
633: pp = peer->procptr;
634: up = (struct nmeaunit *)pp->unitptr;
635:
636: rd_lencode = refclock_gtlin(
637: rbufp,
638: rd_lastcode,
639: sizeof(rd_lastcode),
640: &rd_timestamp);
641:
642: /*
643: * There is a case that a <CR><LF> gives back a "blank" line.
644: * We can't have a well-formed sentence with less than 8 chars.
645: */
646: if (0 == rd_lencode)
647: return;
648:
649: if (rd_lencode < 8) {
650: refclock_report(peer, CEVNT_BADREPLY);
651: return;
652: }
653:
654: DPRINTF(1, ("nmea: gpsread %d %s\n", rd_lencode, rd_lastcode));
655:
656: /*
657: * We check the timecode format and decode its contents. The
658: * we only care about a few of them. The most important being
659: * the $GPRMC format
660: * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
661: * mode (0,1,2,3) selects sentence ANY/ALL, RMC, GGA, GLL, ZDA
662: * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21
663: * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F
664: * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77
665: *
666: * Defining GPZDA to support Standard Time & Date
667: * sentence. The sentence has the following format
668: *
669: * $--ZDA,HHMMSS.SS,DD,MM,YYYY,TH,TM,*CS<CR><LF>
670: *
671: * Apart from the familiar fields,
672: * 'TH' Time zone Hours
673: * 'TM' Time zone Minutes
674: *
675: * Defining GPZDG to support Accord GPS Clock's custom NMEA
676: * sentence. The sentence has the following format
677: *
678: * $GPZDG,HHMMSS.S,DD,MM,YYYY,AA.BB,V*CS<CR><LF>
679: *
680: * It contains the GPS timestamp valid for next PPS pulse.
681: * Apart from the familiar fields,
682: * 'AA.BB' denotes the signal strength( should be < 05.00 )
683: * 'V' denotes the GPS sync status :
684: * '0' indicates INVALID time,
685: * '1' indicates accuracy of +/-20 ms
686: * '2' indicates accuracy of +/-100 ns
687: */
688:
689: cp = rd_lastcode;
690: if (cp[0] == '$') {
691: /* Allow for GLGGA and GPGGA etc. */
692: msg = cp + 3;
693:
694: if (strncmp(msg, "RMC", 3) == 0)
695: sentence = NMEA_GPRMC;
696: else if (strncmp(msg, "GGA", 3) == 0)
697: sentence = NMEA_GPGGA;
698: else if (strncmp(msg, "GLL", 3) == 0)
699: sentence = NMEA_GPGLL;
700: else if (strncmp(msg, "ZDG", 3) == 0)
701: sentence = NMEA_GPZDG;
702: else if (strncmp(msg, "ZDA", 3) == 0)
703: sentence = NMEA_GPZDA;
704: else
705: return;
706: } else
707: return;
708:
709: /* See if I want to process this message type */
710: if ((peer->ttl & NMEA_MESSAGE_MASK) &&
711: !(peer->ttl & sentence_mode[sentence]))
712: return;
713:
714: /*
715: * $GPZDG provides GPS time not UTC, and the two mix poorly.
716: * Once have processed a $GPZDG, do not process any further
717: * UTC sentences (all but $GPZDG currently).
718: */
719: if (up->gps_time && NMEA_GPZDG != sentence)
720: return;
721:
722: /*
723: * Apparently, older NMEA specifications (which are expensive)
724: * did not require the checksum for all sentences. $GPMRC is
725: * the only one so far identified which has always been required
726: * to include a checksum.
727: *
728: * Today, most NMEA GPS receivers checksum every sentence. To
729: * preserve its error-detection capabilities with modern GPSes
730: * while allowing operation without checksums on all but $GPMRC,
731: * we keep track of whether we've ever seen a checksum on a
732: * given sentence, and if so, reject future checksum failures.
733: */
734: if (nmea_checksum_ok(rd_lastcode)) {
735: up->cksum_seen[sentence] = TRUE;
736: } else if (NMEA_GPRMC == sentence || up->cksum_seen[sentence]) {
737: refclock_report(peer, CEVNT_BADREPLY);
738: return;
739: }
740:
741: cp = rd_lastcode;
742:
743: /* Grab field depending on clock string type */
744: memset(&date, 0, sizeof(date));
745: switch (sentence) {
746:
747: case NMEA_GPRMC:
748: /*
749: * Test for synchronization. Check for quality byte.
750: */
751: dp = field_parse(cp, 2);
752: if (dp[0] != 'A')
753: pp->leap = LEAP_NOTINSYNC;
754: else
755: pp->leap = LEAP_NOWARNING;
756:
757: /* Now point at the time field */
758: dp = field_parse(cp, 1);
759: break;
760:
761: case NMEA_GPGGA:
762: /*
763: * Test for synchronization. Check for quality byte.
764: */
765: dp = field_parse(cp, 6);
766: if (dp[0] == '0')
767: pp->leap = LEAP_NOTINSYNC;
768: else
769: pp->leap = LEAP_NOWARNING;
770:
771: /* Now point at the time field */
772: dp = field_parse(cp, 1);
773: break;
774:
775: case NMEA_GPGLL:
776: /*
777: * Test for synchronization. Check for quality byte.
778: */
779: dp = field_parse(cp, 6);
780: if (dp[0] != 'A')
781: pp->leap = LEAP_NOTINSYNC;
782: else
783: pp->leap = LEAP_NOWARNING;
784:
785: /* Now point at the time field */
786: dp = field_parse(cp, 5);
787: break;
788:
789: case NMEA_GPZDG:
790: /* For $GPZDG check for validity of GPS time. */
791: dp = field_parse(cp, 6);
792: if (dp[0] == '0')
793: pp->leap = LEAP_NOTINSYNC;
794: else
795: pp->leap = LEAP_NOWARNING;
796: /* fall through to NMEA_GPZDA */
797:
798: case NMEA_GPZDA:
799: if (NMEA_GPZDA == sentence)
800: pp->leap = LEAP_NOWARNING;
801:
802: /* Now point at the time field */
803: dp = field_parse(cp, 1);
804: break;
805:
806: default:
807: return;
808: }
809:
810: /*
811: * Check time code format of NMEA
812: */
813: if (!isdigit((int)dp[0]) ||
814: !isdigit((int)dp[1]) ||
815: !isdigit((int)dp[2]) ||
816: !isdigit((int)dp[3]) ||
817: !isdigit((int)dp[4]) ||
818: !isdigit((int)dp[5])) {
819:
820: DPRINTF(1, ("NMEA time code %c%c%c%c%c%c non-numeric",
821: dp[0], dp[1], dp[2], dp[3], dp[4], dp[5]));
822: refclock_report(peer, CEVNT_BADTIME);
823: return;
824: }
825:
826: /*
827: * Convert time and check values.
828: */
829: date.hour = ((dp[0] - '0') * 10) + dp[1] - '0';
830: date.minute = ((dp[2] - '0') * 10) + dp[3] - '0';
831: date.second = ((dp[4] - '0') * 10) + dp[5] - '0';
832: /*
833: * Default to 0 milliseconds, if decimal convert milliseconds in
834: * one, two or three digits
835: */
836: pp->nsec = 0;
837: if (dp[6] == '.') {
838: if (isdigit((int)dp[7])) {
839: pp->nsec = (dp[7] - '0') * 100000000;
840: if (isdigit((int)dp[8])) {
841: pp->nsec += (dp[8] - '0') * 10000000;
842: if (isdigit((int)dp[9])) {
843: pp->nsec += (dp[9] - '0') * 1000000;
844: }
845: }
846: }
847: }
848:
849: if (date.hour > 23 || date.minute > 59 ||
850: date.second > 59 || pp->nsec > 1000000000) {
851:
852: DPRINTF(1, ("NMEA hour/min/sec/nsec range %02d:%02d:%02d.%09ld\n",
853: pp->hour, pp->minute, pp->second, pp->nsec));
854: refclock_report(peer, CEVNT_BADTIME);
855: return;
856: }
857:
858: /*
859: * Used only the first recognized sentence each second.
860: */
861: if (date.hour == up->used.hour &&
862: date.minute == up->used.minute &&
863: date.second == up->used.second)
864: return;
865:
866: pp->lencode = (u_short)rd_lencode;
867: memcpy(pp->a_lastcode, rd_lastcode, pp->lencode + 1);
868: up->tstamp = rd_timestamp;
869: pp->lastrec = up->tstamp;
870: DPRINTF(1, ("nmea: timecode %d %s\n", pp->lencode, pp->a_lastcode));
871:
872: /*
873: * Convert date and check values.
874: */
875: if (NMEA_GPRMC == sentence) {
876:
877: dp = field_parse(cp,9);
878: date.monthday = 10 * (dp[0] - '0') + (dp[1] - '0');
879: date.month = 10 * (dp[2] - '0') + (dp[3] - '0');
880: date.year = 10 * (dp[4] - '0') + (dp[5] - '0');
881: nmea_century_unfold(&date);
882:
883: } else if (NMEA_GPZDA == sentence || NMEA_GPZDG == sentence) {
884:
885: dp = field_parse(cp, 2);
886: date.monthday = 10 * (dp[0] - '0') + (dp[1] - '0');
887: dp = field_parse(cp, 3);
888: date.month = 10 * (dp[0] - '0') + (dp[1] - '0');
889: dp = field_parse(cp, 4);
890: date.year = 1000 * (dp[0] - '0') + 100 * (dp[1] - '0')
891: + 10 * (dp[2] - '0') + (dp[3] - '0');
892:
893: } else
894: nmea_day_unfold(&date);
895:
896: if (date.month < 1 || date.month > 12 ||
897: date.monthday < 1 || date.monthday > 31) {
898: refclock_report(peer, CEVNT_BADDATE);
899: return;
900: }
901:
902: up->used.hour = date.hour;
903: up->used.minute = date.minute;
904: up->used.second = date.second;
905:
906: /*
907: * If "fudge 127.127.20.__ flag4 1" is configured in ntp.conf,
908: * remove the location and checksum from the NMEA sentence
909: * recorded as the last timecode and visible to remote users
910: * with:
911: *
912: * ntpq -c clockvar <server>
913: *
914: * Note that this also removes the location from the clockstats
915: * log (if it is enabled). Some NTP operators monitor their
916: * NMEA GPS using the change in location in clockstats over
917: * time as as a proxy for the quality of GPS reception and
918: * thereby time reported.
919: */
920: if (CLK_FLAG4 & pp->sloppyclockflag) {
921: /*
922: * Start by pointing cp and dp at the fields with
923: * longitude and latitude in the last timecode.
924: */
925: switch (sentence) {
926:
927: case NMEA_GPGLL:
928: cp = field_parse(pp->a_lastcode, 1);
929: dp = field_parse(cp, 2);
930: break;
931:
932: case NMEA_GPGGA:
933: cp = field_parse(pp->a_lastcode, 2);
934: dp = field_parse(cp, 2);
935: break;
936:
937: case NMEA_GPRMC:
938: cp = field_parse(pp->a_lastcode, 3);
939: dp = field_parse(cp, 2);
940: break;
941:
942: case NMEA_GPZDA:
943: case NMEA_GPZDG:
944: default:
945: cp = dp = NULL;
946: }
947:
948: /* Blank the entire latitude & longitude. */
949: while (cp) {
950: while (',' != *cp) {
951: if ('.' != *cp)
952: *cp = '_';
953: cp++;
954: }
955:
956: /* Longitude at cp then latitude at dp */
957: if (cp < dp)
958: cp = dp;
959: else
960: cp = NULL;
961: }
962:
963: /* Blank the checksum, the last two characters */
964: if (dp) {
965: cp = pp->a_lastcode + pp->lencode - 2;
966: if (0 == cp[2])
967: cp[0] = cp[1] = '_';
968: }
969:
970: }
971:
972: /*
973: * Get the reference time stamp from the calendar buffer.
974: * Process the new sample in the median filter and determine
975: * the timecode timestamp, but only if the PPS is not in
976: * control.
977: */
978: rd_fudge = pp->fudgetime2;
979: date.yearday = 0; /* make sure it's not used */
980: DTOLFP(pp->nsec * 1.0e-9, &reftime);
981: reftime.l_ui += caltontp(&date);
982:
983: /* $GPZDG postprocessing first... */
984: if (NMEA_GPZDG == sentence) {
985: /*
986: * Note if we're only using GPS timescale from now on.
987: */
988: if (!up->gps_time) {
989: up->gps_time = 1;
990: NLOG(NLOG_CLOCKINFO)
991: msyslog(LOG_INFO, "%s using only $GPZDG",
992: refnumtoa(&peer->srcadr));
993: }
994: /*
995: * $GPZDG indicates the second after the *next* PPS
996: * pulse. So we remove 1 second from the reference
997: * time now.
998: */
999: reftime.l_ui--;
1000: }
1001:
1002: #ifdef HAVE_PPSAPI
1003: up->tcount++;
1004: /*
1005: * If we have PPS running, we try to associate the sentence with
1006: * the last active edge of the PPS signal.
1007: */
1008: if (up->ppsapi_lit)
1009: switch (refclock_ppsrelate(pp, &up->atom, &reftime,
1010: &rd_timestamp, pp->fudgetime1,
1011: &rd_fudge))
1012: {
1013: case PPS_RELATE_EDGE:
1014: up->ppsapi_gate = 0;
1015: break;
1016: case PPS_RELATE_PHASE:
1017: up->ppsapi_gate = 1;
1018: break;
1019: default:
1020: break;
1021: }
1022: else
1023: up->ppsapi_gate = 0;
1024:
1025: if (up->ppsapi_gate && (peer->flags & FLAG_PPS))
1026: return;
1027: #endif /* HAVE_PPSAPI */
1028:
1029: refclock_process_offset(pp, reftime, rd_timestamp, rd_fudge);
1030: }
1031:
1032:
1033: /*
1034: * nmea_poll - called by the transmit procedure
1035: *
1036: * We go to great pains to avoid changing state here, since there may be
1037: * more than one eavesdropper receiving the same timecode.
1038: */
1039: static void
1040: nmea_poll(
1041: int unit,
1042: struct peer *peer
1043: )
1044: {
1045: register struct nmeaunit *up;
1046: struct refclockproc *pp;
1047:
1048: pp = peer->procptr;
1049: up = (struct nmeaunit *)pp->unitptr;
1050:
1051: /*
1052: * Process median filter samples. If none received, declare a
1053: * timeout and keep going.
1054: */
1055: #ifdef HAVE_PPSAPI
1056: if (up->pcount == 0) {
1057: peer->flags &= ~FLAG_PPS;
1058: peer->precision = PRECISION;
1059: }
1060: if (up->tcount == 0) {
1061: pp->coderecv = pp->codeproc;
1062: refclock_report(peer, CEVNT_TIMEOUT);
1063: return;
1064: }
1065: up->pcount = up->tcount = 0;
1066: #else /* HAVE_PPSAPI */
1067: if (pp->coderecv == pp->codeproc) {
1068: refclock_report(peer, CEVNT_TIMEOUT);
1069: return;
1070: }
1071: #endif /* HAVE_PPSAPI */
1072:
1073: pp->polls++;
1074: pp->lastref = pp->lastrec;
1075: refclock_receive(peer);
1076: record_clock_stats(&peer->srcadr, pp->a_lastcode);
1077:
1078: /*
1079: * usually nmea_receive can get a timestamp every second,
1080: * but at least one Motorola unit needs prompting each
1081: * time.
1082: */
1083:
1084: gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
1085: }
1086:
1087:
1088: /*
1089: *
1090: * gps_send(fd,cmd, peer) Sends a command to the GPS receiver.
1091: * as gps_send(fd,"rqts,u\r", peer);
1092: *
1093: * We don't currently send any data, but would like to send
1094: * RTCM SC104 messages for differential positioning. It should
1095: * also give us better time. Without a PPS output, we're
1096: * Just fooling ourselves because of the serial code paths
1097: *
1098: */
1099: static void
1100: gps_send(
1101: int fd,
1102: const char *cmd,
1103: struct peer *peer
1104: )
1105: {
1106: if (write(fd, cmd, strlen(cmd)) == -1) {
1107: refclock_report(peer, CEVNT_FAULT);
1108: }
1109: }
1110:
1111:
1112: static char *
1113: field_parse(
1114: char *cp,
1115: int fn
1116: )
1117: {
1118: char *tp;
1119: int i = fn;
1120:
1121: for (tp = cp; i && *tp; tp++)
1122: if (*tp == ',')
1123: i--;
1124:
1125: return tp;
1126: }
1127:
1128:
1129: /*
1130: * nmea_checksum_ok verifies 8-bit XOR checksum is correct then returns 1
1131: *
1132: * format is $XXXXX,1,2,3,4*ML
1133: *
1134: * 8-bit XOR of characters between $ and * noninclusive is transmitted
1135: * in last two chars M and L holding most and least significant nibbles
1136: * in hex representation such as:
1137: *
1138: * $GPGLL,5057.970,N,00146.110,E,142451,A*27
1139: * $GPVTG,089.0,T,,,15.2,N,,*7F
1140: */
1141: int
1142: nmea_checksum_ok(
1143: const char *sentence
1144: )
1145: {
1146: u_char my_cs;
1147: u_long input_cs;
1148: const char *p;
1149:
1150: my_cs = 0;
1151: p = sentence;
1152:
1153: if ('$' != *p++)
1154: return 0;
1155:
1156: for ( ; *p && '*' != *p; p++) {
1157:
1158: my_cs ^= *p;
1159: }
1160:
1161: if ('*' != *p++)
1162: return 0;
1163:
1164: if (0 == p[0] || 0 == p[1] || 0 != p[2])
1165: return 0;
1166:
1167: if (0 == hextoint(p, &input_cs))
1168: return 0;
1169:
1170: if (my_cs != input_cs)
1171: return 0;
1172:
1173: return 1;
1174: }
1175:
1176: /*
1177: * -------------------------------------------------------------------
1178: * funny calendar-oriented stuff -- a bit hard to grok.
1179: * -------------------------------------------------------------------
1180: */
1181: /*
1182: * Do a periodic unfolding of a truncated value around a given pivot
1183: * value.
1184: * The result r will hold to pivot <= r < pivot+period (period>0) or
1185: * pivot+period < r <= pivot (period < 0) and value % period == r % period,
1186: * using floor division convention.
1187: */
1188: static time_t
1189: nmea_periodic_unfold(
1190: time_t pivot,
1191: time_t value,
1192: time_t period)
1193: {
1194: /*
1195: * This will only work as long as 'value - pivot%period' does
1196: * not create a signed overflow condition.
1197: */
1198: value = (value - (pivot % period)) % period;
1199: if (value && (value ^ period) < 0)
1200: value += period;
1201: return pivot + value;
1202: }
1203:
1204: /*
1205: * Unfold a time-of-day (seconds since midnight) around the current
1206: * system time in a manner that guarantees an absolute difference of
1207: * less than 12hrs.
1208: *
1209: * This function is used for NMEA sentences that contain no date
1210: * information. This requires the system clock to be in +/-12hrs
1211: * around the true time, or the clock will synchronize the system 1day
1212: * off if not augmented with a time sources that also provide the
1213: * necessary date information.
1214: *
1215: * The function updates the refclockproc structure is also uses as
1216: * input to fetch the time from.
1217: */
1218: static void
1219: nmea_day_unfold(
1220: struct calendar *jd)
1221: {
1222: time_t value, pivot;
1223: struct tm *tdate;
1224:
1225: value = ((time_t)jd->hour * MINSPERHR
1226: + (time_t)jd->minute) * SECSPERMIN
1227: + (time_t)jd->second;
1228: pivot = time(NULL) - SECSPERDAY/2;
1229:
1230: value = nmea_periodic_unfold(pivot, value, SECSPERDAY);
1231: tdate = gmtime(&value);
1232: if (tdate) {
1233: jd->year = tdate->tm_year + 1900;
1234: jd->yearday = tdate->tm_yday + 1;
1235: jd->month = tdate->tm_mon + 1;
1236: jd->monthday = tdate->tm_mday;
1237: jd->hour = tdate->tm_hour;
1238: jd->minute = tdate->tm_min;
1239: jd->second = tdate->tm_sec;
1240: } else {
1241: jd->year = 0;
1242: jd->yearday = 0;
1243: jd->month = 0;
1244: jd->monthday = 0;
1245: }
1246: }
1247:
1248: /*
1249: * Unfold a 2-digit year into full year spec around the current year
1250: * of the system time. This requires the system clock to be in -79/+19
1251: * years around the true time, or the result will be off by
1252: * 100years. The assymetric behaviour was chosen to enable inital sync
1253: * for systems that do not have a battery-backup-clock and start with
1254: * a date that is typically years in the past.
1255: *
1256: * The function updates the calendar structure that is also used as
1257: * input to fetch the year from.
1258: */
1259: static void
1260: nmea_century_unfold(
1261: struct calendar *jd)
1262: {
1263: time_t pivot_time;
1264: struct tm *pivot_date;
1265: time_t pivot_year;
1266:
1267: /* get warp limit and century start of pivot from system time */
1268: pivot_time = time(NULL);
1269: pivot_date = gmtime(&pivot_time);
1270: pivot_year = pivot_date->tm_year + 1900 - 20;
1271: jd->year = nmea_periodic_unfold(pivot_year, jd->year, 100);
1272: }
1273:
1274: #else
1275: int refclock_nmea_bs;
1276: #endif /* REFCLOCK && CLOCK_NMEA */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>