On Sat, Nov 03, 2018 at 05:00:47PM +0100, Landry Breuil wrote:
> Hi,

New diff adding speed and # of satellites, uses SENSOR_VELOCITY as
posted in the previous diff, and relies on SENSOR_DISTANCE being shown
as meters. I redid the message type matching to account for
constellation types which was commented out in the previous diff. More
proofreading welcome, especially in the conversions and the handrolled
atoi implem...

That gives:

nmea0.indicator0                                   On    OK    Signal
nmea0.raw0                                      5 raw    OK    Nb satellites
nmea0.raw1                                      1 raw    OK    GPS fix
nmea0.raw2                                   2030 raw WARNING  HDOP
nmea0.raw3                                   2330 raw WARNING  VDOP
nmea0.raw4                                   3080 raw WARNING  PDOP
nmea0.timedelta0                            30.311 ms    OK    GPS autonomous
nmea0.angle0                          xx.yyyy degrees    OK    Latitude
nmea0.angle1                           z.wwww degrees    OK    Longitude
nmea0.distance0                           381.00000 m    OK    Altitude
nmea0.velocity0                             0.319 m/s    OK    Ground speed

I need to do more velocity testing when moving.. as GPS is not reliable
when *not* moving :)

When there's no signal (ie, staying inside without satellite coverage)

nmea0.indicator0                                  Off CRITICAL Signal
nmea0.raw0                                      0 raw    OK    Nb satellites
nmea0.raw1                                      0 raw CRITICAL Invalid
nmea0.raw2                                  99990 raw WARNING  HDOP
nmea0.raw3                                  99990 raw WARNING  VDOP
nmea0.raw4                                  99990 raw WARNING  PDOP
nmea0.timedelta0                            27.195 ms    OK    GPS invalid
nmea0.angle0                          xx.yyyy degrees WARNING  Latitude
nmea0.angle1                           z.wwww degrees WARNING  Longitude
nmea0.distance0                             0.00000 m WARNING  Altitude
nmea0.velocity0                             0.000 m/s WARNING  Ground speed

Most statuses should just be CRITICAL, and i think in that case the position
is 'last known'.

Landry
Index: kern/tty_nmea.c
===================================================================
RCS file: /cvs/src/sys/kern/tty_nmea.c,v
retrieving revision 1.47
diff -u -r1.47 tty_nmea.c
--- kern/tty_nmea.c     1 Sep 2018 06:09:26 -0000       1.47
+++ kern/tty_nmea.c     3 Nov 2018 19:08:47 -0000
@@ -29,6 +29,10 @@
 #ifdef NMEA_DEBUG
 #define DPRINTFN(n, x) do { if (nmeadebug > (n)) printf x; } while (0)
 int nmeadebug = 0;
+/*
+ * 1 = print interesting messages
+ * 2 = print all messages
+ */
 #else
 #define DPRINTFN(n, x)
 #endif
@@ -52,6 +56,13 @@
        struct ksensor          signal;         /* signal status */
        struct ksensor          latitude;
        struct ksensor          longitude;
+       struct ksensor          altitude;
+       struct ksensor          speed;
+       struct ksensor          nsat;
+       struct ksensor          quality;
+       struct ksensor          hdop;
+       struct ksensor          vdop;
+       struct ksensor          pdop;
        struct ksensordev       timedev;
        struct timespec         ts;             /* current timestamp */
        struct timespec         lts;            /* timestamp of last '$' seen */
@@ -70,6 +81,8 @@
 /* NMEA decoding */
 void   nmea_scan(struct nmea *, struct tty *);
 void   nmea_gprmc(struct nmea *, struct tty *, char *fld[], int fldcnt);
+void   nmea_decode_gsa(struct nmea *, struct tty *, char *fld[], int fldcnt);
+void   nmea_decode_gga(struct nmea *, struct tty *, char *fld[], int fldcnt);
 
 /* date and time conversion */
 int    nmea_date_to_nano(char *s, int64_t *nano);
@@ -77,6 +90,7 @@
 
 /* longitude and latitude conversion */
 int    nmea_degrees(int64_t *dst, char *src, int neg);
+int    nmea_atoi(int64_t *dst, char *src, int decimal);
 
 /* degrade the timedelta sensor */
 void   nmea_timeout(void *);
@@ -126,6 +140,55 @@
        strlcpy(np->longitude.desc, "Longitude", sizeof(np->longitude.desc));
        sensor_attach(&np->timedev, &np->longitude);
 
+       np->altitude.type = SENSOR_DISTANCE;
+       np->altitude.status = SENSOR_S_UNKNOWN;
+       np->altitude.flags = SENSOR_FINVALID;
+       np->altitude.value = 0;
+       strlcpy(np->altitude.desc, "Altitude", sizeof(np->altitude.desc));
+       sensor_attach(&np->timedev, &np->altitude);
+
+       np->speed.type = SENSOR_VELOCITY;
+       np->speed.status = SENSOR_S_UNKNOWN;
+       np->speed.flags = SENSOR_FINVALID;
+       np->speed.value = 0;
+       strlcpy(np->speed.desc, "Ground speed", sizeof(np->speed.desc));
+       sensor_attach(&np->timedev, &np->speed);
+
+       np->nsat.type = SENSOR_INTEGER;
+       np->nsat.status = SENSOR_S_UNKNOWN;
+       np->nsat.flags = SENSOR_FINVALID;
+       np->nsat.value = 0;
+       strlcpy(np->nsat.desc, "Nb satellites", sizeof(np->nsat.desc));
+       sensor_attach(&np->timedev, &np->nsat);
+
+       np->quality.type = SENSOR_INTEGER;
+       np->quality.status = SENSOR_S_UNKNOWN;
+       np->quality.flags = SENSOR_FINVALID;
+       np->quality.value = 0;
+       strlcpy(np->quality.desc, "Fix Quality", sizeof(np->quality.desc));
+       sensor_attach(&np->timedev, &np->quality);
+
+       np->hdop.type = SENSOR_INTEGER;
+       np->hdop.status = SENSOR_S_UNKNOWN;
+       np->hdop.flags = SENSOR_FINVALID;
+       np->hdop.value = 0;
+       strlcpy(np->hdop.desc, "HDOP", sizeof(np->hdop.desc));
+       sensor_attach(&np->timedev, &np->hdop);
+
+       np->vdop.type = SENSOR_INTEGER;
+       np->vdop.status = SENSOR_S_UNKNOWN;
+       np->vdop.flags = SENSOR_FINVALID;
+       np->vdop.value = 0;
+       strlcpy(np->vdop.desc, "VDOP", sizeof(np->vdop.desc));
+       sensor_attach(&np->timedev, &np->vdop);
+
+       np->pdop.type = SENSOR_INTEGER;
+       np->pdop.status = SENSOR_S_UNKNOWN;
+       np->pdop.flags = SENSOR_FINVALID;
+       np->pdop.value = 0;
+       strlcpy(np->pdop.desc, "PDOP", sizeof(np->pdop.desc));
+       sensor_attach(&np->timedev, &np->pdop);
+
        np->sync = 1;
        tp->t_sc = (caddr_t)np;
 
@@ -182,7 +245,7 @@
                np->ts.tv_nsec = ts.tv_nsec;
 
 #ifdef NMEA_DEBUG
-               if (nmeadebug > 0) {
+               if (nmeadebug > 1) {
                        linesw[TTYDISC].l_rint('[', tp);
                        linesw[TTYDISC].l_rint('0' + np->gapno++, tp);
                        linesw[TTYDISC].l_rint(']', tp);
@@ -261,7 +324,7 @@
        }
 
        /*
-        * we only look at the RMC message, which can come from different 
'talkers',
+        * we only look at the messages coming from well-known sources or 
'talkers',
         * distinguished by the two-chars prefix, the most common being:
         * GPS (GP)
         * Glonass (GL)
@@ -269,11 +332,17 @@
         * Galileo (GA)
         * 'Any kind/a mix of GNSS systems' (GN)
         */
-       if (strcmp(fld[0], "BDRMC") &&
-           strcmp(fld[0], "GARMC") &&
-           strcmp(fld[0], "GLRMC") &&
-           strcmp(fld[0], "GNRMC") &&
-           strcmp(fld[0], "GPRMC"))
+       if (strncmp(fld[0], "BD", 2) &&
+           strncmp(fld[0], "GA", 2) &&
+           strncmp(fld[0], "GL", 2) &&
+           strncmp(fld[0], "GN", 2) &&
+           strncmp(fld[0], "GP", 2))
+               return;
+               
+       /* we look for the RMC, GSA & GGA messages */
+       if (strncmp(fld[0] + 2, "RMC", 3) &&
+           strncmp(fld[0] + 2, "GSA", 3) &&
+           strncmp(fld[0] + 2, "GGA", 3))
                return;
 
        /* if we have a checksum, verify it */
@@ -299,7 +368,12 @@
                        return;
                }
        }
-       nmea_gprmc(np, tp, fld, fldcnt);
+       if (strncmp(fld[0] + 2, "RMC", 3) == 0)
+               nmea_gprmc(np, tp, fld, fldcnt);
+       if (strncmp(fld[0] + 2, "GSA", 3) == 0)
+               nmea_decode_gsa(np, tp, fld, fldcnt);
+       if (strncmp(fld[0] + 2, "GGA", 3) == 0)
+               nmea_decode_gga(np, tp, fld, fldcnt);
 }
 
 /* Decode the recommended minimum specific GPS/TRANSIT data. */
@@ -385,9 +459,11 @@
                np->signal.status = SENSOR_S_OK;
                np->latitude.status = SENSOR_S_OK;
                np->longitude.status = SENSOR_S_OK;
+               np->speed.status = SENSOR_S_OK;
                np->time.flags &= ~SENSOR_FINVALID;
                np->latitude.flags &= ~SENSOR_FINVALID;
                np->longitude.flags &= ~SENSOR_FINVALID;
+               np->speed.flags &= ~SENSOR_FINVALID;
                break;
        case 'V':       /*
                         * The GPS indicates a warning status, do not add to
@@ -399,6 +475,7 @@
                np->signal.status = SENSOR_S_CRIT;
                np->latitude.status = SENSOR_S_WARN;
                np->longitude.status = SENSOR_S_WARN;
+               np->speed.status = SENSOR_S_WARN;
                break;
        }
        if (nmea_degrees(&np->latitude.value, fld[3], *fld[4] == 'S' ? 1 : 0))
@@ -406,6 +483,11 @@
        if (nmea_degrees(&np->longitude.value,fld[5], *fld[6] == 'W' ? 1 : 0))
                np->longitude.status = SENSOR_S_WARN;
 
+       if (nmea_atoi(&np->speed.value, fld[7], 1))
+               np->speed.status = SENSOR_S_WARN;
+       /* convert from knot to um/s */
+       np->speed.value *= (1000 / 1.9438);
+
        if (jumped)
                np->time.status = SENSOR_S_WARN;
        if (np->time.status == SENSOR_S_OK)
@@ -418,6 +500,120 @@
                np->time.status = SENSOR_S_CRIT;
 }
 
+/* Decode the GPS fix data for altitude and fix quality. */
+void
+nmea_decode_gga(struct nmea *np, struct tty *tp, char *fld[], int fldcnt)
+{
+       if (fldcnt != 14 && fldcnt != 15) {
+               DPRINTF(("GGA: field count mismatch, %d\n", fldcnt));
+               return;
+       }
+#ifdef NMEA_DEBUG
+       if (nmeadebug > 0) {
+               linesw[TTYDISC].l_rint('[', tp);
+               linesw[TTYDISC].l_rint('C', tp);
+               linesw[TTYDISC].l_rint(']', tp);
+       }
+#endif
+
+       np->altitude.status = SENSOR_S_OK;
+       if (nmea_atoi(&np->altitude.value, fld[9], 1))
+               np->altitude.status = SENSOR_S_WARN;
+
+       /* convert to uMeter */
+       np->altitude.value *= 1000;
+       np->altitude.flags &= ~SENSOR_FINVALID;
+
+       np->nsat.status = SENSOR_S_OK;
+       if (nmea_atoi(&np->nsat.value, fld[7], 0))
+               np->nsat.status = SENSOR_S_WARN;
+       np->nsat.flags &= ~SENSOR_FINVALID;
+
+       np->quality.status = SENSOR_S_OK;
+       np->quality.flags &= ~SENSOR_FINVALID;
+       np->quality.value = *fld[6] - '0';
+
+       switch (np->quality.value) {
+       case 2:
+               strlcpy(np->quality.desc, "DGPS fix",
+                   sizeof(np->quality.desc));
+               break;
+       case 1:
+               strlcpy(np->quality.desc, "GPS fix",
+                   sizeof(np->quality.desc));
+               break;
+       case 0:
+       default :
+               np->quality.status = SENSOR_S_CRIT;
+               strlcpy(np->quality.desc, "Invalid",
+                   sizeof(np->quality.desc));
+               break;
+       }
+}
+
+/* Decode the GPS DOP data. */
+void
+nmea_decode_gsa(struct nmea *np, struct tty *tp, char *fld[], int fldcnt)
+{
+       if (fldcnt != 17 && fldcnt != 18) {
+               DPRINTF(("GSA: field count mismatch, %d\n", fldcnt));
+               return;
+       }
+#ifdef NMEA_DEBUG
+       if (nmeadebug > 0) {
+               linesw[TTYDISC].l_rint('[', tp);
+               linesw[TTYDISC].l_rint('C', tp);
+               linesw[TTYDISC].l_rint(']', tp);
+       }
+#endif
+       np->hdop.status = SENSOR_S_OK;
+       if (nmea_atoi(&np->hdop.value, fld[16], 1) || np->hdop.value > 2000)
+               np->hdop.status = SENSOR_S_WARN;
+       np->hdop.flags &= ~SENSOR_FINVALID;
+
+       np->vdop.status = SENSOR_S_OK;
+       if (nmea_atoi(&np->vdop.value, fld[17], 1) || np->vdop.value > 2000)
+               np->vdop.status = SENSOR_S_WARN;
+       np->vdop.flags &= ~SENSOR_FINVALID;
+
+       np->pdop.status = SENSOR_S_OK;
+       if (nmea_atoi(&np->pdop.value, fld[15], 1) || np->pdop.value > 2000)
+               np->pdop.status = SENSOR_S_WARN;
+       np->pdop.flags &= ~SENSOR_FINVALID;
+}
+
+/*
+ * Convert nmea integer/decimal values in the form of XXXX.Y to an integer 
value
+ * if it's a meter/altitude value, will be returned as mm
+ */
+int
+nmea_atoi(int64_t *dst, char *src, int decimal)
+{
+       char *p;
+       int i = 3; /* take 3 digits */
+       *dst = 0;
+
+       for (p = src; *p && *p != '.' && *p >= '0' && *p <= '9' ; )
+               *dst = *dst * 10 + (*p++ - '0');
+
+       if (! decimal)
+               return (0);
+       /* *p should be '.' at that point */
+       if (*p != '.')
+               return (-1);    /* no decimal point, or bogus value ? */
+       p++;
+
+       /* read digits after decimal point */
+       for (; *p && i > 0 && *p >= '0' && *p <= '9' ; i--)
+               *dst = *dst * 10 + (*p++ - '0');
+
+       for (; i > 0 ; i--)
+               *dst *= 10;
+
+       DPRINTFN(2,("%s -> %lld\n", src, *dst));
+       return 0;
+}
+
 /*
  * Convert a nmea position in the form DDDMM.MMMM to an
  * angle sensor value (degrees*1000000)
@@ -557,6 +753,13 @@
                np->time.status = SENSOR_S_WARN;
                np->latitude.status = SENSOR_S_WARN;
                np->longitude.status = SENSOR_S_WARN;
+               np->altitude.status = SENSOR_S_WARN;
+               np->speed.status = SENSOR_S_WARN;
+               np->nsat.status = SENSOR_S_WARN;
+               np->hdop.status = SENSOR_S_WARN;
+               np->vdop.status = SENSOR_S_WARN;
+               np->pdop.status = SENSOR_S_WARN;
+               np->quality.status = SENSOR_S_WARN;
                /*
                 * further degrade in TRUSTTIME seconds if no new valid NMEA
                 * sentences are received.
@@ -566,5 +769,12 @@
                np->time.status = SENSOR_S_CRIT;
                np->latitude.status = SENSOR_S_CRIT;
                np->longitude.status = SENSOR_S_CRIT;
+               np->altitude.status = SENSOR_S_CRIT;
+               np->speed.status = SENSOR_S_CRIT;
+               np->nsat.status = SENSOR_S_CRIT;
+               np->hdop.status = SENSOR_S_CRIT;
+               np->vdop.status = SENSOR_S_CRIT;
+               np->pdop.status = SENSOR_S_CRIT;
+               np->quality.status = SENSOR_S_CRIT;
        }
 }

Reply via email to