this converts the disabled NMEA_POS_IN_DESC code into an angle
sensor.  so far, it's only tested with gpsfeed+ (a simulator) looped
back to another serial port.

any comments/suggestions (especially about the slightly hairy
string conversion code)?

other data (including track angle, available in fld[8]) might be
useful too - but one thing at a time...

Index: share/man/man4/nmea.4
===================================================================
RCS file: /cvs/src/share/man/man4/nmea.4,v
retrieving revision 1.22
diff -u -p -r1.22 nmea.4
--- share/man/man4/nmea.4       6 May 2008 09:01:08 -0000       1.22
+++ share/man/man4/nmea.4       21 Apr 2010 22:54:18 -0000
@@ -34,10 +34,11 @@ ioctl(fildes, TIOCSETD, &ldisc);
 .Ed
 .Pp
 The byte stream is unaltered by the line discipline which
-maintains a timedelta sensor using the NMEA data.
-The timedelta sensor will appear as nmea* in the list of sensors and the delta
-(in nanoseconds) between the received time information and the local time can
-be accessed through the
+maintains timedelta and position sensors using the NMEA data.
+The sensors will appear as nmea* in the list.
+The timedelta (nanoseconds difference between the received time
+information and the local time), and position (calculated
+latitude and longitude in degrees) can be accessed through the
 .Xr sysctl 8
 interface.
 .Sh NMEA SENTENCES
@@ -47,7 +48,7 @@ line discipline decodes the following NM
 .Bl -tag -width "GPRMCXX"
 .It GPRMC
 Recommended Minimum Specific GPS/TRANSIT Data.
-Only the time and date information is extracted.
+The time and date information and position are extracted.
 The warning indication is used to provide the sensor status (see below).
 If the attached device sends the GPRMC message in the 13-field format,
 the operation mode of the GPS device is reported in the sensor description.
@@ -60,7 +61,7 @@ a message block is received from the NME
 The quality of the timedelta is reported as the sensor status:
 .Bl -tag -width "CRITICALXX" -offset indent
 .It OK
-The time information is valid.
+The time information and position are valid.
 The timedelta is safe to use for applications like
 .Xr ntpd 8 .
 .It WARN
Index: sys/kern/tty_nmea.c
===================================================================
RCS file: /cvs/src/sys/kern/tty_nmea.c,v
retrieving revision 1.36
diff -u -p -r1.36 tty_nmea.c
--- sys/kern/tty_nmea.c 12 Apr 2010 12:57:52 -0000      1.36
+++ sys/kern/tty_nmea.c 21 Apr 2010 22:54:18 -0000
@@ -54,6 +54,8 @@ struct nmea {
        char                    cbuf[NMEAMAX];  /* receive buffer */
        struct ksensor          time;           /* the timedelta sensor */
        struct ksensor          signal;         /* signal status */
+       struct ksensor          latitude;
+       struct ksensor          longitude;
        struct ksensordev       timedev;
        struct timespec         ts;             /* current timestamp */
        struct timespec         lts;            /* timestamp of last '$' seen */
@@ -77,10 +79,8 @@ void nmea_gprmc(struct nmea *, struct tt
 int    nmea_date_to_nano(char *s, int64_t *nano);
 int    nmea_time_to_nano(char *s, int64_t *nano);
 
-#if NMEA_POS_IN_DESC
-/* longitude and latitude formatting and copying */
-void   nmea_degrees(char *dst, char *src, int neg, size_t len);
-#endif
+/* longitude and latitude conversion */
+int    nmea_degrees(int64_t *dst, char *src, int neg);
 
 /* degrade the timedelta sensor */
 void   nmea_timeout(void *);
@@ -114,6 +114,18 @@ nmeaopen(dev_t dev, struct tty *tp, stru
        strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
        sensor_attach(&np->timedev, &np->signal);
 
+       np->latitude.type = SENSOR_ANGLE;
+       np->latitude.status = SENSOR_S_UNKNOWN;
+       np->latitude.value = 0;
+       strlcpy(np->latitude.desc, "Latitude", sizeof(np->latitude.desc));
+       sensor_attach(&np->timedev, &np->latitude);
+
+       np->longitude.type = SENSOR_ANGLE;
+       np->longitude.status = SENSOR_S_UNKNOWN;
+       np->longitude.value = 0;
+       strlcpy(np->longitude.desc, "Longitude", sizeof(np->longitude.desc));
+       sensor_attach(&np->timedev, &np->longitude);
+
        np->sync = 1;
        tp->t_sc = (caddr_t)np;
 
@@ -353,16 +365,12 @@ nmea_gprmc(struct nmea *np, struct tty *
                        DPRINTF(("gprmc: unknown mode '%c'\n", np->mode));
                }
        }
-#if NMEA_POS_IN_DESC
-       nmea_degrees(np->time.desc, fld[3], *fld[4] == 'S' ? 1 : 0,
-           sizeof(np->time.desc));
-       nmea_degrees(np->time.desc, fld[5], *fld[6] == 'W' ? 1 : 0,
-           sizeof(np->time.desc));
-#endif
        switch (*fld[2]) {
        case 'A':       /* The GPS has a fix, (re)arm the timeout. */
                np->time.status = SENSOR_S_OK;
                np->signal.status = SENSOR_S_OK;
+               np->latitude.status = SENSOR_S_OK;
+               np->longitude.status = SENSOR_S_OK;
                break;
        case 'V':       /*
                         * The GPS indicates a warning status, do not add to
@@ -371,8 +379,14 @@ nmea_gprmc(struct nmea *np, struct tty *
                         * the signal sensor.
                         */
                np->signal.status = SENSOR_S_WARN;
+               np->latitude.status = SENSOR_S_WARN;
+               np->longitude.status = SENSOR_S_WARN;
                break;
        }
+       if (nmea_degrees(&np->latitude.value, fld[3], *fld[4] == 'W' ? 1 : 0))
+               np->latitude.status = SENSOR_S_WARN;
+       if (nmea_degrees(&np->longitude.value,fld[5], *fld[6] == 'S' ? 1 : 0))
+               np->longitude.status = SENSOR_S_WARN;
 
        if (jumped)
                np->time.status = SENSOR_S_WARN;
@@ -386,18 +400,18 @@ nmea_gprmc(struct nmea *np, struct tty *
                np->time.status = SENSOR_S_CRIT;
 }
 
-#ifdef NMEA_POS_IN_DESC
-/* format a nmea position in the form DDDMM.MMMM to DDDdMM.MMm */
-void
-nmea_degrees(char *dst, char *src, int neg, size_t len)
+/*
+ * Convert a nmea position in the form DDDMM.MMMM to an
+ * angle sensor value (degrees*1000000)
+ */
+int
+nmea_degrees(int64_t *dst, char *src, int neg)
 {
-       size_t dlen, ppos, rlen;
-       int n;
+       size_t ppos;
+       int i, n;
+       int64_t deg = 0, min = 0;
        char *p;
 
-       for (dlen = 0; *dst; dlen++)
-               dst++;
-
        while (*src == '0')
                ++src;  /* skip leading zeroes */
 
@@ -406,42 +420,27 @@ nmea_degrees(char *dst, char *src, int n
                        break;
 
        if (*p == '\0')
-               return; /* no decimal point */
+               return -1;      /* no decimal point */
 
-       /*
-        * we need at least room for a comma, an optional '-', the src data up
-        * to the decimal point, the decimal point itself, two digits after
-        * it and some additional characters:  an optional leading '0' in case
-        * there a no degrees in src, the 'd' degrees indicator, the 'm'
-        * minutes indicator and the terminating NUL character.
-        */
-       rlen = dlen + ppos + 7;
-       if (neg)
-               rlen++;
-       if (ppos < 3)
-               rlen++;
-       if (len < rlen)
-               return;         /* not enough room in dst */
-
-       *dst++ = ',';
-       if (neg)
-               *dst++ = '-';
+       for (n = 0; *src && n + 2 < ppos; n++)
+               deg = deg * 10 + (*src++ - '0');
 
-       if (ppos < 3)
-               *dst++ = '0';
+       for (; *src && n < ppos; n++)
+               min = min * 10 + (*src++ - '0');
 
-       for (n = 0; *src && n + 2 < ppos; n++)
-               *dst++ = *src++;
-       *dst++ = 'd';
-       if (ppos == 0)
-               *dst++ = '0';
-
-       for (; *src && n < (ppos + 3); n++)
-               *dst++ = *src++;
-       *dst++ = 'm';
-       *dst = '\0';
+       *src++;         /* skip decimal point */
+
+       for (; *src && n < (ppos + 4); n++)
+               min = min * 10 + (*src++ - '0');
+
+       for (i=0; i < 6 + ppos - n; i++)
+               min *= 10;
+
+       deg = deg * 1000000 + (min/60);
+
+       *dst = neg ? -deg : deg;
+       return 0;
 }
-#endif
 
 /*
  * Convert a NMEA 0183 formatted date string to seconds since the epoch.
@@ -536,6 +535,8 @@ nmea_timeout(void *xnp)
 
        if (np->time.status == SENSOR_S_OK) {
                np->time.status = SENSOR_S_WARN;
+               np->latitude.status = SENSOR_S_WARN;
+               np->longitude.status = SENSOR_S_WARN;
                /*
                 * further degrade in TRUSTTIME seconds if no new valid NMEA
                 * sentences are received.
@@ -543,4 +544,6 @@ nmea_timeout(void *xnp)
                timeout_add_sec(&np->nmea_tout, TRUSTTIME);
        } else
                np->time.status = SENSOR_S_CRIT;
+               np->latitude.status = SENSOR_S_CRIT;
+               np->longitude.status = SENSOR_S_CRIT;
 }

Reply via email to