refclock_nmea.c revision 54359
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 */
48static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
49static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
50
51/*
52 * Unit control structure
53 */
54struct 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 */
63static	int	nmea_start	P((int, struct peer *));
64static	void	nmea_shutdown	P((int, struct peer *));
65static	void	nmea_receive	P((struct recvbuf *));
66static	void	nmea_poll	P((int, struct peer *));
67static	void	gps_send	P((int, const char *, struct peer *));
68static	char	*field_parse	P((char *, int));
69
70/*
71 * Transfer vector
72 */
73struct	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 */
86static int
87nmea_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 */
140static void
141nmea_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 */
158static void
159nmea_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 */
343static void
344nmea_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 */
379static void
380gps_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
392static char *
393field_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
410int refclock_nmea_bs;
411#endif /* REFCLOCK */
412