/src/ntp-dev/ntpd/refclock_nmea.c
Line | Count | Source (jump to first uncovered line) |
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_calendar.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 0x80 |
93 | 0 | #define NMEA_EXTLOG_MASK 0x00010000U |
94 | 0 | #define NMEA_DATETRUST_MASK 0x02000000U |
95 | | |
96 | 0 | #define NMEA_PROTO_IDLEN 5 /* tag name must be at least 5 chars */ |
97 | | #define NMEA_PROTO_MINLEN 6 /* min chars in sentence, excluding CS */ |
98 | 0 | #define NMEA_PROTO_MAXLEN 80 /* max chars in sentence, excluding CS */ |
99 | 0 | #define NMEA_PROTO_FIELDS 32 /* not official; limit on fields per record */ |
100 | | |
101 | | /* |
102 | | * We check the timecode format and decode its contents. We only care |
103 | | * about a few of them, the most important being the $GPRMC format: |
104 | | * |
105 | | * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC |
106 | | * |
107 | | * mode (0,1,2,3) selects sentence ANY/ALL, RMC, GGA, GLL, ZDA |
108 | | * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21 |
109 | | * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F |
110 | | * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77 |
111 | | * |
112 | | * Defining GPZDA to support Standard Time & Date |
113 | | * sentence. The sentence has the following format |
114 | | * |
115 | | * $--ZDA,HHMMSS.SS,DD,MM,YYYY,TH,TM,*CS<CR><LF> |
116 | | * |
117 | | * Apart from the familiar fields, |
118 | | * 'TH' Time zone Hours |
119 | | * 'TM' Time zone Minutes |
120 | | * |
121 | | * Defining GPZDG to support Accord GPS Clock's custom NMEA |
122 | | * sentence. The sentence has the following format |
123 | | * |
124 | | * $GPZDG,HHMMSS.S,DD,MM,YYYY,AA.BB,V*CS<CR><LF> |
125 | | * |
126 | | * It contains the GPS timestamp valid for next PPS pulse. |
127 | | * Apart from the familiar fields, |
128 | | * 'AA.BB' denotes the signal strength( should be < 05.00 ) |
129 | | * 'V' denotes the GPS sync status : |
130 | | * '0' indicates INVALID time, |
131 | | * '1' indicates accuracy of +/-20 ms |
132 | | * '2' indicates accuracy of +/-100 ns |
133 | | * |
134 | | * Defining PGRMF for Garmin GPS Fix Data |
135 | | * $PGRMF,WN,WS,DATE,TIME,LS,LAT,LAT_DIR,LON,LON_DIR,MODE,FIX,SPD,DIR,PDOP,TDOP |
136 | | * WN -- GPS week number (weeks since 1980-01-06, mod 1024) |
137 | | * WS -- GPS seconds in week |
138 | | * LS -- GPS leap seconds, accumulated ( UTC + LS == GPS ) |
139 | | * FIX -- Fix type: 0=nofix, 1=2D, 2=3D |
140 | | * DATE/TIME are standard date/time strings in UTC time scale |
141 | | * |
142 | | * The GPS time can be used to get the full century for the truncated |
143 | | * date spec. |
144 | | */ |
145 | | |
146 | | /* |
147 | | * Definitions |
148 | | */ |
149 | | #define DEVICE "/dev/gps%d" /* GPS serial device */ |
150 | | #define PPSDEV "/dev/gpspps%d" /* PPSAPI device override */ |
151 | 0 | #define SPEED232 B4800 /* uart speed (4800 bps) */ |
152 | 0 | #define PRECISION (-9) /* precision assumed (about 2 ms) */ |
153 | | #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */ |
154 | 0 | #define REFID "GPS\0" /* reference id */ |
155 | 0 | #define DESCRIPTION "NMEA GPS Clock" /* who we are */ |
156 | | #ifndef O_NOCTTY |
157 | | #define M_NOCTTY 0 |
158 | | #else |
159 | | #define M_NOCTTY O_NOCTTY |
160 | | #endif |
161 | | #ifndef O_NONBLOCK |
162 | | #define M_NONBLOCK 0 |
163 | | #else |
164 | | #define M_NONBLOCK O_NONBLOCK |
165 | | #endif |
166 | | #define PPSOPENMODE (O_RDWR | M_NOCTTY | M_NONBLOCK) |
167 | | |
168 | | /* NMEA sentence array indexes for those we use */ |
169 | 0 | #define NMEA_GPRMC 0 /* recommended min. nav. */ |
170 | 0 | #define NMEA_GPGGA 1 /* fix and quality */ |
171 | 0 | #define NMEA_GPGLL 2 /* geo. lat/long */ |
172 | 0 | #define NMEA_GPZDA 3 /* date/time */ |
173 | | /* |
174 | | * $GPZDG is a proprietary sentence that violates the spec, by not |
175 | | * using $P and an assigned company identifier to prefix the sentence |
176 | | * identifier. When used with this driver, the system needs to be |
177 | | * isolated from other NTP networks, as it operates in GPS time, not |
178 | | * UTC as is much more common. GPS time is >15 seconds different from |
179 | | * UTC due to not respecting leap seconds since 1970 or so. Other |
180 | | * than the different timebase, $GPZDG is similar to $GPZDA. |
181 | | */ |
182 | 0 | #define NMEA_GPZDG 4 |
183 | 0 | #define NMEA_PGRMF 5 |
184 | | #define NMEA_ARRAY_SIZE (NMEA_PGRMF + 1) |
185 | | |
186 | | /* |
187 | | * Sentence selection mode bits |
188 | | */ |
189 | | #define USE_GPRMC 0x00000001u |
190 | | #define USE_GPGGA 0x00000002u |
191 | | #define USE_GPGLL 0x00000004u |
192 | | #define USE_GPZDA 0x00000008u |
193 | | #define USE_PGRMF 0x00000100u |
194 | | |
195 | | /* mapping from sentence index to controlling mode bit */ |
196 | | static const u_int32 sentence_mode[NMEA_ARRAY_SIZE] = |
197 | | { |
198 | | USE_GPRMC, |
199 | | USE_GPGGA, |
200 | | USE_GPGLL, |
201 | | USE_GPZDA, |
202 | | USE_GPZDA, |
203 | | USE_PGRMF |
204 | | }; |
205 | | |
206 | | /* date formats we support */ |
207 | | enum date_fmt { |
208 | | DATE_1_DDMMYY, /* use 1 field with 2-digit year */ |
209 | | DATE_3_DDMMYYYY /* use 3 fields with 4-digit year */ |
210 | | }; |
211 | | |
212 | | /* results for 'field_init()' |
213 | | * |
214 | | * Note: If a checksum is present, the checksum test must pass OK or the |
215 | | * sentence is tagged invalid. |
216 | | */ |
217 | 0 | #define CHECK_EMPTY -1 /* no data */ |
218 | 0 | #define CHECK_INVALID 0 /* not a valid NMEA sentence */ |
219 | 0 | #define CHECK_VALID 1 /* valid but without checksum */ |
220 | 0 | #define CHECK_CSVALID 2 /* valid with checksum OK */ |
221 | | |
222 | | /* |
223 | | * Unit control structure |
224 | | */ |
225 | | typedef struct { |
226 | | #ifdef HAVE_PPSAPI |
227 | | struct refclock_atom atom; /* PPSAPI structure */ |
228 | | int ppsapi_fd; /* fd used with PPSAPI */ |
229 | | u_char ppsapi_tried; /* attempt PPSAPI once */ |
230 | | u_char ppsapi_lit; /* time_pps_create() worked */ |
231 | | u_char ppsapi_gate; /* system is on PPS */ |
232 | | #endif /* HAVE_PPSAPI */ |
233 | | u_char gps_time; /* use GPS time, not UTC */ |
234 | | u_short century_cache; /* cached current century */ |
235 | | l_fp last_reftime; /* last processed reference stamp */ |
236 | | short epoch_warp; /* last epoch warp, for logging */ |
237 | | /* tally stats, reset each poll cycle */ |
238 | | struct |
239 | | { |
240 | | u_int total; |
241 | | u_int accepted; |
242 | | u_int rejected; /* GPS said not enough signal */ |
243 | | u_int malformed; /* Bad checksum, invalid date or time */ |
244 | | u_int filtered; /* mode bits, not GPZDG, same second */ |
245 | | u_int pps_used; |
246 | | } |
247 | | tally; |
248 | | /* per sentence checksum seen flag */ |
249 | | u_char cksum_type[NMEA_ARRAY_SIZE]; |
250 | | } nmea_unit; |
251 | | |
252 | | /* |
253 | | * helper for faster field access |
254 | | */ |
255 | | typedef struct { |
256 | | char *base; /* buffer base */ |
257 | | char *cptr; /* current field ptr */ |
258 | | int blen; /* buffer length */ |
259 | | int cidx; /* current field index */ |
260 | | } nmea_data; |
261 | | |
262 | | /* |
263 | | * NMEA gps week/time information |
264 | | * This record contains the number of weeks since 1980-01-06 modulo |
265 | | * 1024, the seconds elapsed since start of the week, and the number of |
266 | | * leap seconds that are the difference between GPS and UTC time scale. |
267 | | */ |
268 | | typedef struct { |
269 | | u_int32 wt_time; /* seconds since weekstart */ |
270 | | u_short wt_week; /* week number */ |
271 | | short wt_leap; /* leap seconds */ |
272 | | } gps_weektm; |
273 | | |
274 | | /* |
275 | | * The GPS week time scale starts on Sunday, 1980-01-06. We need the |
276 | | * rata die number of this day. |
277 | | */ |
278 | | #ifndef DAY_GPS_STARTS |
279 | | #define DAY_GPS_STARTS 722820 |
280 | | #endif |
281 | | |
282 | | /* |
283 | | * Function prototypes |
284 | | */ |
285 | | static void nmea_init (void); |
286 | | static int nmea_start (int, struct peer *); |
287 | | static void nmea_shutdown (int, struct peer *); |
288 | | static void nmea_receive (struct recvbuf *); |
289 | | static void nmea_poll (int, struct peer *); |
290 | | #ifdef HAVE_PPSAPI |
291 | | static void nmea_control (int, const struct refclockstat *, |
292 | | struct refclockstat *, struct peer *); |
293 | | #define NMEA_CONTROL nmea_control |
294 | | #else |
295 | | #define NMEA_CONTROL noentry |
296 | | #endif /* HAVE_PPSAPI */ |
297 | | static void nmea_timer (int, struct peer *); |
298 | | |
299 | | /* parsing helpers */ |
300 | | static int field_init (nmea_data * data, char * cp, int len); |
301 | | static char * field_parse (nmea_data * data, int fn); |
302 | | static void field_wipe (nmea_data * data, ...); |
303 | | static u_char parse_qual (nmea_data * data, int idx, |
304 | | char tag, int inv); |
305 | | static int parse_time (struct calendar * jd, long * nsec, |
306 | | nmea_data *, int idx); |
307 | | static int parse_date (struct calendar *jd, nmea_data*, |
308 | | int idx, enum date_fmt fmt); |
309 | | static int parse_weekdata (gps_weektm *, nmea_data *, |
310 | | int weekidx, int timeidx, int leapidx); |
311 | | /* calendar / date helpers */ |
312 | | static int unfold_day (struct calendar * jd, u_int32 rec_ui); |
313 | | static int unfold_century (struct calendar * jd, u_int32 rec_ui); |
314 | | static int gpsfix_century (struct calendar * jd, const gps_weektm * wd, |
315 | | u_short * ccentury); |
316 | | static l_fp eval_gps_time (struct peer * peer, const struct calendar * gpst, |
317 | | const struct timespec * gpso, const l_fp * xrecv); |
318 | | |
319 | | static int nmead_open (const char * device); |
320 | | static void save_ltc (struct refclockproc * const, const char * const, |
321 | | size_t); |
322 | | |
323 | | /* |
324 | | * If we want the driver to ouput sentences, too: re-enable the send |
325 | | * support functions by defining NMEA_WRITE_SUPPORT to non-zero... |
326 | | */ |
327 | | #if NMEA_WRITE_SUPPORT |
328 | | |
329 | | static void gps_send(int, const char *, struct peer *); |
330 | | # ifdef SYS_WINNT |
331 | | # undef write /* ports/winnt/include/config.h: #define write _write */ |
332 | | extern int async_write(int, const void *, unsigned int); |
333 | | # define write(fd, data, octets) async_write(fd, data, octets) |
334 | | # endif /* SYS_WINNT */ |
335 | | |
336 | | #endif /* NMEA_WRITE_SUPPORT */ |
337 | | |
338 | | static int32_t g_gpsMinBase; |
339 | | static int32_t g_gpsMinYear; |
340 | | |
341 | | /* |
342 | | * ------------------------------------------------------------------- |
343 | | * Transfer vector |
344 | | * ------------------------------------------------------------------- |
345 | | */ |
346 | | struct refclock refclock_nmea = { |
347 | | nmea_start, /* start up driver */ |
348 | | nmea_shutdown, /* shut down driver */ |
349 | | nmea_poll, /* transmit poll message */ |
350 | | NMEA_CONTROL, /* fudge control */ |
351 | | nmea_init, /* initialize driver */ |
352 | | noentry, /* buginfo */ |
353 | | nmea_timer /* called once per second */ |
354 | | }; |
355 | | |
356 | | /* |
357 | | * ------------------------------------------------------------------- |
358 | | * nmea_init - initialise data |
359 | | * |
360 | | * calculates a few runtime constants that cannot be made compile time |
361 | | * constants. |
362 | | * ------------------------------------------------------------------- |
363 | | */ |
364 | | static void |
365 | | nmea_init(void) |
366 | 0 | { |
367 | 0 | struct calendar date; |
368 | | |
369 | | /* - calculate min. base value for GPS epoch & century unfolding |
370 | | * This assumes that the build system was roughly in sync with |
371 | | * the world, and that really synchronising to a time before the |
372 | | * program was created would be unsafe or insane. If the build |
373 | | * date cannot be stablished, at least use the start of GPS |
374 | | * (1980-01-06) as minimum, because GPS can surely NOT |
375 | | * synchronise beyond it's own big bang. We add a little safety |
376 | | * margin for the fuzziness of the build date, which is in an |
377 | | * undefined time zone. */ |
378 | 0 | if (ntpcal_get_build_date(&date)) |
379 | 0 | g_gpsMinBase = ntpcal_date_to_rd(&date) - 2; |
380 | 0 | else |
381 | 0 | g_gpsMinBase = 0; |
382 | |
|
383 | 0 | if (g_gpsMinBase < DAY_GPS_STARTS) |
384 | 0 | g_gpsMinBase = DAY_GPS_STARTS; |
385 | |
|
386 | 0 | ntpcal_rd_to_date(&date, g_gpsMinBase); |
387 | 0 | g_gpsMinYear = date.year; |
388 | 0 | g_gpsMinBase -= DAY_NTP_STARTS; |
389 | 0 | } |
390 | | |
391 | | /* |
392 | | * ------------------------------------------------------------------- |
393 | | * nmea_start - open the GPS devices and initialize data for processing |
394 | | * |
395 | | * return 0 on error, 1 on success. Even on error the peer structures |
396 | | * must be in a state that permits 'nmea_shutdown()' to clean up all |
397 | | * resources, because it will be called immediately to do so. |
398 | | * ------------------------------------------------------------------- |
399 | | */ |
400 | | static int |
401 | | nmea_start( |
402 | | int unit, |
403 | | struct peer * peer |
404 | | ) |
405 | 0 | { |
406 | 0 | struct refclockproc * const pp = peer->procptr; |
407 | 0 | nmea_unit * const up = emalloc_zero(sizeof(*up)); |
408 | 0 | char device[20]; |
409 | 0 | size_t devlen; |
410 | 0 | u_int32 rate; |
411 | 0 | int baudrate; |
412 | 0 | const char * baudtext; |
413 | | |
414 | | |
415 | | /* Get baudrate choice from mode byte bits 4/5/6 */ |
416 | 0 | rate = (peer->ttl & NMEA_BAUDRATE_MASK) >> NMEA_BAUDRATE_SHIFT; |
417 | |
|
418 | 0 | switch (rate) { |
419 | 0 | case 0: |
420 | 0 | baudrate = SPEED232; |
421 | 0 | baudtext = "4800"; |
422 | 0 | break; |
423 | 0 | case 1: |
424 | 0 | baudrate = B9600; |
425 | 0 | baudtext = "9600"; |
426 | 0 | break; |
427 | 0 | case 2: |
428 | 0 | baudrate = B19200; |
429 | 0 | baudtext = "19200"; |
430 | 0 | break; |
431 | 0 | case 3: |
432 | 0 | baudrate = B38400; |
433 | 0 | baudtext = "38400"; |
434 | 0 | break; |
435 | 0 | #ifdef B57600 |
436 | 0 | case 4: |
437 | 0 | baudrate = B57600; |
438 | 0 | baudtext = "57600"; |
439 | 0 | break; |
440 | 0 | #endif |
441 | 0 | #ifdef B115200 |
442 | 0 | case 5: |
443 | 0 | baudrate = B115200; |
444 | 0 | baudtext = "115200"; |
445 | 0 | break; |
446 | 0 | #endif |
447 | 0 | default: |
448 | 0 | baudrate = SPEED232; |
449 | 0 | baudtext = "4800 (fallback)"; |
450 | 0 | break; |
451 | 0 | } |
452 | | |
453 | | /* Allocate and initialize unit structure */ |
454 | 0 | pp->unitptr = (caddr_t)up; |
455 | 0 | pp->io.fd = -1; |
456 | 0 | pp->io.clock_recv = nmea_receive; |
457 | 0 | pp->io.srcclock = peer; |
458 | 0 | pp->io.datalen = 0; |
459 | | /* force change detection on first valid message */ |
460 | 0 | memset(&up->last_reftime, 0xFF, sizeof(up->last_reftime)); |
461 | | /* force checksum on GPRMC, see below */ |
462 | 0 | up->cksum_type[NMEA_GPRMC] = CHECK_CSVALID; |
463 | | #ifdef HAVE_PPSAPI |
464 | | up->ppsapi_fd = -1; |
465 | | #endif |
466 | 0 | ZERO(up->tally); |
467 | | |
468 | | /* Initialize miscellaneous variables */ |
469 | 0 | peer->precision = PRECISION; |
470 | 0 | pp->clockdesc = DESCRIPTION; |
471 | 0 | memcpy(&pp->refid, REFID, 4); |
472 | | |
473 | | /* Open serial port. Use CLK line discipline, if available. */ |
474 | 0 | devlen = snprintf(device, sizeof(device), DEVICE, unit); |
475 | 0 | if (devlen >= sizeof(device)) { |
476 | 0 | msyslog(LOG_ERR, "%s clock device name too long", |
477 | 0 | refnumtoa(&peer->srcadr)); |
478 | 0 | return FALSE; /* buffer overflow */ |
479 | 0 | } |
480 | 0 | pp->io.fd = refclock_open(device, baudrate, LDISC_CLK); |
481 | 0 | if (0 >= pp->io.fd) { |
482 | 0 | pp->io.fd = nmead_open(device); |
483 | 0 | if (-1 == pp->io.fd) |
484 | 0 | return FALSE; |
485 | 0 | } |
486 | 0 | LOGIF(CLOCKINFO, (LOG_NOTICE, "%s serial %s open at %s bps", |
487 | 0 | refnumtoa(&peer->srcadr), device, baudtext)); |
488 | | |
489 | | /* succeed if this clock can be added */ |
490 | 0 | return io_addclock(&pp->io) != 0; |
491 | 0 | } |
492 | | |
493 | | |
494 | | /* |
495 | | * ------------------------------------------------------------------- |
496 | | * nmea_shutdown - shut down a GPS clock |
497 | | * |
498 | | * NOTE this routine is called after nmea_start() returns failure, |
499 | | * as well as during a normal shutdown due to ntpq :config unpeer. |
500 | | * ------------------------------------------------------------------- |
501 | | */ |
502 | | static void |
503 | | nmea_shutdown( |
504 | | int unit, |
505 | | struct peer * peer |
506 | | ) |
507 | 0 | { |
508 | 0 | struct refclockproc * const pp = peer->procptr; |
509 | 0 | nmea_unit * const up = (nmea_unit *)pp->unitptr; |
510 | |
|
511 | 0 | UNUSED_ARG(unit); |
512 | |
|
513 | 0 | if (up != NULL) { |
514 | | #ifdef HAVE_PPSAPI |
515 | | if (up->ppsapi_lit) |
516 | | time_pps_destroy(up->atom.handle); |
517 | | if (up->ppsapi_tried && up->ppsapi_fd != pp->io.fd) |
518 | | close(up->ppsapi_fd); |
519 | | #endif |
520 | 0 | free(up); |
521 | 0 | } |
522 | 0 | pp->unitptr = (caddr_t)NULL; |
523 | 0 | if (-1 != pp->io.fd) |
524 | 0 | io_closeclock(&pp->io); |
525 | 0 | pp->io.fd = -1; |
526 | 0 | } |
527 | | |
528 | | /* |
529 | | * ------------------------------------------------------------------- |
530 | | * nmea_control - configure fudge params |
531 | | * ------------------------------------------------------------------- |
532 | | */ |
533 | | #ifdef HAVE_PPSAPI |
534 | | static void |
535 | | nmea_control( |
536 | | int unit, |
537 | | const struct refclockstat * in_st, |
538 | | struct refclockstat * out_st, |
539 | | struct peer * peer |
540 | | ) |
541 | | { |
542 | | struct refclockproc * const pp = peer->procptr; |
543 | | nmea_unit * const up = (nmea_unit *)pp->unitptr; |
544 | | |
545 | | char device[32]; |
546 | | size_t devlen; |
547 | | |
548 | | UNUSED_ARG(in_st); |
549 | | UNUSED_ARG(out_st); |
550 | | |
551 | | /* |
552 | | * PPS control |
553 | | * |
554 | | * If /dev/gpspps$UNIT can be opened that will be used for |
555 | | * PPSAPI. Otherwise, the GPS serial device /dev/gps$UNIT |
556 | | * already opened is used for PPSAPI as well. (This might not |
557 | | * work, in which case the PPS API remains unavailable...) |
558 | | */ |
559 | | |
560 | | /* Light up the PPSAPI interface if not yet attempted. */ |
561 | | if ((CLK_FLAG1 & pp->sloppyclockflag) && !up->ppsapi_tried) { |
562 | | up->ppsapi_tried = TRUE; |
563 | | devlen = snprintf(device, sizeof(device), PPSDEV, unit); |
564 | | if (devlen < sizeof(device)) { |
565 | | up->ppsapi_fd = open(device, PPSOPENMODE, |
566 | | S_IRUSR | S_IWUSR); |
567 | | } else { |
568 | | up->ppsapi_fd = -1; |
569 | | msyslog(LOG_ERR, "%s PPS device name too long", |
570 | | refnumtoa(&peer->srcadr)); |
571 | | } |
572 | | if (-1 == up->ppsapi_fd) |
573 | | up->ppsapi_fd = pp->io.fd; |
574 | | if (refclock_ppsapi(up->ppsapi_fd, &up->atom)) { |
575 | | /* use the PPS API for our own purposes now. */ |
576 | | up->ppsapi_lit = refclock_params( |
577 | | pp->sloppyclockflag, &up->atom); |
578 | | if (!up->ppsapi_lit) { |
579 | | /* failed to configure, drop PPS unit */ |
580 | | time_pps_destroy(up->atom.handle); |
581 | | msyslog(LOG_WARNING, |
582 | | "%s set PPSAPI params fails", |
583 | | refnumtoa(&peer->srcadr)); |
584 | | } |
585 | | /* note: the PPS I/O handle remains valid until |
586 | | * flag1 is cleared or the clock is shut down. |
587 | | */ |
588 | | } else { |
589 | | msyslog(LOG_WARNING, |
590 | | "%s flag1 1 but PPSAPI fails", |
591 | | refnumtoa(&peer->srcadr)); |
592 | | } |
593 | | } |
594 | | |
595 | | /* shut down PPS API if activated */ |
596 | | if (!(CLK_FLAG1 & pp->sloppyclockflag) && up->ppsapi_tried) { |
597 | | /* shutdown PPS API */ |
598 | | if (up->ppsapi_lit) |
599 | | time_pps_destroy(up->atom.handle); |
600 | | up->atom.handle = 0; |
601 | | /* close/drop PPS fd */ |
602 | | if (up->ppsapi_fd != pp->io.fd) |
603 | | close(up->ppsapi_fd); |
604 | | up->ppsapi_fd = -1; |
605 | | |
606 | | /* clear markers and peer items */ |
607 | | up->ppsapi_gate = FALSE; |
608 | | up->ppsapi_lit = FALSE; |
609 | | up->ppsapi_tried = FALSE; |
610 | | |
611 | | peer->flags &= ~FLAG_PPS; |
612 | | peer->precision = PRECISION; |
613 | | } |
614 | | } |
615 | | #endif /* HAVE_PPSAPI */ |
616 | | |
617 | | /* |
618 | | * ------------------------------------------------------------------- |
619 | | * nmea_timer - called once per second |
620 | | * this only polls (older?) Oncore devices now |
621 | | * |
622 | | * Usually 'nmea_receive()' can get a timestamp every second, but at |
623 | | * least one Motorola unit needs prompting each time. Doing so in |
624 | | * 'nmea_poll()' gives only one sample per poll cycle, which actually |
625 | | * defeats the purpose of the median filter. Polling once per second |
626 | | * seems a much better idea. |
627 | | * ------------------------------------------------------------------- |
628 | | */ |
629 | | static void |
630 | | nmea_timer( |
631 | | int unit, |
632 | | struct peer * peer |
633 | | ) |
634 | 0 | { |
635 | | #if NMEA_WRITE_SUPPORT |
636 | | |
637 | | struct refclockproc * const pp = peer->procptr; |
638 | | |
639 | | UNUSED_ARG(unit); |
640 | | |
641 | | if (-1 != pp->io.fd) /* any mode bits to evaluate here? */ |
642 | | gps_send(pp->io.fd, "$PMOTG,RMC,0000*1D\r\n", peer); |
643 | | #else |
644 | | |
645 | 0 | UNUSED_ARG(unit); |
646 | 0 | UNUSED_ARG(peer); |
647 | | |
648 | 0 | #endif /* NMEA_WRITE_SUPPORT */ |
649 | 0 | } |
650 | | |
651 | | #ifdef HAVE_PPSAPI |
652 | | /* |
653 | | * ------------------------------------------------------------------- |
654 | | * refclock_ppsrelate(...) -- correlate with PPS edge |
655 | | * |
656 | | * This function is used to correlate a receive time stamp and a |
657 | | * reference time with a PPS edge time stamp. It applies the necessary |
658 | | * fudges (fudge1 for PPS, fudge2 for receive time) and then tries to |
659 | | * move the receive time stamp to the corresponding edge. This can warp |
660 | | * into future, if a transmission delay of more than 500ms is not |
661 | | * compensated with a corresponding fudge time2 value, because then the |
662 | | * next PPS edge is nearer than the last. (Similiar to what the PPS ATOM |
663 | | * driver does, but we deal with full time stamps here, not just phase |
664 | | * shift information.) Likewise, a negative fudge time2 value must be |
665 | | * used if the reference time stamp correlates with the *following* PPS |
666 | | * pulse. |
667 | | * |
668 | | * Note that the receive time fudge value only needs to move the receive |
669 | | * stamp near a PPS edge but that close proximity is not required; |
670 | | * +/-100ms precision should be enough. But since the fudge value will |
671 | | * probably also be used to compensate the transmission delay when no |
672 | | * PPS edge can be related to the time stamp, it's best to get it as |
673 | | * close as possible. |
674 | | * |
675 | | * It should also be noted that the typical use case is matching to the |
676 | | * preceeding edge, as most units relate their sentences to the current |
677 | | * second. |
678 | | * |
679 | | * The function returns PPS_RELATE_NONE (0) if no PPS edge correlation |
680 | | * can be fixed; PPS_RELATE_EDGE (1) when a PPS edge could be fixed, but |
681 | | * the distance to the reference time stamp is too big (exceeds |
682 | | * +/-400ms) and the ATOM driver PLL cannot be used to fix the phase; |
683 | | * and PPS_RELATE_PHASE (2) when the ATOM driver PLL code can be used. |
684 | | * |
685 | | * On output, the receive time stamp is replaced with the corresponding |
686 | | * PPS edge time if a fix could be made; the PPS fudge is updated to |
687 | | * reflect the proper fudge time to apply. (This implies that |
688 | | * 'refclock_process_offset()' must be used!) |
689 | | * ------------------------------------------------------------------- |
690 | | */ |
691 | | #define PPS_RELATE_NONE 0 /* no pps correlation possible */ |
692 | | #define PPS_RELATE_EDGE 1 /* recv time fixed, no phase lock */ |
693 | | #define PPS_RELATE_PHASE 2 /* recv time fixed, phase lock ok */ |
694 | | |
695 | | static int |
696 | | refclock_ppsrelate( |
697 | | const struct refclockproc * pp , /* for sanity */ |
698 | | const struct refclock_atom * ap , /* for PPS io */ |
699 | | const l_fp * reftime , |
700 | | l_fp * rd_stamp, /* i/o read stamp */ |
701 | | double pp_fudge, /* pps fudge */ |
702 | | double * rd_fudge /* i/o read fudge */ |
703 | | ) |
704 | | { |
705 | | pps_info_t pps_info; |
706 | | struct timespec timeout; |
707 | | l_fp pp_stamp, pp_delta; |
708 | | double delta, idelta; |
709 | | |
710 | | if (pp->leap == LEAP_NOTINSYNC) |
711 | | return PPS_RELATE_NONE; /* clock is insane, no chance */ |
712 | | |
713 | | ZERO(timeout); |
714 | | ZERO(pps_info); |
715 | | if (time_pps_fetch(ap->handle, PPS_TSFMT_TSPEC, |
716 | | &pps_info, &timeout) < 0) |
717 | | return PPS_RELATE_NONE; /* can't get time stamps */ |
718 | | |
719 | | /* get last active PPS edge before receive */ |
720 | | if (ap->pps_params.mode & PPS_CAPTUREASSERT) |
721 | | timeout = pps_info.assert_timestamp; |
722 | | else if (ap->pps_params.mode & PPS_CAPTURECLEAR) |
723 | | timeout = pps_info.clear_timestamp; |
724 | | else |
725 | | return PPS_RELATE_NONE; /* WHICH edge, please?!? */ |
726 | | |
727 | | /* get delta between receive time and PPS time */ |
728 | | pp_stamp = tspec_stamp_to_lfp(timeout); |
729 | | pp_delta = *rd_stamp; |
730 | | L_SUB(&pp_delta, &pp_stamp); |
731 | | LFPTOD(&pp_delta, delta); |
732 | | delta += pp_fudge - *rd_fudge; |
733 | | if (fabs(delta) > 1.5) |
734 | | return PPS_RELATE_NONE; /* PPS timeout control */ |
735 | | |
736 | | /* eventually warp edges, check phase */ |
737 | | idelta = floor(delta + 0.5); |
738 | | pp_fudge -= idelta; |
739 | | delta -= idelta; |
740 | | if (fabs(delta) > 0.45) |
741 | | return PPS_RELATE_NONE; /* dead band control */ |
742 | | |
743 | | /* we actually have a PPS edge to relate with! */ |
744 | | *rd_stamp = pp_stamp; |
745 | | *rd_fudge = pp_fudge; |
746 | | |
747 | | /* if whole system out-of-sync, do not try to PLL */ |
748 | | if (sys_leap == LEAP_NOTINSYNC) |
749 | | return PPS_RELATE_EDGE; /* cannot PLL with atom code */ |
750 | | |
751 | | /* check against reftime if ATOM PLL can be used */ |
752 | | pp_delta = *reftime; |
753 | | L_SUB(&pp_delta, &pp_stamp); |
754 | | LFPTOD(&pp_delta, delta); |
755 | | delta += pp_fudge; |
756 | | if (fabs(delta) > 0.45) |
757 | | return PPS_RELATE_EDGE; /* cannot PLL with atom code */ |
758 | | |
759 | | /* all checks passed, gets an AAA rating here! */ |
760 | | return PPS_RELATE_PHASE; /* can PLL with atom code */ |
761 | | } |
762 | | #endif /* HAVE_PPSAPI */ |
763 | | |
764 | | /* |
765 | | * ------------------------------------------------------------------- |
766 | | * nmea_receive - receive data from the serial interface |
767 | | * |
768 | | * This is the workhorse for NMEA data evaluation: |
769 | | * |
770 | | * + it checks all NMEA data, and rejects sentences that are not valid |
771 | | * NMEA sentences |
772 | | * + it checks whether a sentence is known and to be used |
773 | | * + it parses the time and date data from the NMEA data string and |
774 | | * augments the missing bits. (century in dat, whole date, ...) |
775 | | * + it rejects data that is not from the first accepted sentence in a |
776 | | * burst |
777 | | * + it eventually replaces the receive time with the PPS edge time. |
778 | | * + it feeds the data to the internal processing stages. |
779 | | * ------------------------------------------------------------------- |
780 | | */ |
781 | | static void |
782 | | nmea_receive( |
783 | | struct recvbuf * rbufp |
784 | | ) |
785 | 0 | { |
786 | | /* declare & init control structure ptrs */ |
787 | 0 | struct peer * const peer = rbufp->recv_peer; |
788 | 0 | struct refclockproc * const pp = peer->procptr; |
789 | 0 | nmea_unit * const up = (nmea_unit*)pp->unitptr; |
790 | | |
791 | | /* Use these variables to hold data until we decide its worth keeping */ |
792 | 0 | nmea_data rdata; |
793 | 0 | char rd_lastcode[BMAX]; |
794 | 0 | l_fp rd_timestamp, rd_reftime; |
795 | 0 | int rd_lencode; |
796 | 0 | double rd_fudge; |
797 | | |
798 | | /* working stuff */ |
799 | 0 | struct calendar date; /* to keep & convert the time stamp */ |
800 | 0 | struct timespec tofs; /* offset to full-second reftime */ |
801 | 0 | gps_weektm gpsw; /* week time storage */ |
802 | | /* results of sentence/date/time parsing */ |
803 | 0 | u_char sentence; /* sentence tag */ |
804 | 0 | int checkres; |
805 | 0 | char * cp; |
806 | 0 | int rc_date; |
807 | 0 | int rc_time; |
808 | | |
809 | | /* make sure data has defined pristine state */ |
810 | 0 | ZERO(tofs); |
811 | 0 | ZERO(date); |
812 | 0 | ZERO(gpsw); |
813 | | |
814 | | /* |
815 | | * Read the timecode and timestamp, then initialise field |
816 | | * processing. The <CR><LF> at the NMEA line end is translated |
817 | | * to <LF><LF> by the terminal input routines on most systems, |
818 | | * and this gives us one spurious empty read per record which we |
819 | | * better ignore silently. |
820 | | */ |
821 | 0 | rd_lencode = refclock_gtlin(rbufp, rd_lastcode, |
822 | 0 | sizeof(rd_lastcode), &rd_timestamp); |
823 | 0 | checkres = field_init(&rdata, rd_lastcode, rd_lencode); |
824 | 0 | switch (checkres) { |
825 | | |
826 | 0 | case CHECK_INVALID: |
827 | 0 | DPRINTF(1, ("%s invalid data: '%s'\n", |
828 | 0 | refnumtoa(&peer->srcadr), rd_lastcode)); |
829 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
830 | 0 | return; |
831 | | |
832 | 0 | case CHECK_EMPTY: |
833 | 0 | return; |
834 | | |
835 | 0 | default: |
836 | 0 | DPRINTF(1, ("%s gpsread: %d '%s'\n", |
837 | 0 | refnumtoa(&peer->srcadr), rd_lencode, |
838 | 0 | rd_lastcode)); |
839 | 0 | break; |
840 | 0 | } |
841 | 0 | up->tally.total++; |
842 | | |
843 | | /* |
844 | | * --> below this point we have a valid NMEA sentence <-- |
845 | | * |
846 | | * Check sentence name. Skip first 2 chars (talker ID) in most |
847 | | * cases, to allow for $GLGGA and $GPGGA etc. Since the name |
848 | | * field has at least 5 chars we can simply shift the field |
849 | | * start. |
850 | | */ |
851 | 0 | cp = field_parse(&rdata, 0); |
852 | 0 | if (strncmp(cp + 2, "RMC,", 4) == 0) |
853 | 0 | sentence = NMEA_GPRMC; |
854 | 0 | else if (strncmp(cp + 2, "GGA,", 4) == 0) |
855 | 0 | sentence = NMEA_GPGGA; |
856 | 0 | else if (strncmp(cp + 2, "GLL,", 4) == 0) |
857 | 0 | sentence = NMEA_GPGLL; |
858 | 0 | else if (strncmp(cp + 2, "ZDA,", 4) == 0) |
859 | 0 | sentence = NMEA_GPZDA; |
860 | 0 | else if (strncmp(cp + 2, "ZDG,", 4) == 0) |
861 | 0 | sentence = NMEA_GPZDG; |
862 | 0 | else if (strncmp(cp, "PGRMF,", 6) == 0) |
863 | 0 | sentence = NMEA_PGRMF; |
864 | 0 | else |
865 | 0 | return; /* not something we know about */ |
866 | | |
867 | | /* Eventually output delay measurement now. */ |
868 | 0 | if (peer->ttl & NMEA_DELAYMEAS_MASK) { |
869 | 0 | mprintf_clock_stats(&peer->srcadr, "delay %0.6f %.*s", |
870 | 0 | ldexp(rd_timestamp.l_uf, -32), |
871 | 0 | (int)(strchr(rd_lastcode, ',') - rd_lastcode), |
872 | 0 | rd_lastcode); |
873 | 0 | } |
874 | | |
875 | | /* See if I want to process this message type */ |
876 | 0 | if ((peer->ttl & NMEA_MESSAGE_MASK) && |
877 | 0 | !(peer->ttl & sentence_mode[sentence])) { |
878 | 0 | up->tally.filtered++; |
879 | 0 | return; |
880 | 0 | } |
881 | | |
882 | | /* |
883 | | * make sure it came in clean |
884 | | * |
885 | | * Apparently, older NMEA specifications (which are expensive) |
886 | | * did not require the checksum for all sentences. $GPMRC is |
887 | | * the only one so far identified which has always been required |
888 | | * to include a checksum. |
889 | | * |
890 | | * Today, most NMEA GPS receivers checksum every sentence. To |
891 | | * preserve its error-detection capabilities with modern GPSes |
892 | | * while allowing operation without checksums on all but $GPMRC, |
893 | | * we keep track of whether we've ever seen a valid checksum on |
894 | | * a given sentence, and if so, reject future instances without |
895 | | * checksum. ('up->cksum_type[NMEA_GPRMC]' is set in |
896 | | * 'nmea_start()' to enforce checksums for $GPRMC right from the |
897 | | * start.) |
898 | | */ |
899 | 0 | if (up->cksum_type[sentence] <= (u_char)checkres) { |
900 | 0 | up->cksum_type[sentence] = (u_char)checkres; |
901 | 0 | } else { |
902 | 0 | DPRINTF(1, ("%s checksum missing: '%s'\n", |
903 | 0 | refnumtoa(&peer->srcadr), rd_lastcode)); |
904 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
905 | 0 | up->tally.malformed++; |
906 | 0 | return; |
907 | 0 | } |
908 | | |
909 | | /* |
910 | | * $GPZDG provides GPS time not UTC, and the two mix poorly. |
911 | | * Once have processed a $GPZDG, do not process any further UTC |
912 | | * sentences (all but $GPZDG currently). |
913 | | */ |
914 | 0 | if (up->gps_time && NMEA_GPZDG != sentence) { |
915 | 0 | up->tally.filtered++; |
916 | 0 | return; |
917 | 0 | } |
918 | | |
919 | 0 | DPRINTF(1, ("%s processing %d bytes, timecode '%s'\n", |
920 | 0 | refnumtoa(&peer->srcadr), rd_lencode, rd_lastcode)); |
921 | | |
922 | | /* |
923 | | * Grab fields depending on clock string type and possibly wipe |
924 | | * sensitive data from the last timecode. |
925 | | */ |
926 | 0 | switch (sentence) { |
927 | | |
928 | 0 | case NMEA_GPRMC: |
929 | | /* Check quality byte, fetch data & time */ |
930 | 0 | rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1); |
931 | 0 | pp->leap = parse_qual(&rdata, 2, 'A', 0); |
932 | 0 | rc_date = parse_date(&date, &rdata, 9, DATE_1_DDMMYY) |
933 | 0 | && unfold_century(&date, rd_timestamp.l_ui); |
934 | 0 | if (CLK_FLAG4 & pp->sloppyclockflag) |
935 | 0 | field_wipe(&rdata, 3, 4, 5, 6, -1); |
936 | 0 | break; |
937 | | |
938 | 0 | case NMEA_GPGGA: |
939 | | /* Check quality byte, fetch time only */ |
940 | 0 | rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1); |
941 | 0 | pp->leap = parse_qual(&rdata, 6, '0', 1); |
942 | 0 | rc_date = unfold_day(&date, rd_timestamp.l_ui); |
943 | 0 | if (CLK_FLAG4 & pp->sloppyclockflag) |
944 | 0 | field_wipe(&rdata, 2, 4, -1); |
945 | 0 | break; |
946 | | |
947 | 0 | case NMEA_GPGLL: |
948 | | /* Check quality byte, fetch time only */ |
949 | 0 | rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 5); |
950 | 0 | pp->leap = parse_qual(&rdata, 6, 'A', 0); |
951 | 0 | rc_date = unfold_day(&date, rd_timestamp.l_ui); |
952 | 0 | if (CLK_FLAG4 & pp->sloppyclockflag) |
953 | 0 | field_wipe(&rdata, 1, 3, -1); |
954 | 0 | break; |
955 | | |
956 | 0 | case NMEA_GPZDA: |
957 | | /* No quality. Assume best, fetch time & full date */ |
958 | 0 | pp->leap = LEAP_NOWARNING; |
959 | 0 | rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1); |
960 | 0 | rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY); |
961 | 0 | break; |
962 | | |
963 | 0 | case NMEA_GPZDG: |
964 | | /* Check quality byte, fetch time & full date */ |
965 | 0 | rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1); |
966 | 0 | rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY); |
967 | 0 | pp->leap = parse_qual(&rdata, 4, '0', 1); |
968 | 0 | tofs.tv_sec = -1; /* GPZDG is following second */ |
969 | 0 | break; |
970 | | |
971 | 0 | case NMEA_PGRMF: |
972 | | /* get date, time, qualifier and GPS weektime. We need |
973 | | * date and time-of-day for the century fix, so we read |
974 | | * them first. |
975 | | */ |
976 | 0 | rc_date = parse_weekdata(&gpsw, &rdata, 1, 2, 5) |
977 | 0 | && parse_date(&date, &rdata, 3, DATE_1_DDMMYY); |
978 | 0 | rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 4); |
979 | 0 | pp->leap = parse_qual(&rdata, 11, '0', 1); |
980 | 0 | rc_date = rc_date |
981 | 0 | && gpsfix_century(&date, &gpsw, &up->century_cache); |
982 | 0 | if (CLK_FLAG4 & pp->sloppyclockflag) |
983 | 0 | field_wipe(&rdata, 6, 8, -1); |
984 | 0 | break; |
985 | | |
986 | 0 | default: |
987 | 0 | INVARIANT(0); /* Coverity 97123 */ |
988 | 0 | return; |
989 | 0 | } |
990 | | |
991 | | /* Check sanity of time-of-day. */ |
992 | 0 | if (rc_time == 0) { /* no time or conversion error? */ |
993 | 0 | checkres = CEVNT_BADTIME; |
994 | 0 | up->tally.malformed++; |
995 | 0 | } |
996 | | /* Check sanity of date. */ |
997 | 0 | else if (rc_date == 0) {/* no date or conversion error? */ |
998 | 0 | checkres = CEVNT_BADDATE; |
999 | 0 | up->tally.malformed++; |
1000 | 0 | } |
1001 | | /* check clock sanity; [bug 2143] */ |
1002 | 0 | else if (pp->leap == LEAP_NOTINSYNC) { /* no good status? */ |
1003 | 0 | checkres = CEVNT_BADREPLY; |
1004 | 0 | up->tally.rejected++; |
1005 | 0 | } |
1006 | 0 | else |
1007 | 0 | checkres = -1; |
1008 | |
|
1009 | 0 | if (checkres != -1) { |
1010 | 0 | save_ltc(pp, rd_lastcode, rd_lencode); |
1011 | 0 | refclock_report(peer, checkres); |
1012 | 0 | return; |
1013 | 0 | } |
1014 | | |
1015 | 0 | DPRINTF(1, ("%s effective timecode: %04u-%02u-%02u %02d:%02d:%02d\n", |
1016 | 0 | refnumtoa(&peer->srcadr), |
1017 | 0 | date.year, date.month, date.monthday, |
1018 | 0 | date.hour, date.minute, date.second)); |
1019 | | |
1020 | | /* Check if we must enter GPS time mode; log so if we do */ |
1021 | 0 | if (!up->gps_time && (sentence == NMEA_GPZDG)) { |
1022 | 0 | msyslog(LOG_INFO, "%s using GPS time as if it were UTC", |
1023 | 0 | refnumtoa(&peer->srcadr)); |
1024 | 0 | up->gps_time = 1; |
1025 | 0 | } |
1026 | | |
1027 | | /* |
1028 | | * Get the reference time stamp from the calendar buffer. |
1029 | | * Process the new sample in the median filter and determine the |
1030 | | * timecode timestamp, but only if the PPS is not in control. |
1031 | | * Discard sentence if reference time did not change. |
1032 | | */ |
1033 | 0 | rd_reftime = eval_gps_time(peer, &date, &tofs, &rd_timestamp); |
1034 | 0 | if (L_ISEQU(&up->last_reftime, &rd_reftime)) { |
1035 | | /* Do not touch pp->a_lastcode on purpose! */ |
1036 | 0 | up->tally.filtered++; |
1037 | 0 | return; |
1038 | 0 | } |
1039 | 0 | up->last_reftime = rd_reftime; |
1040 | 0 | rd_fudge = pp->fudgetime2; |
1041 | |
|
1042 | 0 | DPRINTF(1, ("%s using '%s'\n", |
1043 | 0 | refnumtoa(&peer->srcadr), rd_lastcode)); |
1044 | | |
1045 | | /* Data will be accepted. Update stats & log data. */ |
1046 | 0 | up->tally.accepted++; |
1047 | 0 | save_ltc(pp, rd_lastcode, rd_lencode); |
1048 | 0 | pp->lastrec = rd_timestamp; |
1049 | |
|
1050 | | #ifdef HAVE_PPSAPI |
1051 | | /* |
1052 | | * If we have PPS running, we try to associate the sentence |
1053 | | * with the last active edge of the PPS signal. |
1054 | | */ |
1055 | | if (up->ppsapi_lit) |
1056 | | switch (refclock_ppsrelate( |
1057 | | pp, &up->atom, &rd_reftime, &rd_timestamp, |
1058 | | pp->fudgetime1, &rd_fudge)) |
1059 | | { |
1060 | | case PPS_RELATE_PHASE: |
1061 | | up->ppsapi_gate = TRUE; |
1062 | | peer->precision = PPS_PRECISION; |
1063 | | peer->flags |= FLAG_PPS; |
1064 | | DPRINTF(2, ("%s PPS_RELATE_PHASE\n", |
1065 | | refnumtoa(&peer->srcadr))); |
1066 | | up->tally.pps_used++; |
1067 | | break; |
1068 | | |
1069 | | case PPS_RELATE_EDGE: |
1070 | | up->ppsapi_gate = TRUE; |
1071 | | peer->precision = PPS_PRECISION; |
1072 | | DPRINTF(2, ("%s PPS_RELATE_EDGE\n", |
1073 | | refnumtoa(&peer->srcadr))); |
1074 | | break; |
1075 | | |
1076 | | case PPS_RELATE_NONE: |
1077 | | default: |
1078 | | /* |
1079 | | * Resetting precision and PPS flag is done in |
1080 | | * 'nmea_poll', since it might be a glitch. But |
1081 | | * at the end of the poll cycle we know... |
1082 | | */ |
1083 | | DPRINTF(2, ("%s PPS_RELATE_NONE\n", |
1084 | | refnumtoa(&peer->srcadr))); |
1085 | | break; |
1086 | | } |
1087 | | #endif /* HAVE_PPSAPI */ |
1088 | |
|
1089 | 0 | refclock_process_offset(pp, rd_reftime, rd_timestamp, rd_fudge); |
1090 | 0 | } |
1091 | | |
1092 | | |
1093 | | /* |
1094 | | * ------------------------------------------------------------------- |
1095 | | * nmea_poll - called by the transmit procedure |
1096 | | * |
1097 | | * Does the necessary bookkeeping stuff to keep the reported state of |
1098 | | * the clock in sync with reality. |
1099 | | * |
1100 | | * We go to great pains to avoid changing state here, since there may |
1101 | | * be more than one eavesdropper receiving the same timecode. |
1102 | | * ------------------------------------------------------------------- |
1103 | | */ |
1104 | | static void |
1105 | | nmea_poll( |
1106 | | int unit, |
1107 | | struct peer * peer |
1108 | | ) |
1109 | 0 | { |
1110 | 0 | struct refclockproc * const pp = peer->procptr; |
1111 | 0 | nmea_unit * const up = (nmea_unit *)pp->unitptr; |
1112 | | |
1113 | | /* |
1114 | | * Process median filter samples. If none received, declare a |
1115 | | * timeout and keep going. |
1116 | | */ |
1117 | | #ifdef HAVE_PPSAPI |
1118 | | /* |
1119 | | * If we don't have PPS pulses and time stamps, turn PPS down |
1120 | | * for now. |
1121 | | */ |
1122 | | if (!up->ppsapi_gate) { |
1123 | | peer->flags &= ~FLAG_PPS; |
1124 | | peer->precision = PRECISION; |
1125 | | } else { |
1126 | | up->ppsapi_gate = FALSE; |
1127 | | } |
1128 | | #endif /* HAVE_PPSAPI */ |
1129 | | |
1130 | | /* |
1131 | | * If the median filter is empty, claim a timeout. Else process |
1132 | | * the input data and keep the stats going. |
1133 | | */ |
1134 | 0 | if (pp->coderecv == pp->codeproc) { |
1135 | 0 | refclock_report(peer, CEVNT_TIMEOUT); |
1136 | 0 | } else { |
1137 | 0 | pp->polls++; |
1138 | 0 | pp->lastref = pp->lastrec; |
1139 | 0 | refclock_receive(peer); |
1140 | 0 | } |
1141 | | |
1142 | | /* |
1143 | | * If extended logging is required, write the tally stats to the |
1144 | | * clockstats file; otherwise just do a normal clock stats |
1145 | | * record. Clear the tally stats anyway. |
1146 | | */ |
1147 | 0 | if (peer->ttl & NMEA_EXTLOG_MASK) { |
1148 | | /* Log & reset counters with extended logging */ |
1149 | 0 | const char *nmea = pp->a_lastcode; |
1150 | 0 | if (*nmea == '\0') nmea = "(none)"; |
1151 | 0 | mprintf_clock_stats( |
1152 | 0 | &peer->srcadr, "%s %u %u %u %u %u %u", |
1153 | 0 | nmea, |
1154 | 0 | up->tally.total, up->tally.accepted, |
1155 | 0 | up->tally.rejected, up->tally.malformed, |
1156 | 0 | up->tally.filtered, up->tally.pps_used); |
1157 | 0 | } else { |
1158 | 0 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
1159 | 0 | } |
1160 | 0 | ZERO(up->tally); |
1161 | 0 | } |
1162 | | |
1163 | | /* |
1164 | | * ------------------------------------------------------------------- |
1165 | | * Save the last timecode string, making sure it's properly truncated |
1166 | | * if necessary and NUL terminated in any case. |
1167 | | */ |
1168 | | static void |
1169 | | save_ltc( |
1170 | | struct refclockproc * const pp, |
1171 | | const char * const tc, |
1172 | | size_t len |
1173 | | ) |
1174 | 0 | { |
1175 | 0 | if (len >= sizeof(pp->a_lastcode)) |
1176 | 0 | len = sizeof(pp->a_lastcode) - 1; |
1177 | 0 | pp->lencode = (u_short)len; |
1178 | 0 | memcpy(pp->a_lastcode, tc, len); |
1179 | 0 | pp->a_lastcode[len] = '\0'; |
1180 | 0 | } |
1181 | | |
1182 | | |
1183 | | #if NMEA_WRITE_SUPPORT |
1184 | | /* |
1185 | | * ------------------------------------------------------------------- |
1186 | | * gps_send(fd, cmd, peer) Sends a command to the GPS receiver. |
1187 | | * as in gps_send(fd, "rqts,u", peer); |
1188 | | * |
1189 | | * If 'cmd' starts with a '$' it is assumed that this command is in raw |
1190 | | * format, that is, starts with '$', ends with '<cr><lf>' and that any |
1191 | | * checksum is correctly provided; the command will be send 'as is' in |
1192 | | * that case. Otherwise the function will create the necessary frame |
1193 | | * (start char, chksum, final CRLF) on the fly. |
1194 | | * |
1195 | | * We don't currently send any data, but would like to send RTCM SC104 |
1196 | | * messages for differential positioning. It should also give us better |
1197 | | * time. Without a PPS output, we're Just fooling ourselves because of |
1198 | | * the serial code paths |
1199 | | * ------------------------------------------------------------------- |
1200 | | */ |
1201 | | static void |
1202 | | gps_send( |
1203 | | int fd, |
1204 | | const char * cmd, |
1205 | | struct peer * peer |
1206 | | ) |
1207 | | { |
1208 | | /* $...*xy<CR><LF><NUL> add 7 */ |
1209 | | char buf[NMEA_PROTO_MAXLEN + 7]; |
1210 | | int len; |
1211 | | u_char dcs; |
1212 | | const u_char *beg, *end; |
1213 | | |
1214 | | if (*cmd != '$') { |
1215 | | /* get checksum and length */ |
1216 | | beg = end = (const u_char*)cmd; |
1217 | | dcs = 0; |
1218 | | while (*end >= ' ' && *end != '*') |
1219 | | dcs ^= *end++; |
1220 | | len = end - beg; |
1221 | | /* format into output buffer with overflow check */ |
1222 | | len = snprintf(buf, sizeof(buf), "$%.*s*%02X\r\n", |
1223 | | len, beg, dcs); |
1224 | | if ((size_t)len >= sizeof(buf)) { |
1225 | | DPRINTF(1, ("%s gps_send: buffer overflow for command '%s'\n", |
1226 | | refnumtoa(&peer->srcadr), cmd)); |
1227 | | return; /* game over player 1 */ |
1228 | | } |
1229 | | cmd = buf; |
1230 | | } else { |
1231 | | len = strlen(cmd); |
1232 | | } |
1233 | | |
1234 | | DPRINTF(1, ("%s gps_send: '%.*s'\n", refnumtoa(&peer->srcadr), |
1235 | | len - 2, cmd)); |
1236 | | |
1237 | | /* send out the whole stuff */ |
1238 | | if (write(fd, cmd, len) == -1) |
1239 | | refclock_report(peer, CEVNT_FAULT); |
1240 | | } |
1241 | | #endif /* NMEA_WRITE_SUPPORT */ |
1242 | | |
1243 | | /* |
1244 | | * ------------------------------------------------------------------- |
1245 | | * helpers for faster field splitting |
1246 | | * ------------------------------------------------------------------- |
1247 | | * |
1248 | | * set up a field record, check syntax and verify checksum |
1249 | | * |
1250 | | * format is $XXXXX,1,2,3,4*ML |
1251 | | * |
1252 | | * 8-bit XOR of characters between $ and * noninclusive is transmitted |
1253 | | * in last two chars M and L holding most and least significant nibbles |
1254 | | * in hex representation such as: |
1255 | | * |
1256 | | * $GPGLL,5057.970,N,00146.110,E,142451,A*27 |
1257 | | * $GPVTG,089.0,T,,,15.2,N,,*7F |
1258 | | * |
1259 | | * Some other constraints: |
1260 | | * + The field name must at least 5 upcase characters or digits and must |
1261 | | * start with a character. |
1262 | | * + The checksum (if present) must be uppercase hex digits. |
1263 | | * + The length of a sentence is limited to 80 characters (not including |
1264 | | * the final CR/LF nor the checksum, but including the leading '$') |
1265 | | * |
1266 | | * Return values: |
1267 | | * + CHECK_INVALID |
1268 | | * The data does not form a valid NMEA sentence or a checksum error |
1269 | | * occurred. |
1270 | | * + CHECK_VALID |
1271 | | * The data is a valid NMEA sentence but contains no checksum. |
1272 | | * + CHECK_CSVALID |
1273 | | * The data is a valid NMEA sentence and passed the checksum test. |
1274 | | * ------------------------------------------------------------------- |
1275 | | */ |
1276 | | static int |
1277 | | field_init( |
1278 | | nmea_data * data, /* context structure */ |
1279 | | char * cptr, /* start of raw data */ |
1280 | | int dlen /* data len, not counting trailing NUL */ |
1281 | | ) |
1282 | 0 | { |
1283 | 0 | u_char cs_l; /* checksum local computed */ |
1284 | 0 | u_char cs_r; /* checksum remote given */ |
1285 | 0 | char * eptr; /* buffer end end pointer */ |
1286 | 0 | char tmp; /* char buffer */ |
1287 | | |
1288 | 0 | cs_l = 0; |
1289 | 0 | cs_r = 0; |
1290 | | /* some basic input constraints */ |
1291 | 0 | if (dlen < 0) |
1292 | 0 | dlen = 0; |
1293 | 0 | eptr = cptr + dlen; |
1294 | 0 | *eptr = '\0'; |
1295 | | |
1296 | | /* load data context */ |
1297 | 0 | data->base = cptr; |
1298 | 0 | data->cptr = cptr; |
1299 | 0 | data->cidx = 0; |
1300 | 0 | data->blen = dlen; |
1301 | | |
1302 | | /* syntax check follows here. check allowed character |
1303 | | * sequences, updating the local computed checksum as we go. |
1304 | | * |
1305 | | * regex equiv: '^\$[A-Z][A-Z0-9]{4,}[^*]*(\*[0-9A-F]{2})?$' |
1306 | | */ |
1307 | | |
1308 | | /* -*- start character: '^\$' */ |
1309 | 0 | if (*cptr == '\0') |
1310 | 0 | return CHECK_EMPTY; |
1311 | 0 | if (*cptr++ != '$') |
1312 | 0 | return CHECK_INVALID; |
1313 | | |
1314 | | /* -*- advance context beyond start character */ |
1315 | 0 | data->base++; |
1316 | 0 | data->cptr++; |
1317 | 0 | data->blen--; |
1318 | | |
1319 | | /* -*- field name: '[A-Z][A-Z0-9]{4,},' */ |
1320 | 0 | if (*cptr < 'A' || *cptr > 'Z') |
1321 | 0 | return CHECK_INVALID; |
1322 | 0 | cs_l ^= *cptr++; |
1323 | 0 | while ((*cptr >= 'A' && *cptr <= 'Z') || |
1324 | 0 | (*cptr >= '0' && *cptr <= '9') ) |
1325 | 0 | cs_l ^= *cptr++; |
1326 | 0 | if (*cptr != ',' || (cptr - data->base) < NMEA_PROTO_IDLEN) |
1327 | 0 | return CHECK_INVALID; |
1328 | 0 | cs_l ^= *cptr++; |
1329 | | |
1330 | | /* -*- data: '[^*]*' */ |
1331 | 0 | while (*cptr && *cptr != '*') |
1332 | 0 | cs_l ^= *cptr++; |
1333 | | |
1334 | | /* -*- checksum field: (\*[0-9A-F]{2})?$ */ |
1335 | 0 | if (*cptr == '\0') |
1336 | 0 | return CHECK_VALID; |
1337 | 0 | if (*cptr != '*' || cptr != eptr - 3 || |
1338 | 0 | (cptr - data->base) >= NMEA_PROTO_MAXLEN) |
1339 | 0 | return CHECK_INVALID; |
1340 | | |
1341 | 0 | for (cptr++; (tmp = *cptr) != '\0'; cptr++) { |
1342 | 0 | if (tmp >= '0' && tmp <= '9') |
1343 | 0 | cs_r = (cs_r << 4) + (tmp - '0'); |
1344 | 0 | else if (tmp >= 'A' && tmp <= 'F') |
1345 | 0 | cs_r = (cs_r << 4) + (tmp - 'A' + 10); |
1346 | 0 | else |
1347 | 0 | break; |
1348 | 0 | } |
1349 | | |
1350 | | /* -*- make sure we are at end of string and csum matches */ |
1351 | 0 | if (cptr != eptr || cs_l != cs_r) |
1352 | 0 | return CHECK_INVALID; |
1353 | | |
1354 | 0 | return CHECK_CSVALID; |
1355 | 0 | } |
1356 | | |
1357 | | /* |
1358 | | * ------------------------------------------------------------------- |
1359 | | * fetch a data field by index, zero being the name field. If this |
1360 | | * function is called repeatedly with increasing indices, the total load |
1361 | | * is O(n), n being the length of the string; if it is called with |
1362 | | * decreasing indices, the total load is O(n^2). Try not to go backwards |
1363 | | * too often. |
1364 | | * ------------------------------------------------------------------- |
1365 | | */ |
1366 | | static char * |
1367 | | field_parse( |
1368 | | nmea_data * data, |
1369 | | int fn |
1370 | | ) |
1371 | 0 | { |
1372 | 0 | char tmp; |
1373 | |
|
1374 | 0 | if (fn < data->cidx) { |
1375 | 0 | data->cidx = 0; |
1376 | 0 | data->cptr = data->base; |
1377 | 0 | } |
1378 | 0 | while ((fn > data->cidx) && (tmp = *data->cptr) != '\0') { |
1379 | 0 | data->cidx += (tmp == ','); |
1380 | 0 | data->cptr++; |
1381 | 0 | } |
1382 | 0 | return data->cptr; |
1383 | 0 | } |
1384 | | |
1385 | | /* |
1386 | | * ------------------------------------------------------------------- |
1387 | | * Wipe (that is, overwrite with '_') data fields and the checksum in |
1388 | | * the last timecode. The list of field indices is given as integers |
1389 | | * in a varargs list, preferrably in ascending order, in any case |
1390 | | * terminated by a negative field index. |
1391 | | * |
1392 | | * A maximum number of 8 fields can be overwritten at once to guard |
1393 | | * against runaway (that is, unterminated) argument lists. |
1394 | | * |
1395 | | * This function affects what a remote user can see with |
1396 | | * |
1397 | | * ntpq -c clockvar <server> |
1398 | | * |
1399 | | * Note that this also removes the wiped fields from any clockstats |
1400 | | * log. Some NTP operators monitor their NMEA GPS using the change in |
1401 | | * location in clockstats over time as as a proxy for the quality of |
1402 | | * GPS reception and thereby time reported. |
1403 | | * ------------------------------------------------------------------- |
1404 | | */ |
1405 | | static void |
1406 | | field_wipe( |
1407 | | nmea_data * data, |
1408 | | ... |
1409 | | ) |
1410 | 0 | { |
1411 | 0 | va_list va; /* vararg index list */ |
1412 | 0 | int fcnt; /* safeguard against runaway arglist */ |
1413 | 0 | int fidx; /* field to nuke, or -1 for checksum */ |
1414 | 0 | char * cp; /* overwrite destination */ |
1415 | | |
1416 | 0 | fcnt = 8; |
1417 | 0 | cp = NULL; |
1418 | 0 | va_start(va, data); |
1419 | 0 | do { |
1420 | 0 | fidx = va_arg(va, int); |
1421 | 0 | if (fidx >= 0 && fidx <= NMEA_PROTO_FIELDS) { |
1422 | 0 | cp = field_parse(data, fidx); |
1423 | 0 | } else { |
1424 | 0 | cp = data->base + data->blen; |
1425 | 0 | if (data->blen >= 3 && cp[-3] == '*') |
1426 | 0 | cp -= 2; |
1427 | 0 | } |
1428 | 0 | for ( ; '\0' != *cp && '*' != *cp && ',' != *cp; cp++) |
1429 | 0 | if ('.' != *cp) |
1430 | 0 | *cp = '_'; |
1431 | 0 | } while (fcnt-- && fidx >= 0); |
1432 | 0 | va_end(va); |
1433 | 0 | } |
1434 | | |
1435 | | /* |
1436 | | * ------------------------------------------------------------------- |
1437 | | * PARSING HELPERS |
1438 | | * ------------------------------------------------------------------- |
1439 | | * |
1440 | | * Check sync status |
1441 | | * |
1442 | | * If the character at the data field start matches the tag value, |
1443 | | * return LEAP_NOWARNING and LEAP_NOTINSYNC otherwise. If the 'inverted' |
1444 | | * flag is given, just the opposite value is returned. If there is no |
1445 | | * data field (*cp points to the NUL byte) the result is LEAP_NOTINSYNC. |
1446 | | * ------------------------------------------------------------------- |
1447 | | */ |
1448 | | static u_char |
1449 | | parse_qual( |
1450 | | nmea_data * rd, |
1451 | | int idx, |
1452 | | char tag, |
1453 | | int inv |
1454 | | ) |
1455 | 0 | { |
1456 | 0 | static const u_char table[2] = |
1457 | 0 | { LEAP_NOTINSYNC, LEAP_NOWARNING }; |
1458 | 0 | char * dp; |
1459 | |
|
1460 | 0 | dp = field_parse(rd, idx); |
1461 | | |
1462 | 0 | return table[ *dp && ((*dp == tag) == !inv) ]; |
1463 | 0 | } |
1464 | | |
1465 | | /* |
1466 | | * ------------------------------------------------------------------- |
1467 | | * Parse a time stamp in HHMMSS[.sss] format with error checking. |
1468 | | * |
1469 | | * returns 1 on success, 0 on failure |
1470 | | * ------------------------------------------------------------------- |
1471 | | */ |
1472 | | static int |
1473 | | parse_time( |
1474 | | struct calendar * jd, /* result calendar pointer */ |
1475 | | long * ns, /* storage for nsec fraction */ |
1476 | | nmea_data * rd, |
1477 | | int idx |
1478 | | ) |
1479 | 0 | { |
1480 | 0 | static const unsigned long weight[4] = { |
1481 | 0 | 0, 100000000, 10000000, 1000000 |
1482 | 0 | }; |
1483 | |
|
1484 | 0 | int rc; |
1485 | 0 | u_int h; |
1486 | 0 | u_int m; |
1487 | 0 | u_int s; |
1488 | 0 | int p1; |
1489 | 0 | int p2; |
1490 | 0 | u_long f; |
1491 | 0 | char * dp; |
1492 | |
|
1493 | 0 | dp = field_parse(rd, idx); |
1494 | 0 | rc = sscanf(dp, "%2u%2u%2u%n.%3lu%n", &h, &m, &s, &p1, &f, &p2); |
1495 | 0 | if (rc < 3 || p1 != 6) { |
1496 | 0 | DPRINTF(1, ("nmea: invalid time code: '%.6s'\n", dp)); |
1497 | 0 | return FALSE; |
1498 | 0 | } |
1499 | | |
1500 | | /* value sanity check */ |
1501 | 0 | if (h > 23 || m > 59 || s > 60) { |
1502 | 0 | DPRINTF(1, ("nmea: invalid time spec %02u:%02u:%02u\n", |
1503 | 0 | h, m, s)); |
1504 | 0 | return FALSE; |
1505 | 0 | } |
1506 | | |
1507 | 0 | jd->hour = (u_char)h; |
1508 | 0 | jd->minute = (u_char)m; |
1509 | 0 | jd->second = (u_char)s; |
1510 | | /* if we have a fraction, scale it up to nanoseconds. */ |
1511 | 0 | if (rc == 4) |
1512 | 0 | *ns = f * weight[p2 - p1 - 1]; |
1513 | 0 | else |
1514 | 0 | *ns = 0; |
1515 | |
|
1516 | 0 | return TRUE; |
1517 | 0 | } |
1518 | | |
1519 | | /* |
1520 | | * ------------------------------------------------------------------- |
1521 | | * Parse a date string from an NMEA sentence. This could either be a |
1522 | | * partial date in DDMMYY format in one field, or DD,MM,YYYY full date |
1523 | | * spec spanning three fields. This function does some extensive error |
1524 | | * checking to make sure the date string was consistent. |
1525 | | * |
1526 | | * returns 1 on success, 0 on failure |
1527 | | * ------------------------------------------------------------------- |
1528 | | */ |
1529 | | static int |
1530 | | parse_date( |
1531 | | struct calendar * jd, /* result pointer */ |
1532 | | nmea_data * rd, |
1533 | | int idx, |
1534 | | enum date_fmt fmt |
1535 | | ) |
1536 | 0 | { |
1537 | 0 | int rc; |
1538 | 0 | u_int y; |
1539 | 0 | u_int m; |
1540 | 0 | u_int d; |
1541 | 0 | int p; |
1542 | 0 | char * dp; |
1543 | | |
1544 | 0 | dp = field_parse(rd, idx); |
1545 | 0 | switch (fmt) { |
1546 | | |
1547 | 0 | case DATE_1_DDMMYY: |
1548 | 0 | rc = sscanf(dp, "%2u%2u%2u%n", &d, &m, &y, &p); |
1549 | 0 | if (rc != 3 || p != 6) { |
1550 | 0 | DPRINTF(1, ("nmea: invalid date code: '%.6s'\n", |
1551 | 0 | dp)); |
1552 | 0 | return FALSE; |
1553 | 0 | } |
1554 | 0 | break; |
1555 | | |
1556 | 0 | case DATE_3_DDMMYYYY: |
1557 | 0 | rc = sscanf(dp, "%2u,%2u,%4u%n", &d, &m, &y, &p); |
1558 | 0 | if (rc != 3 || p != 10) { |
1559 | 0 | DPRINTF(1, ("nmea: invalid date code: '%.10s'\n", |
1560 | 0 | dp)); |
1561 | 0 | return FALSE; |
1562 | 0 | } |
1563 | 0 | break; |
1564 | | |
1565 | 0 | default: |
1566 | 0 | DPRINTF(1, ("nmea: invalid parse format: %d\n", fmt)); |
1567 | 0 | return FALSE; |
1568 | 0 | } |
1569 | | |
1570 | | /* value sanity check */ |
1571 | 0 | if (d < 1 || d > 31 || m < 1 || m > 12) { |
1572 | 0 | DPRINTF(1, ("nmea: invalid date spec (YMD) %04u:%02u:%02u\n", |
1573 | 0 | y, m, d)); |
1574 | 0 | return FALSE; |
1575 | 0 | } |
1576 | | |
1577 | | /* store results */ |
1578 | 0 | jd->monthday = (u_char)d; |
1579 | 0 | jd->month = (u_char)m; |
1580 | 0 | jd->year = (u_short)y; |
1581 | |
|
1582 | 0 | return TRUE; |
1583 | 0 | } |
1584 | | |
1585 | | /* |
1586 | | * ------------------------------------------------------------------- |
1587 | | * Parse GPS week time info from an NMEA sentence. This info contains |
1588 | | * the GPS week number, the GPS time-of-week and the leap seconds GPS |
1589 | | * to UTC. |
1590 | | * |
1591 | | * returns 1 on success, 0 on failure |
1592 | | * ------------------------------------------------------------------- |
1593 | | */ |
1594 | | static int |
1595 | | parse_weekdata( |
1596 | | gps_weektm * wd, |
1597 | | nmea_data * rd, |
1598 | | int weekidx, |
1599 | | int timeidx, |
1600 | | int leapidx |
1601 | | ) |
1602 | 0 | { |
1603 | 0 | u_long secs; |
1604 | 0 | int fcnt; |
1605 | | |
1606 | | /* parse fields and count success */ |
1607 | 0 | fcnt = sscanf(field_parse(rd, weekidx), "%hu", &wd->wt_week); |
1608 | 0 | fcnt += sscanf(field_parse(rd, timeidx), "%lu", &secs); |
1609 | 0 | fcnt += sscanf(field_parse(rd, leapidx), "%hd", &wd->wt_leap); |
1610 | 0 | if (fcnt != 3 || wd->wt_week >= 1024 || secs >= 7*SECSPERDAY) { |
1611 | 0 | DPRINTF(1, ("nmea: parse_weekdata: invalid weektime spec\n")); |
1612 | 0 | return FALSE; |
1613 | 0 | } |
1614 | 0 | wd->wt_time = (u_int32)secs; |
1615 | |
|
1616 | 0 | return TRUE; |
1617 | 0 | } |
1618 | | |
1619 | | /* |
1620 | | * ------------------------------------------------------------------- |
1621 | | * funny calendar-oriented stuff -- perhaps a bit hard to grok. |
1622 | | * ------------------------------------------------------------------- |
1623 | | * |
1624 | | * Unfold a time-of-day (seconds since midnight) around the current |
1625 | | * system time in a manner that guarantees an absolute difference of |
1626 | | * less than 12hrs. |
1627 | | * |
1628 | | * This function is used for NMEA sentences that contain no date |
1629 | | * information. This requires the system clock to be in +/-12hrs |
1630 | | * around the true time, or the clock will synchronize the system 1day |
1631 | | * off if not augmented with a time sources that also provide the |
1632 | | * necessary date information. |
1633 | | * |
1634 | | * The function updates the calendar structure it also uses as |
1635 | | * input to fetch the time from. |
1636 | | * |
1637 | | * returns 1 on success, 0 on failure |
1638 | | * ------------------------------------------------------------------- |
1639 | | */ |
1640 | | static int |
1641 | | unfold_day( |
1642 | | struct calendar * jd, |
1643 | | u_int32 rec_ui |
1644 | | ) |
1645 | 0 | { |
1646 | 0 | vint64 rec_qw; |
1647 | 0 | ntpcal_split rec_ds; |
1648 | | |
1649 | | /* |
1650 | | * basically this is the peridiodic extension of the receive |
1651 | | * time - 12hrs to the time-of-day with a period of 1 day. |
1652 | | * But we would have to execute this in 64bit arithmetic, and we |
1653 | | * cannot assume we can do this; therefore this is done |
1654 | | * in split representation. |
1655 | | */ |
1656 | 0 | rec_qw = ntpcal_ntp_to_ntp(rec_ui - SECSPERDAY/2, NULL); |
1657 | 0 | rec_ds = ntpcal_daysplit(&rec_qw); |
1658 | 0 | rec_ds.lo = ntpcal_periodic_extend(rec_ds.lo, |
1659 | 0 | ntpcal_date_to_daysec(jd), |
1660 | 0 | SECSPERDAY); |
1661 | 0 | rec_ds.hi += ntpcal_daysec_to_date(jd, rec_ds.lo); |
1662 | 0 | return (ntpcal_rd_to_date(jd, rec_ds.hi + DAY_NTP_STARTS) >= 0); |
1663 | 0 | } |
1664 | | |
1665 | | /* |
1666 | | * ------------------------------------------------------------------- |
1667 | | * A 2-digit year is expanded into full year spec around the year found |
1668 | | * in 'jd->year'. This should be in +79/-19 years around the system time, |
1669 | | * or the result will be off by 100 years. The assymetric behaviour was |
1670 | | * chosen to enable inital sync for systems that do not have a |
1671 | | * battery-backup clock and start with a date that is typically years in |
1672 | | * the past. |
1673 | | * |
1674 | | * Since the GPS epoch starts at 1980-01-06, the resulting year will be |
1675 | | * not be before 1980 in any case. |
1676 | | * |
1677 | | * returns 1 on success, 0 on failure |
1678 | | * ------------------------------------------------------------------- |
1679 | | */ |
1680 | | static int |
1681 | | unfold_century( |
1682 | | struct calendar * jd, |
1683 | | u_int32 rec_ui |
1684 | | ) |
1685 | 0 | { |
1686 | 0 | struct calendar rec; |
1687 | 0 | int32 baseyear; |
1688 | |
|
1689 | 0 | ntpcal_ntp_to_date(&rec, rec_ui, NULL); |
1690 | 0 | baseyear = rec.year - 20; |
1691 | 0 | if (baseyear < g_gpsMinYear) |
1692 | 0 | baseyear = g_gpsMinYear; |
1693 | 0 | jd->year = (u_short)ntpcal_periodic_extend(baseyear, jd->year, |
1694 | 0 | 100); |
1695 | |
|
1696 | 0 | return ((baseyear <= jd->year) && (baseyear + 100 > jd->year)); |
1697 | 0 | } |
1698 | | |
1699 | | /* |
1700 | | * ------------------------------------------------------------------- |
1701 | | * A 2-digit year is expanded into a full year spec by correlation with |
1702 | | * a GPS week number and the current leap second count. |
1703 | | * |
1704 | | * The GPS week time scale counts weeks since Sunday, 1980-01-06, modulo |
1705 | | * 1024 and seconds since start of the week. The GPS time scale is based |
1706 | | * on international atomic time (TAI), so the leap second difference to |
1707 | | * UTC is also needed for a proper conversion. |
1708 | | * |
1709 | | * A brute-force analysis (that is, test for every date) shows that a |
1710 | | * wrong assignment of the century can not happen between the years 1900 |
1711 | | * to 2399 when comparing the week signatures for different |
1712 | | * centuries. (I *think* that will not happen for 400*1024 years, but I |
1713 | | * have no valid proof. -*-perlinger@ntp.org-*-) |
1714 | | * |
1715 | | * This function is bound to to work between years 1980 and 2399 |
1716 | | * (inclusive), which should suffice for now ;-) |
1717 | | * |
1718 | | * Note: This function needs a full date&time spec on input due to the |
1719 | | * necessary leap second corrections! |
1720 | | * |
1721 | | * returns 1 on success, 0 on failure |
1722 | | * ------------------------------------------------------------------- |
1723 | | */ |
1724 | | static int |
1725 | | gpsfix_century( |
1726 | | struct calendar * jd, |
1727 | | const gps_weektm * wd, |
1728 | | u_short * century |
1729 | | ) |
1730 | 0 | { |
1731 | 0 | int32 days; |
1732 | 0 | int32 doff; |
1733 | 0 | u_short week; |
1734 | 0 | u_short year; |
1735 | 0 | int loop; |
1736 | | |
1737 | | /* Get day offset. Assumes that the input time is in range and |
1738 | | * that the leap seconds do not shift more than +/-1 day. |
1739 | | */ |
1740 | 0 | doff = ntpcal_date_to_daysec(jd) + wd->wt_leap; |
1741 | 0 | doff = (doff >= SECSPERDAY) - (doff < 0); |
1742 | | |
1743 | | /* |
1744 | | * Loop over centuries to get a match, starting with the last |
1745 | | * successful one. (Or with the 19th century if the cached value |
1746 | | * is out of range...) |
1747 | | */ |
1748 | 0 | year = jd->year % 100; |
1749 | 0 | for (loop = 5; loop > 0; loop--,(*century)++) { |
1750 | 0 | if (*century < 19 || *century >= 24) |
1751 | 0 | *century = 19; |
1752 | | /* Get days and week in GPS epoch */ |
1753 | 0 | jd->year = year + *century * 100; |
1754 | 0 | days = ntpcal_date_to_rd(jd) - DAY_GPS_STARTS + doff; |
1755 | 0 | week = (days / 7) % 1024; |
1756 | 0 | if (days >= 0 && wd->wt_week == week) |
1757 | 0 | return TRUE; /* matched... */ |
1758 | 0 | } |
1759 | | |
1760 | 0 | jd->year = year; |
1761 | 0 | return FALSE; /* match failed... */ |
1762 | 0 | } |
1763 | | |
1764 | | /* |
1765 | | * ------------------------------------------------------------------- |
1766 | | * And now the final execise: Considering the fact that many (most?) |
1767 | | * GPS receivers cannot handle a GPS epoch wrap well, we try to |
1768 | | * compensate for that problem by unwrapping a GPS epoch around the |
1769 | | * receive stamp. Another execise in periodic unfolding, of course, |
1770 | | * but with enough points to take care of. |
1771 | | * |
1772 | | * Note: The integral part of 'tofs' is intended to handle small(!) |
1773 | | * systematic offsets, as -1 for handling $GPZDG, which gives the |
1774 | | * following second. (sigh...) The absolute value shall be less than a |
1775 | | * day (86400 seconds). |
1776 | | * ------------------------------------------------------------------- |
1777 | | */ |
1778 | | static l_fp |
1779 | | eval_gps_time( |
1780 | | struct peer * peer, /* for logging etc */ |
1781 | | const struct calendar * gpst, /* GPS time stamp */ |
1782 | | const struct timespec * tofs, /* GPS frac second & offset */ |
1783 | | const l_fp * xrecv /* receive time stamp */ |
1784 | | ) |
1785 | 0 | { |
1786 | 0 | struct refclockproc * const pp = peer->procptr; |
1787 | 0 | nmea_unit * const up = (nmea_unit *)pp->unitptr; |
1788 | |
|
1789 | 0 | l_fp retv; |
1790 | | |
1791 | | /* components of calculation */ |
1792 | 0 | int32_t rcv_sec, rcv_day; /* receive ToD and day */ |
1793 | 0 | int32_t gps_sec, gps_day; /* GPS ToD and day in NTP epoch */ |
1794 | 0 | int32_t adj_day, weeks; /* adjusted GPS day and week shift */ |
1795 | | |
1796 | | /* some temporaries to shuffle data */ |
1797 | 0 | vint64 vi64; |
1798 | 0 | ntpcal_split rs64; |
1799 | | |
1800 | | /* evaluate time stamp from receiver. */ |
1801 | 0 | gps_sec = ntpcal_date_to_daysec(gpst); |
1802 | 0 | gps_day = ntpcal_date_to_rd(gpst) - DAY_NTP_STARTS; |
1803 | | |
1804 | | /* merge in fractional offset */ |
1805 | 0 | retv = tspec_intv_to_lfp(*tofs); |
1806 | 0 | gps_sec += retv.l_i; |
1807 | | |
1808 | | /* If we fully trust the GPS receiver, just combine days and |
1809 | | * seconds and be done. */ |
1810 | 0 | if (peer->ttl & NMEA_DATETRUST_MASK) { |
1811 | 0 | retv.l_ui = ntpcal_dayjoin(gps_day, gps_sec).D_s.lo; |
1812 | 0 | return retv; |
1813 | 0 | } |
1814 | | |
1815 | | /* So we do not trust the GPS receiver to deliver a correct date |
1816 | | * due to the GPS epoch changes. We map the date from the |
1817 | | * receiver into the +/-512 week interval around the receive |
1818 | | * time in that case. This would be a tad easier with 64bit |
1819 | | * calculations, but again, we restrict the code to 32bit ops |
1820 | | * when possible. */ |
1821 | | |
1822 | | /* - make sure the GPS fractional day is normalised |
1823 | | * Applying the offset value might have put us slightly over the |
1824 | | * edge of the allowed range for seconds-of-day. Doing a full |
1825 | | * division with floor correction is overkill here; a simple |
1826 | | * addition or subtraction step is sufficient. Using WHILE loops |
1827 | | * gives the right result even if the offset exceeds one day, |
1828 | | * which is NOT what it's intented for! */ |
1829 | 0 | while (gps_sec >= SECSPERDAY) { |
1830 | 0 | gps_sec -= SECSPERDAY; |
1831 | 0 | gps_day += 1; |
1832 | 0 | } |
1833 | 0 | while (gps_sec < 0) { |
1834 | 0 | gps_sec += SECSPERDAY; |
1835 | 0 | gps_day -= 1; |
1836 | 0 | } |
1837 | | |
1838 | | /* - get unfold base: day of full recv time - 512 weeks */ |
1839 | 0 | vi64 = ntpcal_ntp_to_ntp(xrecv->l_ui, NULL); |
1840 | 0 | rs64 = ntpcal_daysplit(&vi64); |
1841 | 0 | rcv_sec = rs64.lo; |
1842 | 0 | rcv_day = rs64.hi - 512 * 7; |
1843 | | |
1844 | | /* - take the fractional days into account |
1845 | | * If the fractional day of the GPS time is smaller than the |
1846 | | * fractional day of the receive time, we shift the base day for |
1847 | | * the unfold by 1. */ |
1848 | 0 | if ( gps_sec < rcv_sec |
1849 | 0 | || (gps_sec == rcv_sec && retv.l_uf < xrecv->l_uf)) |
1850 | 0 | rcv_day += 1; |
1851 | | |
1852 | | /* - don't warp ahead of GPS invention! */ |
1853 | 0 | if (rcv_day < g_gpsMinBase) |
1854 | 0 | rcv_day = g_gpsMinBase; |
1855 | | |
1856 | | /* - let the magic happen: */ |
1857 | 0 | adj_day = ntpcal_periodic_extend(rcv_day, gps_day, 1024*7); |
1858 | | |
1859 | | /* - check if we should log a GPS epoch warp */ |
1860 | 0 | weeks = (adj_day - gps_day) / 7; |
1861 | 0 | if (weeks != up->epoch_warp) { |
1862 | 0 | up->epoch_warp = weeks; |
1863 | 0 | LOGIF(CLOCKINFO, (LOG_INFO, |
1864 | 0 | "%s Changed GPS epoch warp to %d weeks", |
1865 | 0 | refnumtoa(&peer->srcadr), weeks)); |
1866 | 0 | } |
1867 | | |
1868 | | /* - build result and be done */ |
1869 | 0 | retv.l_ui = ntpcal_dayjoin(adj_day, gps_sec).D_s.lo; |
1870 | 0 | return retv; |
1871 | 0 | } |
1872 | | |
1873 | | /* |
1874 | | * =================================================================== |
1875 | | * |
1876 | | * NMEAD support |
1877 | | * |
1878 | | * original nmead support added by Jon Miner (cp_n18@yahoo.com) |
1879 | | * |
1880 | | * See http://home.hiwaay.net/~taylorc/gps/nmea-server/ |
1881 | | * for information about nmead |
1882 | | * |
1883 | | * To use this, you need to create a link from /dev/gpsX to |
1884 | | * the server:port where nmead is running. Something like this: |
1885 | | * |
1886 | | * ln -s server:port /dev/gps1 |
1887 | | * |
1888 | | * Split into separate function by Juergen Perlinger |
1889 | | * (perlinger-at-ntp-dot-org) |
1890 | | * |
1891 | | * =================================================================== |
1892 | | */ |
1893 | | static int |
1894 | | nmead_open( |
1895 | | const char * device |
1896 | | ) |
1897 | 0 | { |
1898 | 0 | int fd = -1; /* result file descriptor */ |
1899 | | |
1900 | 0 | #ifdef HAVE_READLINK |
1901 | 0 | char host[80]; /* link target buffer */ |
1902 | 0 | char * port; /* port name or number */ |
1903 | 0 | int rc; /* result code (several)*/ |
1904 | 0 | int sh; /* socket handle */ |
1905 | 0 | struct addrinfo ai_hint; /* resolution hint */ |
1906 | 0 | struct addrinfo *ai_list; /* resolution result */ |
1907 | 0 | struct addrinfo *ai; /* result scan ptr */ |
1908 | |
|
1909 | 0 | fd = -1; |
1910 | | |
1911 | | /* try to read as link, make sure no overflow occurs */ |
1912 | 0 | rc = readlink(device, host, sizeof(host)); |
1913 | 0 | if ((size_t)rc >= sizeof(host)) |
1914 | 0 | return fd; /* error / overflow / truncation */ |
1915 | 0 | host[rc] = '\0'; /* readlink does not place NUL */ |
1916 | | |
1917 | | /* get port */ |
1918 | 0 | port = strchr(host, ':'); |
1919 | 0 | if (!port) |
1920 | 0 | return fd; /* not 'host:port' syntax ? */ |
1921 | 0 | *port++ = '\0'; /* put in separator */ |
1922 | | |
1923 | | /* get address infos and try to open socket |
1924 | | * |
1925 | | * This getaddrinfo() is naughty in ntpd's nonblocking main |
1926 | | * thread, but you have to go out of your wary to use this code |
1927 | | * and typically the blocking is at startup where its impact is |
1928 | | * reduced. The same holds for the 'connect()', as it is |
1929 | | * blocking, too... |
1930 | | */ |
1931 | 0 | ZERO(ai_hint); |
1932 | 0 | ai_hint.ai_protocol = IPPROTO_TCP; |
1933 | 0 | ai_hint.ai_socktype = SOCK_STREAM; |
1934 | 0 | if (getaddrinfo(host, port, &ai_hint, &ai_list)) |
1935 | 0 | return fd; |
1936 | | |
1937 | 0 | for (ai = ai_list; ai && (fd == -1); ai = ai->ai_next) { |
1938 | 0 | sh = socket(ai->ai_family, ai->ai_socktype, |
1939 | 0 | ai->ai_protocol); |
1940 | 0 | if (INVALID_SOCKET == sh) |
1941 | 0 | continue; |
1942 | 0 | rc = connect(sh, ai->ai_addr, ai->ai_addrlen); |
1943 | 0 | if (-1 != rc) |
1944 | 0 | fd = sh; |
1945 | 0 | else |
1946 | 0 | close(sh); |
1947 | 0 | } |
1948 | 0 | freeaddrinfo(ai_list); |
1949 | | #else |
1950 | | fd = -1; |
1951 | | #endif |
1952 | |
|
1953 | 0 | return fd; |
1954 | 0 | } |
1955 | | #else |
1956 | | NONEMPTY_TRANSLATION_UNIT |
1957 | | #endif /* REFCLOCK && CLOCK_NMEA */ |