tty_nmea.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /* $OpenBSD: tty_nmea.c,v 1.44 2014/11/03 03:08:00 deraadt Exp $ */
  2. /*
  3. * Copyright (c) 2006, 2007, 2008 Marc Balmer <mbalmer@openbsd.org>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. /* A tty line discipline to decode NMEA 0183 data to get the time. */
  18. #include <sys/param.h>
  19. #include <sys/systm.h>
  20. #include <sys/malloc.h>
  21. #include <sys/sensors.h>
  22. #include <sys/tty.h>
  23. #include <sys/conf.h>
  24. #include <sys/time.h>
  25. #ifdef NMEA_DEBUG
  26. #define DPRINTFN(n, x) do { if (nmeadebug > (n)) printf x; } while (0)
  27. int nmeadebug = 0;
  28. #else
  29. #define DPRINTFN(n, x)
  30. #endif
  31. #define DPRINTF(x) DPRINTFN(0, x)
  32. int nmeaopen(dev_t, struct tty *, struct proc *);
  33. int nmeaclose(struct tty *, int, struct proc *);
  34. int nmeainput(int, struct tty *);
  35. void nmeaattach(int);
  36. #define NMEAMAX 82
  37. #define MAXFLDS 32
  38. #ifdef NMEA_DEBUG
  39. #define TRUSTTIME 30
  40. #else
  41. #define TRUSTTIME (10 * 60) /* 10 minutes */
  42. #endif
  43. int nmea_count, nmea_nxid;
  44. struct nmea {
  45. char cbuf[NMEAMAX]; /* receive buffer */
  46. struct ksensor time; /* the timedelta sensor */
  47. struct ksensor signal; /* signal status */
  48. struct ksensor latitude;
  49. struct ksensor longitude;
  50. struct ksensordev timedev;
  51. struct timespec ts; /* current timestamp */
  52. struct timespec lts; /* timestamp of last '$' seen */
  53. struct timeout nmea_tout; /* invalidate sensor */
  54. int64_t gap; /* gap between two sentences */
  55. #ifdef NMEA_DEBUG
  56. int gapno;
  57. #endif
  58. int64_t last; /* last time rcvd */
  59. int sync; /* if 1, waiting for '$' */
  60. int pos; /* position in rcv buffer */
  61. int no_pps; /* no PPS although requested */
  62. char mode; /* GPS mode */
  63. };
  64. /* NMEA decoding */
  65. void nmea_scan(struct nmea *, struct tty *);
  66. void nmea_gprmc(struct nmea *, struct tty *, char *fld[], int fldcnt);
  67. /* date and time conversion */
  68. int nmea_date_to_nano(char *s, int64_t *nano);
  69. int nmea_time_to_nano(char *s, int64_t *nano);
  70. /* longitude and latitude conversion */
  71. int nmea_degrees(int64_t *dst, char *src, int neg);
  72. /* degrade the timedelta sensor */
  73. void nmea_timeout(void *);
  74. void
  75. nmeaattach(int dummy)
  76. {
  77. /* noop */
  78. }
  79. int
  80. nmeaopen(dev_t dev, struct tty *tp, struct proc *p)
  81. {
  82. struct nmea *np;
  83. int error;
  84. if (tp->t_line == NMEADISC)
  85. return (ENODEV);
  86. if ((error = suser(p, 0)) != 0)
  87. return (error);
  88. np = malloc(sizeof(struct nmea), M_DEVBUF, M_WAITOK | M_ZERO);
  89. snprintf(np->timedev.xname, sizeof(np->timedev.xname), "nmea%d",
  90. nmea_nxid++);
  91. nmea_count++;
  92. np->time.status = SENSOR_S_UNKNOWN;
  93. np->time.type = SENSOR_TIMEDELTA;
  94. np->time.flags = SENSOR_FINVALID;
  95. sensor_attach(&np->timedev, &np->time);
  96. np->signal.type = SENSOR_INDICATOR;
  97. np->signal.status = SENSOR_S_UNKNOWN;
  98. np->signal.value = 0;
  99. strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
  100. sensor_attach(&np->timedev, &np->signal);
  101. np->latitude.type = SENSOR_ANGLE;
  102. np->latitude.status = SENSOR_S_UNKNOWN;
  103. np->latitude.flags = SENSOR_FINVALID;
  104. np->latitude.value = 0;
  105. strlcpy(np->latitude.desc, "Latitude", sizeof(np->latitude.desc));
  106. sensor_attach(&np->timedev, &np->latitude);
  107. np->longitude.type = SENSOR_ANGLE;
  108. np->longitude.status = SENSOR_S_UNKNOWN;
  109. np->longitude.flags = SENSOR_FINVALID;
  110. np->longitude.value = 0;
  111. strlcpy(np->longitude.desc, "Longitude", sizeof(np->longitude.desc));
  112. sensor_attach(&np->timedev, &np->longitude);
  113. np->sync = 1;
  114. tp->t_sc = (caddr_t)np;
  115. error = linesw[TTYDISC].l_open(dev, tp, p);
  116. if (error) {
  117. free(np, M_DEVBUF, sizeof(*np));
  118. tp->t_sc = NULL;
  119. } else {
  120. sensordev_install(&np->timedev);
  121. timeout_set(&np->nmea_tout, nmea_timeout, np);
  122. }
  123. return (error);
  124. }
  125. int
  126. nmeaclose(struct tty *tp, int flags, struct proc *p)
  127. {
  128. struct nmea *np = (struct nmea *)tp->t_sc;
  129. tp->t_line = TTYDISC; /* switch back to termios */
  130. timeout_del(&np->nmea_tout);
  131. sensordev_deinstall(&np->timedev);
  132. free(np, M_DEVBUF, sizeof(*np));
  133. tp->t_sc = NULL;
  134. nmea_count--;
  135. if (nmea_count == 0)
  136. nmea_nxid = 0;
  137. return (linesw[TTYDISC].l_close(tp, flags, p));
  138. }
  139. /* Collect NMEA sentences from the tty. */
  140. int
  141. nmeainput(int c, struct tty *tp)
  142. {
  143. struct nmea *np = (struct nmea *)tp->t_sc;
  144. struct timespec ts;
  145. int64_t gap;
  146. long tmin, tmax;
  147. switch (c) {
  148. case '$':
  149. nanotime(&ts);
  150. np->pos = np->sync = 0;
  151. gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
  152. (np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
  153. np->lts.tv_sec = ts.tv_sec;
  154. np->lts.tv_nsec = ts.tv_nsec;
  155. if (gap <= np->gap)
  156. break;
  157. np->ts.tv_sec = ts.tv_sec;
  158. np->ts.tv_nsec = ts.tv_nsec;
  159. #ifdef NMEA_DEBUG
  160. if (nmeadebug > 0) {
  161. linesw[TTYDISC].l_rint('[', tp);
  162. linesw[TTYDISC].l_rint('0' + np->gapno++, tp);
  163. linesw[TTYDISC].l_rint(']', tp);
  164. }
  165. #endif
  166. np->gap = gap;
  167. /*
  168. * If a tty timestamp is available, make sure its value is
  169. * reasonable by comparing against the timestamp just taken.
  170. * If they differ by more than 2 seconds, assume no PPS signal
  171. * is present, note the fact, and keep using the timestamp
  172. * value. When this happens, the sensor state is set to
  173. * CRITICAL later when the GPRMC sentence is decoded.
  174. */
  175. if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
  176. TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
  177. tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
  178. tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
  179. if (tmax - tmin > 1)
  180. np->no_pps = 1;
  181. else {
  182. np->ts.tv_sec = tp->t_tv.tv_sec;
  183. np->ts.tv_nsec = tp->t_tv.tv_usec *
  184. 1000L;
  185. np->no_pps = 0;
  186. }
  187. }
  188. break;
  189. case '\r':
  190. case '\n':
  191. if (!np->sync) {
  192. np->cbuf[np->pos] = '\0';
  193. nmea_scan(np, tp);
  194. np->sync = 1;
  195. }
  196. break;
  197. default:
  198. if (!np->sync && np->pos < (NMEAMAX - 1))
  199. np->cbuf[np->pos++] = c;
  200. break;
  201. }
  202. /* pass data to termios */
  203. return (linesw[TTYDISC].l_rint(c, tp));
  204. }
  205. /* Scan the NMEA sentence just received. */
  206. void
  207. nmea_scan(struct nmea *np, struct tty *tp)
  208. {
  209. int fldcnt = 0, cksum = 0, msgcksum, n;
  210. char *fld[MAXFLDS], *cs;
  211. /* split into fields and calculate the checksum */
  212. fld[fldcnt++] = &np->cbuf[0]; /* message type */
  213. for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
  214. switch (np->cbuf[n]) {
  215. case '*':
  216. np->cbuf[n] = '\0';
  217. cs = &np->cbuf[n + 1];
  218. break;
  219. case ',':
  220. if (fldcnt < MAXFLDS) {
  221. cksum ^= np->cbuf[n];
  222. np->cbuf[n] = '\0';
  223. fld[fldcnt++] = &np->cbuf[n + 1];
  224. } else {
  225. DPRINTF(("nr of fields in %s sentence exceeds "
  226. "maximum of %d\n", fld[0], MAXFLDS));
  227. return;
  228. }
  229. break;
  230. default:
  231. cksum ^= np->cbuf[n];
  232. }
  233. }
  234. /* we only look at the GPRMC message */
  235. if (strcmp(fld[0], "GPRMC"))
  236. return;
  237. /* if we have a checksum, verify it */
  238. if (cs != NULL) {
  239. msgcksum = 0;
  240. while (*cs) {
  241. if ((*cs >= '0' && *cs <= '9') ||
  242. (*cs >= 'A' && *cs <= 'F')) {
  243. if (msgcksum)
  244. msgcksum <<= 4;
  245. if (*cs >= '0' && *cs<= '9')
  246. msgcksum += *cs - '0';
  247. else if (*cs >= 'A' && *cs <= 'F')
  248. msgcksum += 10 + *cs - 'A';
  249. cs++;
  250. } else {
  251. DPRINTF(("bad char %c in checksum\n", *cs));
  252. return;
  253. }
  254. }
  255. if (msgcksum != cksum) {
  256. DPRINTF(("checksum mismatch\n"));
  257. return;
  258. }
  259. }
  260. nmea_gprmc(np, tp, fld, fldcnt);
  261. }
  262. /* Decode the recommended minimum specific GPS/TRANSIT data. */
  263. void
  264. nmea_gprmc(struct nmea *np, struct tty *tp, char *fld[], int fldcnt)
  265. {
  266. int64_t date_nano, time_nano, nmea_now;
  267. int jumped = 0;
  268. if (fldcnt != 12 && fldcnt != 13) {
  269. DPRINTF(("gprmc: field count mismatch, %d\n", fldcnt));
  270. return;
  271. }
  272. if (nmea_time_to_nano(fld[1], &time_nano)) {
  273. DPRINTF(("gprmc: illegal time, %s\n", fld[1]));
  274. return;
  275. }
  276. if (nmea_date_to_nano(fld[9], &date_nano)) {
  277. DPRINTF(("gprmc: illegal date, %s\n", fld[9]));
  278. return;
  279. }
  280. nmea_now = date_nano + time_nano;
  281. if (nmea_now <= np->last) {
  282. DPRINTF(("gprmc: time not monotonically increasing\n"));
  283. jumped = 1;
  284. }
  285. np->last = nmea_now;
  286. np->gap = 0LL;
  287. #ifdef NMEA_DEBUG
  288. if (np->time.status == SENSOR_S_UNKNOWN) {
  289. np->time.status = SENSOR_S_OK;
  290. timeout_add_sec(&np->nmea_tout, TRUSTTIME);
  291. }
  292. np->gapno = 0;
  293. if (nmeadebug > 0) {
  294. linesw[TTYDISC].l_rint('[', tp);
  295. linesw[TTYDISC].l_rint('C', tp);
  296. linesw[TTYDISC].l_rint(']', tp);
  297. }
  298. #endif
  299. np->time.value = np->ts.tv_sec * 1000000000LL +
  300. np->ts.tv_nsec - nmea_now;
  301. np->time.tv.tv_sec = np->ts.tv_sec;
  302. np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
  303. if (fldcnt != 13)
  304. strlcpy(np->time.desc, "GPS", sizeof(np->time.desc));
  305. else if (fldcnt == 13 && *fld[12] != np->mode) {
  306. np->mode = *fld[12];
  307. switch (np->mode) {
  308. case 'S':
  309. strlcpy(np->time.desc, "GPS simulated",
  310. sizeof(np->time.desc));
  311. break;
  312. case 'E':
  313. strlcpy(np->time.desc, "GPS estimated",
  314. sizeof(np->time.desc));
  315. break;
  316. case 'A':
  317. strlcpy(np->time.desc, "GPS autonomous",
  318. sizeof(np->time.desc));
  319. break;
  320. case 'D':
  321. strlcpy(np->time.desc, "GPS differential",
  322. sizeof(np->time.desc));
  323. break;
  324. case 'N':
  325. strlcpy(np->time.desc, "GPS invalid",
  326. sizeof(np->time.desc));
  327. break;
  328. default:
  329. strlcpy(np->time.desc, "GPS unknown",
  330. sizeof(np->time.desc));
  331. DPRINTF(("gprmc: unknown mode '%c'\n", np->mode));
  332. }
  333. }
  334. switch (*fld[2]) {
  335. case 'A': /* The GPS has a fix, (re)arm the timeout. */
  336. /* XXX is 'D' also a valid state? */
  337. np->time.status = SENSOR_S_OK;
  338. np->signal.value = 1;
  339. np->signal.status = SENSOR_S_OK;
  340. np->latitude.status = SENSOR_S_OK;
  341. np->longitude.status = SENSOR_S_OK;
  342. np->time.flags &= ~SENSOR_FINVALID;
  343. np->latitude.flags &= ~SENSOR_FINVALID;
  344. np->longitude.flags &= ~SENSOR_FINVALID;
  345. break;
  346. case 'V': /*
  347. * The GPS indicates a warning status, do not add to
  348. * the timeout, if the condition persist, the sensor
  349. * will be degraded. Signal the condition through
  350. * the signal sensor.
  351. */
  352. np->signal.value = 0;
  353. np->signal.status = SENSOR_S_CRIT;
  354. np->latitude.status = SENSOR_S_WARN;
  355. np->longitude.status = SENSOR_S_WARN;
  356. break;
  357. }
  358. if (nmea_degrees(&np->latitude.value, fld[3], *fld[4] == 'S' ? 1 : 0))
  359. np->latitude.status = SENSOR_S_WARN;
  360. if (nmea_degrees(&np->longitude.value,fld[5], *fld[6] == 'W' ? 1 : 0))
  361. np->longitude.status = SENSOR_S_WARN;
  362. if (jumped)
  363. np->time.status = SENSOR_S_WARN;
  364. if (np->time.status == SENSOR_S_OK)
  365. timeout_add_sec(&np->nmea_tout, TRUSTTIME);
  366. /*
  367. * If tty timestamping is requested, but no PPS signal is present, set
  368. * the sensor state to CRITICAL.
  369. */
  370. if (np->no_pps)
  371. np->time.status = SENSOR_S_CRIT;
  372. }
  373. /*
  374. * Convert a nmea position in the form DDDMM.MMMM to an
  375. * angle sensor value (degrees*1000000)
  376. */
  377. int
  378. nmea_degrees(int64_t *dst, char *src, int neg)
  379. {
  380. size_t ppos;
  381. int i, n;
  382. int64_t deg = 0, min = 0;
  383. char *p;
  384. while (*src == '0')
  385. ++src; /* skip leading zeroes */
  386. for (p = src, ppos = 0; *p; ppos++)
  387. if (*p++ == '.')
  388. break;
  389. if (*p == '\0')
  390. return (-1); /* no decimal point */
  391. for (n = 0; *src && n + 2 < ppos; n++)
  392. deg = deg * 10 + (*src++ - '0');
  393. for (; *src && n < ppos; n++)
  394. min = min * 10 + (*src++ - '0');
  395. src++; /* skip decimal point */
  396. for (; *src && n < (ppos + 4); n++)
  397. min = min * 10 + (*src++ - '0');
  398. for (i=0; i < 6 + ppos - n; i++)
  399. min *= 10;
  400. deg = deg * 1000000 + (min/60);
  401. *dst = neg ? -deg : deg;
  402. return (0);
  403. }
  404. /*
  405. * Convert a NMEA 0183 formatted date string to seconds since the epoch.
  406. * The string must be of the form DDMMYY.
  407. * Return 0 on success, -1 if illegal characters are encountered.
  408. */
  409. int
  410. nmea_date_to_nano(char *s, int64_t *nano)
  411. {
  412. struct clock_ymdhms ymd;
  413. time_t secs;
  414. char *p;
  415. int n;
  416. /* make sure the input contains only numbers and is six digits long */
  417. for (n = 0, p = s; n < 6 && *p && *p >= '0' && *p <= '9'; n++, p++)
  418. ;
  419. if (n != 6 || (*p != '\0'))
  420. return (-1);
  421. ymd.dt_year = 2000 + (s[4] - '0') * 10 + (s[5] - '0');
  422. ymd.dt_mon = (s[2] - '0') * 10 + (s[3] - '0');
  423. ymd.dt_day = (s[0] - '0') * 10 + (s[1] - '0');
  424. ymd.dt_hour = ymd.dt_min = ymd.dt_sec = 0;
  425. secs = clock_ymdhms_to_secs(&ymd);
  426. *nano = secs * 1000000000LL;
  427. return (0);
  428. }
  429. /*
  430. * Convert NMEA 0183 formatted time string to nanoseconds since midnight.
  431. * The string must be of the form HHMMSS[.[sss]] (e.g. 143724 or 143723.615).
  432. * Return 0 on success, -1 if illegal characters are encountered.
  433. */
  434. int
  435. nmea_time_to_nano(char *s, int64_t *nano)
  436. {
  437. long fac = 36000L, div = 6L, secs = 0L, frac = 0L;
  438. char ul = '2';
  439. int n;
  440. for (n = 0, secs = 0; fac && *s && *s >= '0' && *s <= ul; s++, n++) {
  441. secs += (*s - '0') * fac;
  442. div = 16 - div;
  443. fac /= div;
  444. switch (n) {
  445. case 0:
  446. if (*s <= '1')
  447. ul = '9';
  448. else
  449. ul = '3';
  450. break;
  451. case 1:
  452. case 3:
  453. ul = '5';
  454. break;
  455. case 2:
  456. case 4:
  457. ul = '9';
  458. break;
  459. }
  460. }
  461. if (fac)
  462. return (-1);
  463. /* Handle the fractions of a second, up to a maximum of 6 digits. */
  464. div = 1L;
  465. if (*s == '.') {
  466. for (++s; div < 1000000 && *s && *s >= '0' && *s <= '9'; s++) {
  467. frac *= 10;
  468. frac += (*s - '0');
  469. div *= 10;
  470. }
  471. }
  472. if (*s != '\0')
  473. return (-1);
  474. *nano = secs * 1000000000LL + (int64_t)frac * (1000000000 / div);
  475. return (0);
  476. }
  477. /*
  478. * Degrade the sensor state if we received no NMEA sentences for more than
  479. * TRUSTTIME seconds.
  480. */
  481. void
  482. nmea_timeout(void *xnp)
  483. {
  484. struct nmea *np = xnp;
  485. np->signal.value = 0;
  486. np->signal.status = SENSOR_S_CRIT;
  487. if (np->time.status == SENSOR_S_OK) {
  488. np->time.status = SENSOR_S_WARN;
  489. np->latitude.status = SENSOR_S_WARN;
  490. np->longitude.status = SENSOR_S_WARN;
  491. /*
  492. * further degrade in TRUSTTIME seconds if no new valid NMEA
  493. * sentences are received.
  494. */
  495. timeout_add_sec(&np->nmea_tout, TRUSTTIME);
  496. } else {
  497. np->time.status = SENSOR_S_CRIT;
  498. np->latitude.status = SENSOR_S_CRIT;
  499. np->longitude.status = SENSOR_S_CRIT;
  500. }
  501. }