xref: /openbsd/sys/kern/tty_nmea.c (revision 274d7c50)
1 /*	$OpenBSD: tty_nmea.c,v 1.49 2019/08/15 15:08:14 otto Exp $ */
2 
3 /*
4  * Copyright (c) 2006, 2007, 2008 Marc Balmer <mbalmer@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /* A tty line discipline to decode NMEA 0183 data to get the time. */
20 
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/malloc.h>
24 #include <sys/sensors.h>
25 #include <sys/tty.h>
26 #include <sys/conf.h>
27 #include <sys/time.h>
28 
29 #ifdef NMEA_DEBUG
30 #define DPRINTFN(n, x)	do { if (nmeadebug > (n)) printf x; } while (0)
31 int nmeadebug = 0;
32 #else
33 #define DPRINTFN(n, x)
34 #endif
35 #define DPRINTF(x)	DPRINTFN(0, x)
36 
37 void	nmeaattach(int);
38 
39 #define NMEAMAX		82
40 #define MAXFLDS		32
41 #define KNOTTOMS	(51444 / 100)
42 #ifdef NMEA_DEBUG
43 #define TRUSTTIME	30
44 #else
45 #define TRUSTTIME	(10 * 60)	/* 10 minutes */
46 #endif
47 
48 int nmea_count, nmea_nxid;
49 
50 struct nmea {
51 	char			cbuf[NMEAMAX];	/* receive buffer */
52 	struct ksensor		time;		/* the timedelta sensor */
53 	struct ksensor		signal;		/* signal status */
54 	struct ksensor		latitude;
55 	struct ksensor		longitude;
56 	struct ksensor		altitude;
57 	struct ksensor		speed;
58 	struct ksensordev	timedev;
59 	struct timespec		ts;		/* current timestamp */
60 	struct timespec		lts;		/* timestamp of last '$' seen */
61 	struct timeout		nmea_tout;	/* invalidate sensor */
62 	int64_t			gap;		/* gap between two sentences */
63 #ifdef NMEA_DEBUG
64 	int			gapno;
65 #endif
66 	int64_t			last;		/* last time rcvd */
67 	int			sync;		/* if 1, waiting for '$' */
68 	int			pos;		/* position in rcv buffer */
69 	int			no_pps;		/* no PPS although requested */
70 	char			mode;		/* GPS mode */
71 };
72 
73 /* NMEA decoding */
74 void	nmea_scan(struct nmea *, struct tty *);
75 void	nmea_gprmc(struct nmea *, struct tty *, char *fld[], int fldcnt);
76 void	nmea_decode_gga(struct nmea *, struct tty *, char *fld[], int fldcnt);
77 
78 /* date and time conversion */
79 int	nmea_date_to_nano(char *s, int64_t *nano);
80 int	nmea_time_to_nano(char *s, int64_t *nano);
81 
82 /* longitude and latitude conversion */
83 int	nmea_degrees(int64_t *dst, char *src, int neg);
84 int	nmea_atoi(int64_t *dst, char *src);
85 
86 /* degrade the timedelta sensor */
87 void	nmea_timeout(void *);
88 
89 void
90 nmeaattach(int dummy)
91 {
92 	/* noop */
93 }
94 
95 int
96 nmeaopen(dev_t dev, struct tty *tp, struct proc *p)
97 {
98 	struct nmea *np;
99 	int error;
100 
101 	if (tp->t_line == NMEADISC)
102 		return (ENODEV);
103 	if ((error = suser(p)) != 0)
104 		return (error);
105 	np = malloc(sizeof(struct nmea), M_DEVBUF, M_WAITOK | M_ZERO);
106 	snprintf(np->timedev.xname, sizeof(np->timedev.xname), "nmea%d",
107 	    nmea_nxid++);
108 	nmea_count++;
109 	np->time.status = SENSOR_S_UNKNOWN;
110 	np->time.type = SENSOR_TIMEDELTA;
111 	np->time.flags = SENSOR_FINVALID;
112 	sensor_attach(&np->timedev, &np->time);
113 
114 	np->signal.type = SENSOR_INDICATOR;
115 	np->signal.status = SENSOR_S_UNKNOWN;
116 	np->signal.value = 0;
117 	strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
118 	sensor_attach(&np->timedev, &np->signal);
119 
120 	np->latitude.type = SENSOR_ANGLE;
121 	np->latitude.status = SENSOR_S_UNKNOWN;
122 	np->latitude.flags = SENSOR_FINVALID;
123 	np->latitude.value = 0;
124 	strlcpy(np->latitude.desc, "Latitude", sizeof(np->latitude.desc));
125 	sensor_attach(&np->timedev, &np->latitude);
126 
127 	np->longitude.type = SENSOR_ANGLE;
128 	np->longitude.status = SENSOR_S_UNKNOWN;
129 	np->longitude.flags = SENSOR_FINVALID;
130 	np->longitude.value = 0;
131 	strlcpy(np->longitude.desc, "Longitude", sizeof(np->longitude.desc));
132 	sensor_attach(&np->timedev, &np->longitude);
133 
134 	np->altitude.type = SENSOR_DISTANCE;
135 	np->altitude.status = SENSOR_S_UNKNOWN;
136 	np->altitude.flags = SENSOR_FINVALID;
137 	np->altitude.value = 0;
138 	strlcpy(np->altitude.desc, "Altitude", sizeof(np->altitude.desc));
139 	sensor_attach(&np->timedev, &np->altitude);
140 
141 	np->speed.type = SENSOR_VELOCITY;
142 	np->speed.status = SENSOR_S_UNKNOWN;
143 	np->speed.flags = SENSOR_FINVALID;
144 	np->speed.value = 0;
145 	strlcpy(np->speed.desc, "Ground speed", sizeof(np->speed.desc));
146 	sensor_attach(&np->timedev, &np->speed);
147 
148 	np->sync = 1;
149 	tp->t_sc = (caddr_t)np;
150 
151 	error = linesw[TTYDISC].l_open(dev, tp, p);
152 	if (error) {
153 		free(np, M_DEVBUF, sizeof(*np));
154 		tp->t_sc = NULL;
155 	} else {
156 		sensordev_install(&np->timedev);
157 		timeout_set(&np->nmea_tout, nmea_timeout, np);
158 	}
159 	return (error);
160 }
161 
162 int
163 nmeaclose(struct tty *tp, int flags, struct proc *p)
164 {
165 	struct nmea *np = (struct nmea *)tp->t_sc;
166 
167 	tp->t_line = TTYDISC;	/* switch back to termios */
168 	timeout_del(&np->nmea_tout);
169 	sensordev_deinstall(&np->timedev);
170 	free(np, M_DEVBUF, sizeof(*np));
171 	tp->t_sc = NULL;
172 	nmea_count--;
173 	if (nmea_count == 0)
174 		nmea_nxid = 0;
175 	return (linesw[TTYDISC].l_close(tp, flags, p));
176 }
177 
178 /* Collect NMEA sentences from the tty. */
179 int
180 nmeainput(int c, struct tty *tp)
181 {
182 	struct nmea *np = (struct nmea *)tp->t_sc;
183 	struct timespec ts;
184 	int64_t gap;
185 	long tmin, tmax;
186 
187 	switch (c) {
188 	case '$':
189 		nanotime(&ts);
190 		np->pos = np->sync = 0;
191 		gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
192 		    (np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
193 
194 		np->lts.tv_sec = ts.tv_sec;
195 		np->lts.tv_nsec = ts.tv_nsec;
196 
197 		if (gap <= np->gap)
198 			break;
199 
200 		np->ts.tv_sec = ts.tv_sec;
201 		np->ts.tv_nsec = ts.tv_nsec;
202 
203 #ifdef NMEA_DEBUG
204 		if (nmeadebug > 0) {
205 			linesw[TTYDISC].l_rint('[', tp);
206 			linesw[TTYDISC].l_rint('0' + np->gapno++, tp);
207 			linesw[TTYDISC].l_rint(']', tp);
208 		}
209 #endif
210 		np->gap = gap;
211 
212 		/*
213 		 * If a tty timestamp is available, make sure its value is
214 		 * reasonable by comparing against the timestamp just taken.
215 		 * If they differ by more than 2 seconds, assume no PPS signal
216 		 * is present, note the fact, and keep using the timestamp
217 		 * value.  When this happens, the sensor state is set to
218 		 * CRITICAL later when the GPRMC sentence is decoded.
219 		 */
220 		if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
221 		    TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
222 			tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
223 			tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
224 			if (tmax - tmin > 1)
225 				np->no_pps = 1;
226 			else {
227 				np->ts.tv_sec = tp->t_tv.tv_sec;
228 				np->ts.tv_nsec = tp->t_tv.tv_usec *
229 				    1000L;
230 				np->no_pps = 0;
231 			}
232 		}
233 		break;
234 	case '\r':
235 	case '\n':
236 		if (!np->sync) {
237 			np->cbuf[np->pos] = '\0';
238 			nmea_scan(np, tp);
239 			np->sync = 1;
240 		}
241 		break;
242 	default:
243 		if (!np->sync && np->pos < (NMEAMAX - 1))
244 			np->cbuf[np->pos++] = c;
245 		break;
246 	}
247 	/* pass data to termios */
248 	return (linesw[TTYDISC].l_rint(c, tp));
249 }
250 
251 /* Scan the NMEA sentence just received. */
252 void
253 nmea_scan(struct nmea *np, struct tty *tp)
254 {
255 	int fldcnt = 0, cksum = 0, msgcksum, n;
256 	char *fld[MAXFLDS], *cs;
257 
258 	/* split into fields and calculate the checksum */
259 	fld[fldcnt++] = &np->cbuf[0];	/* message type */
260 	for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
261 		switch (np->cbuf[n]) {
262 		case '*':
263 			np->cbuf[n] = '\0';
264 			cs = &np->cbuf[n + 1];
265 			break;
266 		case ',':
267 			if (fldcnt < MAXFLDS) {
268 				cksum ^= np->cbuf[n];
269 				np->cbuf[n] = '\0';
270 				fld[fldcnt++] = &np->cbuf[n + 1];
271 			} else {
272 				DPRINTF(("nr of fields in %s sentence exceeds "
273 				    "maximum of %d\n", fld[0], MAXFLDS));
274 				return;
275 			}
276 			break;
277 		default:
278 			cksum ^= np->cbuf[n];
279 		}
280 	}
281 
282 	/*
283 	 * we only look at the messages coming from well-known sources or 'talkers',
284 	 * distinguished by the two-chars prefix, the most common being:
285 	 * GPS (GP)
286 	 * Glonass (GL)
287 	 * BeiDou (BD)
288 	 * Galileo (GA)
289 	 * 'Any kind/a mix of GNSS systems' (GN)
290 	 */
291 	if (strncmp(fld[0], "BD", 2) &&
292 	    strncmp(fld[0], "GA", 2) &&
293 	    strncmp(fld[0], "GL", 2) &&
294 	    strncmp(fld[0], "GN", 2) &&
295 	    strncmp(fld[0], "GP", 2))
296 		return;
297 
298 	/* we look for the RMC & GGA messages */
299 	if (strncmp(fld[0] + 2, "RMC", 3) &&
300 	    strncmp(fld[0] + 2, "GGA", 3))
301 		return;
302 
303 	/* if we have a checksum, verify it */
304 	if (cs != NULL) {
305 		msgcksum = 0;
306 		while (*cs) {
307 			if ((*cs >= '0' && *cs <= '9') ||
308 			    (*cs >= 'A' && *cs <= 'F')) {
309 				if (msgcksum)
310 					msgcksum <<= 4;
311 				if (*cs >= '0' && *cs<= '9')
312 					msgcksum += *cs - '0';
313 				else if (*cs >= 'A' && *cs <= 'F')
314 					msgcksum += 10 + *cs - 'A';
315 				cs++;
316 			} else {
317 				DPRINTF(("bad char %c in checksum\n", *cs));
318 				return;
319 			}
320 		}
321 		if (msgcksum != cksum) {
322 			DPRINTF(("checksum mismatch\n"));
323 			return;
324 		}
325 	}
326 	if (strncmp(fld[0] + 2, "RMC", 3) == 0)
327 		nmea_gprmc(np, tp, fld, fldcnt);
328 	if (strncmp(fld[0] + 2, "GGA", 3) == 0)
329 		nmea_decode_gga(np, tp, fld, fldcnt);
330 }
331 
332 /* Decode the recommended minimum specific GPS/TRANSIT data. */
333 void
334 nmea_gprmc(struct nmea *np, struct tty *tp, char *fld[], int fldcnt)
335 {
336 	int64_t date_nano, time_nano, nmea_now;
337 	int jumped = 0;
338 
339 	if (fldcnt < 12 || fldcnt > 14) {
340 		DPRINTF(("gprmc: field count mismatch, %d\n", fldcnt));
341 		return;
342 	}
343 	if (nmea_time_to_nano(fld[1], &time_nano)) {
344 		DPRINTF(("gprmc: illegal time, %s\n", fld[1]));
345 		return;
346 	}
347 	if (nmea_date_to_nano(fld[9], &date_nano)) {
348 		DPRINTF(("gprmc: illegal date, %s\n", fld[9]));
349 		return;
350 	}
351 	nmea_now = date_nano + time_nano;
352 	if (nmea_now <= np->last) {
353 		DPRINTF(("gprmc: time not monotonically increasing\n"));
354 		jumped = 1;
355 	}
356 	np->last = nmea_now;
357 	np->gap = 0LL;
358 #ifdef NMEA_DEBUG
359 	if (np->time.status == SENSOR_S_UNKNOWN) {
360 		np->time.status = SENSOR_S_OK;
361 		timeout_add_sec(&np->nmea_tout, TRUSTTIME);
362 	}
363 	np->gapno = 0;
364 	if (nmeadebug > 0) {
365 		linesw[TTYDISC].l_rint('[', tp);
366 		linesw[TTYDISC].l_rint('C', tp);
367 		linesw[TTYDISC].l_rint(']', tp);
368 	}
369 #endif
370 
371 	np->time.value = np->ts.tv_sec * 1000000000LL +
372 	    np->ts.tv_nsec - nmea_now;
373 	np->time.tv.tv_sec = np->ts.tv_sec;
374 	np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
375 
376 	if (fldcnt < 13)
377 		strlcpy(np->time.desc, "GPS", sizeof(np->time.desc));
378 	else if (*fld[12] != np->mode) {
379 		np->mode = *fld[12];
380 		switch (np->mode) {
381 		case 'S':
382 			strlcpy(np->time.desc, "GPS simulated",
383 			    sizeof(np->time.desc));
384 			break;
385 		case 'E':
386 			strlcpy(np->time.desc, "GPS estimated",
387 			    sizeof(np->time.desc));
388 			break;
389 		case 'A':
390 			strlcpy(np->time.desc, "GPS autonomous",
391 			    sizeof(np->time.desc));
392 			break;
393 		case 'D':
394 			strlcpy(np->time.desc, "GPS differential",
395 			    sizeof(np->time.desc));
396 			break;
397 		case 'N':
398 			strlcpy(np->time.desc, "GPS invalid",
399 			    sizeof(np->time.desc));
400 			break;
401 		default:
402 			strlcpy(np->time.desc, "GPS unknown",
403 			    sizeof(np->time.desc));
404 			DPRINTF(("gprmc: unknown mode '%c'\n", np->mode));
405 		}
406 	}
407 	switch (*fld[2]) {
408 	case 'A':	/* The GPS has a fix, (re)arm the timeout. */
409 			/* XXX is 'D' also a valid state? */
410 		np->time.status = SENSOR_S_OK;
411 		np->signal.value = 1;
412 		np->signal.status = SENSOR_S_OK;
413 		np->latitude.status = SENSOR_S_OK;
414 		np->longitude.status = SENSOR_S_OK;
415 		np->speed.status = SENSOR_S_OK;
416 		np->time.flags &= ~SENSOR_FINVALID;
417 		np->latitude.flags &= ~SENSOR_FINVALID;
418 		np->longitude.flags &= ~SENSOR_FINVALID;
419 		np->speed.flags &= ~SENSOR_FINVALID;
420 		break;
421 	case 'V':	/*
422 			 * The GPS indicates a warning status, do not add to
423 			 * the timeout, if the condition persist, the sensor
424 			 * will be degraded.  Signal the condition through
425 			 * the signal sensor.
426 			 */
427 		np->signal.value = 0;
428 		np->signal.status = SENSOR_S_CRIT;
429 		np->latitude.status = SENSOR_S_WARN;
430 		np->longitude.status = SENSOR_S_WARN;
431 		np->speed.status = SENSOR_S_WARN;
432 		break;
433 	}
434 	if (nmea_degrees(&np->latitude.value, fld[3], *fld[4] == 'S' ? 1 : 0))
435 		np->latitude.status = SENSOR_S_WARN;
436 	if (nmea_degrees(&np->longitude.value,fld[5], *fld[6] == 'W' ? 1 : 0))
437 		np->longitude.status = SENSOR_S_WARN;
438 
439 	if (nmea_atoi(&np->speed.value, fld[7]))
440 		np->speed.status = SENSOR_S_WARN;
441 	/* convert from knot to um/s */
442 	np->speed.value *= KNOTTOMS;
443 
444 	if (jumped)
445 		np->time.status = SENSOR_S_WARN;
446 	if (np->time.status == SENSOR_S_OK)
447 		timeout_add_sec(&np->nmea_tout, TRUSTTIME);
448 	/*
449 	 * If tty timestamping is requested, but no PPS signal is present, set
450 	 * the sensor state to CRITICAL.
451 	 */
452 	if (np->no_pps)
453 		np->time.status = SENSOR_S_CRIT;
454 }
455 
456 /* Decode the GPS fix data for altitude.
457  * - field 9 is the altitude in meters
458  * $GNGGA,085901.00,1234.5678,N,00987.12345,E,1,12,0.84,1040.9,M,47.4,M,,*4B
459  */
460 void
461 nmea_decode_gga(struct nmea *np, struct tty *tp, char *fld[], int fldcnt)
462 {
463 	if (fldcnt != 15) {
464 		DPRINTF(("GGA: field count mismatch, %d\n", fldcnt));
465 		return;
466 	}
467 #ifdef NMEA_DEBUG
468 	if (nmeadebug > 0) {
469 		linesw[TTYDISC].l_rint('[', tp);
470 		linesw[TTYDISC].l_rint('C', tp);
471 		linesw[TTYDISC].l_rint(']', tp);
472 	}
473 #endif
474 
475 	np->altitude.status = SENSOR_S_OK;
476 	if (nmea_atoi(&np->altitude.value, fld[9]))
477 		np->altitude.status = SENSOR_S_WARN;
478 
479 	/* convert to uMeter */
480 	np->altitude.value *= 1000;
481 	np->altitude.flags &= ~SENSOR_FINVALID;
482 }
483 
484 /*
485  * Convert nmea integer/decimal values in the form of XXXX.Y to an integer value
486  * if it's a meter/altitude value, will be returned as mm
487  */
488 int
489 nmea_atoi(int64_t *dst, char *src)
490 {
491 	char *p;
492 	int i = 3; /* take 3 digits */
493 	*dst = 0;
494 
495 	for (p = src; *p && *p != '.' && *p >= '0' && *p <= '9' ; )
496 		*dst = *dst * 10 + (*p++ - '0');
497 
498 	/* *p should be '.' at that point */
499 	if (*p != '.')
500 		return -1;	/* no decimal point, or bogus value ? */
501 	p++;
502 
503 	/* read digits after decimal point, stop at first non-digit */
504 	for (; *p && i > 0 && *p >= '0' && *p <= '9' ; i--)
505 		*dst = *dst * 10 + (*p++ - '0');
506 
507 	for (; i > 0 ; i--)
508 		*dst *= 10;
509 
510 	DPRINTFN(2,("%s -> %lld\n", src, *dst));
511 	return 0;
512 }
513 
514 /*
515  * Convert a nmea position in the form DDDMM.MMMM to an
516  * angle sensor value (degrees*1000000)
517  */
518 int
519 nmea_degrees(int64_t *dst, char *src, int neg)
520 {
521 	size_t ppos;
522 	int i, n;
523 	int64_t deg = 0, min = 0;
524 	char *p;
525 
526 	while (*src == '0')
527 		++src;	/* skip leading zeroes */
528 
529 	for (p = src, ppos = 0; *p; ppos++)
530 		if (*p++ == '.')
531 			break;
532 
533 	if (*p == '\0')
534 		return (-1);	/* no decimal point */
535 
536 	for (n = 0; *src && n + 2 < ppos; n++)
537 		deg = deg * 10 + (*src++ - '0');
538 
539 	for (; *src && n < ppos; n++)
540 		min = min * 10 + (*src++ - '0');
541 
542 	src++;		/* skip decimal point */
543 
544 	for (; *src && n < (ppos + 4); n++)
545 		min = min * 10 + (*src++ - '0');
546 
547 	for (i=0; i < 6 + ppos - n; i++)
548 		min *= 10;
549 
550 	deg = deg * 1000000 + (min/60);
551 
552 	*dst = neg ? -deg : deg;
553 	return (0);
554 }
555 
556 /*
557  * Convert a NMEA 0183 formatted date string to seconds since the epoch.
558  * The string must be of the form DDMMYY.
559  * Return 0 on success, -1 if illegal characters are encountered.
560  */
561 int
562 nmea_date_to_nano(char *s, int64_t *nano)
563 {
564 	struct clock_ymdhms ymd;
565 	time_t secs;
566 	char *p;
567 	int n;
568 
569 	/* make sure the input contains only numbers and is six digits long */
570 	for (n = 0, p = s; n < 6 && *p && *p >= '0' && *p <= '9'; n++, p++)
571 		;
572 	if (n != 6 || (*p != '\0'))
573 		return (-1);
574 
575 	ymd.dt_year = 2000 + (s[4] - '0') * 10 + (s[5] - '0');
576 	ymd.dt_mon = (s[2] - '0') * 10 + (s[3] - '0');
577 	ymd.dt_day = (s[0] - '0') * 10 + (s[1] - '0');
578 	ymd.dt_hour = ymd.dt_min = ymd.dt_sec = 0;
579 
580 	secs = clock_ymdhms_to_secs(&ymd);
581 	*nano = secs * 1000000000LL;
582 	return (0);
583 }
584 
585 /*
586  * Convert NMEA 0183 formatted time string to nanoseconds since midnight.
587  * The string must be of the form HHMMSS[.[sss]] (e.g. 143724 or 143723.615).
588  * Return 0 on success, -1 if illegal characters are encountered.
589  */
590 int
591 nmea_time_to_nano(char *s, int64_t *nano)
592 {
593 	long fac = 36000L, div = 6L, secs = 0L, frac = 0L;
594 	char ul = '2';
595 	int n;
596 
597 	for (n = 0, secs = 0; fac && *s && *s >= '0' && *s <= ul; s++, n++) {
598 		secs += (*s - '0') * fac;
599 		div = 16 - div;
600 		fac /= div;
601 		switch (n) {
602 		case 0:
603 			if (*s <= '1')
604 				ul = '9';
605 			else
606 				ul = '3';
607 			break;
608 		case 1:
609 		case 3:
610 			ul = '5';
611 			break;
612 		case 2:
613 		case 4:
614 			ul = '9';
615 			break;
616 		}
617 	}
618 	if (fac)
619 		return (-1);
620 
621 	/* Handle the fractions of a second, up to a maximum of 6 digits. */
622 	div = 1L;
623 	if (*s == '.') {
624 		for (++s; div < 1000000 && *s && *s >= '0' && *s <= '9'; s++) {
625 			frac *= 10;
626 			frac += (*s - '0');
627 			div *= 10;
628 		}
629 	}
630 
631 	if (*s != '\0')
632 		return (-1);
633 
634 	*nano = secs * 1000000000LL + (int64_t)frac * (1000000000 / div);
635 	return (0);
636 }
637 
638 /*
639  * Degrade the sensor state if we received no NMEA sentences for more than
640  * TRUSTTIME seconds.
641  */
642 void
643 nmea_timeout(void *xnp)
644 {
645 	struct nmea *np = xnp;
646 
647 	np->signal.value = 0;
648 	np->signal.status = SENSOR_S_CRIT;
649 	if (np->time.status == SENSOR_S_OK) {
650 		np->time.status = SENSOR_S_WARN;
651 		np->latitude.status = SENSOR_S_WARN;
652 		np->longitude.status = SENSOR_S_WARN;
653 		np->altitude.status = SENSOR_S_WARN;
654 		np->speed.status = SENSOR_S_WARN;
655 		/*
656 		 * further degrade in TRUSTTIME seconds if no new valid NMEA
657 		 * sentences are received.
658 		 */
659 		timeout_add_sec(&np->nmea_tout, TRUSTTIME);
660 	} else {
661 		np->time.status = SENSOR_S_CRIT;
662 		np->latitude.status = SENSOR_S_CRIT;
663 		np->longitude.status = SENSOR_S_CRIT;
664 		np->altitude.status = SENSOR_S_CRIT;
665 		np->speed.status = SENSOR_S_CRIT;
666 	}
667 }
668