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