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