]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ntp/ntpd/refclock_nmea.c
This commit was generated by cvs2svn to compensate for changes in r58310,
[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(REFCLOCK) && defined(CLOCK_NMEA)
11
12 #include <stdio.h>
13 #include <ctype.h>
14 #include <sys/time.h>
15 #include <time.h>
16
17 #include "ntpd.h"
18 #include "ntp_io.h"
19 #include "ntp_refclock.h"
20 #include "ntp_stdlib.h"
21
22 /*
23  * This driver supports the NMEA GPS Receiver with
24  *
25  * Protype was refclock_trak.c, Thanks a lot.
26  *
27  * The receiver used spits out the NMEA sentences for boat navigation.
28  * And you thought it was an information superhighway.  Try a raging river
29  * filled with rapids and whirlpools that rip away your data and warp time.
30  */
31
32 /*
33  * Definitions
34  */
35 #ifdef SYS_WINNT
36 # define DEVICE "COM%d:"        /* COM 1 - 3 supported */
37 #else
38 # define DEVICE "/dev/gps%d"    /* name of radio device */
39 #endif
40 #define SPEED232        B4800   /* uart speed (4800 bps) */
41 #define PRECISION       (-9)    /* precision assumed (about 2 ms) */
42 #define DCD_PRECISION   (-20)   /* precision assumed (about 1 us) */
43 #define REFID           "GPS\0" /* reference id */
44 #define DESCRIPTION     "NMEA GPS Clock" /* who we are */
45
46 #define LENNMEA         75      /* min timecode length */
47
48 /*
49  * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
50  * leap.
51  */
52 static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
53 static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
54
55 /*
56  * Unit control structure
57  */
58 struct nmeaunit {
59         int     pollcnt;        /* poll message counter */
60         int     polled;         /* Hand in a sample? */
61         l_fp    tstamp;         /* timestamp of last poll */
62 };
63
64 /*
65  * Function prototypes
66  */
67 static  int     nmea_start      P((int, struct peer *));
68 static  void    nmea_shutdown   P((int, struct peer *));
69 static  void    nmea_receive    P((struct recvbuf *));
70 static  void    nmea_poll       P((int, struct peer *));
71 static  void    gps_send        P((int, const char *, struct peer *));
72 static  char    *field_parse    P((char *, int));
73
74 /*
75  * Transfer vector
76  */
77 struct  refclock refclock_nmea = {
78         nmea_start,             /* start up driver */
79         nmea_shutdown,  /* shut down driver */
80         nmea_poll,              /* transmit poll message */
81         noentry,                /* handle control */
82         noentry,                /* initialize driver */
83         noentry,                /* buginfo */
84         NOFLAGS                 /* not used */
85 };
86
87 /*
88  * nmea_start - open the GPS devices and initialize data for processing
89  */
90 static int
91 nmea_start(
92         int unit,
93         struct peer *peer
94         )
95 {
96         register struct nmeaunit *up;
97         struct refclockproc *pp;
98         int fd;
99         char device[20];
100
101         /*
102          * Open serial port. Use CLK line discipline, if available.
103          */
104         (void)sprintf(device, DEVICE, unit);
105
106         if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
107             return (0);
108
109         /*
110          * Allocate and initialize unit structure
111          */
112         if (!(up = (struct nmeaunit *)
113               emalloc(sizeof(struct nmeaunit)))) {
114                 (void) close(fd);
115                 return (0);
116         }
117         memset((char *)up, 0, sizeof(struct nmeaunit));
118         pp = peer->procptr;
119         pp->io.clock_recv = nmea_receive;
120         pp->io.srcclock = (caddr_t)peer;
121         pp->io.datalen = 0;
122         pp->io.fd = fd;
123         if (!io_addclock(&pp->io)) {
124                 (void) close(fd);
125                 free(up);
126                 return (0);
127         }
128         pp->unitptr = (caddr_t)up;
129
130         /*
131          * Initialize miscellaneous variables
132          */
133         peer->precision = DCD_PRECISION;
134         pp->clockdesc = DESCRIPTION;
135         memcpy((char *)&pp->refid, REFID, 4);
136         up->pollcnt = 2;
137         gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
138
139         return (1);
140 }
141
142 /*
143  * nmea_shutdown - shut down a GPS clock
144  */
145 static void
146 nmea_shutdown(
147         int unit,
148         struct peer *peer
149         )
150 {
151         register struct nmeaunit *up;
152         struct refclockproc *pp;
153
154         pp = peer->procptr;
155         up = (struct nmeaunit *)pp->unitptr;
156         io_closeclock(&pp->io);
157         free(up);
158 }
159
160 /*
161  * nmea_receive - receive data from the serial interface
162  */
163 static void
164 nmea_receive(
165         struct recvbuf *rbufp
166         )
167 {
168         register struct nmeaunit *up;
169         struct refclockproc *pp;
170         struct peer *peer;
171         l_fp trtmp;
172         int month, day;
173         int i;
174         char *cp, *dp;
175         int cmdtype;
176
177         /*
178          * Initialize pointers and read the timecode and timestamp
179          */
180         peer = (struct peer *)rbufp->recv_srcclock;
181         pp = peer->procptr;
182         up = (struct nmeaunit *)pp->unitptr;
183         pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
184
185         /*
186          * There is a case that a <CR><LF> gives back a "blank" line
187          */
188         if (pp->lencode == 0)
189             return;
190
191         /*
192          * We get a buffer and timestamp for each <cr>.
193          */
194         pp->lastrec = up->tstamp = trtmp;
195         up->pollcnt = 2;
196 #ifdef DEBUG
197         if (debug)
198             printf("nmea: timecode %d %s\n", pp->lencode,
199                    pp->a_lastcode);
200 #endif
201
202         /*
203          * We check the timecode format and decode its contents. The
204          * we only care about a few of them.  The most important being
205          * the $GPRMC format
206          * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
207          * $GPGGA,162617.0,4548.339,N,00837.719,E,1,07,0.97,00262,M,048,M,,*5D
208          */
209 #define GPRMC   0
210 #define GPXXX   1
211 #define GPGCA   2
212         cp = pp->a_lastcode;
213         cmdtype=0;
214         if(strncmp(cp,"$GPRMC",6)==0) {
215                 cmdtype=GPRMC;
216         }
217         else if(strncmp(cp,"$GPGGA",6)==0) {
218                 cmdtype=GPGCA;
219         }
220         else if(strncmp(cp,"$GPXXX",6)==0) {
221                 cmdtype=GPXXX;
222         }
223         else
224             return;
225
226         switch( cmdtype ) {
227             case GPRMC:
228             case GPGCA:
229                 /*
230                  *      Check time code format of NMEA
231                  */
232
233                 dp = field_parse(cp,1);
234                 if( !isdigit((int)dp[0]) ||
235                     !isdigit((int)dp[1]) ||
236                     !isdigit((int)dp[2]) ||
237                     !isdigit((int)dp[3]) ||
238                     !isdigit((int)dp[4]) ||
239                     !isdigit((int)dp[5])        
240                     ) {
241                         refclock_report(peer, CEVNT_BADREPLY);
242                         return;
243                 }
244
245                 /*
246                  * Test for synchronization.  Check for quality byte.
247                  */
248                 dp = field_parse(cp,2);
249                 if( dp[0] != 'A')  {
250                         refclock_report(peer, CEVNT_BADREPLY);
251                         return;
252                 }
253                 break;
254             case GPXXX:
255                 return;
256             default:
257                 return;
258
259         }
260
261         if (cmdtype ==GPGCA) {
262                 /* only time */
263                 time_t tt = time(NULL);
264                 struct tm * t = gmtime(&tt);
265                 day = t->tm_mday;
266                 month = t->tm_mon + 1;
267                 pp->year= t->tm_year;
268         } else {
269         dp = field_parse(cp,9);
270         /*
271          * Convert date and check values.
272          */
273         day = dp[0] - '0';
274         day = (day * 10) + dp[1] - '0';
275         month = dp[2] - '0';
276         month = (month * 10) + dp[3] - '0';
277         pp->year = dp[4] - '0';
278         pp->year = (pp->year * 10) + dp[5] - '0';
279         }
280
281         if (month < 1 || month > 12 || day < 1) {
282                 refclock_report(peer, CEVNT_BADTIME);
283                 return;
284         }
285
286         if (pp->year % 4) {
287                 if (day > day1tab[month - 1]) {
288                         refclock_report(peer, CEVNT_BADTIME);
289                         return;
290                 }
291                 for (i = 0; i < month - 1; i++)
292                     day += day1tab[i];
293         } else {
294                 if (day > day2tab[month - 1]) {
295                         refclock_report(peer, CEVNT_BADTIME);
296                         return;
297                 }
298                 for (i = 0; i < month - 1; i++)
299                     day += day2tab[i];
300         }
301         pp->day = day;
302
303         dp = field_parse(cp,1);
304         /*
305          * Convert time and check values.
306          */
307         pp->hour = ((dp[0] - '0') * 10) + dp[1] - '0';
308         pp->minute = ((dp[2] - '0') * 10) + dp[3] -  '0';
309         pp->second = ((dp[4] - '0') * 10) + dp[5] - '0';
310         pp->msec = 0; 
311
312         if (pp->hour > 23 || pp->minute > 59 || pp->second > 59) {
313                 refclock_report(peer, CEVNT_BADTIME);
314                 return;
315         }
316
317         /*
318          * Process the new sample in the median filter and determine the
319          * reference clock offset and dispersion. We use lastrec as both
320          * the reference time and receive time, in order to avoid being
321          * cute, like setting the reference time later than the receive
322          * time, which may cause a paranoid protocol module to chuck out
323          * the data.
324          */
325         if (!refclock_process(pp)) {
326                 refclock_report(peer, CEVNT_BADTIME);
327                 return;
328         }
329
330         /*
331          * Only go on if we had been polled.
332          */
333         if (!up->polled)
334             return;
335         up->polled = 0;
336
337         refclock_receive(peer);
338
339         record_clock_stats(&peer->srcadr, pp->a_lastcode);
340 }
341
342 /*
343  * nmea_poll - called by the transmit procedure
344  *
345  * We go to great pains to avoid changing state here, since there may be
346  * more than one eavesdropper receiving the same timecode.
347  */
348 static void
349 nmea_poll(
350         int unit,
351         struct peer *peer
352         )
353 {
354         register struct nmeaunit *up;
355         struct refclockproc *pp;
356
357         pp = peer->procptr;
358         up = (struct nmeaunit *)pp->unitptr;
359         if (up->pollcnt == 0)
360             refclock_report(peer, CEVNT_TIMEOUT);
361         else
362             up->pollcnt--;
363         pp->polls++;
364         up->polled = 1;
365
366         /*
367          * usually nmea_receive can get a timestamp every second
368          */
369
370         gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
371 }
372
373 /*
374  *
375  *      gps_send(fd,cmd, peer)  Sends a command to the GPS receiver.
376  *       as     gps_send(fd,"rqts,u\r", peer);
377  *
378  *      We don't currently send any data, but would like to send
379  *      RTCM SC104 messages for differential positioning. It should
380  *      also give us better time. Without a PPS output, we're
381  *      Just fooling ourselves because of the serial code paths
382  *
383  */
384 static void
385 gps_send(
386         int fd,
387         const char *cmd,
388         struct peer *peer
389         )
390 {
391
392         if (write(fd, cmd, strlen(cmd)) == -1) {
393                 refclock_report(peer, CEVNT_FAULT);
394         }
395 }
396
397 static char *
398 field_parse(
399         char *cp,
400         int fn
401         )
402 {
403         char *tp;
404         int i = fn;
405
406         for (tp = cp; *tp != '\0'; tp++) {
407                 if (*tp == ',')
408                     i--;
409                 if (i == 0)
410                     break;
411         }
412         return (++tp);
413 }
414 #else
415 int refclock_nmea_bs;
416 #endif /* REFCLOCK */