root/kern/tty_nmea.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nmeaattach
  2. nmeaopen
  3. nmeaclose
  4. nmeainput
  5. nmea_scan
  6. nmea_gprmc
  7. nmea_date_to_nano
  8. nmea_time_to_nano

    1 /*      $OpenBSD: tty_nmea.c,v 1.21 2007/03/22 16:55:31 deraadt Exp $ */
    2 
    3 /*
    4  * Copyright (c) 2006, 2007 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/queue.h>
   24 #include <sys/proc.h>
   25 #include <sys/malloc.h>
   26 #include <sys/sensors.h>
   27 #include <sys/tty.h>
   28 #include <sys/conf.h>
   29 #include <sys/time.h>
   30 
   31 #ifdef NMEA_DEBUG
   32 #define DPRINTFN(n, x)  do { if (nmeadebug > (n)) printf x; } while (0)
   33 int nmeadebug = 0;
   34 #else
   35 #define DPRINTFN(n, x)
   36 #endif
   37 #define DPRINTF(x)      DPRINTFN(0, x)
   38 
   39 int     nmeaopen(dev_t, struct tty *);
   40 int     nmeaclose(struct tty *, int);
   41 int     nmeainput(int, struct tty *);
   42 void    nmeaattach(int);
   43 
   44 #define NMEAMAX 82
   45 #define MAXFLDS 32
   46 
   47 int nmea_count; /* this is wrong, it should really be a SLIST */
   48 
   49 struct nmea {
   50         char                    cbuf[NMEAMAX];  /* receive buffer */
   51         struct ksensor          time;           /* the timedelta sensor */
   52         struct ksensordev       timedev;
   53         struct timespec         ts;             /* current timestamp */
   54         struct timespec         lts;            /* timestamp of last '$' seen */
   55         int64_t                 gap;            /* gap between two sentences */
   56 #ifdef NMEA_DEBUG
   57         int                     gapno;
   58 #endif
   59         int64_t                 last;           /* last time rcvd */
   60         int                     sync;           /* if 1, waiting for '$' */
   61         int                     pos;            /* positon in rcv buffer */
   62         int                     no_pps;         /* no PPS although requested */
   63         char                    mode;           /* GPS mode */
   64 };
   65 
   66 /* NMEA decoding */
   67 void    nmea_scan(struct nmea *, struct tty *);
   68 void    nmea_gprmc(struct nmea *, struct tty *, char *fld[], int fldcnt);
   69 
   70 /* date and time conversion */
   71 int     nmea_date_to_nano(char *s, int64_t *nano);
   72 int     nmea_time_to_nano(char *s, int64_t *nano);
   73 
   74 void
   75 nmeaattach(int dummy)
   76 {
   77 }
   78 
   79 int
   80 nmeaopen(dev_t dev, struct tty *tp)
   81 {
   82         struct proc *p = curproc;
   83         struct nmea *np;
   84         int error;
   85 
   86         if (tp->t_line == NMEADISC)
   87                 return ENODEV;
   88         if ((error = suser(p, 0)) != 0)
   89                 return error;
   90         np = malloc(sizeof(struct nmea), M_DEVBUF, M_WAITOK);
   91         bzero(np, sizeof(*np));
   92         snprintf(np->timedev.xname, sizeof(np->timedev.xname), "nmea%d",
   93             nmea_count++);
   94         np->time.status = SENSOR_S_UNKNOWN;
   95         np->time.type = SENSOR_TIMEDELTA;
   96         np->time.flags = SENSOR_FINVALID;
   97         sensor_attach(&np->timedev, &np->time);
   98         np->sync = 1;
   99         tp->t_sc = (caddr_t)np;
  100 
  101         error = linesw[TTYDISC].l_open(dev, tp);
  102         if (error) {
  103                 free(np, M_DEVBUF);
  104                 tp->t_sc = NULL;
  105         } else
  106                 sensordev_install(&np->timedev);
  107         return error;
  108 }
  109 
  110 int
  111 nmeaclose(struct tty *tp, int flags)
  112 {
  113         struct nmea *np = (struct nmea *)tp->t_sc;
  114 
  115         tp->t_line = TTYDISC;   /* switch back to termios */
  116         sensordev_deinstall(&np->timedev);
  117         free(np, M_DEVBUF);
  118         tp->t_sc = NULL;
  119         nmea_count--;
  120         return linesw[TTYDISC].l_close(tp, flags);
  121 }
  122 
  123 /* collect NMEA sentence from tty */
  124 int
  125 nmeainput(int c, struct tty *tp)
  126 {
  127         struct nmea *np = (struct nmea *)tp->t_sc;
  128         struct timespec ts;
  129         int64_t gap;
  130         long tmin, tmax;
  131 
  132         switch (c) {
  133         case '$':
  134                 nanotime(&ts);
  135                 np->pos = np->sync = 0;
  136                 gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
  137                     (np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
  138 
  139                 np->lts.tv_sec = ts.tv_sec;
  140                 np->lts.tv_nsec = ts.tv_nsec;
  141 
  142                 if (gap <= np->gap)
  143                         break;
  144 
  145                 np->ts.tv_sec = ts.tv_sec;
  146                 np->ts.tv_nsec = ts.tv_nsec;
  147 
  148 #ifdef NMEA_DEBUG
  149                 if (nmeadebug > 0) {
  150                         linesw[TTYDISC].l_rint('[', tp);
  151                         linesw[TTYDISC].l_rint('0' + np->gapno++, tp);
  152                         linesw[TTYDISC].l_rint(']', tp);
  153                 }
  154 #endif
  155                 np->gap = gap;
  156         
  157                 /*
  158                  * If a tty timestamp is available, make sure its value is
  159                  * reasonable by comparing against the timestamp just taken.
  160                  * If they differ by more than 2 seconds, assume no PPS signal
  161                  * is present, note the fact, and keep using the timestamp
  162                  * value.  When this happens, the sensor state is set to
  163                  * CRITICAL later when the GPRMC sentence is decoded.
  164                  */
  165                 if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
  166                     TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
  167                         tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
  168                         tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
  169                         if (tmax - tmin > 1)
  170                                 np->no_pps = 1;
  171                         else {
  172                                 np->ts.tv_sec = tp->t_tv.tv_sec;
  173                                 np->ts.tv_nsec = tp->t_tv.tv_usec *
  174                                     1000L;
  175                                 np->no_pps = 0;
  176                         }
  177                 }
  178                 break;
  179         case '\r':
  180         case '\n':
  181                 if (!np->sync) {
  182                         np->cbuf[np->pos] = '\0';
  183                         nmea_scan(np, tp);
  184                         np->sync = 1;
  185                 }
  186                 break;
  187         default:
  188                 if (!np->sync && np->pos < (NMEAMAX - 1))
  189                         np->cbuf[np->pos++] = c;
  190                 break;
  191         }
  192         /* pass data to termios */
  193         return linesw[TTYDISC].l_rint(c, tp);
  194 }
  195 
  196 /* Scan the NMEA sentence just received */
  197 void
  198 nmea_scan(struct nmea *np, struct tty *tp)
  199 {
  200         int fldcnt = 0, cksum = 0, msgcksum, n;
  201         char *fld[MAXFLDS], *cs;
  202 
  203         /* split into fields and calculate the checksum */
  204         fld[fldcnt++] = &np->cbuf[0];   /* message type */
  205         for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
  206                 switch (np->cbuf[n]) {
  207                 case '*':
  208                         np->cbuf[n] = '\0';
  209                         cs = &np->cbuf[n + 1];
  210                         break;
  211                 case ',':
  212                         if (fldcnt < MAXFLDS) {
  213                                 cksum ^= np->cbuf[n];
  214                                 np->cbuf[n] = '\0';
  215                                 fld[fldcnt++] = &np->cbuf[n + 1];
  216                         } else {
  217                                 DPRINTF(("nr of fields in %s sentence exceeds "
  218                                     "maximum of %d\n", fld[0], MAXFLDS));
  219                                 return;
  220                         }
  221                         break;
  222                 default:
  223                         cksum ^= np->cbuf[n];
  224                 }
  225         }
  226 
  227         /* if we have a checksum, verify it */
  228         if (cs != NULL) {
  229                 msgcksum = 0;
  230                 while (*cs) {
  231                         if ((*cs >= '0' && *cs <= '9') ||
  232                             (*cs >= 'A' && *cs <= 'F')) {
  233                                 if (msgcksum)
  234                                         msgcksum <<= 4;
  235                                 if (*cs >= '0' && *cs<= '9')
  236                                         msgcksum += *cs - '0';
  237                                 else if (*cs >= 'A' && *cs <= 'F')
  238                                         msgcksum += 10 + *cs - 'A';
  239                                 cs++;
  240                         } else {
  241                                 DPRINTF(("bad char %c in checksum\n", *cs));
  242                                 return;
  243                         }
  244                 }
  245                 if (msgcksum != cksum) {
  246                         DPRINTF(("checksum mismatch\n"));
  247                         return;
  248                 }
  249         }
  250 
  251         /* check message type */
  252         if (!strcmp(fld[0], "GPRMC"))
  253                 nmea_gprmc(np, tp, fld, fldcnt);
  254 }
  255 
  256 /* Decode the recommended minimum specific GPS/TRANSIT data */
  257 void
  258 nmea_gprmc(struct nmea *np, struct tty *tp, char *fld[], int fldcnt)
  259 {
  260         int64_t date_nano, time_nano, nmea_now;
  261 
  262         if (fldcnt != 12 && fldcnt != 13) {
  263                 DPRINTF(("gprmc: field count mismatch, %d\n", fldcnt));
  264                 return;
  265         }
  266         if (nmea_time_to_nano(fld[1], &time_nano)) {
  267                 DPRINTF(("gprmc: illegal time, %s\n", fld[1]));
  268                 return;
  269         }
  270         if (nmea_date_to_nano(fld[9], &date_nano)) {
  271                 DPRINTF(("gprmc: illegal date, %s\n", fld[9]));
  272                 return;
  273         }
  274         nmea_now = date_nano + time_nano;
  275         if (nmea_now <= np->last) {
  276                 DPRINTF(("gprmc: time not monotonically increasing\n"));
  277                 return;
  278         }
  279         np->last = nmea_now;
  280         np->gap = 0LL;
  281 #ifdef NMEA_DEBUG
  282         np->gapno = 0;
  283         if (nmeadebug > 0) {
  284                 linesw[TTYDISC].l_rint('[', tp);
  285                 linesw[TTYDISC].l_rint('C', tp);
  286                 linesw[TTYDISC].l_rint(']', tp);
  287         }
  288 #endif
  289 
  290         np->time.value = np->ts.tv_sec * 1000000000LL +
  291             np->ts.tv_nsec - nmea_now;
  292         np->time.tv.tv_sec = np->ts.tv_sec;
  293         np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
  294         if (np->time.status == SENSOR_S_UNKNOWN) {
  295                 np->time.status = SENSOR_S_OK;
  296                 np->time.flags &= ~SENSOR_FINVALID;
  297                 if (fldcnt != 13)
  298                         strlcpy(np->time.desc, "GPS", sizeof(np->time.desc));
  299         }
  300         if (fldcnt == 13 && *fld[12] != np->mode) {
  301                 np->mode = *fld[12];
  302                 switch (np->mode) {
  303                 case 'S':
  304                         strlcpy(np->time.desc, "GPS simulated",
  305                             sizeof(np->time.desc));
  306                         break;
  307                 case 'E':
  308                         strlcpy(np->time.desc, "GPS estimated",
  309                             sizeof(np->time.desc));
  310                         break;
  311                 case 'A':
  312                         strlcpy(np->time.desc, "GPS autonomous",
  313                             sizeof(np->time.desc));
  314                         break;
  315                 case 'D':
  316                         strlcpy(np->time.desc, "GPS differential",
  317                             sizeof(np->time.desc));
  318                         break;
  319                 case 'N':
  320                         strlcpy(np->time.desc, "GPS not valid",
  321                             sizeof(np->time.desc));
  322                         break;
  323                 default:
  324                         strlcpy(np->time.desc, "GPS unknown",
  325                             sizeof(np->time.desc));
  326                         DPRINTF(("gprmc: unknown mode '%c'\n", np->mode));
  327                 }
  328         }
  329         switch (*fld[2]) {
  330         case 'A':
  331                 np->time.status = SENSOR_S_OK;
  332                 break;
  333         case 'V':
  334                 np->time.status = SENSOR_S_WARN;
  335                 break;
  336         default:
  337                 DPRINTF(("gprmc: unknown warning indication\n"));
  338         }
  339 
  340         /*
  341          * If tty timestamping is requested, but not PPS signal is present, set
  342          * the sensor state to CRITICAL.
  343          */
  344         if (np->no_pps)
  345                 np->time.status = SENSOR_S_CRIT;
  346 }
  347 
  348 /*
  349  * Convert a NMEA 0183 formatted date string to seconds since the epoch.
  350  * The string must be of the form DDMMYY.
  351  * Return 0 on success, -1 if illegal characters are encountered.
  352  */
  353 int
  354 nmea_date_to_nano(char *s, int64_t *nano)
  355 {
  356         struct clock_ymdhms ymd;
  357         time_t secs;
  358         char *p;
  359         int n;
  360 
  361         /* make sure the input contains only numbers and is six digits long */
  362         for (n = 0, p = s; n < 6 && *p && *p >= '0' && *p <= '9'; n++, p++)
  363                 ;
  364         if (n != 6 || (*p != '\0'))
  365                 return -1;
  366 
  367         ymd.dt_year = 2000 + (s[4] - '0') * 10 + (s[5] - '0');
  368         ymd.dt_mon = (s[2] - '0') * 10 + (s[3] - '0');
  369         ymd.dt_day = (s[0] - '0') * 10 + (s[1] - '0');
  370         ymd.dt_hour = ymd.dt_min = ymd.dt_sec = 0;
  371 
  372         secs = clock_ymdhms_to_secs(&ymd);
  373         *nano = secs * 1000000000LL;
  374         return 0;
  375 }
  376 
  377 /*
  378  * Convert NMEA 0183 formatted time string to nanoseconds since midnight.
  379  * The string must be of the form HHMMSS[.[sss]] (e.g. 143724 or 143723.615).
  380  * Return 0 on success, -1 if illegal characters are encountered.
  381  */
  382 int
  383 nmea_time_to_nano(char *s, int64_t *nano)
  384 {
  385         long fac = 36000L, div = 6L, secs = 0L, frac = 0L;
  386         char ul = '2';
  387         int n;
  388 
  389         for (n = 0, secs = 0; fac && *s && *s >= '0' && *s <= ul; s++, n++) {
  390                 secs += (*s - '0') * fac;
  391                 div = 16 - div;
  392                 fac /= div;
  393                 switch (n) {
  394                 case 0:
  395                         if (*s <= '1')
  396                                 ul = '9';
  397                         else
  398                                 ul = '3';
  399                         break;
  400                 case 1:
  401                 case 3:
  402                         ul = '5';
  403                         break;
  404                 case 2:
  405                 case 4:
  406                         ul = '9';
  407                         break;
  408                 }
  409         }
  410         if (fac)
  411                 return -1;
  412 
  413         /* Handle the fractions of a second, up to a maximum of 6 digits. */
  414         div = 1L;
  415         if (*s == '.') {
  416                 for (++s; div < 1000000 && *s && *s >= '0' && *s <= '9'; s++) {
  417                         frac *= 10;
  418                         frac += (*s - '0');
  419                         div *= 10;
  420                 }
  421         }
  422 
  423         if (*s != '\0')
  424                 return -1;
  425 
  426         *nano = secs * 1000000000LL + (int64_t)frac * (1000000000 / div);
  427         return 0;
  428 }

/* [<][>][^][v][top][bottom][index][help] */