/src/ntp-dev/ntpd/refclock_nmea.c
Line | Count | Source |
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 | | * Updated to add support for Accord GPS Clock |
7 | | * Venu Gopal Dec 05, 2007 |
8 | | * neo.venu@gmail.com, venugopal_d@pgad.gov.in |
9 | | * |
10 | | * Updated to process 'time1' fudge factor |
11 | | * Venu Gopal May 05, 2008 |
12 | | * |
13 | | * Converted to common PPSAPI code, separate PPS fudge time1 |
14 | | * from serial timecode fudge time2. |
15 | | * Dave Hart July 1, 2009 |
16 | | * hart@ntp.org, davehart@davehart.com |
17 | | */ |
18 | | |
19 | | #ifdef HAVE_CONFIG_H |
20 | | #include <config.h> |
21 | | #endif |
22 | | |
23 | | #include "ntp_types.h" |
24 | | |
25 | | #if defined(REFCLOCK) && defined(CLOCK_NMEA) |
26 | | |
27 | | #define NMEA_WRITE_SUPPORT 0 /* no write support at the moment */ |
28 | | |
29 | | #include <sys/stat.h> |
30 | | #include <stdio.h> |
31 | | #include <ctype.h> |
32 | | #ifdef HAVE_SYS_SOCKET_H |
33 | | #include <sys/socket.h> |
34 | | #endif |
35 | | |
36 | | #include "ntpd.h" |
37 | | #include "ntp_io.h" |
38 | | #include "ntp_unixtime.h" |
39 | | #include "ntp_refclock.h" |
40 | | #include "ntp_stdlib.h" |
41 | | #include "ntp_calgps.h" |
42 | | #include "timespecops.h" |
43 | | |
44 | | #ifdef HAVE_PPSAPI |
45 | | # include "ppsapi_timepps.h" |
46 | | # include "refclock_atom.h" |
47 | | #endif /* HAVE_PPSAPI */ |
48 | | |
49 | | |
50 | | /* |
51 | | * This driver supports NMEA-compatible GPS receivers |
52 | | * |
53 | | * Prototype was refclock_trak.c, Thanks a lot. |
54 | | * |
55 | | * The receiver used spits out the NMEA sentences for boat navigation. |
56 | | * And you thought it was an information superhighway. Try a raging river |
57 | | * filled with rapids and whirlpools that rip away your data and warp time. |
58 | | * |
59 | | * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in. |
60 | | * On startup if initialization of the PPSAPI fails, it will fall back |
61 | | * to the "normal" timestamps. |
62 | | * |
63 | | * The PPSAPI part of the driver understands fudge flag2 and flag3. If |
64 | | * flag2 is set, it will use the clear edge of the pulse. If flag3 is |
65 | | * set, kernel hardpps is enabled. |
66 | | * |
67 | | * GPS sentences other than RMC (the default) may be enabled by setting |
68 | | * the relevent bits of 'mode' in the server configuration line |
69 | | * server 127.127.20.x mode X |
70 | | * |
71 | | * bit 0 - enables RMC (1) |
72 | | * bit 1 - enables GGA (2) |
73 | | * bit 2 - enables GLL (4) |
74 | | * bit 3 - enables ZDA (8) - Standard Time & Date |
75 | | * bit 3 - enables ZDG (8) - Accord GPS Clock's custom sentence with GPS time |
76 | | * very close to standard ZDA |
77 | | * |
78 | | * Multiple sentences may be selected except when ZDG/ZDA is selected. |
79 | | * |
80 | | * bit 4/5/6 - selects the baudrate for serial port : |
81 | | * 0 for 4800 (default) |
82 | | * 1 for 9600 |
83 | | * 2 for 19200 |
84 | | * 3 for 38400 |
85 | | * 4 for 57600 |
86 | | * 5 for 115200 |
87 | | */ |
88 | 0 | #define NMEA_MESSAGE_MASK 0x0000FF0FU |
89 | 0 | #define NMEA_BAUDRATE_MASK 0x00000070U |
90 | 0 | #define NMEA_BAUDRATE_SHIFT 4 |
91 | | |
92 | 0 | #define NMEA_DELAYMEAS_MASK 0x00000080U |
93 | 0 | #define NMEA_EXTLOG_MASK 0x00010000U |
94 | | #define NMEA_QUIETPPS_MASK 0x00020000U |
95 | 0 | #define NMEA_DATETRUST_MASK 0x00040000U |
96 | 0 | #define NMEA_IGNSTATUS_MASK 0x00080000U |
97 | | |
98 | 0 | #define NMEA_PROTO_IDLEN 4 /* tag name must be at least 4 chars */ |
99 | | #define NMEA_PROTO_MINLEN 6 /* min chars in sentence, excluding CS */ |
100 | 0 | #define NMEA_PROTO_MAXLEN 80 /* max chars in sentence, excluding CS */ |
101 | 0 | #define NMEA_PROTO_FIELDS 32 /* not official; limit on fields per record */ |
102 | | |
103 | | /* |
104 | | * We check the timecode format and decode its contents. We only care |
105 | | * about a few of them, the most important being the $GPRMC format: |
106 | | * |
107 | | * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC |
108 | | * |
109 | | * mode (0,1,2,3) selects sentence ANY/ALL, RMC, GGA, GLL, ZDA |
110 | | * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21 |
111 | | * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F |
112 | | * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77 |
113 | | * |
114 | | * Defining GPZDA to support Standard Time & Date |
115 | | * sentence. The sentence has the following format |
116 | | * |
117 | | * $--ZDA,HHMMSS.SS,DD,MM,YYYY,TH,TM,*CS<CR><LF> |
118 | | * |
119 | | * Apart from the familiar fields, |
120 | | * 'TH' Time zone Hours |
121 | | * 'TM' Time zone Minutes |
122 | | * |
123 | | * Defining GPZDG to support Accord GPS Clock's custom NMEA |
124 | | * sentence. The sentence has the following format |
125 | | * |
126 | | * $GPZDG,HHMMSS.S,DD,MM,YYYY,AA.BB,V*CS<CR><LF> |
127 | | * |
128 | | * It contains the GPS timestamp valid for next PPS pulse. |
129 | | * Apart from the familiar fields, |
130 | | * 'AA.BB' denotes the signal strength( should be < 05.00 ) |
131 | | * 'V' denotes the GPS sync status : |
132 | | * '0' indicates INVALID time, |
133 | | * '1' indicates accuracy of +/-20 ms |
134 | | * '2' indicates accuracy of +/-100 ns |
135 | | * |
136 | | * Defining PGRMF for Garmin GPS Fix Data |
137 | | * $PGRMF,WN,WS,DATE,TIME,LS,LAT,LAT_DIR,LON,LON_DIR,MODE,FIX,SPD,DIR,PDOP,TDOP |
138 | | * WN -- GPS week number (weeks since 1980-01-06, mod 1024) |
139 | | * WS -- GPS seconds in week |
140 | | * LS -- GPS leap seconds, accumulated ( UTC + LS == GPS ) |
141 | | * FIX -- Fix type: 0=nofix, 1=2D, 2=3D |
142 | | * DATE/TIME are standard date/time strings in UTC time scale |
143 | | * |
144 | | * The GPS time can be used to get the full century for the truncated |
145 | | * date spec. |
146 | | */ |
147 | | |
148 | | /* |
149 | | * Definitions |
150 | | */ |
151 | | #define DEVICE "/dev/gps%d" /* GPS serial device */ |
152 | | #define PPSDEV "/dev/gpspps%d" /* PPSAPI device override */ |
153 | 0 | #define SPEED232 B4800 /* uart speed (4800 bps) */ |
154 | 0 | #define PRECISION (-9) /* precision assumed (about 2 ms) */ |
155 | | #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */ |
156 | 0 | #define DATE_HOLD 16 /* seconds to hold on provided GPS date */ |
157 | 0 | #define DATE_HLIM 4 /* when do we take ANY date format */ |
158 | 0 | #define REFID "GPS\0" /* reference id */ |
159 | 0 | #define DESCRIPTION "NMEA GPS Clock" /* who we are */ |
160 | | #ifndef O_NOCTTY |
161 | | #define M_NOCTTY 0 |
162 | | #else |
163 | | #define M_NOCTTY O_NOCTTY |
164 | | #endif |
165 | | #ifndef O_NONBLOCK |
166 | | #define M_NONBLOCK 0 |
167 | | #else |
168 | | #define M_NONBLOCK O_NONBLOCK |
169 | | #endif |
170 | | #define PPSOPENMODE (O_RDWR | M_NOCTTY | M_NONBLOCK) |
171 | | |
172 | | /* NMEA sentence array indexes for those we use */ |
173 | 0 | #define NMEA_GPRMC 0 /* recommended min. nav. */ |
174 | 0 | #define NMEA_GPGGA 1 /* fix and quality */ |
175 | 0 | #define NMEA_GPGLL 2 /* geo. lat/long */ |
176 | 0 | #define NMEA_GPZDA 3 /* date/time */ |
177 | | /* |
178 | | * $GPZDG is a proprietary sentence that violates the spec, by not |
179 | | * using $P and an assigned company identifier to prefix the sentence |
180 | | * identifier. When used with this driver, the system needs to be |
181 | | * isolated from other NTP networks, as it operates in GPS time, not |
182 | | * UTC as is much more common. GPS time is >15 seconds different from |
183 | | * UTC due to not respecting leap seconds since 1970 or so. Other |
184 | | * than the different timebase, $GPZDG is similar to $GPZDA. |
185 | | */ |
186 | 0 | #define NMEA_GPZDG 4 |
187 | 0 | #define NMEA_PGRMF 5 |
188 | 0 | #define NMEA_PUBX04 6 |
189 | | #define NMEA_ARRAY_SIZE (NMEA_PUBX04 + 1) |
190 | | |
191 | | /* |
192 | | * Sentence selection mode bits |
193 | | */ |
194 | | #define USE_GPRMC 0x00000001u |
195 | | #define USE_GPGGA 0x00000002u |
196 | | #define USE_GPGLL 0x00000004u |
197 | | #define USE_GPZDA 0x00000008u |
198 | | #define USE_PGRMF 0x00000100u |
199 | | #define USE_PUBX04 0x00000200u |
200 | | |
201 | | /* mapping from sentence index to controlling mode bit */ |
202 | | static const u_int32 sentence_mode[NMEA_ARRAY_SIZE] = |
203 | | { |
204 | | USE_GPRMC, |
205 | | USE_GPGGA, |
206 | | USE_GPGLL, |
207 | | USE_GPZDA, |
208 | | USE_GPZDA, |
209 | | USE_PGRMF, |
210 | | USE_PUBX04 |
211 | | }; |
212 | | |
213 | | /* date formats we support */ |
214 | | enum date_fmt { |
215 | | DATE_1_DDMMYY, /* use 1 field with 2-digit year */ |
216 | | DATE_3_DDMMYYYY /* use 3 fields with 4-digit year */ |
217 | | }; |
218 | | |
219 | | /* date type */ |
220 | | enum date_type { |
221 | | DTYP_NONE, |
222 | | DTYP_Y2D, /* 2-digit year */ |
223 | | DTYP_W10B, /* 10-bit week in GPS epoch */ |
224 | | DTYP_Y4D, /* 4-digit (full) year */ |
225 | | DTYP_WEXT /* extended week in GPS epoch */ |
226 | | }; |
227 | | |
228 | | /* results for 'field_init()' |
229 | | * |
230 | | * Note: If a checksum is present, the checksum test must pass OK or the |
231 | | * sentence is tagged invalid. |
232 | | */ |
233 | 0 | #define CHECK_EMPTY -1 /* no data */ |
234 | 0 | #define CHECK_INVALID 0 /* not a valid NMEA sentence */ |
235 | 0 | #define CHECK_VALID 1 /* valid but without checksum */ |
236 | 0 | #define CHECK_CSVALID 2 /* valid with checksum OK */ |
237 | | |
238 | | /* |
239 | | * Unit control structure |
240 | | */ |
241 | | struct refclock_atom; |
242 | | typedef struct refclock_atom TAtomUnit; |
243 | | typedef struct { |
244 | | # ifdef HAVE_PPSAPI |
245 | | TAtomUnit atom; /* PPSAPI structure */ |
246 | | int ppsapi_fd; /* fd used with PPSAPI */ |
247 | | u_char ppsapi_tried; /* attempt PPSAPI once */ |
248 | | u_char ppsapi_lit; /* time_pps_create() worked */ |
249 | | # endif /* HAVE_PPSAPI */ |
250 | | uint16_t rcvtout; /* one-shot for sample expiration */ |
251 | | u_char ppsapi_gate; /* system is on PPS */ |
252 | | u_char gps_time; /* use GPS time, not UTC */ |
253 | | l_fp last_reftime; /* last processed reference stamp */ |
254 | | TNtpDatum last_gpsdate; /* last processed split date/time */ |
255 | | u_short hold_gpsdate; /* validity ticker for above */ |
256 | | u_short type_gpsdate; /* date info type for above */ |
257 | | /* tally stats, reset each poll cycle */ |
258 | | struct |
259 | | { |
260 | | u_int total; |
261 | | u_int accepted; |
262 | | u_int rejected; /* GPS said not enough signal */ |
263 | | u_int malformed; /* Bad checksum, invalid date or time */ |
264 | | u_int filtered; /* mode bits, not GPZDG, same second */ |
265 | | u_int pps_used; |
266 | | } |
267 | | tally; |
268 | | /* per sentence checksum seen flag */ |
269 | | u_char cksum_type[NMEA_ARRAY_SIZE]; |
270 | | |
271 | | /* line assembly buffer (NMEAD support) */ |
272 | | u_short lb_len; |
273 | | char lb_buf[BMAX]; /* assembly buffer */ |
274 | | } nmea_unit; |
275 | | |
276 | | /* |
277 | | * helper for faster field access |
278 | | */ |
279 | | typedef struct { |
280 | | char *base; /* buffer base */ |
281 | | char *cptr; /* current field ptr */ |
282 | | int blen; /* buffer length */ |
283 | | int cidx; /* current field index */ |
284 | | } nmea_data; |
285 | | |
286 | | /* |
287 | | * Function prototypes |
288 | | */ |
289 | | static int nmea_start (int, struct peer *); |
290 | | static void nmea_shutdown (int, struct peer *); |
291 | | static void nmea_receive (struct recvbuf *); |
292 | | static void nmea_poll (int, struct peer *); |
293 | | static void nmea_procrec (struct peer * const, l_fp); |
294 | | #ifdef HAVE_PPSAPI |
295 | | static double tabsdiffd (l_fp, l_fp); |
296 | | static void nmea_control (int, const struct refclockstat *, |
297 | | struct refclockstat *, struct peer *); |
298 | | #define NMEA_CONTROL nmea_control |
299 | | #else |
300 | | #define NMEA_CONTROL noentry |
301 | | #endif /* HAVE_PPSAPI */ |
302 | | static void nmea_timer (int, struct peer *); |
303 | | |
304 | | /* parsing helpers */ |
305 | | static int field_init (nmea_data * data, char * cp, int len); |
306 | | static char * field_parse (nmea_data * data, int fn); |
307 | | static void field_wipe (nmea_data * data, ...); |
308 | | static u_char parse_qual (nmea_data * data, int idx, |
309 | | char tag, int inv); |
310 | | static int parse_time (TCivilDate * jd, l_fp * fofs, |
311 | | nmea_data *, int idx); |
312 | | static int parse_date (TCivilDate * jd, nmea_data *, |
313 | | int idx, enum date_fmt fmt); |
314 | | static int parse_gpsw (TGpsDatum *, nmea_data *, |
315 | | int weekidx, int timeidx, int leapidx); |
316 | | |
317 | | static int nmead_open (const char * device); |
318 | | |
319 | | /* |
320 | | * If we want the driver to output sentences, too: re-enable the send |
321 | | * support functions by defining NMEA_WRITE_SUPPORT to non-zero... |
322 | | */ |
323 | | #if NMEA_WRITE_SUPPORT |
324 | | static void gps_send(int, const char *, struct peer *); |
325 | | #endif /* NMEA_WRITE_SUPPORT */ |
326 | | |
327 | | /* |
328 | | * ------------------------------------------------------------------- |
329 | | * Transfer vector |
330 | | * ------------------------------------------------------------------- |
331 | | */ |
332 | | struct refclock refclock_nmea = { |
333 | | nmea_start, /* start up driver */ |
334 | | nmea_shutdown, /* shut down driver */ |
335 | | nmea_poll, /* transmit poll message */ |
336 | | NMEA_CONTROL, /* fudge control */ |
337 | | noentry, /* initialize driver */ |
338 | | noentry, /* buginfo */ |
339 | | nmea_timer /* called once per second */ |
340 | | }; |
341 | | |
342 | | |
343 | | /* |
344 | | * ------------------------------------------------------------------- |
345 | | * nmea_start - open the GPS devices and initialize data for processing |
346 | | * |
347 | | * return 0 on error, 1 on success. Even on error the peer structures |
348 | | * must be in a state that permits 'nmea_shutdown()' to clean up all |
349 | | * resources, because it will be called immediately to do so. |
350 | | * ------------------------------------------------------------------- |
351 | | */ |
352 | | static int |
353 | | nmea_start( |
354 | | int unit, |
355 | | struct peer * peer |
356 | | ) |
357 | 0 | { |
358 | 0 | struct refclockproc * const pp = peer->procptr; |
359 | 0 | nmea_unit * const up = emalloc_zero(sizeof(*up)); |
360 | 0 | char device[20]; |
361 | 0 | size_t devlen; |
362 | 0 | u_int32 rate; |
363 | 0 | int baudrate; |
364 | | |
365 | | /* Get baudrate choice from mode byte bits 4/5/6 */ |
366 | 0 | rate = (peer->ttl & NMEA_BAUDRATE_MASK) >> NMEA_BAUDRATE_SHIFT; |
367 | |
|
368 | 0 | switch (rate) { |
369 | 0 | default: |
370 | 0 | case 0: |
371 | 0 | baudrate = SPEED232; |
372 | 0 | break; |
373 | 0 | case 1: |
374 | 0 | baudrate = B9600; |
375 | 0 | break; |
376 | 0 | case 2: |
377 | 0 | baudrate = B19200; |
378 | 0 | break; |
379 | 0 | case 3: |
380 | 0 | baudrate = B38400; |
381 | 0 | break; |
382 | 0 | # ifdef B57600 |
383 | 0 | case 4: |
384 | 0 | baudrate = B57600; |
385 | 0 | break; |
386 | 0 | # endif |
387 | 0 | # ifdef B115200 |
388 | 0 | case 5: |
389 | 0 | baudrate = B115200; |
390 | 0 | break; |
391 | 0 | # endif |
392 | 0 | } |
393 | | |
394 | | /* Allocate and initialize unit structure */ |
395 | 0 | pp->unitptr = (caddr_t)up; |
396 | 0 | pp->io.fd = -1; |
397 | 0 | pp->io.clock_recv = nmea_receive; |
398 | 0 | pp->io.srcclock = peer; |
399 | 0 | pp->io.datalen = 0; |
400 | | /* force change detection on first valid message */ |
401 | 0 | memset(&up->last_reftime, 0xFF, sizeof(up->last_reftime)); |
402 | 0 | memset(&up->last_gpsdate, 0x00, sizeof(up->last_gpsdate)); |
403 | | /* force checksum on GPRMC, see below */ |
404 | 0 | up->cksum_type[NMEA_GPRMC] = CHECK_CSVALID; |
405 | | # ifdef HAVE_PPSAPI |
406 | | up->ppsapi_fd = -1; |
407 | | # endif /* HAVE_PPSAPI */ |
408 | 0 | ZERO(up->tally); |
409 | | |
410 | | /* Initialize miscellaneous variables */ |
411 | 0 | peer->precision = PRECISION; |
412 | 0 | pp->clockdesc = DESCRIPTION; |
413 | 0 | memcpy(&pp->refid, REFID, 4); |
414 | | |
415 | | /* Open serial port. Use CLK line discipline, if available. */ |
416 | 0 | devlen = snprintf(device, sizeof(device), DEVICE, unit); |
417 | 0 | if (devlen >= sizeof(device)) { |
418 | 0 | msyslog(LOG_ERR, "%s clock device name too long", |
419 | 0 | refnumtoa(&peer->srcadr)); |
420 | 0 | return FALSE; /* buffer overflow */ |
421 | 0 | } |
422 | 0 | pp->io.fd = refclock_open(&peer->srcadr, device, baudrate, LDISC_CLK); |
423 | 0 | if (0 >= pp->io.fd) { |
424 | 0 | pp->io.fd = nmead_open(device); |
425 | 0 | if (-1 == pp->io.fd) |
426 | 0 | return FALSE; |
427 | 0 | } |
428 | | |
429 | | /* succeed if this clock can be added */ |
430 | 0 | return io_addclock(&pp->io) != 0; |
431 | 0 | } |
432 | | |
433 | | /* |
434 | | * ------------------------------------------------------------------- |
435 | | * nmea_shutdown - shut down a GPS clock |
436 | | * |
437 | | * NOTE this routine is called after nmea_start() returns failure, |
438 | | * as well as during a normal shutdown due to ntpq :config unpeer. |
439 | | * ------------------------------------------------------------------- |
440 | | */ |
441 | | static void |
442 | | nmea_shutdown( |
443 | | int unit, |
444 | | struct peer * peer |
445 | | ) |
446 | 0 | { |
447 | 0 | struct refclockproc * const pp = peer->procptr; |
448 | 0 | nmea_unit * const up = (nmea_unit *)pp->unitptr; |
449 | |
|
450 | 0 | UNUSED_ARG(unit); |
451 | |
|
452 | 0 | if (up != NULL) { |
453 | | # ifdef HAVE_PPSAPI |
454 | | if (up->ppsapi_lit) |
455 | | time_pps_destroy(up->atom.handle); |
456 | | ppsdev_close(pp->io.fd, up->ppsapi_fd); |
457 | | # endif |
458 | 0 | free(up); |
459 | 0 | } |
460 | 0 | pp->unitptr = (caddr_t)NULL; |
461 | 0 | if (-1 != pp->io.fd) |
462 | 0 | io_closeclock(&pp->io); |
463 | 0 | pp->io.fd = -1; |
464 | 0 | } |
465 | | |
466 | | /* |
467 | | * ------------------------------------------------------------------- |
468 | | * nmea_control - configure fudge params |
469 | | * ------------------------------------------------------------------- |
470 | | */ |
471 | | #ifdef HAVE_PPSAPI |
472 | | static void |
473 | | nmea_control( |
474 | | int unit, |
475 | | const struct refclockstat * in_st, |
476 | | struct refclockstat * out_st, |
477 | | struct peer * peer |
478 | | ) |
479 | | { |
480 | | struct refclockproc * const pp = peer->procptr; |
481 | | nmea_unit * const up = (nmea_unit *)pp->unitptr; |
482 | | |
483 | | char device[32]; |
484 | | size_t devlen; |
485 | | |
486 | | UNUSED_ARG(in_st); |
487 | | UNUSED_ARG(out_st); |
488 | | |
489 | | /* |
490 | | * PPS control |
491 | | * |
492 | | * If /dev/gpspps$UNIT can be opened that will be used for |
493 | | * PPSAPI. On Linux, a PPS device mathing the TTY will be |
494 | | * searched for and possibly created on the fly. Otherwise, the |
495 | | * GPS serial device /dev/gps$UNIT already opened is used for |
496 | | * PPSAPI as well. (This might not work, in which case the PPS |
497 | | * API remains unavailable...) |
498 | | */ |
499 | | |
500 | | /* Light up the PPSAPI interface if not yet attempted. */ |
501 | | if ((CLK_FLAG1 & pp->sloppyclockflag) && !up->ppsapi_tried) { |
502 | | const char *ppsname = device; |
503 | | up->ppsapi_tried = TRUE; |
504 | | /* get FD for the pps device; might be the tty itself! */ |
505 | | devlen = snprintf(device, sizeof(device), PPSDEV, unit); |
506 | | if (devlen >= sizeof(device)) { |
507 | | msyslog(LOG_ERR, "%s PPS device name too long", |
508 | | refnumtoa(&peer->srcadr)); |
509 | | ppsname = NULL; |
510 | | } |
511 | | up->ppsapi_fd = ppsdev_reopen( |
512 | | &peer->srcadr, |
513 | | pp->io.fd, up->ppsapi_fd, |
514 | | ppsname, PPSOPENMODE, (S_IRUSR|S_IWUSR)); |
515 | | /* note 1: the pps fd might be the same as the tty fd |
516 | | * note 2: the current PPS fd remains valid until |
517 | | * - the clock is shut down |
518 | | * - flag1 is set again after being cleared |
519 | | */ |
520 | | if (refclock_ppsapi(up->ppsapi_fd, &up->atom)) { |
521 | | /* use the PPS API for our own purposes now. */ |
522 | | up->ppsapi_lit = refclock_params( |
523 | | pp->sloppyclockflag, &up->atom); |
524 | | if (!up->ppsapi_lit) { |
525 | | /* failed to configure, drop PPS unit */ |
526 | | time_pps_destroy(up->atom.handle); |
527 | | msyslog(LOG_WARNING, |
528 | | "%s set PPSAPI params fails", |
529 | | refnumtoa(&peer->srcadr)); |
530 | | } |
531 | | } else { |
532 | | msyslog(LOG_WARNING, |
533 | | "%s flag1 1 but PPSAPI fails", |
534 | | refnumtoa(&peer->srcadr)); |
535 | | } |
536 | | } |
537 | | |
538 | | /* shut down PPS API if activated */ |
539 | | if ( !(CLK_FLAG1 & pp->sloppyclockflag) && up->ppsapi_tried) { |
540 | | /* shutdown PPS API */ |
541 | | if (up->ppsapi_lit) |
542 | | time_pps_destroy(up->atom.handle); |
543 | | up->atom.handle = 0; |
544 | | /* do !!NOT!! close/drop PPS fd here! */ |
545 | | |
546 | | /* clear markers and peer items */ |
547 | | up->ppsapi_gate = FALSE; |
548 | | up->ppsapi_lit = FALSE; |
549 | | up->ppsapi_tried = FALSE; |
550 | | |
551 | | peer->flags &= ~FLAG_PPS; |
552 | | peer->precision = PRECISION; |
553 | | } |
554 | | } |
555 | | #endif /* HAVE_PPSAPI */ |
556 | | |
557 | | /* |
558 | | * ------------------------------------------------------------------- |
559 | | * nmea_timer - called once per second |
560 | | * |
561 | | * Usually 'nmea_receive()' can get a timestamp every second, but at |
562 | | * least one Motorola unit needs prompting each time. Doing so in |
563 | | * 'nmea_poll()' gives only one sample per poll cycle, which actually |
564 | | * defeats the purpose of the median filter. Polling once per second |
565 | | * seems a much better idea. |
566 | | * |
567 | | * Also takes care of sample expiration if the receiver fails to |
568 | | * provide new input data. |
569 | | * ------------------------------------------------------------------- |
570 | | */ |
571 | | static void |
572 | | nmea_timer( |
573 | | int unit, |
574 | | struct peer * peer |
575 | | ) |
576 | 0 | { |
577 | 0 | struct refclockproc * const pp = peer->procptr; |
578 | 0 | nmea_unit * const up = (nmea_unit *)pp->unitptr; |
579 | |
|
580 | 0 | UNUSED_ARG(unit); |
581 | |
|
582 | | # if NMEA_WRITE_SUPPORT |
583 | | |
584 | | if (-1 != pp->io.fd) /* any mode bits to evaluate here? */ |
585 | | gps_send(pp->io.fd, "$PMOTG,RMC,0000*1D\r\n", peer); |
586 | | |
587 | | # endif /* NMEA_WRITE_SUPPORT */ |
588 | | |
589 | | /* receive timeout occurred? */ |
590 | 0 | if (up->rcvtout) { |
591 | 0 | --up->rcvtout; |
592 | 0 | } else if (pp->codeproc != pp->coderecv) { |
593 | | /* expire one (the oldest) sample, if any */ |
594 | 0 | refclock_samples_expire(pp, 1); |
595 | | /* reset message assembly buffer */ |
596 | 0 | up->lb_buf[0] = '\0'; |
597 | 0 | up->lb_len = 0; |
598 | 0 | } |
599 | |
|
600 | 0 | if (up->hold_gpsdate && (--up->hold_gpsdate < DATE_HLIM)) |
601 | 0 | up->type_gpsdate = DTYP_NONE; |
602 | 0 | } |
603 | | |
604 | | /* |
605 | | * ------------------------------------------------------------------- |
606 | | * nmea_procrec - receive data from the serial interface |
607 | | * |
608 | | * This is the workhorse for NMEA data evaluation: |
609 | | * |
610 | | * + it checks all NMEA data, and rejects sentences that are not valid |
611 | | * NMEA sentences |
612 | | * + it checks whether a sentence is known and to be used |
613 | | * + it parses the time and date data from the NMEA data string and |
614 | | * augments the missing bits. (century in date, whole date, ...) |
615 | | * + it rejects data that is not from the first accepted sentence in a |
616 | | * burst |
617 | | * + it eventually replaces the receive time with the PPS edge time. |
618 | | * + it feeds the data to the internal processing stages. |
619 | | * |
620 | | * This function assumes a non-empty line in the unit line buffer. |
621 | | * ------------------------------------------------------------------- |
622 | | */ |
623 | | static void |
624 | | nmea_procrec( |
625 | | struct peer * const peer, |
626 | | l_fp rd_timestamp |
627 | | ) |
628 | 0 | { |
629 | | /* declare & init control structure pointers */ |
630 | 0 | struct refclockproc * const pp = peer->procptr; |
631 | 0 | nmea_unit * const up = (nmea_unit*)pp->unitptr; |
632 | | |
633 | | /* Use these variables to hold data until we decide its worth keeping */ |
634 | 0 | nmea_data rdata; |
635 | 0 | l_fp rd_reftime; |
636 | | |
637 | | /* working stuff */ |
638 | 0 | TCivilDate date; /* to keep & convert the time stamp */ |
639 | 0 | TGpsDatum wgps; /* week time storage */ |
640 | 0 | TNtpDatum dntp; |
641 | 0 | l_fp tofs; /* offset to full-second reftime */ |
642 | | /* results of sentence/date/time parsing */ |
643 | 0 | u_char sentence; /* sentence tag */ |
644 | 0 | int checkres; |
645 | 0 | int warp; /* warp to GPS base date */ |
646 | 0 | char * cp; |
647 | 0 | int rc_date, rc_time; |
648 | 0 | u_short rc_dtyp; |
649 | | # ifdef HAVE_PPSAPI |
650 | | int withpps = 0; |
651 | | # endif /* HAVE_PPSAPI */ |
652 | | |
653 | | /* make sure data has defined pristine state */ |
654 | 0 | ZERO(tofs); |
655 | 0 | ZERO(date); |
656 | 0 | ZERO(wgps); |
657 | 0 | ZERO(dntp); |
658 | | |
659 | | /* |
660 | | * Read the timecode and timestamp, then initialize field |
661 | | * processing. The <CR><LF> at the NMEA line end is translated |
662 | | * to <LF><LF> by the terminal input routines on most systems, |
663 | | * and this gives us one spurious empty read per record which we |
664 | | * better ignore silently. |
665 | | */ |
666 | 0 | checkres = field_init(&rdata, up->lb_buf, up->lb_len); |
667 | 0 | switch (checkres) { |
668 | | |
669 | 0 | case CHECK_INVALID: |
670 | 0 | DPRINTF(1, ("%s invalid data: '%s'\n", |
671 | 0 | refnumtoa(&peer->srcadr), up->lb_buf)); |
672 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
673 | 0 | return; |
674 | | |
675 | 0 | case CHECK_EMPTY: |
676 | 0 | return; |
677 | | |
678 | 0 | default: |
679 | 0 | DPRINTF(1, ("%s gpsread: %d '%s'\n", |
680 | 0 | refnumtoa(&peer->srcadr), up->lb_len, |
681 | 0 | up->lb_buf)); |
682 | 0 | break; |
683 | 0 | } |
684 | 0 | up->tally.total++; |
685 | | |
686 | | /* |
687 | | * --> below this point we have a valid NMEA sentence <-- |
688 | | * |
689 | | * Check sentence name. Skip first 2 chars (talker ID) in most |
690 | | * cases, to allow for $GLGGA and $GPGGA etc. Since the name |
691 | | * field has at least 5 chars we can simply shift the field |
692 | | * start. |
693 | | */ |
694 | 0 | cp = field_parse(&rdata, 0); |
695 | 0 | if (strncmp(cp + 2, "RMC,", 4) == 0) |
696 | 0 | sentence = NMEA_GPRMC; |
697 | 0 | else if (strncmp(cp + 2, "GGA,", 4) == 0) |
698 | 0 | sentence = NMEA_GPGGA; |
699 | 0 | else if (strncmp(cp + 2, "GLL,", 4) == 0) |
700 | 0 | sentence = NMEA_GPGLL; |
701 | 0 | else if (strncmp(cp + 2, "ZDA,", 4) == 0) |
702 | 0 | sentence = NMEA_GPZDA; |
703 | 0 | else if (strncmp(cp + 2, "ZDG,", 4) == 0) |
704 | 0 | sentence = NMEA_GPZDG; |
705 | 0 | else if (strncmp(cp, "PGRMF,", 6) == 0) |
706 | 0 | sentence = NMEA_PGRMF; |
707 | 0 | else if (strncmp(cp, "PUBX,04,", 8) == 0) |
708 | 0 | sentence = NMEA_PUBX04; |
709 | 0 | else |
710 | 0 | return; /* not something we know about */ |
711 | | |
712 | | /* Eventually output delay measurement now. */ |
713 | 0 | if (peer->ttl & NMEA_DELAYMEAS_MASK) { |
714 | 0 | mprintf_clock_stats(&peer->srcadr, "delay %0.6f %.*s", |
715 | 0 | ldexp(rd_timestamp.l_uf, -32), |
716 | 0 | (int)(strchr(up->lb_buf, ',') - up->lb_buf), |
717 | 0 | up->lb_buf); |
718 | 0 | } |
719 | | |
720 | | /* See if I want to process this message type */ |
721 | 0 | if ((peer->ttl & NMEA_MESSAGE_MASK) && |
722 | 0 | !(peer->ttl & sentence_mode[sentence])) { |
723 | 0 | up->tally.filtered++; |
724 | 0 | return; |
725 | 0 | } |
726 | | |
727 | | /* |
728 | | * make sure it came in clean |
729 | | * |
730 | | * Apparently, older NMEA specifications (which are expensive) |
731 | | * did not require the checksum for all sentences. $GPMRC is |
732 | | * the only one so far identified which has always been required |
733 | | * to include a checksum. |
734 | | * |
735 | | * Today, most NMEA GPS receivers checksum every sentence. To |
736 | | * preserve its error-detection capabilities with modern GPSes |
737 | | * while allowing operation without checksums on all but $GPMRC, |
738 | | * we keep track of whether we've ever seen a valid checksum on |
739 | | * a given sentence, and if so, reject future instances without |
740 | | * checksum. ('up->cksum_type[NMEA_GPRMC]' is set in |
741 | | * 'nmea_start()' to enforce checksums for $GPRMC right from the |
742 | | * start.) |
743 | | */ |
744 | 0 | if (up->cksum_type[sentence] <= (u_char)checkres) { |
745 | 0 | up->cksum_type[sentence] = (u_char)checkres; |
746 | 0 | } else { |
747 | 0 | DPRINTF(1, ("%s checksum missing: '%s'\n", |
748 | 0 | refnumtoa(&peer->srcadr), up->lb_buf)); |
749 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
750 | 0 | up->tally.malformed++; |
751 | 0 | return; |
752 | 0 | } |
753 | | |
754 | | /* |
755 | | * $GPZDG provides GPS time not UTC, and the two mix poorly. |
756 | | * Once have processed a $GPZDG, do not process any further UTC |
757 | | * sentences (all but $GPZDG currently). |
758 | | */ |
759 | 0 | if (sentence == NMEA_GPZDG) { |
760 | 0 | if (!up->gps_time) { |
761 | 0 | msyslog(LOG_INFO, |
762 | 0 | "%s using GPS time as if it were UTC", |
763 | 0 | refnumtoa(&peer->srcadr)); |
764 | 0 | up->gps_time = 1; |
765 | 0 | } |
766 | 0 | } else { |
767 | 0 | if (up->gps_time) { |
768 | 0 | up->tally.filtered++; |
769 | 0 | return; |
770 | 0 | } |
771 | 0 | } |
772 | | |
773 | 0 | DPRINTF(1, ("%s processing %d bytes, timecode '%s'\n", |
774 | 0 | refnumtoa(&peer->srcadr), up->lb_len, up->lb_buf)); |
775 | | |
776 | | /* |
777 | | * Grab fields depending on clock string type and possibly wipe |
778 | | * sensitive data from the last timecode. |
779 | | */ |
780 | 0 | rc_date = -1; /* assume we have to do day-time mapping */ |
781 | 0 | rc_dtyp = DTYP_NONE; |
782 | 0 | switch (sentence) { |
783 | | |
784 | 0 | case NMEA_GPRMC: |
785 | | /* Check quality byte, fetch data & time */ |
786 | 0 | rc_time = parse_time(&date, &tofs, &rdata, 1); |
787 | 0 | pp->leap = parse_qual(&rdata, 2, 'A', 0); |
788 | 0 | if (up->type_gpsdate <= DTYP_Y2D) { |
789 | 0 | rc_date = parse_date(&date, &rdata, 9, DATE_1_DDMMYY); |
790 | 0 | rc_dtyp = DTYP_Y2D; |
791 | 0 | } |
792 | 0 | if (CLK_FLAG4 & pp->sloppyclockflag) |
793 | 0 | field_wipe(&rdata, 3, 4, 5, 6, -1); |
794 | 0 | break; |
795 | | |
796 | 0 | case NMEA_GPGGA: |
797 | | /* Check quality byte, fetch time only */ |
798 | 0 | rc_time = parse_time(&date, &tofs, &rdata, 1); |
799 | 0 | pp->leap = parse_qual(&rdata, 6, '0', 1); |
800 | 0 | if (CLK_FLAG4 & pp->sloppyclockflag) |
801 | 0 | field_wipe(&rdata, 2, 4, -1); |
802 | 0 | break; |
803 | | |
804 | 0 | case NMEA_GPGLL: |
805 | | /* Check quality byte, fetch time only */ |
806 | 0 | rc_time = parse_time(&date, &tofs, &rdata, 5); |
807 | 0 | pp->leap = parse_qual(&rdata, 6, 'A', 0); |
808 | 0 | if (CLK_FLAG4 & pp->sloppyclockflag) |
809 | 0 | field_wipe(&rdata, 1, 3, -1); |
810 | 0 | break; |
811 | | |
812 | 0 | case NMEA_GPZDA: |
813 | | /* No quality. Assume best, fetch time & full date */ |
814 | 0 | rc_time = parse_time(&date, &tofs, &rdata, 1); |
815 | 0 | if (up->type_gpsdate <= DTYP_Y4D) { |
816 | 0 | rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY); |
817 | 0 | rc_dtyp = DTYP_Y4D; |
818 | 0 | } |
819 | 0 | break; |
820 | | |
821 | 0 | case NMEA_GPZDG: |
822 | | /* Check quality byte, fetch time & full date */ |
823 | 0 | rc_time = parse_time(&date, &tofs, &rdata, 1); |
824 | 0 | pp->leap = parse_qual(&rdata, 4, '0', 1); |
825 | 0 | --tofs.l_ui; /* GPZDG gives *following* second */ |
826 | 0 | if (up->type_gpsdate <= DTYP_Y4D) { |
827 | 0 | rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY); |
828 | 0 | rc_dtyp = DTYP_Y4D; |
829 | 0 | } |
830 | 0 | break; |
831 | | |
832 | 0 | case NMEA_PGRMF: |
833 | | /* get time, qualifier and GPS weektime. */ |
834 | 0 | rc_time = parse_time(&date, &tofs, &rdata, 4); |
835 | 0 | if (up->type_gpsdate <= DTYP_W10B) { |
836 | 0 | rc_date = parse_gpsw(&wgps, &rdata, 1, 2, 5); |
837 | 0 | rc_dtyp = DTYP_W10B; |
838 | 0 | } |
839 | 0 | pp->leap = parse_qual(&rdata, 11, '0', 1); |
840 | 0 | if (CLK_FLAG4 & pp->sloppyclockflag) |
841 | 0 | field_wipe(&rdata, 6, 8, -1); |
842 | 0 | break; |
843 | | |
844 | 0 | case NMEA_PUBX04: |
845 | | /* PUBX,04 is peculiar. The UTC time-of-week is the *internal* |
846 | | * time base, which is not exactly on par with the fix time. |
847 | | */ |
848 | 0 | rc_time = parse_time(&date, &tofs, &rdata, 2); |
849 | 0 | if (up->type_gpsdate <= DTYP_WEXT) { |
850 | 0 | rc_date = parse_gpsw(&wgps, &rdata, 5, 4, -1); |
851 | 0 | rc_dtyp = DTYP_WEXT; |
852 | 0 | } |
853 | 0 | break; |
854 | | |
855 | 0 | default: |
856 | 0 | INVARIANT(0); /* Coverity 97123 */ |
857 | 0 | return; |
858 | 0 | } |
859 | | |
860 | | /* ignore receiver status? [bug 3694] */ |
861 | 0 | if (peer->ttl & NMEA_IGNSTATUS_MASK) { /* assume always good? */ |
862 | 0 | pp->leap = LEAP_NOWARNING; |
863 | 0 | } |
864 | | |
865 | | /* check clock sanity; [bug 2143] */ |
866 | 0 | if (pp->leap == LEAP_NOTINSYNC) { /* no good status? */ |
867 | 0 | checkres = CEVNT_PROP; |
868 | 0 | up->tally.rejected++; |
869 | 0 | } |
870 | | /* Check sanity of time-of-day. */ |
871 | 0 | else if (rc_time == 0) { /* no time or conversion error? */ |
872 | 0 | checkres = CEVNT_BADTIME; |
873 | 0 | up->tally.malformed++; |
874 | 0 | } |
875 | | /* Check sanity of date. */ |
876 | 0 | else if (rc_date == 0) { /* no date or conversion error? */ |
877 | 0 | checkres = CEVNT_BADDATE; |
878 | 0 | up->tally.malformed++; |
879 | 0 | } |
880 | 0 | else { |
881 | 0 | checkres = -1; |
882 | 0 | } |
883 | |
|
884 | 0 | if (checkres != -1) { |
885 | 0 | refclock_save_lcode(pp, up->lb_buf, up->lb_len); |
886 | 0 | refclock_report(peer, checkres); |
887 | 0 | return; |
888 | 0 | } |
889 | | |
890 | | /* See if we can augment the receive time stamp. If not, apply |
891 | | * fudge time 2 to the receive time stamp directly. |
892 | | */ |
893 | | # ifdef HAVE_PPSAPI |
894 | | if (up->ppsapi_lit && pp->leap != LEAP_NOTINSYNC) |
895 | | withpps = refclock_ppsaugment( |
896 | | &up->atom, &rd_timestamp, |
897 | | pp->fudgetime2, pp->fudgetime1); |
898 | | else |
899 | | # endif /* HAVE_PPSAPI */ |
900 | 0 | rd_timestamp = ntpfp_with_fudge( |
901 | 0 | rd_timestamp, pp->fudgetime2); |
902 | | |
903 | | /* set the GPS base date, if possible */ |
904 | 0 | warp = !(peer->ttl & NMEA_DATETRUST_MASK); |
905 | 0 | if (rc_dtyp != DTYP_NONE) { |
906 | 0 | DPRINTF(1, ("%s saving date, type=%hu\n", |
907 | 0 | refnumtoa(&peer->srcadr), rc_dtyp)); |
908 | 0 | switch (rc_dtyp) { |
909 | 0 | case DTYP_W10B: |
910 | 0 | up->last_gpsdate = gpsntp_from_gpscal_ex( |
911 | 0 | &wgps, (warp = TRUE)); |
912 | 0 | break; |
913 | 0 | case DTYP_WEXT: |
914 | 0 | up->last_gpsdate = gpsntp_from_gpscal_ex( |
915 | 0 | &wgps, warp); |
916 | 0 | break; |
917 | 0 | default: |
918 | 0 | up->last_gpsdate = gpsntp_from_calendar_ex( |
919 | 0 | &date, tofs, warp); |
920 | 0 | break; |
921 | 0 | } |
922 | 0 | up->type_gpsdate = rc_dtyp; |
923 | 0 | up->hold_gpsdate = DATE_HOLD; |
924 | 0 | } |
925 | | /* now convert and possibly extend/expand the time stamp. */ |
926 | 0 | if (up->hold_gpsdate) { /* time of day, based */ |
927 | 0 | dntp = gpsntp_from_daytime2_ex( |
928 | 0 | &date, tofs, &up->last_gpsdate, warp); |
929 | 0 | } else { /* time of day, floating */ |
930 | 0 | dntp = gpsntp_from_daytime1_ex( |
931 | 0 | &date, tofs, rd_timestamp, warp); |
932 | 0 | } |
933 | |
|
934 | 0 | if (debug) { |
935 | | /* debug print time stamp */ |
936 | 0 | gpsntp_to_calendar(&date, &dntp); |
937 | | # ifdef HAVE_PPSAPI |
938 | | DPRINTF(1, ("%s effective timecode: %s (%s PPS)\n", |
939 | | refnumtoa(&peer->srcadr), |
940 | | ntpcal_iso8601std(NULL, 0, &date), |
941 | | (withpps ? "with" : "without"))); |
942 | | # else /* ?HAVE_PPSAPI */ |
943 | 0 | DPRINTF(1, ("%s effective timecode: %s\n", |
944 | 0 | refnumtoa(&peer->srcadr), |
945 | 0 | ntpcal_iso8601std(NULL, 0, &date))); |
946 | 0 | # endif /* !HAVE_PPSAPI */ |
947 | 0 | } |
948 | | |
949 | | /* Get the reference time stamp from the calendar buffer. |
950 | | * Process the new sample in the median filter and determine the |
951 | | * timecode timestamp, but only if the PPS is not in control. |
952 | | * Discard sentence if reference time did not change. |
953 | | */ |
954 | 0 | rd_reftime = ntpfp_from_ntpdatum(&dntp); |
955 | 0 | if (L_ISEQU(&up->last_reftime, &rd_reftime)) { |
956 | | /* Do not touch pp->a_lastcode on purpose! */ |
957 | 0 | up->tally.filtered++; |
958 | 0 | return; |
959 | 0 | } |
960 | 0 | up->last_reftime = rd_reftime; |
961 | |
|
962 | 0 | DPRINTF(1, ("%s using '%s'\n", |
963 | 0 | refnumtoa(&peer->srcadr), up->lb_buf)); |
964 | | |
965 | | /* Data will be accepted. Update stats & log data. */ |
966 | 0 | up->tally.accepted++; |
967 | 0 | refclock_save_lcode(pp, up->lb_buf, up->lb_len); |
968 | 0 | pp->lastrec = rd_timestamp; |
969 | | |
970 | | /* If we have PPS augmented receive time, we *must* have a |
971 | | * working PPS source and we must set the flags accordingly. |
972 | | */ |
973 | | # ifdef HAVE_PPSAPI |
974 | | if (withpps) { |
975 | | up->ppsapi_gate = TRUE; |
976 | | peer->precision = PPS_PRECISION; |
977 | | if (tabsdiffd(rd_reftime, rd_timestamp) < 0.5) { |
978 | | if ( ! (peer->ttl & NMEA_QUIETPPS_MASK)) |
979 | | peer->flags |= FLAG_PPS; |
980 | | DPRINTF(2, ("%s PPS_RELATE_PHASE\n", |
981 | | refnumtoa(&peer->srcadr))); |
982 | | up->tally.pps_used++; |
983 | | } else { |
984 | | DPRINTF(2, ("%s PPS_RELATE_EDGE\n", |
985 | | refnumtoa(&peer->srcadr))); |
986 | | } |
987 | | /* !Note! 'FLAG_PPS' is reset in 'nmea_poll()' */ |
988 | | } |
989 | | # endif /* HAVE_PPSAPI */ |
990 | | /* Whether the receive time stamp is PPS-augmented or not, |
991 | | * the proper fudge offset is already applied. There's no |
992 | | * residual fudge to process. |
993 | | */ |
994 | 0 | refclock_process_offset(pp, rd_reftime, rd_timestamp, 0.0); |
995 | 0 | up->rcvtout = 2; |
996 | 0 | } |
997 | | |
998 | | /* |
999 | | * ------------------------------------------------------------------- |
1000 | | * nmea_receive - receive data from the serial interface |
1001 | | * |
1002 | | * With serial IO only, a single call to 'refclock_gtlin()' to get the |
1003 | | * string would suffice to get the NMEA data. When using NMEAD, this |
1004 | | * does unfortunately no longer hold, since TCP is stream oriented and |
1005 | | * not line oriented, and there's no one to do the line-splitting work |
1006 | | * of the TTY driver in line/cooked mode. |
1007 | | * |
1008 | | * So we have to do this manually here, and we have to live with the |
1009 | | * fact that there could be more than one sentence in a receive buffer. |
1010 | | * Likewise, there can be partial messages on either end. (Strictly |
1011 | | * speaking, a receive buffer could also contain just a single fragment, |
1012 | | * though that's unlikely.) |
1013 | | * |
1014 | | * We deal with that by scanning the input buffer, copying bytes from |
1015 | | * the receive buffer to the assembly buffer as we go and calling the |
1016 | | * record processor every time we hit a CR/LF, provided the resulting |
1017 | | * line is not empty. Any leftovers are kept for the next round. |
1018 | | * |
1019 | | * Note: When used with a serial data stream, there's no change to the |
1020 | | * previous line-oriented input: One line is copied to the buffer and |
1021 | | * processed per call. Only with NMEAD the behavior changes, and the |
1022 | | * timing is badly affected unless a PPS channel is also associated with |
1023 | | * the clock instance. TCP leaves us nothing to improve on here. |
1024 | | * ------------------------------------------------------------------- |
1025 | | */ |
1026 | | static void |
1027 | | nmea_receive( |
1028 | | struct recvbuf * rbufp |
1029 | | ) |
1030 | 0 | { |
1031 | | /* declare & init control structure pointers */ |
1032 | 0 | struct peer * const peer = rbufp->recv_peer; |
1033 | 0 | struct refclockproc * const pp = peer->procptr; |
1034 | 0 | nmea_unit * const up = (nmea_unit*)pp->unitptr; |
1035 | |
|
1036 | 0 | const char *sp, *se; |
1037 | 0 | char *dp, *de; |
1038 | | |
1039 | | /* paranoia check: */ |
1040 | 0 | if (up->lb_len >= sizeof(up->lb_buf)) |
1041 | 0 | up->lb_len = 0; |
1042 | | |
1043 | | /* pick up last assembly position; leave room for NUL */ |
1044 | 0 | dp = up->lb_buf + up->lb_len; |
1045 | 0 | de = up->lb_buf + sizeof(up->lb_buf) - 1; |
1046 | | /* set up input range */ |
1047 | 0 | sp = (const char *)rbufp->recv_buffer; |
1048 | 0 | se = sp + rbufp->recv_length; |
1049 | | |
1050 | | /* walk over the input data, dropping parity bits and control |
1051 | | * chars as we go, and calling the record processor for each |
1052 | | * complete non-empty line. |
1053 | | */ |
1054 | 0 | while (sp != se) { |
1055 | 0 | char ch = (*sp++ & 0x7f); |
1056 | 0 | if (dp == up->lb_buf) { |
1057 | 0 | if (ch == '$') |
1058 | 0 | *dp++ = ch; |
1059 | 0 | } else if (dp > de) { |
1060 | 0 | dp = up->lb_buf; |
1061 | 0 | } else if (ch == '\n' || ch == '\r') { |
1062 | 0 | *dp = '\0'; |
1063 | 0 | up->lb_len = (int)(dp - up->lb_buf); |
1064 | 0 | dp = up->lb_buf; |
1065 | 0 | nmea_procrec(peer, rbufp->recv_time); |
1066 | 0 | } else if (ch >= 0x20 && ch < 0x7f) { |
1067 | 0 | *dp++ = ch; |
1068 | 0 | } |
1069 | 0 | } |
1070 | | /* update state to keep for next round */ |
1071 | 0 | *dp = '\0'; |
1072 | 0 | up->lb_len = (int)(dp - up->lb_buf); |
1073 | 0 | } |
1074 | | |
1075 | | /* |
1076 | | * ------------------------------------------------------------------- |
1077 | | * nmea_poll - called by the transmit procedure |
1078 | | * |
1079 | | * Does the necessary bookkeeping stuff to keep the reported state of |
1080 | | * the clock in sync with reality. |
1081 | | * |
1082 | | * We go to great pains to avoid changing state here, since there may |
1083 | | * be more than one eavesdropper receiving the same timecode. |
1084 | | * ------------------------------------------------------------------- |
1085 | | */ |
1086 | | static void |
1087 | | nmea_poll( |
1088 | | int unit, |
1089 | | struct peer * peer |
1090 | | ) |
1091 | 0 | { |
1092 | 0 | struct refclockproc * const pp = peer->procptr; |
1093 | 0 | nmea_unit * const up = (nmea_unit *)pp->unitptr; |
1094 | | |
1095 | | /* |
1096 | | * Process median filter samples. If none received, declare a |
1097 | | * timeout and keep going. |
1098 | | */ |
1099 | | # ifdef HAVE_PPSAPI |
1100 | | /* |
1101 | | * If we don't have PPS pulses and time stamps, turn PPS down |
1102 | | * for now. |
1103 | | */ |
1104 | | if (!up->ppsapi_gate) { |
1105 | | peer->flags &= ~FLAG_PPS; |
1106 | | peer->precision = PRECISION; |
1107 | | } else { |
1108 | | up->ppsapi_gate = FALSE; |
1109 | | } |
1110 | | # endif /* HAVE_PPSAPI */ |
1111 | | |
1112 | | /* |
1113 | | * If the median filter is empty, claim a timeout. Else process |
1114 | | * the input data and keep the stats going. |
1115 | | */ |
1116 | 0 | if (pp->coderecv == pp->codeproc) { |
1117 | 0 | peer->flags &= ~FLAG_PPS; |
1118 | 0 | if (pp->currentstatus < CEVNT_TIMEOUT) |
1119 | 0 | refclock_report(peer, CEVNT_TIMEOUT); |
1120 | 0 | memset(&up->last_gpsdate, 0, sizeof(up->last_gpsdate)); |
1121 | 0 | } else { |
1122 | 0 | pp->polls++; |
1123 | 0 | pp->lastref = pp->lastrec; |
1124 | 0 | refclock_receive(peer); |
1125 | 0 | if (pp->currentstatus > CEVNT_NOMINAL) |
1126 | 0 | refclock_report(peer, CEVNT_NOMINAL); |
1127 | 0 | } |
1128 | | |
1129 | | /* |
1130 | | * If extended logging is required, write the tally stats to the |
1131 | | * clockstats file; otherwise just do a normal clock stats |
1132 | | * record. Clear the tally stats anyway. |
1133 | | */ |
1134 | 0 | if (peer->ttl & NMEA_EXTLOG_MASK) { |
1135 | | /* Log & reset counters with extended logging */ |
1136 | 0 | const char *nmea = pp->a_lastcode; |
1137 | 0 | if (*nmea == '\0') nmea = "(none)"; |
1138 | 0 | mprintf_clock_stats( |
1139 | 0 | &peer->srcadr, "%s %u %u %u %u %u %u", |
1140 | 0 | nmea, |
1141 | 0 | up->tally.total, up->tally.accepted, |
1142 | 0 | up->tally.rejected, up->tally.malformed, |
1143 | 0 | up->tally.filtered, up->tally.pps_used); |
1144 | 0 | } else { |
1145 | 0 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
1146 | 0 | } |
1147 | 0 | ZERO(up->tally); |
1148 | 0 | } |
1149 | | |
1150 | | #if NMEA_WRITE_SUPPORT |
1151 | | /* |
1152 | | * ------------------------------------------------------------------- |
1153 | | * gps_send(fd, cmd, peer) Sends a command to the GPS receiver. |
1154 | | * as in gps_send(fd, "rqts,u", peer); |
1155 | | * |
1156 | | * If 'cmd' starts with a '$' it is assumed that this command is in raw |
1157 | | * format, that is, starts with '$', ends with '<cr><lf>' and that any |
1158 | | * checksum is correctly provided; the command will be send 'as is' in |
1159 | | * that case. Otherwise the function will create the necessary frame |
1160 | | * (start char, chksum, final CRLF) on the fly. |
1161 | | * |
1162 | | * We don't currently send any data, but would like to send RTCM SC104 |
1163 | | * messages for differential positioning. It should also give us better |
1164 | | * time. Without a PPS output, we're Just fooling ourselves because of |
1165 | | * the serial code paths |
1166 | | * ------------------------------------------------------------------- |
1167 | | */ |
1168 | | static void |
1169 | | gps_send( |
1170 | | int fd, |
1171 | | const char * cmd, |
1172 | | struct peer * peer |
1173 | | ) |
1174 | | { |
1175 | | /* $...*xy<CR><LF><NUL> add 7 */ |
1176 | | char buf[NMEA_PROTO_MAXLEN + 7]; |
1177 | | int len; |
1178 | | u_char dcs; |
1179 | | const u_char *beg, *end; |
1180 | | |
1181 | | if (*cmd != '$') { |
1182 | | /* get checksum and length */ |
1183 | | beg = end = (const u_char*)cmd; |
1184 | | dcs = 0; |
1185 | | while (*end >= ' ' && *end != '*') |
1186 | | dcs ^= *end++; |
1187 | | len = end - beg; |
1188 | | /* format into output buffer with overflow check */ |
1189 | | len = snprintf(buf, sizeof(buf), "$%.*s*%02X\r\n", |
1190 | | len, beg, dcs); |
1191 | | if ((size_t)len >= sizeof(buf)) { |
1192 | | DPRINTF(1, ("%s gps_send: buffer overflow for command '%s'\n", |
1193 | | refnumtoa(&peer->srcadr), cmd)); |
1194 | | return; /* game over player 1 */ |
1195 | | } |
1196 | | cmd = buf; |
1197 | | } else { |
1198 | | len = strlen(cmd); |
1199 | | } |
1200 | | |
1201 | | DPRINTF(1, ("%s gps_send: '%.*s'\n", refnumtoa(&peer->srcadr), |
1202 | | len - 2, cmd)); |
1203 | | |
1204 | | /* send out the whole stuff */ |
1205 | | if (refclock_fdwrite(peer, fd, cmd, len) != len) |
1206 | | refclock_report(peer, CEVNT_FAULT); |
1207 | | } |
1208 | | #endif /* NMEA_WRITE_SUPPORT */ |
1209 | | |
1210 | | /* |
1211 | | * ------------------------------------------------------------------- |
1212 | | * helpers for faster field splitting |
1213 | | * ------------------------------------------------------------------- |
1214 | | * |
1215 | | * set up a field record, check syntax and verify checksum |
1216 | | * |
1217 | | * format is $XXXXX,1,2,3,4*ML |
1218 | | * |
1219 | | * 8-bit XOR of characters between $ and * noninclusive is transmitted |
1220 | | * in last two chars M and L holding most and least significant nibbles |
1221 | | * in hex representation such as: |
1222 | | * |
1223 | | * $GPGLL,5057.970,N,00146.110,E,142451,A*27 |
1224 | | * $GPVTG,089.0,T,,,15.2,N,,*7F |
1225 | | * |
1226 | | * Some other constraints: |
1227 | | * + The field name must be at least 5 upcase characters or digits and |
1228 | | * must start with a character. |
1229 | | * + The checksum (if present) must be uppercase hex digits. |
1230 | | * + The length of a sentence is limited to 80 characters (not including |
1231 | | * the final CR/LF nor the checksum, but including the leading '$') |
1232 | | * |
1233 | | * Return values: |
1234 | | * + CHECK_INVALID |
1235 | | * The data does not form a valid NMEA sentence or a checksum error |
1236 | | * occurred. |
1237 | | * + CHECK_VALID |
1238 | | * The data is a valid NMEA sentence but contains no checksum. |
1239 | | * + CHECK_CSVALID |
1240 | | * The data is a valid NMEA sentence and passed the checksum test. |
1241 | | * ------------------------------------------------------------------- |
1242 | | */ |
1243 | | static int |
1244 | | field_init( |
1245 | | nmea_data * data, /* context structure */ |
1246 | | char * cptr, /* start of raw data */ |
1247 | | int dlen /* data len, not counting trailing NUL */ |
1248 | | ) |
1249 | 0 | { |
1250 | 0 | u_char cs_l; /* checksum local computed */ |
1251 | 0 | u_char cs_r; /* checksum remote given */ |
1252 | 0 | char * eptr; /* buffer end end pointer */ |
1253 | 0 | char tmp; /* char buffer */ |
1254 | |
|
1255 | 0 | cs_l = 0; |
1256 | 0 | cs_r = 0; |
1257 | | /* some basic input constraints */ |
1258 | 0 | if (dlen < 0) |
1259 | 0 | dlen = 0; |
1260 | 0 | eptr = cptr + dlen; |
1261 | 0 | *eptr = '\0'; |
1262 | | |
1263 | | /* load data context */ |
1264 | 0 | data->base = cptr; |
1265 | 0 | data->cptr = cptr; |
1266 | 0 | data->cidx = 0; |
1267 | 0 | data->blen = dlen; |
1268 | | |
1269 | | /* syntax check follows here. check allowed character |
1270 | | * sequences, updating the local computed checksum as we go. |
1271 | | * |
1272 | | * regex equiv: '^\$[A-Z][A-Z0-9]{4,}[^*]*(\*[0-9A-F]{2})?$' |
1273 | | */ |
1274 | | |
1275 | | /* -*- start character: '^\$' */ |
1276 | 0 | if (*cptr == '\0') |
1277 | 0 | return CHECK_EMPTY; |
1278 | 0 | if (*cptr++ != '$') |
1279 | 0 | return CHECK_INVALID; |
1280 | | |
1281 | | /* -*- advance context beyond start character */ |
1282 | 0 | data->base++; |
1283 | 0 | data->cptr++; |
1284 | 0 | data->blen--; |
1285 | | |
1286 | | /* -*- field name: '[A-Z][A-Z0-9]{4,},' */ |
1287 | 0 | if (*cptr < 'A' || *cptr > 'Z') |
1288 | 0 | return CHECK_INVALID; |
1289 | 0 | cs_l ^= *cptr++; |
1290 | 0 | while ((*cptr >= 'A' && *cptr <= 'Z') || |
1291 | 0 | (*cptr >= '0' && *cptr <= '9') ) |
1292 | 0 | cs_l ^= *cptr++; |
1293 | 0 | if (*cptr != ',' || (cptr - data->base) < NMEA_PROTO_IDLEN) |
1294 | 0 | return CHECK_INVALID; |
1295 | 0 | cs_l ^= *cptr++; |
1296 | | |
1297 | | /* -*- data: '[^*]*' */ |
1298 | 0 | while (*cptr && *cptr != '*') |
1299 | 0 | cs_l ^= *cptr++; |
1300 | | |
1301 | | /* -*- checksum field: (\*[0-9A-F]{2})?$ */ |
1302 | 0 | if (*cptr == '\0') |
1303 | 0 | return CHECK_VALID; |
1304 | 0 | if (*cptr != '*' || cptr != eptr - 3 || |
1305 | 0 | (cptr - data->base) >= NMEA_PROTO_MAXLEN) |
1306 | 0 | return CHECK_INVALID; |
1307 | | |
1308 | 0 | for (cptr++; (tmp = *cptr) != '\0'; cptr++) { |
1309 | 0 | if (tmp >= '0' && tmp <= '9') |
1310 | 0 | cs_r = (cs_r << 4) + (tmp - '0'); |
1311 | 0 | else if (tmp >= 'A' && tmp <= 'F') |
1312 | 0 | cs_r = (cs_r << 4) + (tmp - 'A' + 10); |
1313 | 0 | else |
1314 | 0 | break; |
1315 | 0 | } |
1316 | | |
1317 | | /* -*- make sure we are at end of string and csum matches */ |
1318 | 0 | if (cptr != eptr || cs_l != cs_r) |
1319 | 0 | return CHECK_INVALID; |
1320 | | |
1321 | 0 | return CHECK_CSVALID; |
1322 | 0 | } |
1323 | | |
1324 | | /* |
1325 | | * ------------------------------------------------------------------- |
1326 | | * fetch a data field by index, zero being the name field. If this |
1327 | | * function is called repeatedly with increasing indices, the total load |
1328 | | * is O(n), n being the length of the string; if it is called with |
1329 | | * decreasing indices, the total load is O(n^2). Try not to go backwards |
1330 | | * too often. |
1331 | | * ------------------------------------------------------------------- |
1332 | | */ |
1333 | | static char * |
1334 | | field_parse( |
1335 | | nmea_data * data, |
1336 | | int fn |
1337 | | ) |
1338 | 0 | { |
1339 | 0 | char tmp; |
1340 | |
|
1341 | 0 | if (fn < data->cidx) { |
1342 | 0 | data->cidx = 0; |
1343 | 0 | data->cptr = data->base; |
1344 | 0 | } |
1345 | 0 | while ((fn > data->cidx) && (tmp = *data->cptr) != '\0') { |
1346 | 0 | data->cidx += (tmp == ','); |
1347 | 0 | data->cptr++; |
1348 | 0 | } |
1349 | 0 | return data->cptr; |
1350 | 0 | } |
1351 | | |
1352 | | /* |
1353 | | * ------------------------------------------------------------------- |
1354 | | * Wipe (that is, overwrite with '_') data fields and the checksum in |
1355 | | * the last timecode. The list of field indices is given as integers |
1356 | | * in a varargs list, preferably in ascending order, in any case |
1357 | | * terminated by a negative field index. |
1358 | | * |
1359 | | * A maximum number of 8 fields can be overwritten at once to guard |
1360 | | * against runaway (that is, unterminated) argument lists. |
1361 | | * |
1362 | | * This function affects what a remote user can see with |
1363 | | * |
1364 | | * ntpq -c clockvar <server> |
1365 | | * |
1366 | | * Note that this also removes the wiped fields from any clockstats |
1367 | | * log. Some NTP operators monitor their NMEA GPS using the change in |
1368 | | * location in clockstats over time as as a proxy for the quality of |
1369 | | * GPS reception and thereby time reported. |
1370 | | * ------------------------------------------------------------------- |
1371 | | */ |
1372 | | static void |
1373 | | field_wipe( |
1374 | | nmea_data * data, |
1375 | | ... |
1376 | | ) |
1377 | 0 | { |
1378 | 0 | va_list va; /* vararg index list */ |
1379 | 0 | int fcnt; /* safeguard against runaway arglist */ |
1380 | 0 | int fidx; /* field to nuke, or -1 for checksum */ |
1381 | 0 | char * cp; /* overwrite destination */ |
1382 | |
|
1383 | 0 | fcnt = 8; |
1384 | 0 | cp = NULL; |
1385 | 0 | va_start(va, data); |
1386 | 0 | do { |
1387 | 0 | fidx = va_arg(va, int); |
1388 | 0 | if (fidx >= 0 && fidx <= NMEA_PROTO_FIELDS) { |
1389 | 0 | cp = field_parse(data, fidx); |
1390 | 0 | } else { |
1391 | 0 | cp = data->base + data->blen; |
1392 | 0 | if (data->blen >= 3 && cp[-3] == '*') |
1393 | 0 | cp -= 2; |
1394 | 0 | } |
1395 | 0 | for ( ; '\0' != *cp && '*' != *cp && ',' != *cp; cp++) |
1396 | 0 | if ('.' != *cp) |
1397 | 0 | *cp = '_'; |
1398 | 0 | } while (fcnt-- && fidx >= 0); |
1399 | 0 | va_end(va); |
1400 | 0 | } |
1401 | | |
1402 | | /* |
1403 | | * ------------------------------------------------------------------- |
1404 | | * PARSING HELPERS |
1405 | | * ------------------------------------------------------------------- |
1406 | | */ |
1407 | | typedef unsigned char const UCC; |
1408 | | |
1409 | | static char const * const s_eof_chars = ",*\r\n"; |
1410 | | |
1411 | | #ifdef DEBUG |
1412 | | static int field_length(UCC *cp, unsigned int nfields) |
1413 | 0 | { |
1414 | 0 | char const * ep = (char const*)cp; |
1415 | 0 | ep = strpbrk(ep, s_eof_chars); |
1416 | 0 | if (ep && nfields) |
1417 | 0 | while (--nfields && ep && *ep == ',') |
1418 | 0 | ep = strpbrk(ep + 1, s_eof_chars); |
1419 | 0 | return (ep) |
1420 | 0 | ? (int)((UCC*)ep - cp) |
1421 | 0 | : (int)strlen((char const*)cp); |
1422 | 0 | } |
1423 | | #endif /* DEBUG */ |
1424 | | |
1425 | | /* /[,*\r\n]/ --> skip */ |
1426 | | static int _parse_eof(UCC *cp, UCC ** ep) |
1427 | 0 | { |
1428 | 0 | int rc = (strchr(s_eof_chars, *(char const*)cp) != NULL); |
1429 | 0 | *ep = cp + rc; |
1430 | 0 | return rc; |
1431 | 0 | } |
1432 | | |
1433 | | /* /,/ --> skip */ |
1434 | | static int _parse_sep(UCC *cp, UCC ** ep) |
1435 | 0 | { |
1436 | 0 | int rc = (*cp == ','); |
1437 | 0 | *ep = cp + rc; |
1438 | 0 | return rc; |
1439 | 0 | } |
1440 | | |
1441 | | /* /[[:digit:]]{2}/ --> uint16_t */ |
1442 | | static int _parse_num2d(UCC *cp, UCC ** ep, uint16_t *into) |
1443 | 0 | { |
1444 | 0 | int rc = FALSE; |
1445 | |
|
1446 | 0 | if (isdigit(cp[0]) && isdigit(cp[1])) { |
1447 | 0 | *into = (cp[0] - '0') * 10 + (cp[1] - '0'); |
1448 | 0 | cp += 2; |
1449 | 0 | rc = TRUE; |
1450 | 0 | } |
1451 | 0 | *ep = cp; |
1452 | 0 | return rc; |
1453 | 0 | } |
1454 | | |
1455 | | /* /[[:digit:]]+/ --> uint16_t */ |
1456 | | static int _parse_u16(UCC *cp, UCC **ep, uint16_t *into, unsigned int ndig) |
1457 | 0 | { |
1458 | 0 | uint16_t num = 0; |
1459 | 0 | int rc = FALSE; |
1460 | 0 | if (isdigit(*cp) && ndig) { |
1461 | 0 | rc = TRUE; |
1462 | 0 | do |
1463 | 0 | num = (num * 10) + (*cp - '0'); |
1464 | 0 | while (isdigit(*++cp) && --ndig); |
1465 | 0 | *into = num; |
1466 | 0 | } |
1467 | 0 | *ep = cp; |
1468 | 0 | return rc; |
1469 | 0 | } |
1470 | | |
1471 | | /* /[[:digit:]]+/ --> uint32_t */ |
1472 | | static int _parse_u32(UCC *cp, UCC **ep, uint32_t *into, unsigned int ndig) |
1473 | 0 | { |
1474 | 0 | uint32_t num = 0; |
1475 | 0 | int rc = FALSE; |
1476 | 0 | if (isdigit(*cp) && ndig) { |
1477 | 0 | rc = TRUE; |
1478 | 0 | do |
1479 | 0 | num = (num * 10) + (*cp - '0'); |
1480 | 0 | while (isdigit(*++cp) && --ndig); |
1481 | 0 | *into = num; |
1482 | 0 | } |
1483 | 0 | *ep = cp; |
1484 | 0 | return rc; |
1485 | 0 | } |
1486 | | |
1487 | | /* /(\.[[:digit:]]*)?/ --> l_fp{0, f} |
1488 | | * read fractional seconds, convert to l_fp |
1489 | | * |
1490 | | * Only the first 9 decimal digits are evaluated; any excess is parsed |
1491 | | * away but silently ignored. (--> truncation to 1 nanosecond) |
1492 | | */ |
1493 | | static int _parse_frac(UCC *cp, UCC **ep, l_fp *into) |
1494 | 0 | { |
1495 | 0 | static const uint32_t powtab[10] = { |
1496 | 0 | 0, |
1497 | 0 | 100000000, 10000000, 1000000, |
1498 | 0 | 100000, 10000, 1000, |
1499 | 0 | 100, 10, 1 |
1500 | 0 | }; |
1501 | |
|
1502 | 0 | struct timespec ts; |
1503 | 0 | ZERO(ts); |
1504 | 0 | if (*cp == '.') { |
1505 | 0 | uint32_t fval = 0; |
1506 | 0 | UCC * sp = cp + 1; |
1507 | 0 | if (_parse_u32(sp, &cp, &fval, 9)) |
1508 | 0 | ts.tv_nsec = fval * powtab[(size_t)(cp - sp)]; |
1509 | 0 | while (isdigit(*cp)) |
1510 | 0 | ++cp; |
1511 | 0 | } |
1512 | |
|
1513 | 0 | *ep = cp; |
1514 | 0 | *into = tspec_intv_to_lfp(ts); |
1515 | 0 | return TRUE; |
1516 | 0 | } |
1517 | | |
1518 | | /* /[[:digit:]]{6}/ --> time-of-day |
1519 | | * parses a number string representing 'HHMMSS' |
1520 | | */ |
1521 | | static int _parse_time(UCC *cp, UCC ** ep, TCivilDate *into) |
1522 | 0 | { |
1523 | 0 | uint16_t s, m, h; |
1524 | 0 | int rc; |
1525 | 0 | UCC * xp = cp; |
1526 | |
|
1527 | 0 | rc = _parse_num2d(cp, &cp, &h) && (h < 24) |
1528 | 0 | && _parse_num2d(cp, &cp, &m) && (m < 60) |
1529 | 0 | && _parse_num2d(cp, &cp, &s) && (s < 61); /* leap seconds! */ |
1530 | |
|
1531 | 0 | if (rc) { |
1532 | 0 | into->hour = (uint8_t)h; |
1533 | 0 | into->minute = (uint8_t)m; |
1534 | 0 | into->second = (uint8_t)s; |
1535 | 0 | *ep = cp; |
1536 | 0 | } else { |
1537 | 0 | *ep = xp; |
1538 | 0 | DPRINTF(1, ("nmea: invalid time code: '%.*s'\n", |
1539 | 0 | field_length(xp, 1), xp)); |
1540 | 0 | } |
1541 | 0 | return rc; |
1542 | 0 | } |
1543 | | |
1544 | | /* /[[:digit:]]{6}/ --> civil date |
1545 | | * parses a number string representing 'ddmmyy' |
1546 | | */ |
1547 | | static int _parse_date1(UCC *cp, UCC **ep, TCivilDate *into) |
1548 | 0 | { |
1549 | 0 | unsigned short d, m, y; |
1550 | 0 | int rc; |
1551 | 0 | UCC * xp = cp; |
1552 | |
|
1553 | 0 | rc = _parse_num2d(cp, &cp, &d) && (d - 1 < 31) |
1554 | 0 | && _parse_num2d(cp, &cp, &m) && (m - 1 < 12) |
1555 | 0 | && _parse_num2d(cp, &cp, &y) |
1556 | 0 | && _parse_eof(cp, ep); |
1557 | 0 | if (rc) { |
1558 | 0 | into->monthday = (uint8_t )d; |
1559 | 0 | into->month = (uint8_t )m; |
1560 | 0 | into->year = (uint16_t)y; |
1561 | 0 | *ep = cp; |
1562 | 0 | } else { |
1563 | 0 | *ep = xp; |
1564 | 0 | DPRINTF(1, ("nmea: invalid date code: '%.*s'\n", |
1565 | 0 | field_length(xp, 1), xp)); |
1566 | 0 | } |
1567 | 0 | return rc; |
1568 | 0 | } |
1569 | | |
1570 | | /* /[[:digit:]]+,[[:digit:]]+,[[:digit:]]+/ --> civil date |
1571 | | * parses three successive numeric fields as date: day,month,year |
1572 | | */ |
1573 | | static int _parse_date3(UCC *cp, UCC **ep, TCivilDate *into) |
1574 | 0 | { |
1575 | 0 | uint16_t d, m, y; |
1576 | 0 | int rc; |
1577 | 0 | UCC * xp = cp; |
1578 | |
|
1579 | 0 | rc = _parse_u16(cp, &cp, &d, 2) && (d - 1 < 31) |
1580 | 0 | && _parse_sep(cp, &cp) |
1581 | 0 | && _parse_u16(cp, &cp, &m, 2) && (m - 1 < 12) |
1582 | 0 | && _parse_sep(cp, &cp) |
1583 | 0 | && _parse_u16(cp, &cp, &y, 4) && (y > 1980) |
1584 | 0 | && _parse_eof(cp, ep); |
1585 | 0 | if (rc) { |
1586 | 0 | into->monthday = (uint8_t )d; |
1587 | 0 | into->month = (uint8_t )m; |
1588 | 0 | into->year = (uint16_t)y; |
1589 | 0 | *ep = cp; |
1590 | 0 | } else { |
1591 | 0 | *ep = xp; |
1592 | 0 | DPRINTF(1, ("nmea: invalid date code: '%.*s'\n", |
1593 | 0 | field_length(xp, 3), xp)); |
1594 | 0 | } |
1595 | 0 | return rc; |
1596 | 0 | } |
1597 | | |
1598 | | /* |
1599 | | * ------------------------------------------------------------------- |
1600 | | * Check sync status |
1601 | | * |
1602 | | * If the character at the data field start matches the tag value, |
1603 | | * return LEAP_NOWARNING and LEAP_NOTINSYNC otherwise. If the 'inverted' |
1604 | | * flag is given, just the opposite value is returned. If there is no |
1605 | | * data field (*cp points to the NUL byte) the result is LEAP_NOTINSYNC. |
1606 | | * ------------------------------------------------------------------- |
1607 | | */ |
1608 | | static u_char |
1609 | | parse_qual( |
1610 | | nmea_data * rd, |
1611 | | int idx, |
1612 | | char tag, |
1613 | | int inv |
1614 | | ) |
1615 | 0 | { |
1616 | 0 | static const u_char table[2] = { |
1617 | 0 | LEAP_NOTINSYNC, LEAP_NOWARNING }; |
1618 | |
|
1619 | 0 | char * dp = field_parse(rd, idx); |
1620 | |
|
1621 | 0 | return table[ *dp && ((*dp == tag) == !inv) ]; |
1622 | 0 | } |
1623 | | |
1624 | | /* |
1625 | | * ------------------------------------------------------------------- |
1626 | | * Parse a time stamp in HHMMSS[.sss] format with error checking. |
1627 | | * |
1628 | | * returns 1 on success, 0 on failure |
1629 | | * ------------------------------------------------------------------- |
1630 | | */ |
1631 | | static int |
1632 | | parse_time( |
1633 | | struct calendar * jd, /* result calendar pointer */ |
1634 | | l_fp * fofs, /* storage for nsec fraction */ |
1635 | | nmea_data * rd, |
1636 | | int idx |
1637 | | ) |
1638 | 0 | { |
1639 | 0 | UCC * dp = (UCC*)field_parse(rd, idx); |
1640 | |
|
1641 | 0 | return _parse_time(dp, &dp, jd) |
1642 | 0 | && _parse_frac(dp, &dp, fofs) |
1643 | 0 | && _parse_eof (dp, &dp); |
1644 | 0 | } |
1645 | | |
1646 | | /* |
1647 | | * ------------------------------------------------------------------- |
1648 | | * Parse a date string from an NMEA sentence. This could either be a |
1649 | | * partial date in DDMMYY format in one field, or DD,MM,YYYY full date |
1650 | | * spec spanning three fields. This function does some extensive error |
1651 | | * checking to make sure the date string was consistent. |
1652 | | * |
1653 | | * returns 1 on success, 0 on failure |
1654 | | * ------------------------------------------------------------------- |
1655 | | */ |
1656 | | static int |
1657 | | parse_date( |
1658 | | struct calendar * jd, /* result pointer */ |
1659 | | nmea_data * rd, |
1660 | | int idx, |
1661 | | enum date_fmt fmt |
1662 | | ) |
1663 | 0 | { |
1664 | 0 | UCC * dp = (UCC*)field_parse(rd, idx); |
1665 | |
|
1666 | 0 | switch (fmt) { |
1667 | 0 | case DATE_1_DDMMYY: |
1668 | 0 | return _parse_date1(dp, &dp, jd); |
1669 | 0 | case DATE_3_DDMMYYYY: |
1670 | 0 | return _parse_date3(dp, &dp, jd); |
1671 | 0 | default: |
1672 | 0 | DPRINTF(1, ("nmea: invalid parse format: %d\n", fmt)); |
1673 | 0 | break; |
1674 | 0 | } |
1675 | 0 | return FALSE; |
1676 | 0 | } |
1677 | | |
1678 | | /* |
1679 | | * ------------------------------------------------------------------- |
1680 | | * Parse GPS week time info from an NMEA sentence. This info contains |
1681 | | * the GPS week number, the GPS time-of-week and the leap seconds GPS |
1682 | | * to UTC. |
1683 | | * |
1684 | | * returns 1 on success, 0 on failure |
1685 | | * ------------------------------------------------------------------- |
1686 | | */ |
1687 | | static int |
1688 | | parse_gpsw( |
1689 | | TGpsDatum * wd, |
1690 | | nmea_data * rd, |
1691 | | int weekidx, |
1692 | | int timeidx, |
1693 | | int leapidx |
1694 | | ) |
1695 | 0 | { |
1696 | 0 | uint32_t secs; |
1697 | 0 | uint16_t week, leap = 0; |
1698 | 0 | l_fp fofs; |
1699 | 0 | int rc; |
1700 | |
|
1701 | 0 | UCC * dpw = (UCC*)field_parse(rd, weekidx); |
1702 | 0 | UCC * dps = (UCC*)field_parse(rd, timeidx); |
1703 | |
|
1704 | 0 | rc = _parse_u16 (dpw, &dpw, &week, 5) |
1705 | 0 | && _parse_eof (dpw, &dpw) |
1706 | 0 | && _parse_u32 (dps, &dps, &secs, 9) |
1707 | 0 | && _parse_frac(dps, &dps, &fofs) |
1708 | 0 | && _parse_eof (dps, &dps) |
1709 | 0 | && (secs < 7*SECSPERDAY); |
1710 | 0 | if (rc && leapidx > 0) { |
1711 | 0 | UCC * dpl = (UCC*)field_parse(rd, leapidx); |
1712 | 0 | rc = _parse_u16 (dpl, &dpl, &leap, 5) |
1713 | 0 | && _parse_eof (dpl, &dpl); |
1714 | 0 | } |
1715 | 0 | if (rc) { |
1716 | 0 | fofs.l_ui -= leap; |
1717 | 0 | *wd = gpscal_from_gpsweek(week, secs, fofs); |
1718 | 0 | } else { |
1719 | 0 | DPRINTF(1, ("nmea: parse_gpsw: invalid weektime spec\n")); |
1720 | 0 | } |
1721 | 0 | return rc; |
1722 | 0 | } |
1723 | | |
1724 | | |
1725 | | #ifdef HAVE_PPSAPI |
1726 | | static double |
1727 | | tabsdiffd( |
1728 | | l_fp t1, |
1729 | | l_fp t2 |
1730 | | ) |
1731 | | { |
1732 | | double dd; |
1733 | | L_SUB(&t1, &t2); |
1734 | | LFPTOD(&t1, dd); |
1735 | | return fabs(dd); |
1736 | | } |
1737 | | #endif /* HAVE_PPSAPI */ |
1738 | | |
1739 | | /* |
1740 | | * =================================================================== |
1741 | | * |
1742 | | * NMEAD support |
1743 | | * |
1744 | | * original nmead support added by Jon Miner (cp_n18@yahoo.com) |
1745 | | * |
1746 | | * See http://home.hiwaay.net/~taylorc/gps/nmea-server/ |
1747 | | * for information about nmead |
1748 | | * |
1749 | | * To use this, you need to create a link from /dev/gpsX to |
1750 | | * the server:port where nmead is running. Something like this: |
1751 | | * |
1752 | | * ln -s server:port /dev/gps1 |
1753 | | * |
1754 | | * Split into separate function by Juergen Perlinger |
1755 | | * (perlinger-at-ntp-dot-org) |
1756 | | * |
1757 | | * =================================================================== |
1758 | | */ |
1759 | | static int |
1760 | | nmead_open( |
1761 | | const char * device |
1762 | | ) |
1763 | 0 | { |
1764 | 0 | int fd = -1; /* result file descriptor */ |
1765 | |
|
1766 | 0 | # ifdef HAVE_READLINK |
1767 | 0 | char host[80]; /* link target buffer */ |
1768 | 0 | char * port; /* port name or number */ |
1769 | 0 | int rc; /* result code (several)*/ |
1770 | 0 | int sh; /* socket handle */ |
1771 | 0 | struct addrinfo ai_hint; /* resolution hint */ |
1772 | 0 | struct addrinfo *ai_list; /* resolution result */ |
1773 | 0 | struct addrinfo *ai; /* result scan ptr */ |
1774 | |
|
1775 | 0 | fd = -1; |
1776 | | |
1777 | | /* try to read as link, make sure no overflow occurs */ |
1778 | 0 | rc = readlink(device, host, sizeof(host)); |
1779 | 0 | if ((size_t)rc >= sizeof(host)) |
1780 | 0 | return fd; /* error / overflow / truncation */ |
1781 | 0 | host[rc] = '\0'; /* readlink does not place NUL */ |
1782 | | |
1783 | | /* get port */ |
1784 | 0 | port = strchr(host, ':'); |
1785 | 0 | if (!port) |
1786 | 0 | return fd; /* not 'host:port' syntax ? */ |
1787 | 0 | *port++ = '\0'; /* put in separator */ |
1788 | | |
1789 | | /* get address infos and try to open socket |
1790 | | * |
1791 | | * This getaddrinfo() is naughty in ntpd's nonblocking main |
1792 | | * thread, but you have to go out of your wary to use this code |
1793 | | * and typically the blocking is at startup where its impact is |
1794 | | * reduced. The same holds for the 'connect()', as it is |
1795 | | * blocking, too... |
1796 | | */ |
1797 | 0 | ZERO(ai_hint); |
1798 | 0 | ai_hint.ai_protocol = IPPROTO_TCP; |
1799 | 0 | ai_hint.ai_socktype = SOCK_STREAM; |
1800 | 0 | if (getaddrinfo(host, port, &ai_hint, &ai_list)) |
1801 | 0 | return fd; |
1802 | | |
1803 | 0 | for (ai = ai_list; ai && (fd == -1); ai = ai->ai_next) { |
1804 | 0 | sh = socket(ai->ai_family, ai->ai_socktype, |
1805 | 0 | ai->ai_protocol); |
1806 | 0 | if (INVALID_SOCKET == sh) |
1807 | 0 | continue; |
1808 | 0 | rc = connect(sh, ai->ai_addr, ai->ai_addrlen); |
1809 | 0 | if (-1 != rc) |
1810 | 0 | fd = sh; |
1811 | 0 | else |
1812 | 0 | close(sh); |
1813 | 0 | } |
1814 | 0 | freeaddrinfo(ai_list); |
1815 | 0 | if (fd != -1) |
1816 | 0 | make_socket_nonblocking(fd); |
1817 | | # else |
1818 | | fd = -1; |
1819 | | # endif |
1820 | |
|
1821 | 0 | return fd; |
1822 | 0 | } |
1823 | | #else |
1824 | | NONEMPTY_TRANSLATION_UNIT |
1825 | | #endif /* REFCLOCK && CLOCK_NMEA */ |