source: trunk/third/xntp/xntpd/refclock_nmea.c @ 10832

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