/src/gpsd/gpsd-3.27.6~dev/gpsd/ppsthread.c
Line | Count | Source |
1 | | /* |
2 | | * ppsthread.c - manage PPS watcher threads |
3 | | * |
4 | | * To enable KPPS, this file needs to be compiled with HAVE_SYS_TIMEPPS_H |
5 | | * |
6 | | * If you are not good at threads do not touch this file! |
7 | | * For example: errno is thread safe; strerror() is not. |
8 | | * |
9 | | * It helps to know that there are two PPS measurement methods in |
10 | | * play. One is defined by RFC2783 and typically implemented in the |
11 | | * kernel. It is available on FreeBSD, Linux, and NetBSD. In gpsd it |
12 | | * is referred to as KPPS. KPPS is accessed on Linux via /dev/ppsN |
13 | | * devices. On BSD it is accessed via the same device as the serial |
14 | | * port. This mechanism is preferred as it should provide the smallest |
15 | | * latency and jitter from control line transition to timestamp. |
16 | | * |
17 | | * The other mechanism is user-space PPS, which uses the (not |
18 | | * standardized) TIOCMIWAIT ioctl to wait for PPS transitions on |
19 | | * serial port control lines. It is implemented on Linux and OpenBSD. |
20 | | * |
21 | | * On Linux, RFC2783 PPS requires root permissions for initialization; |
22 | | * user-space PPS does not. User-space PPS loses some functionality |
23 | | * when not initialized as root. In gpsd, user-space PPS is referred |
24 | | * to as "plain PPS". |
25 | | * |
26 | | * On {Free,Net}BSD, RFC2783 PPS should only require access to the |
27 | | * serial port, but details have not yet been tested and documented |
28 | | * here. |
29 | | * |
30 | | * Note that for easy debugging all logging from this file is prefixed |
31 | | * with PPS or KPPS. |
32 | | * |
33 | | * To use the thread manager, you need to first fill in the |
34 | | * devicefd, devicename, and the four hook function members in the thread |
35 | | * context structure. The void *context member is available for your hook |
36 | | * functions to use; the thread-monitor code doesn't touch it. |
37 | | * |
38 | | * After this setup, you can call pps_thread_activate() and the |
39 | | * thread will launch. It is OK to do this before the device is open, |
40 | | * the thread will wait on that. |
41 | | * |
42 | | * WARNING! Loss of precision |
43 | | * UNIX time to nanoSec precision is 62 significant bits |
44 | | * UNIX time to nanoSec precision after 2038 is 63 bits |
45 | | * a double is only 53 significant bits. |
46 | | * |
47 | | * You cannot do PPS math with doubles |
48 | | * |
49 | | * This file is Copyright 2013 by the GPSD project |
50 | | * SPDX-License-Identifier: BSD-2-clause |
51 | | */ |
52 | | |
53 | | #include "../include/gpsd_config.h" // must be before all includes |
54 | | |
55 | | #include <errno.h> |
56 | | #include <inttypes.h> |
57 | | #include <limits.h> |
58 | | #include <math.h> |
59 | | #include <pthread.h> // pacifies OpenBSD's compiler |
60 | | #include <signal.h> |
61 | | #include <stdbool.h> |
62 | | #include <stdio.h> |
63 | | #include <string.h> |
64 | | #include <sys/socket.h> |
65 | | #include <sys/time.h> |
66 | | #include <unistd.h> |
67 | | |
68 | | /* use RFC 2783 PPS API |
69 | | * this needs linux >= 2.6.34 and |
70 | | * CONFIG_PPS=y |
71 | | * CONFIG_PPS_DEBUG=y [optional to kernel log pulses] |
72 | | * CONFIG_PPS_CLIENT_LDISC=y |
73 | | * |
74 | | * Also beware that setting |
75 | | * CONFIG_PPS_CLIENT_KTIMER=y |
76 | | * adds a fake software-generated PPS intended for testing. This |
77 | | * doesn't even run at exactly 1Hz, so any attempt to use it for |
78 | | * real timing is disastrous. Hence we try to avoid it. |
79 | | */ |
80 | | #define FAKE_PPS_NAME "ktimer" |
81 | | |
82 | | #if defined(HAVE_SYS_TIMEPPS_H) |
83 | | // include unistd.h here as it is missing on older pps-tools releases. |
84 | | // 'close' is not defined otherwise. |
85 | | #include <unistd.h> |
86 | | #include <sys/timepps.h> |
87 | | #endif |
88 | | |
89 | | #include "../include/gpsd.h" // for socket_t |
90 | | #include "../include/timespec.h" |
91 | | #include "../include/os_compat.h" |
92 | | #include "../include/ppsthread.h" |
93 | | |
94 | | /* |
95 | | * Tell GCC that we want thread-safe behavior with _REENTRANT; |
96 | | * in particular, errno must be thread-local. |
97 | | * Tell POSIX-conforming implementations with _POSIX_THREAD_SAFE_FUNCTIONS. |
98 | | * See http://www.unix.org/whitepapers/reentrant.html |
99 | | */ |
100 | | #ifndef _REENTRANT |
101 | | #define _REENTRANT |
102 | | #endif |
103 | | #ifndef _POSIX_THREAD_SAFE_FUNCTIONS |
104 | | #define _POSIX_THREAD_SAFE_FUNCTIONS |
105 | | #endif |
106 | | |
107 | | /* |
108 | | * Warning: This is a potential portability problem. |
109 | | * It's needed so that TIOCMIWAIT will be defined and the plain PPS |
110 | | * code will work, but it's not a SuS/POSIX standard header. We're |
111 | | * going to include it unconditionally here because we expect both |
112 | | * Linux and BSD to have it and we want compilation to break with |
113 | | * an audible snapping sound if it's not present. |
114 | | */ |
115 | | #include <sys/ioctl.h> |
116 | | |
117 | | #if defined(HAVE_SYS_TIMEPPS_H) |
118 | | #include <glob.h> |
119 | | #include <fcntl.h> // needed for open() and friends |
120 | | #endif |
121 | | |
122 | | #if defined(TIOCMIWAIT) |
123 | | static int get_edge_tiocmiwait(volatile struct pps_thread_t *, |
124 | | struct timespec *, int *, |
125 | | volatile struct timedelta_t *); |
126 | | #endif // TIOCMIWAIT |
127 | | |
128 | | struct inner_context_t { |
129 | | volatile struct pps_thread_t *pps_thread; |
130 | | bool pps_canwait; // can RFC2783 wait? |
131 | | #if defined(HAVE_SYS_TIMEPPS_H) |
132 | | int pps_caps; // RFC2783 getcaps() |
133 | | pps_handle_t kernelpps_handle; |
134 | | #endif // defined(HAVE_SYS_TIMEPPS_H) |
135 | | int kernelpps_fd; |
136 | | }; |
137 | | |
138 | | #if defined(HAVE_SYS_TIMEPPS_H) |
139 | | static int get_edge_rfc2783(struct inner_context_t *, |
140 | | struct timespec *, |
141 | | int *, |
142 | | struct timespec *, |
143 | | int *, |
144 | | volatile struct timedelta_t *); |
145 | | #endif // defined(HAVE_SYS_TIMEPPS_H) |
146 | | |
147 | | static pthread_mutex_t ppslast_mutex = PTHREAD_MUTEX_INITIALIZER; |
148 | | |
149 | | // Signal used to interrupt blocking ioctl(TIOCMIWAIT) |
150 | 0 | #define PPS_THREAD_SIGNAL SIGUSR1 |
151 | | |
152 | | static void pps_thread_signal_handler(int signum UNUSED) |
153 | 0 | { |
154 | | // nothing to do, just interrupt the ioctl() |
155 | 0 | } |
156 | | |
157 | | /* |
158 | | * Version of strerror_r() which explicitly ignores the return value. |
159 | | * This is needed to avoid warnings from some overly pedantic compilers. |
160 | | * Unfortunately, the return type is platform-dependent, making it |
161 | | * impractical to actually use the return value. |
162 | | * |
163 | | * glibc strerror_r() is not POSIX compliant if _GNU_SOURCE is defined! |
164 | | * sometimes glibc strerror_r() returns a pointer to a static, and |
165 | | * leaves buf untouched! |
166 | | */ |
167 | | static const char *pps_strerror_r(int errnum, char *buf, size_t len) |
168 | 0 | { |
169 | | #ifdef STRERROR_R_STR |
170 | | // glibc strerror_r() returns pointer to the requested string. |
171 | | // which may, or may not, point to buf. |
172 | | return strerror_r(errnum, buf, len); |
173 | | #else |
174 | | // strerror_r() returns 0 on success |
175 | | // assign to an integer to create a warning if strerror_r() returns char * |
176 | 0 | int ret = strerror_r(errnum, buf, len); |
177 | 0 | if (0 == ret) { |
178 | 0 | return buf; |
179 | 0 | } |
180 | 0 | return "strerror_r() failed"; |
181 | 0 | #endif |
182 | 0 | } |
183 | | |
184 | | static void thread_lock(volatile struct pps_thread_t *pps_thread) |
185 | 0 | { |
186 | 0 | int pthread_err = pthread_mutex_lock(&ppslast_mutex); |
187 | 0 | if (0 != pthread_err) { |
188 | 0 | char errbuf[BUFSIZ] = "unknown error"; |
189 | 0 | pps_thread->log_hook(pps_thread, THREAD_ERROR, |
190 | 0 | "PPS:%s pthread_mutex_lock() : %s(%d)\n", |
191 | 0 | pps_thread->devicename, |
192 | 0 | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | | static void thread_unlock(volatile struct pps_thread_t *pps_thread) |
197 | 0 | { |
198 | 0 | int pthread_err = pthread_mutex_unlock(&ppslast_mutex); |
199 | 0 | if (0 != pthread_err) { |
200 | 0 | char errbuf[BUFSIZ] = "unknown error"; |
201 | 0 | pps_thread->log_hook(pps_thread, THREAD_ERROR, |
202 | 0 | "TPPS:%s pthread_mutex_unlock(): %s(%d)\n", |
203 | 0 | pps_thread->devicename, |
204 | 0 | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | | #if defined(HAVE_SYS_TIMEPPS_H) |
209 | | #ifdef __linux__ |
210 | | /* Obtain contents of specified sysfs variable; put in buf |
211 | | * empty (NUL-terminated) string if failure */ |
212 | | static void get_sysfs_var(const char *path, char *buf, size_t bufsize) |
213 | | { |
214 | | int fd = open(path, O_RDONLY); |
215 | | |
216 | | buf[0] = '\0'; |
217 | | if (0 <= fd) { |
218 | | ssize_t r = read(fd, buf, bufsize - 1); |
219 | | |
220 | | if (0 < r) { |
221 | | buf[r - 1] = '\0'; // remove trailing \x0a |
222 | | } else { |
223 | | // pacify coverity |
224 | | buf[0] = '\0'; |
225 | | } |
226 | | (void)close(fd); |
227 | | } |
228 | | } |
229 | | |
230 | | // Check to see whether the named PPS source is the fake one |
231 | | int pps_check_fake(const char *name) { |
232 | | char path[PATH_MAX] = ""; |
233 | | char buf[32]; |
234 | | |
235 | | memset(buf, 0, sizeof(buf)); |
236 | | snprintf(path, sizeof(path), "/sys/devices/virtual/pps/%s/name", name); |
237 | | get_sysfs_var(path, buf, sizeof(buf)); |
238 | | return 0 == strcmp(buf, FAKE_PPS_NAME); |
239 | | } |
240 | | |
241 | | // Get first "real" PPS device, skipping the fake, if any |
242 | | const char *pps_get_first(void) |
243 | | { |
244 | | if (pps_check_fake("pps0")) { |
245 | | return "/dev/pps1"; |
246 | | } |
247 | | return "/dev/pps0"; |
248 | | } |
249 | | #endif // __linux__ |
250 | | |
251 | | /* init_kernel_pps() initialize kernel PPS. |
252 | | * sets KPPS file handle into inner_context->kernelpps_handle |
253 | | * usually requires root privileges |
254 | | * |
255 | | * Return: 0 on success |
256 | | * less than zero on error |
257 | | */ |
258 | | static int init_kernel_pps(struct inner_context_t *inner_context) |
259 | | { |
260 | | pps_params_t pp; |
261 | | int ret; |
262 | | #ifdef __linux__ |
263 | | // These variables are only needed by Linux to find /dev/ppsN. |
264 | | int ldisc = 18; // the PPS line discipline |
265 | | glob_t globbuf; |
266 | | #endif |
267 | | char path[PATH_MAX] = ""; |
268 | | volatile struct pps_thread_t *pps_thread = inner_context->pps_thread; |
269 | | |
270 | | inner_context->kernelpps_handle = (pps_handle_t)-1; |
271 | | inner_context->pps_canwait = false; |
272 | | errno = 0; |
273 | | |
274 | | #ifdef __linux__ |
275 | | /* |
276 | | * Some Linuxes, like the RasPi's, have PPS devices preexisting. |
277 | | * Other OS have no way to automatically determine the proper /dev/ppsX. |
278 | | * Allow user to pass in an explicit PPS device path. |
279 | | */ |
280 | | if (0 == strncmp(pps_thread->devicename, "/dev/pps", 8)) { |
281 | | if (pps_check_fake(pps_thread->devicename + 5)) { |
282 | | pps_thread->log_hook(pps_thread, THREAD_WARN, |
283 | | "KPPS:%s is fake PPS," |
284 | | " timing will be inaccurate\n", |
285 | | pps_thread->devicename); |
286 | | } |
287 | | (void)strlcpy(path, pps_thread->devicename, sizeof(path) - 1); |
288 | | } else { |
289 | | char pps_num = '\0'; // /dev/pps[pps_num] is our device |
290 | | size_t i; // to match type of globbuf.gl_pathc |
291 | | /* |
292 | | * Otherwise one must make calls to associate a serial port with a |
293 | | * /dev/ppsN device and then grovel in system data to determine |
294 | | * the association. |
295 | | */ |
296 | | |
297 | | /* Attach the line PPS discipline, so no need to ldattach. |
298 | | * This activates the magic /dev/pps0 device. |
299 | | * Note: this ioctl() requires root, and device is a tty. |
300 | | * For testing, you can manually create /dev/ppsX: |
301 | | * stty -F /dev/ttyXX line 18 |
302 | | */ |
303 | | if (0 > ioctl(pps_thread->devicefd, TIOCSETD, &ldisc)) { |
304 | | char errbuf[BUFSIZ] = "unknown error"; |
305 | | pps_thread->log_hook(pps_thread, THREAD_INF, |
306 | | "KPPS:%s cannot set PPS line discipline %s(%d)\n", |
307 | | pps_thread->devicename, |
308 | | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
309 | | return -1; |
310 | | } |
311 | | |
312 | | /* uh, oh, magic file names!, RFC2783 neglects to specify how |
313 | | * to associate the serial device and pps device names */ |
314 | | /* need to look in /sys/devices/virtual/pps/pps?/path |
315 | | * (/sys/class/pps/pps?/path is just a link to that) |
316 | | * to find the /dev/pps? that matches our serial port. |
317 | | * this code fails if there are more then 10 pps devices. |
318 | | * |
319 | | * yes, this could be done with libsysfs, but trying to keep |
320 | | * the number of required libs small, and libsysfs would still |
321 | | * be linux only */ |
322 | | memset((void *)&globbuf, 0, sizeof(globbuf)); |
323 | | (void)glob("/sys/devices/virtual/pps/pps?/path", 0, NULL, &globbuf); |
324 | | |
325 | | memset((void *)&path, 0, sizeof(path)); |
326 | | for (i = 0; i < globbuf.gl_pathc; i++) { |
327 | | get_sysfs_var(globbuf.gl_pathv[i], path, sizeof(path)); |
328 | | pps_thread->log_hook(pps_thread, THREAD_PROG, |
329 | | "KPPS:%s checking %s, %s\n", |
330 | | pps_thread->devicename, |
331 | | globbuf.gl_pathv[i], path); |
332 | | if (0 == strncmp(path, pps_thread->devicename, sizeof(path))) { |
333 | | // this is the pps we are looking for |
334 | | // FIXME, now build the proper pps device path |
335 | | pps_num = globbuf.gl_pathv[i][28]; |
336 | | break; |
337 | | } |
338 | | memset((void *)&path, 0, sizeof(path)); |
339 | | } |
340 | | // done with blob, clear it |
341 | | globfree(&globbuf); |
342 | | |
343 | | if (0 == (int)pps_num) { |
344 | | pps_thread->log_hook(pps_thread, THREAD_INF, |
345 | | "KPPS:%s device not found.\n", |
346 | | pps_thread->devicename); |
347 | | return -1; |
348 | | } |
349 | | // construct the magic device path |
350 | | (void)snprintf(path, sizeof(path), "/dev/pps%c", pps_num); |
351 | | } |
352 | | |
353 | | /* root privs are probably required for this device open |
354 | | * do not bother to check uid, just go for the open() */ |
355 | | |
356 | | inner_context->kernelpps_fd = open(path, O_RDWR); |
357 | | #else // not __linux__ |
358 | | /* |
359 | | * On BSDs that support RFC2783, one uses the API calls on serial |
360 | | * port file descriptor. |
361 | | * |
362 | | * FIXME! need more specific than 'not linux' |
363 | | */ |
364 | | (void)strlcpy(path, pps_thread->devicename, sizeof(path)); |
365 | | inner_context->kernelpps_fd = pps_thread->devicefd; |
366 | | |
367 | | // If this is a PPS device, the devicefd is simply a placeholder |
368 | | if (PLACEHOLDING_FD == inner_context->kernelpps_fd) { |
369 | | inner_context->kernelpps_fd = open(path, O_RDWR); |
370 | | } |
371 | | #endif |
372 | | |
373 | | // Should be a valid descriptor by this point |
374 | | if (0 > inner_context->kernelpps_fd) { |
375 | | char errbuf[BUFSIZ] = "unknown error"; |
376 | | |
377 | | // sometimes geteuid() and geteiud() are long |
378 | | pps_thread->log_hook(pps_thread, THREAD_INF, |
379 | | "KPPS:%s running as %ld/%ld, cannot open %s: %s(%d)\n", |
380 | | pps_thread->devicename, |
381 | | (long)getuid(), (long)geteuid(), |
382 | | path, |
383 | | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
384 | | return -1; |
385 | | } |
386 | | |
387 | | pps_thread->log_hook(pps_thread, THREAD_INF, |
388 | | "KPPS:%s RFC2783 path:%s, fd %d\n", |
389 | | pps_thread->devicename, path, inner_context->kernelpps_fd); |
390 | | |
391 | | /* RFC 2783 implies the time_pps_setcap() needs privileges |
392 | | * keep root a tad longer just in case */ |
393 | | if (0 > time_pps_create(inner_context->kernelpps_fd, |
394 | | (pps_handle_t *)&inner_context->kernelpps_handle)) { |
395 | | char errbuf[BUFSIZ] = "unknown error"; |
396 | | |
397 | | pps_thread->log_hook(pps_thread, THREAD_INF, |
398 | | "KPPS:%s time_pps_create(%d) failed: %s(%d)\n", |
399 | | pps_thread->devicename, |
400 | | inner_context->kernelpps_fd, |
401 | | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
402 | | return -1; |
403 | | } |
404 | | |
405 | | // have kernel PPS handle. get RFC2783 features supported |
406 | | inner_context->pps_caps = 0; |
407 | | if (0 > time_pps_getcap(inner_context->kernelpps_handle, |
408 | | &inner_context->pps_caps)) { |
409 | | char errbuf[BUFSIZ] = "unknown error"; |
410 | | |
411 | | inner_context->pps_caps = 0; |
412 | | pps_thread->log_hook(pps_thread, THREAD_INF, |
413 | | "KPPS:%s time_pps_getcap() failed: %s(%d)\n", |
414 | | pps_thread->devicename, |
415 | | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
416 | | return -1; |
417 | | } else { |
418 | | pps_thread->log_hook(pps_thread, THREAD_INF, |
419 | | "KPPS:%s pps_caps 0x%02X\n", |
420 | | pps_thread->devicename, |
421 | | inner_context->pps_caps); |
422 | | } |
423 | | |
424 | | // construct the setparms structure |
425 | | memset((void *)&pp, 0, sizeof(pps_params_t)); |
426 | | pp.api_version = PPS_API_VERS_1; // version 1 protocol |
427 | | if (0 == (PPS_TSFMT_TSPEC & inner_context->pps_caps)) { |
428 | | /* PPS_TSFMT_TSPEC means return a timespec |
429 | | * mandatory for driver to implement, require it */ |
430 | | pps_thread->log_hook(pps_thread, THREAD_WARN, |
431 | | "KPPS:%s fail, missing mandatory PPS_TSFMT_TSPEC\n", |
432 | | pps_thread->devicename); |
433 | | return -1; |
434 | | } |
435 | | if (0 != (PPS_CANWAIT & inner_context->pps_caps)) { |
436 | | // we can wait! so no need for TIOCMIWAIT |
437 | | pps_thread->log_hook(pps_thread, THREAD_INF, |
438 | | "KPPS:%s have PPS_CANWAIT\n", |
439 | | pps_thread->devicename); |
440 | | inner_context->pps_canwait = true; |
441 | | } |
442 | | |
443 | | pp.mode = PPS_TSFMT_TSPEC; |
444 | | switch ((PPS_CAPTUREASSERT | PPS_CAPTURECLEAR) & inner_context->pps_caps) { |
445 | | case PPS_CAPTUREASSERT: |
446 | | pps_thread->log_hook(pps_thread, THREAD_WARN, |
447 | | "KPPS:%s missing PPS_CAPTURECLEAR, pulse may be offset\n", |
448 | | pps_thread->devicename); |
449 | | pp.mode |= PPS_CAPTUREASSERT; |
450 | | break; |
451 | | case PPS_CAPTURECLEAR: |
452 | | pps_thread->log_hook(pps_thread, THREAD_WARN, |
453 | | "KPPS:%s missing PPS_CAPTUREASSERT, pulse may be offset\n", |
454 | | pps_thread->devicename); |
455 | | pp.mode |= PPS_CAPTURECLEAR; |
456 | | break; |
457 | | case PPS_CAPTUREASSERT | PPS_CAPTURECLEAR: |
458 | | pp.mode |= PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; |
459 | | break; |
460 | | default: |
461 | | // THREAD_ERR in the calling routine |
462 | | pps_thread->log_hook(pps_thread, THREAD_INF, |
463 | | "KPPS:%s missing PPS_CAPTUREASSERT and CLEAR\n", |
464 | | pps_thread->devicename); |
465 | | return -1; |
466 | | } |
467 | | |
468 | | if (0 > time_pps_setparams(inner_context->kernelpps_handle, &pp)) { |
469 | | char errbuf[BUFSIZ] = "unknown error"; |
470 | | |
471 | | pps_thread->log_hook(pps_thread, THREAD_ERROR, |
472 | | "KPPS:%s time_pps_setparams(mode=0x%02X) failed: %s(%d)\n", |
473 | | pps_thread->devicename, pp.mode, |
474 | | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
475 | | ret = time_pps_destroy(inner_context->kernelpps_handle); |
476 | | if (0 > ret) { |
477 | | pps_thread->log_hook(pps_thread, THREAD_ERROR, |
478 | | "KPPS:%s time_pps_destroy(%d) %s(%d)\n", |
479 | | pps_thread->devicename, |
480 | | inner_context->kernelpps_handle, |
481 | | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
482 | | } |
483 | | return -1; |
484 | | } |
485 | | return 0; |
486 | | } |
487 | | #endif // defined(HAVE_SYS_TIMEPPS_H) |
488 | | |
489 | | #if defined(TIOCMIWAIT) |
490 | | /* wait for, and get, an edge using TIOCMIWAIT |
491 | | * return -1 for error |
492 | | * 0 for OK |
493 | | */ |
494 | | static int get_edge_tiocmiwait(volatile struct pps_thread_t *thread_context, |
495 | | struct timespec *clock_ts, |
496 | | int *state, |
497 | | volatile struct timedelta_t *last_fixtime) |
498 | 0 | { |
499 | 0 | char ts_str[TIMESPEC_LEN]; |
500 | | |
501 | | // we are lucky to have TIOCMIWAIT, so wait for next edge |
502 | 0 | #define PPS_LINE_TIOC (TIOCM_CD|TIOCM_RI|TIOCM_CTS|TIOCM_DSR) |
503 | | /* |
504 | | * DB9 DB25 Name Full name |
505 | | * --- ---- ---- -------------------- |
506 | | * 3 2 TXD --> Transmit Data |
507 | | * 2 3 RXD <-- Receive Data |
508 | | * 7 4 RTS --> Request To Send |
509 | | * 8 5 CTS <-- Clear To Send |
510 | | * 6 6 DSR <-- Data Set Ready |
511 | | * 4 20 DTR --> Data Terminal Ready |
512 | | * 1 8 DCD <-- Data Carrier Detect |
513 | | * 9 22 RI <-- Ring Indicator |
514 | | * 5 7 GND Signal ground |
515 | | * |
516 | | * Wait for status change on any handshake line. Just one edge, |
517 | | * we do not want to be spinning waiting for the trailing edge of |
518 | | * a pulse. The only assumption here is that no GPS lights up more |
519 | | * than one of these pins. By waiting on all of them we remove a |
520 | | * configuration switch. |
521 | | * |
522 | | * Note that it only makes sense to wait on handshake lines |
523 | | * activated from the receive side (DCE->DTE) here; in this |
524 | | * context "DCE" is the GPS. {CD,RI,CTS,DSR} is the |
525 | | * entire set of these. |
526 | | * |
527 | | */ |
528 | |
|
529 | 0 | if (0 != ioctl(thread_context->devicefd, TIOCMIWAIT, PPS_LINE_TIOC)) { |
530 | 0 | char errbuf[BUFSIZ] = "unknown error"; |
531 | |
|
532 | 0 | thread_context->log_hook(thread_context, THREAD_WARN, |
533 | 0 | "TPPS:%s ioctl(TIOCMIWAIT) failed: %s(%d)\n", |
534 | 0 | thread_context->devicename, |
535 | 0 | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
536 | 0 | return -1; |
537 | 0 | } |
538 | | |
539 | | /* |
540 | | * Start of time critical section |
541 | | * Only error reporting, not success reporting in critical section |
542 | | */ |
543 | | |
544 | | // duplicate copy in get_edge_rfc2783 |
545 | | // quick, grab a copy of last_fixtime before it changes |
546 | 0 | thread_lock(thread_context); |
547 | 0 | *last_fixtime = thread_context->fix_in; |
548 | 0 | thread_unlock(thread_context); |
549 | | // end duplicate copy in get_edge_rfc2783 |
550 | | |
551 | | // get the time after we just woke up |
552 | 0 | if (0 > clock_gettime(CLOCK_REALTIME, clock_ts)) { |
553 | | // uh, oh, can not get time! |
554 | 0 | char errbuf[BUFSIZ] = "unknown error"; |
555 | |
|
556 | 0 | thread_context->log_hook(thread_context, THREAD_ERROR, |
557 | 0 | "TPPS:%s clock_gettime() failed: %s(%d)\n", |
558 | 0 | thread_context->devicename, |
559 | 0 | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
560 | 0 | return -1; |
561 | 0 | } |
562 | | |
563 | | /* got the edge, got the time just after the edge, now quickly |
564 | | * get the edge state */ |
565 | 0 | if (0 != ioctl(thread_context->devicefd, (unsigned long)TIOCMGET, state)) { |
566 | 0 | char errbuf[BUFSIZ] = "unknown error"; |
567 | | |
568 | | // cast for 32-bit intptr_t |
569 | 0 | thread_context->log_hook(thread_context, THREAD_ERROR, |
570 | 0 | "TPPS:%s ioctl(%ld, TIOCMGET) failed: %s(%d)\n", |
571 | 0 | thread_context->devicename, (long)thread_context->devicefd, |
572 | 0 | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
573 | 0 | return -1; |
574 | 0 | } |
575 | | // end of time critical section |
576 | | // mask for monitored lines |
577 | | |
578 | 0 | *state &= PPS_LINE_TIOC; |
579 | |
|
580 | 0 | thread_context->log_hook(thread_context, THREAD_PROG, |
581 | 0 | "TPPS:%s ioctl(TIOCMIWAIT) succeeded, time:%s, state: %d\n", |
582 | 0 | thread_context->devicename, |
583 | 0 | timespec_str(clock_ts, ts_str, sizeof(ts_str)), |
584 | 0 | *state); |
585 | |
|
586 | 0 | return 0; |
587 | 0 | } |
588 | | |
589 | | #endif // TIOCMIWAIT |
590 | | |
591 | | #if defined(HAVE_SYS_TIMEPPS_H) |
592 | | /* wait for, and get, last two edges using RFC2783 |
593 | | * return -1 for error |
594 | | * 0 for OK |
595 | | * 1 no edge found, continue |
596 | | * |
597 | | * on a quad core 2.4GHz Xeon using KPPS timestamp instead of plain |
598 | | * PPS timestamp removes about 20uS of latency, and about +/-5uS |
599 | | * of jitter |
600 | | */ |
601 | | static int get_edge_rfc2783(struct inner_context_t *inner_context, |
602 | | struct timespec *prev_clock_ts, |
603 | | int *prev_edge, |
604 | | struct timespec *clock_ts, |
605 | | int *edge, |
606 | | volatile struct timedelta_t *last_fixtime) |
607 | | { |
608 | | pps_info_t pi; |
609 | | struct timespec pi_diff; |
610 | | char ts_str1[TIMESPEC_LEN], ts_str2[TIMESPEC_LEN]; |
611 | | struct timespec kernelpps_tv; |
612 | | volatile struct pps_thread_t *thread_context = inner_context->pps_thread; |
613 | | |
614 | | if (inner_context->pps_canwait) { |
615 | | /* |
616 | | * RFC2783 specifies that a NULL timeval means to wait, if |
617 | | * PPS_CANWAIT is available. |
618 | | * |
619 | | * since we pps_canwait, we skipped the TIOMCIWAIT |
620 | | * |
621 | | * 3 second time out, some GPS output 0.5Hz and some RFC2783 |
622 | | * can only trigger on one edge |
623 | | * a better and more complex solution would be to wait |
624 | | * for 1/20 second and suffer the cycles |
625 | | */ |
626 | | kernelpps_tv.tv_sec = 3; |
627 | | kernelpps_tv.tv_nsec = 0; |
628 | | } else { |
629 | | /* |
630 | | * We use of a non-NULL zero timespec here, |
631 | | * which means to return immediately with -1 (section |
632 | | * 3.4.3). This is because we know we just got a pulse because |
633 | | * TIOCMIWAIT just woke up. |
634 | | * The timestamp has already been captured in the kernel, and we |
635 | | * are merely fetching it here. |
636 | | */ |
637 | | memset((void *)&kernelpps_tv, 0, sizeof(kernelpps_tv)); |
638 | | } |
639 | | memset((void *)&pi, 0, sizeof(pi)); // paranoia |
640 | | if (0 > time_pps_fetch(inner_context->kernelpps_handle, PPS_TSFMT_TSPEC, |
641 | | &pi, &kernelpps_tv)) { |
642 | | char errbuf[BUFSIZ] = "unknown error"; |
643 | | |
644 | | if (ETIMEDOUT == errno || |
645 | | EINTR == errno) { |
646 | | // just a timeout |
647 | | thread_context->log_hook(thread_context, THREAD_INF, |
648 | | "KPPS:%s kernel PPS timeout %s(%d)\n", |
649 | | thread_context->devicename, |
650 | | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
651 | | return 1; |
652 | | } |
653 | | thread_context->log_hook(thread_context, THREAD_WARN, |
654 | | "KPPS:%s kernel PPS failed %s(%d)\n", |
655 | | thread_context->devicename, |
656 | | pps_strerror_r(errno, errbuf, |
657 | | sizeof(errbuf)), errno); |
658 | | return 0; |
659 | | } |
660 | | if (inner_context->pps_canwait) { |
661 | | // get_edge_tiocmiwait() got this if !pps_canwait |
662 | | |
663 | | // duplicate copy in get_edge_tiocmiwait() |
664 | | // quick, grab a copy of last fixtime before it changes */ |
665 | | thread_lock(thread_context); |
666 | | *last_fixtime = thread_context->fix_in; |
667 | | thread_unlock(thread_context); |
668 | | // duplicate copy in get_edge_tiocmiwait() |
669 | | } |
670 | | |
671 | | // find the last edge |
672 | | TS_SUB(&pi_diff, &pi.assert_timestamp, &pi.clear_timestamp); |
673 | | if (TS_GZ(&pi_diff)) { |
674 | | // assert after clear |
675 | | *edge = 1; |
676 | | *prev_edge = 0; |
677 | | if (0 == pi.clear_timestamp.tv_sec) { |
678 | | /* brain damaged pps-gpio sometimes never fills in clear |
679 | | * so make it look like an invisible pulse |
680 | | * if clear is the leading edge, then we are off by the |
681 | | * pulse width */ |
682 | | *prev_clock_ts = pi.assert_timestamp; |
683 | | } else { |
684 | | *prev_clock_ts = pi.clear_timestamp; |
685 | | } |
686 | | *clock_ts = pi.assert_timestamp; |
687 | | } else { |
688 | | // assert before clear |
689 | | *edge = 0; |
690 | | *prev_edge = 1; |
691 | | *prev_clock_ts = pi.assert_timestamp; |
692 | | *clock_ts = pi.clear_timestamp; |
693 | | } |
694 | | /* |
695 | | * pps_seq_t is uint32_t on NetBSD, so cast to |
696 | | * unsigned long as a wider-or-equal type to |
697 | | * accommodate Linux's type. |
698 | | */ |
699 | | thread_context->log_hook(thread_context, THREAD_PROG, |
700 | | "KPPS:%s assert %s, sequence: %lu, " |
701 | | "clear %s, sequence: %lu - using: %.10s\n", |
702 | | thread_context->devicename, |
703 | | timespec_str(&pi.assert_timestamp, |
704 | | ts_str1, sizeof(ts_str1)), |
705 | | (unsigned long) pi.assert_sequence, |
706 | | timespec_str(&pi.clear_timestamp, |
707 | | ts_str2, sizeof(ts_str2)), |
708 | | (unsigned long) pi.clear_sequence, |
709 | | *edge ? "assert" : "clear"); |
710 | | |
711 | | return 0; |
712 | | } |
713 | | #endif // defined(HAVE_SYS_TIMEPPS_H) |
714 | | |
715 | | /* gpsd_ppsmonitor() |
716 | | * |
717 | | * the core loop of the PPS thread. |
718 | | * All else is initialization, cleanup or subroutine |
719 | | */ |
720 | | static void *gpsd_ppsmonitor(void *arg) |
721 | 0 | { |
722 | 0 | char ts_str1[TIMESPEC_LEN], ts_str2[TIMESPEC_LEN]; |
723 | 0 | struct inner_context_t inner_context = *((struct inner_context_t *)arg); |
724 | 0 | volatile struct pps_thread_t *thread_context = inner_context.pps_thread; |
725 | 0 | struct sigaction sa; |
726 | | |
727 | | /* Install signal handler to allow interruption of ioctl(TIOCMIWAIT) */ |
728 | 0 | memset(&sa, 0, sizeof(sa)); |
729 | 0 | sa.sa_handler = pps_thread_signal_handler; |
730 | 0 | sigaction(PPS_THREAD_SIGNAL, &sa, NULL); |
731 | | |
732 | | /* the GPS time and system clock timme, to the nSec, |
733 | | * when the last fix received |
734 | | * using a double would cause loss of precision */ |
735 | 0 | volatile struct timedelta_t last_fixtime = {{0, 0}, {0, 0}}; |
736 | 0 | struct timespec clock_ts = {0, 0}; |
737 | 0 | time_t last_second_used = 0; |
738 | 0 | int64_t cycle = 0, duration = 0; |
739 | | // state is the last state of the tty control signals |
740 | 0 | int state = 0; |
741 | | // count of how many cycles unchanged data |
742 | 0 | int unchanged = 0; |
743 | | // state_last is previous state |
744 | 0 | int state_last = 0; |
745 | | // edge, used as index into pulse to find previous edges |
746 | 0 | int edge = 0; // 0 = clear edge, 1 = assert edge |
747 | |
|
748 | 0 | #if defined(TIOCMIWAIT) |
749 | 0 | int edge_tio = 0; |
750 | 0 | int64_t cycle_tio = 0; |
751 | 0 | int64_t duration_tio = 0; |
752 | 0 | int state_tio = 0; |
753 | 0 | int state_last_tio = 0; |
754 | 0 | struct timespec clock_ts_tio = {0, 0}; |
755 | | // pulse stores the time of the last two edges |
756 | 0 | struct timespec pulse_tio[2] = { {0, 0}, {0, 0} }; |
757 | 0 | #endif // TIOCMIWAIT |
758 | |
|
759 | | #if defined(HAVE_SYS_TIMEPPS_H) |
760 | | int64_t cycle_kpps = 0, duration_kpps = 0; |
761 | | // kpps_pulse stores the time of the last two edges |
762 | | struct timespec pulse_kpps[2] = { {0, 0}, {0, 0} }; |
763 | | #endif // defined(HAVE_SYS_TIMEPPS_H) |
764 | 0 | bool not_a_tty = false; |
765 | | |
766 | | // Acknowledge that we've grabbed the inner_context data |
767 | 0 | ((volatile struct inner_context_t *)arg)->pps_thread = NULL; |
768 | | |
769 | | /* before the loop, figure out how we can detect edges: |
770 | | * TIOMCIWAIT, which is linux specific |
771 | | * RFC2783, a.k.a kernel PPS (KPPS) |
772 | | * or if KPPS is deficient a combination of the two */ |
773 | 0 | if (0 > thread_context->devicefd || |
774 | 0 | 0 == isatty(thread_context->devicefd)) { |
775 | | // cast for 32-bit intptr_t |
776 | 0 | thread_context->log_hook(thread_context, THREAD_PROG, |
777 | 0 | "KPPS:%s gps_fd:%ld not a tty, can not use TIOMCIWAIT\n", |
778 | 0 | thread_context->devicename, |
779 | 0 | (long)thread_context->devicefd); |
780 | | /* why do we care the device is a tty? so as not to ioctl(TIO..) |
781 | | * /dev/pps0 is not a tty and we need to use it */ |
782 | 0 | not_a_tty = true; |
783 | 0 | } |
784 | | // if no TIOCMIWAIT, we hope to have PPS_CANWAIT |
785 | |
|
786 | 0 | if (not_a_tty && |
787 | 0 | !inner_context.pps_canwait) { |
788 | | /* for now, no way to wait for an edge, in the future maybe figure out |
789 | | * a sleep */ |
790 | 0 | } |
791 | | |
792 | | /* |
793 | | * this is the main loop, exit and never any further PPS processing. |
794 | | * |
795 | | * Four stages to the loop, |
796 | | * an unwanted condition at any point and the loop restarts |
797 | | * an error condition and we exit for all time. |
798 | | * |
799 | | * Stage One: wait for the next edge. |
800 | | * If we have KPPS |
801 | | * If we have PPS_CANWAIT |
802 | | * use KPPS and PPS_CANWAIT - this is the most accurate |
803 | | * else |
804 | | * use KPPS and TIOMCIWAIT together - this is pretty accurate |
805 | | * else If we have TIOMCIWAIT |
806 | | * use TIOMCIWAIT - this is the least accurate |
807 | | * else |
808 | | * give up |
809 | | * |
810 | | * Success is we have a good edge, otherwise loop some more |
811 | | * |
812 | | * On a successful stage one, we know this about the exact moment |
813 | | * of current pulse: |
814 | | * GPS (real) time |
815 | | * system (clock) time |
816 | | * edge type: Assert (rising) or Clear (falling) |
817 | | * |
818 | | * From the above 3 items, we can compute: |
819 | | * cycle length - elapsed time from the previous edge of the same type |
820 | | * pulse length (duration) - elapsed time from the previous edge |
821 | | * (the previous edge would be the opposite type) |
822 | | * |
823 | | * Stage Two: Categorize the current edge |
824 | | * Decide if we have 0.5Hz, 1Hz, 5 Hz cycle time |
825 | | * knowing cycle time determine if we have the leading or trailing edge |
826 | | * restart the loop if the edge looks dodgy |
827 | | * |
828 | | * Stage Three: Calculate |
829 | | * Calculate the offset (difference) between the system time |
830 | | * and the GPS time at the pulse moment |
831 | | * restart the loop if the offset looks dodgy |
832 | | * |
833 | | * Stage Four: Tell ntpd, chronyd, or gpsmon what we learned |
834 | | * a few more sanity checks |
835 | | * call the report hook with our PPS report |
836 | | */ |
837 | 0 | while (NULL != thread_context->report_hook) { |
838 | 0 | bool ok = false; |
839 | 0 | char *log = NULL; |
840 | 0 | char *edge_str = ""; |
841 | |
|
842 | 0 | if (10 == ++unchanged) { |
843 | | // last ten edges no good, stop spinning, just wait 10 seconds |
844 | 0 | unchanged = 0; |
845 | 0 | thread_context->log_hook(thread_context, THREAD_WARN, |
846 | 0 | "PPS:%s unchanged state, ppsmonitor sleeps 10\n", |
847 | 0 | thread_context->devicename); |
848 | 0 | (void)sleep(10); |
849 | 0 | } |
850 | | |
851 | | // Stage One; wait for the next edge |
852 | 0 | #if defined(TIOCMIWAIT) |
853 | 0 | if (!not_a_tty && |
854 | 0 | !inner_context.pps_canwait) { |
855 | 0 | int ret; |
856 | | |
857 | | /* we are a tty, so can TIOCMIWAIT |
858 | | * we have no PPS_CANWAIT, so must TIOCMIWAIT */ |
859 | |
|
860 | 0 | ret = get_edge_tiocmiwait(thread_context, &clock_ts_tio, |
861 | 0 | &state_tio, &last_fixtime); |
862 | 0 | if (0 != ret) { |
863 | 0 | thread_context->log_hook(thread_context, THREAD_PROG, |
864 | 0 | "PPS:%s die: TIOCMIWAIT Error\n", |
865 | 0 | thread_context->devicename); |
866 | 0 | break; |
867 | 0 | } |
868 | | |
869 | 0 | edge_tio = (state_tio > state_last_tio) ? 1 : 0; |
870 | |
|
871 | 0 | state_last_tio = state_tio; |
872 | | |
873 | | /* three things now known about the current edge: |
874 | | * clock_ts - time of the edge |
875 | | * state - the serial line input states |
876 | | * edge - rising edge (1), falling edge (0) or invisible edge (0) |
877 | | */ |
878 | | |
879 | | // calculate cycle and duration from previous edges |
880 | 0 | cycle_tio = timespec_diff_ns(clock_ts_tio, pulse_tio[edge_tio]); |
881 | 0 | cycle_tio /= 1000; // nsec to usec |
882 | 0 | duration_tio = timespec_diff_ns(clock_ts_tio, |
883 | 0 | pulse_tio[edge_tio ? 0 : 1]) / 1000; |
884 | | |
885 | | // save this edge so we know next cycle time |
886 | 0 | pulse_tio[edge_tio] = clock_ts_tio; |
887 | | |
888 | | // use this data |
889 | 0 | ok = true; |
890 | 0 | clock_ts = clock_ts_tio; |
891 | 0 | state = edge_tio; |
892 | 0 | edge = edge_tio; |
893 | 0 | edge_str = edge ? "Assert" : "Clear"; |
894 | 0 | cycle = cycle_tio; |
895 | 0 | duration = duration_tio; |
896 | | |
897 | | // (long long) for 32-bit compat. PRId64 segfaults |
898 | 0 | thread_context->log_hook(thread_context, THREAD_PROG, |
899 | 0 | "TPPS:%s %.10s, cycle: %lld, duration: %lld @ %s\n", |
900 | 0 | thread_context->devicename, edge_str, |
901 | 0 | (long long)cycle, (long long)duration, |
902 | 0 | timespec_str(&clock_ts, ts_str1, sizeof(ts_str1))); |
903 | |
|
904 | 0 | } |
905 | 0 | #endif // TIOCMIWAIT |
906 | | |
907 | | // ok and log used by KPPS and TIOCMIWAIT |
908 | 0 | log = NULL; |
909 | | #if defined(HAVE_SYS_TIMEPPS_H) |
910 | | if ((pps_handle_t)0 <= inner_context.kernelpps_handle) { |
911 | | int ret; |
912 | | int edge_kpps = 0; // 0 = clear edge, 1 = assert edge |
913 | | // time of the last edge */ |
914 | | struct timespec clock_ts_kpps = {0, 0}; |
915 | | // time of the edge before the last edge |
916 | | struct timespec prev_clock_ts = {0, 0}; |
917 | | // direction of next to last edge 1 = assert, 0 = clear |
918 | | int prev_edge = 0; |
919 | | |
920 | | /* get last and previous edges, in order |
921 | | * optionally wait for goood data |
922 | | */ |
923 | | ret = get_edge_rfc2783(&inner_context, |
924 | | &prev_clock_ts, |
925 | | &prev_edge, |
926 | | &clock_ts_kpps, |
927 | | &edge_kpps, |
928 | | &last_fixtime); |
929 | | |
930 | | if (-1 == ret) { |
931 | | // error, so break |
932 | | thread_context->log_hook(thread_context, THREAD_ERROR, |
933 | | "PPS:%s die: RFC2783 Error\n", |
934 | | thread_context->devicename); |
935 | | break; |
936 | | } |
937 | | |
938 | | if (1 == ret) { |
939 | | /* no edge found, so continue |
940 | | * maybe use TIOCMIWAIT edge instead?? */ |
941 | | continue; |
942 | | } |
943 | | /* for now, as we have been doing all of gpsd 3.x, just |
944 | | *use the last edge, not the previous edge */ |
945 | | |
946 | | // compute time from previous saved similar edge |
947 | | cycle_kpps = timespec_diff_ns(clock_ts_kpps, pulse_kpps[edge_kpps]); |
948 | | cycle_kpps /= 1000; |
949 | | // compute time from previous saved dis-similar edge |
950 | | duration_kpps = timespec_diff_ns(clock_ts_kpps, prev_clock_ts) / |
951 | | 1000; |
952 | | |
953 | | // save for later |
954 | | pulse_kpps[edge_kpps] = clock_ts_kpps; |
955 | | pulse_kpps[edge_kpps ? 0 : 1] = prev_clock_ts; |
956 | | // sanity checks are later |
957 | | |
958 | | // use this data |
959 | | state = edge_kpps; |
960 | | edge = edge_kpps; |
961 | | edge_str = edge ? "Assert" : "Clear"; |
962 | | clock_ts = clock_ts_kpps; |
963 | | cycle = cycle_kpps; |
964 | | duration = duration_kpps; |
965 | | |
966 | | // (long long) for 32-bit compat. PRId64 segfaults |
967 | | thread_context->log_hook(thread_context, THREAD_PROG, |
968 | | "KPPS:%s %.10s cycle: %lld, duration: %lld @ %s\n", |
969 | | thread_context->devicename, |
970 | | edge_str, |
971 | | (long long)cycle_kpps, (long long)duration_kpps, |
972 | | timespec_str(&clock_ts_kpps, ts_str1, sizeof(ts_str1))); |
973 | | |
974 | | } |
975 | | #endif // defined(HAVE_SYS_TIMEPPS_H) |
976 | |
|
977 | 0 | if (not_a_tty && |
978 | 0 | !inner_context.pps_canwait) { |
979 | | // uh, oh, no TIOMCIWAIT, nor RFC2783, die |
980 | 0 | thread_context->log_hook(thread_context, THREAD_WARN, |
981 | 0 | "PPS:%s die: no TIOMCIWAIT, nor RFC2783 CANWAIT\n", |
982 | 0 | thread_context->devicename); |
983 | 0 | break; |
984 | 0 | } |
985 | | /* |
986 | | * End of Stage One |
987 | | * we now know this about the exact moment of current pulse: |
988 | | * GPS (real) time |
989 | | * system (clock) time |
990 | | * edge type: Assert (rising) or Clear (falling) |
991 | | * |
992 | | * we have computed: |
993 | | * cycle length |
994 | | * pulse length (duration) |
995 | | */ |
996 | | |
997 | | /* |
998 | | * Stage Two: Categorize the current edge |
999 | | * Decide if we have 0.5Hz, 1Hz, 5 Hz cycle time |
1000 | | * determine if we have the leading or trailing edge |
1001 | | */ |
1002 | | |
1003 | | /* FIXME! this block duplicates a lot of the next block |
1004 | | * of cycle detection code */ |
1005 | 0 | if (state != state_last) { |
1006 | 0 | thread_context->log_hook(thread_context, THREAD_RAW, |
1007 | 0 | "PPS:%s %.10s pps-detect changed to %d\n", |
1008 | 0 | thread_context->devicename, edge_str, state); |
1009 | 0 | unchanged = 0; |
1010 | 0 | } else if ((180000 < cycle && |
1011 | 0 | 220000 > cycle) || // 5Hz |
1012 | 0 | (900000 < cycle && |
1013 | 0 | 1100000 > cycle) || // 1Hz |
1014 | 0 | (1800000 < cycle && |
1015 | 0 | 2200000 > cycle)) { // 0.5Hz |
1016 | | |
1017 | | /* some pulses may be so short that state never changes |
1018 | | * and some RFC2783 only can detect one edge */ |
1019 | |
|
1020 | 0 | duration = 0; |
1021 | 0 | unchanged = 0; |
1022 | 0 | thread_context->log_hook(thread_context, THREAD_RAW, |
1023 | 0 | "PPS:%s %.10s pps-detect invisible pulse\n", |
1024 | 0 | thread_context->devicename, edge_str); |
1025 | 0 | } |
1026 | | // else, unchannged state, and weird cycle time |
1027 | |
|
1028 | 0 | state_last = state; |
1029 | | // (long long) for 32-bit compat. PRId64 segfaults |
1030 | 0 | thread_context->log_hook(thread_context, THREAD_PROG, |
1031 | 0 | "PPS:%s %.10s cycle: %lld, duration: %lld @ %s\n", |
1032 | 0 | thread_context->devicename, |
1033 | 0 | edge_str, |
1034 | 0 | (long long)cycle, (long long)duration, |
1035 | 0 | timespec_str(&clock_ts, ts_str1, sizeof(ts_str1))); |
1036 | 0 | if (unchanged) { |
1037 | | // strange, try again |
1038 | 0 | continue; |
1039 | 0 | } |
1040 | | |
1041 | | /* |
1042 | | * The PPS pulse is normally a short pulse with a frequency of |
1043 | | * 1 Hz, and the UTC second is defined by the front edge. But we |
1044 | | * don't know the polarity of the pulse (different receivers |
1045 | | * emit different polarities). The duration variable is used to |
1046 | | * determine which way the pulse is going. When the duration |
1047 | | * is less than 1/2 the cycle we are on the trailing edge. |
1048 | | * |
1049 | | * Some GPSes instead output a square wave that is 0.5 Hz and each |
1050 | | * edge denotes the start of a second. |
1051 | | * |
1052 | | * Some GPSes, like the Globalsat MR-350P, output a 1uS pulse. |
1053 | | * The pulse is so short that TIOCMIWAIT sees a state change |
1054 | | * but by the time TIOCMGET is called the pulse is gone. gpsd |
1055 | | * calls that an invisible pulse. |
1056 | | * |
1057 | | * A few stupid GPSes, like the Furuno GPSClock, output a 1.0 Hz |
1058 | | * square wave where the leading edge is the start of a second |
1059 | | * gpsd can only guess the correct edge. |
1060 | | * |
1061 | | * 5Hz GPS (Garmin 18-5Hz) pulses at 5Hz. Set the pulse length to |
1062 | | * 40ms which gives a 160ms pulse before going high. |
1063 | | * |
1064 | | * You may think that PPS is very accurate, so the cycle time |
1065 | | * valid window should be very small. This is not the case, |
1066 | | * The Raspberry Pi clock is very coarse when it starts and/or chronyd |
1067 | | * may be doing a fast slew. chronyd by default will slew up |
1068 | | * to 8.334%! So the cycle time as measured by the system clock |
1069 | | * may be almost +/- 9%. Therefore, gpsd uses a 10% window. |
1070 | | * Don't worry, ntpd and chronyd will do further validation. |
1071 | | */ |
1072 | | |
1073 | 0 | log = "Unknown error"; |
1074 | 0 | if (0 > cycle) { |
1075 | 0 | log = "Rejecting negative cycle\n"; |
1076 | 0 | } else if (180000 > cycle) { |
1077 | | /* shorter than 200 milliSec - 10% |
1078 | | * too short to even be a 5Hz pulse */ |
1079 | 0 | log = "Too short for 5Hz\n"; |
1080 | 0 | } else if (201000 > cycle) { |
1081 | | /* longer than 200 milliSec - 10% |
1082 | | * shorter than 200 milliSec + 10% |
1083 | | * about 200 milliSec cycle |
1084 | | * looks like 5hz PPS pulse */ |
1085 | 0 | if (100000 > duration) { |
1086 | | // this is the end of the long part |
1087 | | /* BUG: how does the code know to tell ntpd |
1088 | | * which 1/5 of a second to use?? */ |
1089 | 0 | ok = true; |
1090 | 0 | log = "5Hz PPS pulse\n"; |
1091 | 0 | } |
1092 | 0 | } else if (900000 > cycle) { |
1093 | | /* longer than 200 milliSec + 10% |
1094 | | * shorter than 1.000 Sec - 10% |
1095 | | * Yes, 10% window. The Raspberry Pi clock is very coarse |
1096 | | * when it starts and chronyd may be doing a fast slew. |
1097 | | * chronyd by default will slew up to 8.334% ! */ |
1098 | 0 | log = "Too long for 5Hz, too short for 1Hz\n"; |
1099 | 0 | } else if (1100000 > cycle) { |
1100 | | /* longer than 1.000 Sec - 10% |
1101 | | * shorter than 1.000 Sec + 10% |
1102 | | * Yes, 10% window. |
1103 | | * looks like 1Hz PPS pulse or square wave */ |
1104 | 0 | if (0 == duration) { |
1105 | 0 | ok = true; |
1106 | 0 | log = "invisible pulse\n"; |
1107 | 0 | } else if (450000 > duration) { |
1108 | | /* pulse shorter than 500 milliSec - 10% |
1109 | | * end of the short "half" of the cycle |
1110 | | * aka the trailing edge */ |
1111 | 0 | log = "1Hz trailing edge\n"; |
1112 | 0 | } else if (555000 > duration) { |
1113 | | /* pulse longer than 500 milliSec - 10% |
1114 | | * pulse shorter than 500 milliSec + 10% |
1115 | | * looks like 1.0 Hz square wave, ignore trailing edge |
1116 | | * except we can't tell which is which, so we guess */ |
1117 | 0 | if (1 == edge) { |
1118 | 0 | ok = true; |
1119 | 0 | log = "square\n"; |
1120 | 0 | } |
1121 | 0 | } else { |
1122 | | /* pulse longer than 500 milliSec + 10% |
1123 | | * end of the long "half" of the cycle |
1124 | | * aka the leading edge, |
1125 | | * the edge that marks the start of the second */ |
1126 | 0 | ok = true; |
1127 | 0 | log = "1Hz leading edge\n"; |
1128 | 0 | } |
1129 | 0 | } else if (1800000 > cycle) { |
1130 | | /* cycle longer than 1.000 Sec + 10% |
1131 | | * cycle shorter than 2.000 Sec - 10% |
1132 | | * Too long for 1Hz, too short for 2Hz */ |
1133 | 0 | log = "Too long for 1Hz, too short for 2Hz\n"; |
1134 | 0 | } else if (2200000 > cycle) { |
1135 | | /* cycle longer than 2.000 Sec - 10% |
1136 | | * cycle shorter than 2.000 Sec + 10% |
1137 | | * looks like 0.5 Hz square wave */ |
1138 | 0 | if (990000 > duration) { |
1139 | | /* pulse shorter than 1.000 Sec - 10% |
1140 | | * too short to be a 2Hx square wave */ |
1141 | 0 | log = "0.5 Hz square too short duration\n"; |
1142 | 0 | } else if (1100000 > duration) { |
1143 | | /* pulse longer than 1.000 Sec - 10% |
1144 | | * pulse shorter than 1.000 Sec + 10% |
1145 | | * and nice 0.5Hz square wave */ |
1146 | 0 | ok = true; |
1147 | 0 | log = "0.5 Hz square wave\n"; |
1148 | 0 | } else { |
1149 | 0 | log = "0.5 Hz square too long duration\n"; |
1150 | 0 | } |
1151 | 0 | } else { |
1152 | | /* cycle longer than 2.000 Sec + 10% |
1153 | | * can't be anything */ |
1154 | 0 | log = "Too long for 0.5Hz\n"; |
1155 | 0 | } |
1156 | | |
1157 | | /* end of Stage two |
1158 | | * we now know what type of PPS pulse, and if we have a good |
1159 | | * leading edge or not |
1160 | | */ |
1161 | | |
1162 | | /* Stage Three: Calculate |
1163 | | * Calculate the offset (difference) between the system time |
1164 | | * and the GPS time at the pulse moment |
1165 | | */ |
1166 | | |
1167 | | /* |
1168 | | * If there has not yet been any valid in-band time stashed |
1169 | | * from the GPS when the PPS event was asserted, we can do |
1170 | | * nothing further. gpsd can not tell what second this pulse is |
1171 | | * in reference to. |
1172 | | * |
1173 | | * Some GPSes like Garmin always send a PPS, valid or not. |
1174 | | * Other GPSes like some uBlox may only send PPS when time is valid. |
1175 | | * It is common to get PPS, and no fixtime, while autobauding. |
1176 | | */ |
1177 | | /* FIXME, some GPS, like Skytraq, may output a the fixtime so |
1178 | | * late in the cycle as to be ambiguous. */ |
1179 | 0 | if (0 == last_fixtime.real.tv_sec) { |
1180 | | // probably should log computed offset just for grins here |
1181 | 0 | ok = false; |
1182 | 0 | log = "missing last_fixtime\n"; |
1183 | 0 | } else if (ok && |
1184 | 0 | last_second_used >= last_fixtime.real.tv_sec) { |
1185 | | // uh, oh, this second already handled |
1186 | 0 | ok = false; |
1187 | 0 | log = "this second already handled\n"; |
1188 | 0 | } |
1189 | |
|
1190 | 0 | if (!ok) { |
1191 | | // can not use this pulse, reject and retry |
1192 | 0 | thread_context->log_hook(thread_context, THREAD_PROG, |
1193 | 0 | "PPS:%s %.10s ignored %.100s", |
1194 | 0 | thread_context->devicename, edge_str, log); |
1195 | 0 | continue; |
1196 | 0 | } |
1197 | | |
1198 | | // we have validated a goood cycle, mark it |
1199 | 0 | unchanged = 0; |
1200 | | // offset is the skew from expected to observed pulse time |
1201 | 0 | struct timespec offset; |
1202 | | // offset as a printable string |
1203 | 0 | char offset_str[TIMESPEC_LEN]; |
1204 | | // delay after last fix |
1205 | 0 | struct timespec delay; |
1206 | | // delay as a printable string |
1207 | 0 | char delay_str[TIMESPEC_LEN]; |
1208 | 0 | char *log1 = ""; |
1209 | | // ppstimes.real is the time we think the pulse represents |
1210 | | // it should be on an exact second (tv_nsec = 0) |
1211 | 0 | struct timedelta_t ppstimes; |
1212 | |
|
1213 | 0 | thread_context->log_hook(thread_context, THREAD_RAW, |
1214 | 0 | "PPS:%s %.10s categorized %.100s", |
1215 | 0 | thread_context->devicename, edge_str, log); |
1216 | | |
1217 | | /* FIXME! The GR-601W at 38,400 or faster can send the |
1218 | | * serial fix before the interrupt event carrying the PPS |
1219 | | * line assertion by about 10 mSec! |
1220 | | */ |
1221 | | |
1222 | | /* |
1223 | | * We get the time of the last fix recorded before the PPS came in, |
1224 | | * which is for the previous cycle. Only works for integral cycle |
1225 | | * times, but more than 1Hz is pointless. |
1226 | | */ |
1227 | |
|
1228 | 0 | ppstimes.real.tv_sec = last_fixtime.real.tv_sec + 1; |
1229 | 0 | if (900000000 < last_fixtime.real.tv_nsec) { |
1230 | | // handle x.9999 as x+1 |
1231 | 0 | ppstimes.real.tv_sec++; |
1232 | 0 | } |
1233 | 0 | ppstimes.real.tv_nsec = 0; // need to be fixed for 5Hz |
1234 | 0 | ppstimes.clock = clock_ts; |
1235 | | |
1236 | | // Here would be a good place to apply qErr |
1237 | |
|
1238 | 0 | TS_SUB(&offset, &ppstimes.real, &ppstimes.clock); |
1239 | 0 | TS_SUB(&delay, &ppstimes.clock, &last_fixtime.clock); |
1240 | 0 | timespec_str(&delay, delay_str, sizeof(delay_str)); |
1241 | | |
1242 | | /* end Stage Three: now known about the exact edge moment: |
1243 | | * UTC time of PPS edge |
1244 | | * offset of system time to PS time |
1245 | | */ |
1246 | | |
1247 | | /* Stage Four: Tell ntpd, chronyd, or gpsmon what we learned |
1248 | | * a few more sanity checks |
1249 | | * call the report hook with our PPS report |
1250 | | */ |
1251 | |
|
1252 | 0 | if (0 > delay.tv_sec || |
1253 | 0 | 0 > delay.tv_nsec) { |
1254 | 0 | thread_context->log_hook(thread_context, THREAD_RAW, |
1255 | 0 | "PPS:%s %.10s system clock went backwards: %.20s\n", |
1256 | 0 | thread_context->devicename, |
1257 | 0 | edge_str, |
1258 | 0 | delay_str); |
1259 | 0 | log1 = "system clock went backwards"; |
1260 | 0 | } else if ((2 < delay.tv_sec) || |
1261 | 0 | (1 == delay.tv_sec && |
1262 | 0 | 100000000 < delay.tv_nsec)) { |
1263 | | /* system clock could be slewing so allow up to 1.1 sec delay |
1264 | | * chronyd can slew +/-8.33% */ |
1265 | 0 | thread_context->log_hook(thread_context, THREAD_RAW, |
1266 | 0 | "PPS:%s %.10s no current GPS seconds: %.20s\n", |
1267 | 0 | thread_context->devicename, |
1268 | 0 | edge_str, |
1269 | 0 | delay_str); |
1270 | 0 | log1 = "timestamp out of range"; |
1271 | 0 | } else { |
1272 | 0 | last_second_used = last_fixtime.real.tv_sec; |
1273 | 0 | if (NULL != thread_context->report_hook) { |
1274 | 0 | log1 = thread_context->report_hook(thread_context, &ppstimes); |
1275 | 0 | } else { |
1276 | 0 | log1 = "no report hook"; |
1277 | 0 | } |
1278 | 0 | thread_lock(thread_context); |
1279 | 0 | thread_context->pps_out = ppstimes; |
1280 | 0 | thread_context->ppsout_count++; |
1281 | 0 | thread_unlock(thread_context); |
1282 | 0 | thread_context->log_hook(thread_context, THREAD_INF, |
1283 | 0 | "PPS:%s %.10s hooks called clock: %s real: %s: %.20s\n", |
1284 | 0 | thread_context->devicename, |
1285 | 0 | edge_str, |
1286 | 0 | timespec_str(&ppstimes.clock, ts_str1, sizeof(ts_str1)), |
1287 | 0 | timespec_str(&ppstimes.real, ts_str2, sizeof(ts_str2)), |
1288 | 0 | log1); |
1289 | 0 | } |
1290 | 0 | thread_context->log_hook(thread_context, THREAD_PROG, |
1291 | 0 | "PPS:%s %.10s %.30s @ %s offset %.20s\n", |
1292 | 0 | thread_context->devicename, |
1293 | 0 | edge_str, |
1294 | 0 | log1, |
1295 | 0 | timespec_str(&clock_ts, ts_str1, sizeof(ts_str1)), |
1296 | 0 | timespec_str(&offset, offset_str, sizeof(offset_str))); |
1297 | | // end Stage four, end of the loop, do it again |
1298 | 0 | } |
1299 | | #if defined(HAVE_SYS_TIMEPPS_H) |
1300 | | if ((pps_handle_t)0 < inner_context.kernelpps_handle) { |
1301 | | int ret; |
1302 | | char errbuf[BUFSIZ] = "unknown error"; |
1303 | | |
1304 | | errno = 0; |
1305 | | ret = time_pps_destroy(inner_context.kernelpps_handle); |
1306 | | if (0 > ret) { |
1307 | | thread_context->log_hook(thread_context, THREAD_ERROR, |
1308 | | "KPPS:%s time_pps_destroy(%d) %s(%d)\n", |
1309 | | thread_context->devicename, |
1310 | | inner_context.kernelpps_handle, |
1311 | | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
1312 | | } |
1313 | | ret = close(inner_context.kernelpps_fd); |
1314 | | if (0 < ret) { |
1315 | | thread_context->log_hook(thread_context, THREAD_PROG, |
1316 | | "KPPS:%s fd %d/%d cleaned up\n", |
1317 | | thread_context->devicename, |
1318 | | inner_context.kernelpps_handle, |
1319 | | inner_context.kernelpps_fd); |
1320 | | } else { |
1321 | | thread_context->log_hook(thread_context, THREAD_ERROR, |
1322 | | "KPPS:%s fd %d close(%d) %s(%d)\n", |
1323 | | thread_context->devicename, |
1324 | | inner_context.kernelpps_handle, |
1325 | | inner_context.kernelpps_fd, |
1326 | | pps_strerror_r(errno, errbuf, sizeof(errbuf)), errno); |
1327 | | } |
1328 | | } |
1329 | | #endif |
1330 | 0 | thread_context->log_hook(thread_context, THREAD_PROG, |
1331 | 0 | "PPS:%s gpsd_ppsmonitor exited.\n", |
1332 | 0 | thread_context->devicename); |
1333 | 0 | return NULL; |
1334 | 0 | } |
1335 | | |
1336 | | /* |
1337 | | * Entry points begin here. |
1338 | | */ |
1339 | | |
1340 | | // activate a thread to watch the device's PPS transitions |
1341 | | void pps_thread_activate(volatile struct pps_thread_t *pps_thread) |
1342 | 0 | { |
1343 | 0 | int retval; |
1344 | 0 | pthread_t pt; |
1345 | 0 | struct timespec start_delay = {0, 1000000}; // 1 ms |
1346 | | /* |
1347 | | * FIXME: this launch code is not itself thread-safe! |
1348 | | * It would be if inner_context could be auto, but the monitor |
1349 | | * routine gets garbage when we try that. Ideally the body |
1350 | | * of this function would be guarded by a separate mutex. |
1351 | | * Either that, or this should be an exception to the no-malloc rule. |
1352 | | */ |
1353 | 0 | static struct inner_context_t inner_context = {0}; |
1354 | |
|
1355 | 0 | inner_context.pps_thread = pps_thread; |
1356 | | #if defined(HAVE_SYS_TIMEPPS_H) |
1357 | | // some operations in init_kernel_pps() require root privs |
1358 | | (void)init_kernel_pps(&inner_context); |
1359 | | if ((pps_handle_t)0 <= inner_context.kernelpps_handle) { |
1360 | | pps_thread->log_hook(pps_thread, THREAD_INF, |
1361 | | "KPPS:%s kernel PPS will be used\n", |
1362 | | pps_thread->devicename); |
1363 | | } else { |
1364 | | pps_thread->log_hook(pps_thread, THREAD_WARN, |
1365 | | "KPPS:%s kernel PPS unavailable, " |
1366 | | "PPS accuracy will suffer\n", |
1367 | | pps_thread->devicename); |
1368 | | } |
1369 | | #else |
1370 | 0 | pps_thread->log_hook(pps_thread, THREAD_WARN, |
1371 | 0 | "KPPS:%s no HAVE_SYS_TIMEPPS_H, PPS accuracy will suffer\n", |
1372 | 0 | pps_thread->devicename); |
1373 | 0 | #endif |
1374 | |
|
1375 | 0 | memset(&pt, 0, sizeof(pt)); |
1376 | 0 | retval = pthread_create(&pt, NULL, gpsd_ppsmonitor, (void *)&inner_context); |
1377 | 0 | pps_thread->log_hook(pps_thread, THREAD_PROG, "PPS:%s thread %s\n", |
1378 | 0 | pps_thread->devicename, |
1379 | 0 | (retval==0) ? "launched" : "FAILED"); |
1380 | 0 | if (0 == retval) { |
1381 | 0 | pps_thread->thread_id = pt; |
1382 | | |
1383 | | /* The monitor thread may not run immediately, particularly on a single- |
1384 | | * core machine, so we need to wait for it to acknowledge its copying |
1385 | | * of the inner_context struct before proceeding. |
1386 | | */ |
1387 | 0 | while (inner_context.pps_thread) { |
1388 | 0 | (void)nanosleep(&start_delay, NULL); |
1389 | 0 | } |
1390 | 0 | } else { |
1391 | 0 | pps_thread->thread_id = pthread_self(); |
1392 | 0 | } |
1393 | 0 | } |
1394 | | |
1395 | | // cleanly terminate PPS thread |
1396 | | void pps_thread_deactivate(volatile struct pps_thread_t *pps_thread) |
1397 | 0 | { |
1398 | 0 | pps_thread->report_hook = NULL; |
1399 | 0 | if (pthread_self() != pps_thread->thread_id || |
1400 | 0 | 0 != pps_thread->thread_id) { |
1401 | | // Send signal to interrupt ioctl(TIOCMIWAIT) |
1402 | 0 | if (0 != pthread_kill(pps_thread->thread_id, PPS_THREAD_SIGNAL)) { |
1403 | 0 | pps_thread->log_hook(pps_thread, THREAD_PROG, |
1404 | 0 | "PPS:%s pthread_kill() failed\n", |
1405 | 0 | pps_thread->devicename); |
1406 | 0 | } |
1407 | | // Detach so thread resources are freed when it exits |
1408 | 0 | if (0 != pthread_detach(pps_thread->thread_id)) { |
1409 | 0 | pps_thread->log_hook(pps_thread, THREAD_PROG, |
1410 | 0 | "PPS:%s pthread_detach() failed\n", |
1411 | 0 | pps_thread->devicename); |
1412 | 0 | } |
1413 | 0 | } |
1414 | 0 | } |
1415 | | |
1416 | | // thread-safe update of last fix time - only way we pass data in |
1417 | | void pps_thread_fixin(volatile struct pps_thread_t *pps_thread, |
1418 | | volatile struct timedelta_t *fix_in) |
1419 | 0 | { |
1420 | 0 | thread_lock(pps_thread); |
1421 | 0 | pps_thread->fix_in = *fix_in; |
1422 | 0 | thread_unlock(pps_thread); |
1423 | 0 | } |
1424 | | |
1425 | | // thread-safe update of qErr and qErr_time - only way we pass data in |
1426 | | void pps_thread_qErrin(volatile struct pps_thread_t *pps_thread, |
1427 | | long qErr, struct timespec qErr_time) |
1428 | 0 | { |
1429 | 0 | thread_lock(pps_thread); |
1430 | 0 | pps_thread->qErr = qErr; |
1431 | 0 | pps_thread->qErr_time = qErr_time; |
1432 | 0 | thread_unlock(pps_thread); |
1433 | 0 | } |
1434 | | |
1435 | | // return the delta at the time of the last PPS - only way we pass data out |
1436 | | int pps_thread_ppsout(volatile struct pps_thread_t *pps_thread, |
1437 | | volatile struct timedelta_t *td) |
1438 | 0 | { |
1439 | 0 | volatile int ret; |
1440 | |
|
1441 | 0 | thread_lock(pps_thread); |
1442 | 0 | *td = pps_thread->pps_out; |
1443 | 0 | ret = pps_thread->ppsout_count; |
1444 | 0 | thread_unlock(pps_thread); |
1445 | |
|
1446 | 0 | return ret; |
1447 | 0 | } |
1448 | | |
1449 | | // vim: set expandtab shiftwidth=4 |