Hi, so now that the sensor types got adjusted, here's the tty_nmea.c diff again, adding 7 new sensors: - # satellites (raw) - GPS/DGPS fix (or not) (raw) - HDOP/VDOP/PDOP (raw) - Altitude (m) - Speed (m/s)
i'd welcome eyes on the logic, and especially on the nmea_atoi() implementation - it is inspired by nmea_degrees() just below but it feels awkward. I can of course only add altitude/speed as a start.. And testing with other nmea devices would be great ! So far i've tested this in various static locations and recording data points when driving, and it matched the gps data from my smartphone. Landry
Index: tty_nmea.c =================================================================== RCS file: /cvs/src/sys/kern/tty_nmea.c,v retrieving revision 1.47 diff -u -r1.47 tty_nmea.c --- tty_nmea.c 1 Sep 2018 06:09:26 -0000 1.47 +++ tty_nmea.c 28 Dec 2018 20:47:20 -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; } }