Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / ntp / dist / ntpd / refclock_nmea.c
blobd3958deafe396ffc507d2369092f7a9c15e9067d
1 /* $NetBSD: refclock_nmea.c,v 1.1.1.1 2009/12/13 16:55:55 kardel Exp $ */
3 /*
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
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #if defined(REFCLOCK) && defined(CLOCK_NMEA)
26 #include <sys/stat.h>
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <sys/socket.h>
31 #include "ntpd.h"
32 #include "ntp_io.h"
33 #include "ntp_unixtime.h"
34 #include "ntp_refclock.h"
35 #include "ntp_stdlib.h"
37 #ifdef HAVE_PPSAPI
38 # include "ppsapi_timepps.h"
39 #include "refclock_atom.h"
40 #endif /* HAVE_PPSAPI */
42 #ifdef SYS_WINNT
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)
46 #endif
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)
80 * 1 for 9600
81 * 2 for 19200
82 * 3 for 38400
83 * 4 for 57600
84 * 5 for 115200
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
94 * Definitions
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) */
105 #ifndef O_NOCTTY
106 #define M_NOCTTY 0
107 #else
108 #define M_NOCTTY O_NOCTTY
109 #endif
110 #ifndef O_NONBLOCK
111 #define M_NONBLOCK 0
112 #else
113 #define M_NONBLOCK O_NONBLOCK
114 #endif
115 #define PPSOPENMODE (O_RDWR | M_NOCTTY | M_NONBLOCK)
118 * Unit control structure
120 struct nmeaunit {
121 #ifdef HAVE_PPSAPI
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 *);
140 #ifdef HAVE_PPSAPI
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
146 #else
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 *);
155 * Transfer vector
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
170 static int
171 nmea_start(
172 int unit,
173 struct peer *peer
176 register struct nmeaunit *up;
177 struct refclockproc *pp;
178 int fd;
179 char device[20];
180 int baudrate;
181 char *baudtext;
183 pp = peer->procptr;
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) {
195 case 0:
196 case 6:
197 case 7:
198 default:
199 baudrate = SPEED232;
200 baudtext = "4800";
201 break;
202 case 1:
203 baudrate = B9600;
204 baudtext = "9600";
205 break;
206 case 2:
207 baudrate = B19200;
208 baudtext = "19200";
209 break;
210 case 3:
211 baudrate = B38400;
212 baudtext = "38400";
213 break;
214 #ifdef B57600
215 case 4:
216 baudrate = B57600;
217 baudtext = "57600";
218 break;
219 #endif
220 #ifdef B115200
221 case 5:
222 baudrate = B115200;
223 baudtext = "115200";
224 break;
225 #endif
228 fd = refclock_open(device, baudrate, LDISC_CLK);
230 if (fd <= 0) {
231 #ifdef HAVE_READLINK
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
242 char buffer[80];
243 char *nmea_host, *nmea_tail;
244 int nmea_port;
245 int len;
246 struct hostent *he;
247 struct protoent *p;
248 struct sockaddr_in so_addr;
250 if ((len = readlink(device,buffer,sizeof(buffer))) == -1)
251 return(0);
252 buffer[len] = 0;
254 if ((nmea_host = strtok(buffer,":")) == NULL)
255 return(0);
256 if ((nmea_tail = strtok(NULL,":")) == NULL)
257 return(0);
259 nmea_port = atoi(nmea_tail);
261 if ((he = gethostbyname(nmea_host)) == NULL)
262 return(0);
263 if ((p = getprotobyname("ip")) == NULL)
264 return(0);
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)
271 return(0);
272 if (connect(fd,(struct sockaddr *)&so_addr, sizeof(so_addr)) == -1) {
273 close(fd);
274 return (0);
276 #else
277 pp->io.fd = -1;
278 return (0);
279 #endif
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;
292 pp->io.datalen = 0;
293 pp->io.fd = fd;
294 if (!io_addclock(&pp->io)) {
295 pp->io.fd = -1;
296 close(fd);
297 free(up);
298 return (0);
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);
311 return (1);
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.
321 static void
322 nmea_shutdown(
323 int unit,
324 struct peer *peer
327 register struct nmeaunit *up;
328 struct refclockproc *pp;
330 UNUSED_ARG(unit);
332 pp = peer->procptr;
333 up = (struct nmeaunit *)pp->unitptr;
334 if (up != NULL) {
335 #ifdef HAVE_PPSAPI
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);
341 #endif
342 free(up);
344 if (-1 != pp->io.fd)
345 io_closeclock(&pp->io);
349 * nmea_control - configure fudge params
351 #ifdef HAVE_PPSAPI
352 static void
353 nmea_control(
354 int unit,
355 struct refclockstat *in_st,
356 struct refclockstat *out_st,
357 struct peer *peer
360 char device[32];
361 register struct nmeaunit *up;
362 struct refclockproc *pp;
363 int pps_fd;
365 UNUSED_ARG(in_st);
366 UNUSED_ARG(out_st);
368 pp = peer->procptr;
369 up = (struct nmeaunit *)pp->unitptr;
371 if (!(CLK_FLAG1 & pp->sloppyclockflag)) {
372 if (!up->ppsapi_tried)
373 return;
374 up->ppsapi_tried = 0;
375 if (!up->ppsapi_lit)
376 return;
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);
382 up->atom.handle = 0;
383 up->ppsapi_lit = 0;
384 up->ppsapi_fd = -1;
385 return;
388 if (up->ppsapi_tried)
389 return;
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);
404 if (-1 == pps_fd)
405 pps_fd = pp->io.fd;
407 if (refclock_ppsapi(pps_fd, &up->atom)) {
408 up->ppsapi_lit = 1;
409 up->ppsapi_fd = pps_fd;
410 return;
413 NLOG(NLOG_CLOCKINFO)
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.
424 #ifdef HAVE_PPSAPI
425 static void
426 nmea_timer(
427 int unit,
428 struct peer * peer
431 struct nmeaunit *up;
432 struct refclockproc *pp;
434 UNUSED_ARG(unit);
436 pp = peer->procptr;
437 up = (struct nmeaunit *)pp->unitptr;
439 if (up->ppsapi_lit &&
440 refclock_pps(peer, &up->atom, pp->sloppyclockflag) > 0) {
441 up->pcount++,
442 peer->flags |= FLAG_PPS;
443 peer->precision = PPS_PRECISION;
446 #endif /* HAVE_PPSAPI */
450 * nmea_receive - receive data from the serial interface
452 static void
453 nmea_receive(
454 struct recvbuf *rbufp
457 register struct nmeaunit *up;
458 struct refclockproc *pp;
459 struct peer *peer;
460 int month, day;
461 char *cp, *dp, *msg;
462 int cmdtype;
463 int cmdtypezdg = 0;
464 /* Use these variables to hold data until we decide its worth keeping */
465 char rd_lastcode[BMAX];
466 l_fp rd_timestamp;
467 int rd_lencode;
470 * Initialize pointers and read the timecode and timestamp
472 peer = rbufp->recv_peer;
473 pp = peer->procptr;
474 up = (struct nmeaunit *)pp->unitptr;
476 rd_lencode = refclock_gtlin(
477 rbufp,
478 rd_lastcode,
479 sizeof(rd_lastcode),
480 &rd_timestamp);
483 * There is a case that a <CR><LF> gives back a "blank" line
485 if (rd_lencode == 0)
486 return;
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
493 * the $GPRMC format
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 */
523 #define GPRMC 1
524 #define GPGGA 2
525 #define GPGLL 4
526 #define GPZDG_ZDA 8
528 cp = rd_lastcode;
529 cmdtype=0;
530 if (cp[0] == '$') {
531 /* Allow for GLGGA and GPGGA etc. */
532 msg = cp + 3;
534 if (strncmp(msg, "RMC", 3) == 0)
535 cmdtype = GPRMC;
536 else if (strncmp(msg, "GGA", 3) == 0)
537 cmdtype = GPGGA;
538 else if (strncmp(msg, "GLL", 3) == 0)
539 cmdtype = GPGLL;
540 else if (strncmp(msg, "ZD", 2) == 0) {
541 cmdtype = GPZDG_ZDA;
542 if ('G' == msg[2])
543 cmdtypezdg = 1;
544 else if ('A' != msg[2])
545 return;
546 } else
547 return;
548 } else
549 return;
551 /* See if I want to process this message type */
552 if (peer->ttl && !(cmdtype & (peer->ttl & NMEA_MESSAGE_MASK)))
553 return;
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)
561 return;
563 /* make sure it came in clean */
564 if (!nmea_checksum_ok(rd_lastcode)) {
565 refclock_report(peer, CEVNT_BADREPLY);
566 return;
569 pp->lencode = (u_short) rd_lencode;
570 memcpy(pp->a_lastcode, rd_lastcode, pp->lencode + 1);
571 cp = pp->a_lastcode;
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 */
579 switch (cmdtype) {
581 case GPRMC:
583 * Test for synchronization. Check for quality byte.
585 dp = field_parse(cp, 2);
586 if (dp[0] != 'A')
587 pp->leap = LEAP_NOTINSYNC;
588 else
589 pp->leap = LEAP_NOWARNING;
591 /* Now point at the time field */
592 dp = field_parse(cp, 1);
593 break;
595 case GPGGA:
597 * Test for synchronization. Check for quality byte.
599 dp = field_parse(cp, 6);
600 if (dp[0] == '0')
601 pp->leap = LEAP_NOTINSYNC;
602 else
603 pp->leap = LEAP_NOWARNING;
605 /* Now point at the time field */
606 dp = field_parse(cp, 1);
607 break;
609 case GPGLL:
611 * Test for synchronization. Check for quality byte.
613 dp = field_parse(cp, 6);
614 if (dp[0] != 'A')
615 pp->leap = LEAP_NOTINSYNC;
616 else
617 pp->leap = LEAP_NOWARNING;
619 /* Now point at the time field */
620 dp = field_parse(cp, 5);
621 break;
623 case GPZDG_ZDA:
625 * Test for synchronization. For $GPZDG check for validity of GPS time.
627 if (cmdtypezdg) {
628 dp = field_parse(cp, 6);
629 if (dp[0] == '0')
630 pp->leap = LEAP_NOTINSYNC;
631 else
632 pp->leap = LEAP_NOWARNING;
633 } else
634 pp->leap = LEAP_NOWARNING;
636 /* Now point at the time field */
637 dp = field_parse(cp, 1);
638 break;
640 default:
641 return;
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);
657 return;
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
670 pp->nsec = 0;
671 if (dp[6] == '.') {
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
688 if (cmdtypezdg) {
689 if (pp->second == 0) {
690 pp->second = 59;
691 if (pp->minute == 0) {
692 pp->minute = 59;
693 if (pp->hour == 0)
694 pp->hour = 23;
696 } else
697 pp->second -= 1;
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);
706 return;
710 * Convert date and check values.
712 if (GPRMC == cmdtype) {
714 dp = field_parse(cp,9);
715 day = dp[0] - '0';
716 day = (day * 10) + dp[1] - '0';
717 month = dp[2] - '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');
731 } else {
732 /* only time */
733 time_t tt = time(NULL);
734 struct tm * t = gmtime(&tt);
735 day = t->tm_mday;
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);
742 return;
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 */
748 pp->year += 2100;
749 else
750 pp->year += 2000;
753 /* pp->year now 4 digits as ymd2yd requires */
754 day = ymd2yd(pp->year, month, day);
755 if (-1 == day) {
756 refclock_report(peer, CEVNT_BADDATE);
757 return;
759 pp->day = day;
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
765 * with:
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.
780 switch (cmdtype) {
782 case GPGLL:
783 cp = field_parse(pp->a_lastcode, 1);
784 dp = field_parse(cp, 2);
785 break;
787 case GPGGA:
788 cp = field_parse(pp->a_lastcode, 2);
789 dp = field_parse(cp, 2);
790 break;
792 case GPRMC:
793 cp = field_parse(pp->a_lastcode, 3);
794 dp = field_parse(cp, 2);
795 break;
797 case GPZDG_ZDA:
798 default:
799 cp = dp = NULL;
802 /* Blank the entire latitude & longitude. */
803 while (cp) {
804 while (',' != *cp) {
805 if ('.' != *cp)
806 *cp = '_';
807 cp++;
810 /* Longitude at cp then latitude at dp */
811 if (cp < dp)
812 cp = dp;
813 else
814 cp = NULL;
817 /* Blank the checksum, the last two characters */
818 if (dp) {
819 cp = pp->a_lastcode + pp->lencode - 2;
820 if (0 == cp[2])
821 cp[0] = cp[1] = '_';
827 * Note if we're only using GPS timescale from now on.
829 if (cmdtypezdg && !up->gps_time) {
830 up->gps_time = 1;
831 NLOG(NLOG_CLOCKINFO)
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.
840 #ifdef HAVE_PPSAPI
841 up->tcount++;
842 if (peer->flags & FLAG_PPS)
843 return;
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.
856 static void
857 nmea_poll(
858 int unit,
859 struct peer *peer
862 register struct nmeaunit *up;
863 struct refclockproc *pp;
865 pp = peer->procptr;
866 up = (struct nmeaunit *)pp->unitptr;
869 * Process median filter samples. If none received, declare a
870 * timeout and keep going.
872 #ifdef HAVE_PPSAPI
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);
880 return;
882 up->pcount = up->tcount = 0;
883 #else /* HAVE_PPSAPI */
884 if (pp->coderecv == pp->codeproc) {
885 refclock_report(peer, CEVNT_TIMEOUT);
886 return;
888 #endif /* HAVE_PPSAPI */
890 pp->polls++;
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
898 * time.
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
916 static void
917 gps_send(
918 int fd,
919 const char *cmd,
920 struct peer *peer
923 if (write(fd, cmd, strlen(cmd)) == -1) {
924 refclock_report(peer, CEVNT_FAULT);
929 static char *
930 field_parse(
931 char *cp,
932 int fn
935 char *tp;
936 int i = fn;
938 for (tp = cp; i && *tp; tp++)
939 if (*tp == ',')
940 i--;
942 return 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
959 nmea_checksum_ok(
960 const char *sentence
963 u_char my_cs;
964 u_long input_cs;
965 const char *p;
967 my_cs = 0;
968 p = sentence;
970 if ('$' != *p++)
971 return 0;
973 for ( ; *p && '*' != *p; p++) {
975 my_cs ^= *p;
978 if ('*' != *p++)
979 return 0;
981 if (0 == p[0] || 0 == p[1] || 0 != p[2])
982 return 0;
984 if (0 == hextoint(p, &input_cs))
985 return 0;
987 if (my_cs != input_cs)
988 return 0;
990 return 1;
992 #else
993 int refclock_nmea_bs;
994 #endif /* REFCLOCK && CLOCK_NMEA */