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