From 0bfd2834f5c4401eba628b45df6735fdfaef7751 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 23 Jan 2019 00:14:23 +0100 Subject: [PATCH 1/2] Add GSV & GSA sentences to get satellite ID and SNR information Also made the parsing of the sentence type a bit more flexible to allow for more NMEA Talker IDs (Glonass, Galileo, BeiDou) --- src/TinyGPS++.cpp | 243 ++++++++++++++++++++++++++++++++++------------ src/TinyGPS++.h | 65 ++++++++++++- 2 files changed, 247 insertions(+), 61 deletions(-) diff --git a/src/TinyGPS++.cpp b/src/TinyGPS++.cpp index e1ec777..026ef73 100644 --- a/src/TinyGPS++.cpp +++ b/src/TinyGPS++.cpp @@ -27,15 +27,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include -#define _GPRMCterm "GPRMC" -#define _GPGGAterm "GPGGA" -#define _GNRMCterm "GNRMC" -#define _GNGGAterm "GNGGA" - TinyGPSPlus::TinyGPSPlus() : parity(0) , isChecksumTerm(false) , curSentenceType(GPS_SENTENCE_OTHER) + , curSentenceSystem(GPS_SYSTEM_GPS) , curTermNumber(0) , curTermOffset(0) , sentenceHasFix(false) @@ -82,6 +78,7 @@ bool TinyGPSPlus::encode(char c) curTermNumber = curTermOffset = 0; parity = 0; curSentenceType = GPS_SENTENCE_OTHER; + curSentenceSystem = GPS_SYSTEM_GPS; isChecksumTerm = false; sentenceHasFix = false; return false; @@ -190,6 +187,15 @@ bool TinyGPSPlus::endOfTermHandler() satellites.commit(); hdop.commit(); break; + case GPS_SENTENCE_GPGSV: + satellitesStats.commit(); + break; + case GPS_SENTENCE_GPGSA: + if (!satellitesStats.snrDataPresent) { + satellitesStats.commit(); + } + hdop.commit(); + break; } // Commit all custom listeners of this sentence type @@ -209,12 +215,7 @@ bool TinyGPSPlus::endOfTermHandler() // the first term determines the sentence type if (curTermNumber == 0) { - if (!strcmp(term, _GPRMCterm) || !strcmp(term, _GNRMCterm)) - curSentenceType = GPS_SENTENCE_GPRMC; - else if (!strcmp(term, _GPGGAterm) || !strcmp(term, _GNGGAterm)) - curSentenceType = GPS_SENTENCE_GPGGA; - else - curSentenceType = GPS_SENTENCE_OTHER; + parseSentenceType(term); // Any custom candidates of this sentence type? for (customCandidates = customElts; customCandidates != NULL && strcmp(customCandidates->sentenceName, term) < 0; customCandidates = customCandidates->next); @@ -224,59 +225,98 @@ bool TinyGPSPlus::endOfTermHandler() return false; } - if (curSentenceType != GPS_SENTENCE_OTHER && term[0]) + if (curSentenceType != GPS_SENTENCE_OTHER && term[0]) { switch(COMBINE(curSentenceType, curTermNumber)) - { - case COMBINE(GPS_SENTENCE_GPRMC, 1): // Time in both sentences - case COMBINE(GPS_SENTENCE_GPGGA, 1): - time.setTime(term); - break; - case COMBINE(GPS_SENTENCE_GPRMC, 2): // GPRMC validity - sentenceHasFix = term[0] == 'A'; - break; - case COMBINE(GPS_SENTENCE_GPRMC, 3): // Latitude - case COMBINE(GPS_SENTENCE_GPGGA, 2): - location.setLatitude(term); - break; - case COMBINE(GPS_SENTENCE_GPRMC, 4): // N/S - case COMBINE(GPS_SENTENCE_GPGGA, 3): - location.rawNewLatData.negative = term[0] == 'S'; - break; - case COMBINE(GPS_SENTENCE_GPRMC, 5): // Longitude - case COMBINE(GPS_SENTENCE_GPGGA, 4): - location.setLongitude(term); - break; - case COMBINE(GPS_SENTENCE_GPRMC, 6): // E/W - case COMBINE(GPS_SENTENCE_GPGGA, 5): - location.rawNewLngData.negative = term[0] == 'W'; - break; - case COMBINE(GPS_SENTENCE_GPRMC, 7): // Speed (GPRMC) - speed.set(term); - break; - case COMBINE(GPS_SENTENCE_GPRMC, 8): // Course (GPRMC) - course.set(term); - break; - case COMBINE(GPS_SENTENCE_GPRMC, 9): // Date (GPRMC) - date.setDate(term); - break; - case COMBINE(GPS_SENTENCE_GPGGA, 6): // Fix data (GPGGA) - sentenceHasFix = term[0] > '0'; - break; - case COMBINE(GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA) - satellites.set(term); - break; - case COMBINE(GPS_SENTENCE_GPGGA, 8): // HDOP - hdop.set(term); - break; - case COMBINE(GPS_SENTENCE_GPGGA, 9): // Altitude (GPGGA) - altitude.set(term); - break; + { + case COMBINE(GPS_SENTENCE_GPRMC, 1): // Time in both sentences + case COMBINE(GPS_SENTENCE_GPGGA, 1): + time.setTime(term); + break; + case COMBINE(GPS_SENTENCE_GPRMC, 2): // GPRMC validity + sentenceHasFix = term[0] == 'A'; + break; + case COMBINE(GPS_SENTENCE_GPRMC, 3): // Latitude + case COMBINE(GPS_SENTENCE_GPGGA, 2): + location.setLatitude(term); + break; + case COMBINE(GPS_SENTENCE_GPRMC, 4): // N/S + case COMBINE(GPS_SENTENCE_GPGGA, 3): + location.rawNewLatData.negative = term[0] == 'S'; + break; + case COMBINE(GPS_SENTENCE_GPRMC, 5): // Longitude + case COMBINE(GPS_SENTENCE_GPGGA, 4): + location.setLongitude(term); + break; + case COMBINE(GPS_SENTENCE_GPRMC, 6): // E/W + case COMBINE(GPS_SENTENCE_GPGGA, 5): + location.rawNewLngData.negative = term[0] == 'W'; + break; + case COMBINE(GPS_SENTENCE_GPRMC, 7): // Speed (GPRMC) + speed.set(term); + break; + case COMBINE(GPS_SENTENCE_GPRMC, 8): // Course (GPRMC) + course.set(term); + break; + case COMBINE(GPS_SENTENCE_GPRMC, 9): // Date (GPRMC) + date.setDate(term); + break; + case COMBINE(GPS_SENTENCE_GPGGA, 6): // Fix data (GPGGA) + sentenceHasFix = term[0] > '0'; + location.newFixQuality = sentenceHasFix ? (FixQuality)(term[0] - '0') : Invalid; + break; + case COMBINE(GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA) + satellites.set(term); + break; + case COMBINE(GPS_SENTENCE_GPGGA, 8): // HDOP + case COMBINE(GPS_SENTENCE_GPGSA, 16): // HDOP + hdop.set(term); + break; + case COMBINE(GPS_SENTENCE_GPGGA, 9): // Altitude (GPGGA) + altitude.set(term); + break; + case COMBINE(GPS_SENTENCE_GPRMC, 12): + location.newFixMode = (FixMode)term[0]; + break; + case COMBINE(GPS_SENTENCE_GPGSV, 2): // GSV message index + satellitesStats.setMessageSeqNr(term, curSentenceSystem); + break; + case COMBINE(GPS_SENTENCE_GPGSV, 4): // GSV satellite PRN number + case COMBINE(GPS_SENTENCE_GPGSV, 8): + case COMBINE(GPS_SENTENCE_GPGSV, 12): + case COMBINE(GPS_SENTENCE_GPGSV, 16): + satellitesStats.setSatId(term); + break; + case COMBINE(GPS_SENTENCE_GPGSV, 7): // GSV satellite SNR + case COMBINE(GPS_SENTENCE_GPGSV, 11): + case COMBINE(GPS_SENTENCE_GPGSV, 15): + case COMBINE(GPS_SENTENCE_GPGSV, 19): + satellitesStats.setSatSNR(term); + break; + } + // GSA messages have the items sequential, so handle them separately + if (curSentenceType == GPS_SENTENCE_GPGSA) + { + if (!satellitesStats.snrDataPresent) + { + // GSA messages may only be used when no GSV messages are being processed. + // Satellite IDs in GSA messages may be in different order compared to GSV messages. + if (curTermNumber >= 3 && curTermNumber <= 14) + { + satellitesStats.setSatId(term); + } + } + } } // Set custom values as needed - for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0 && p->termNumber <= curTermNumber; p = p->next) - if (p->termNumber == curTermNumber) - p->set(term); + for (TinyGPSCustom *p = customCandidates; + p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0 && p->termNumber <= curTermNumber; + p = p->next) + { + if (p->termNumber == curTermNumber) { + p->set(term); + } + } return false; } @@ -366,6 +406,23 @@ double TinyGPSLocation::lng() return rawLngData.negative ? -ret : ret; } +void TinyGPSSatellites::commit() +{ + satsUsed = 0; + bestSNR = 0; + for (byte i = 0; i < _GPS_MAX_ARRAY_LENGTH; ++i) { + if ((id[i] != 0) && (snrDataPresent || (snr[i] != 0))) { + ++satsUsed; + if (snr[i] > bestSNR) { + bestSNR = snr[i]; + } + } + } + pos = -1; + lastCommitTime = millis(); + valid = updated = true; +} + void TinyGPSDate::commit() { date = newDate; @@ -380,6 +437,40 @@ void TinyGPSTime::commit() valid = updated = true; } +void TinyGPSSatellites::setSatId(const char *term) +{ + if (pos < _GPS_MAX_ARRAY_LENGTH) { + ++pos; + uint32_t value = atol(term); + if (id[pos] != value) { + id[pos] = static_cast(value); + snr[pos] = 0; + } + } +} + +void TinyGPSSatellites::setSatSNR(const char *term) +{ + if (pos < _GPS_MAX_ARRAY_LENGTH) { + // Do not increase pos, GSV line has ID first and other parameters later + snr[pos] = atol(term); + snrDataPresent = true; + } +} + +void TinyGPSSatellites::setMessageSeqNr(const char *term, uint8_t sentenceSystem) +{ + int32_t seqNr = atol(term); + int32_t newPos = (seqNr - 1) * 4 + (sentenceSystem * _GPS_MAX_NR_ACTIVE_SATELLITES); + if (newPos >= 0 && newPos < _GPS_MAX_ARRAY_LENGTH) { + for (byte i = newPos; i < (newPos + 4) && i < _GPS_MAX_ARRAY_LENGTH; ++i) { + id[i] = 0; + snr[i] = 0; + } + pos = static_cast(newPos - 1); // setSatId will increment pos first. + } +} + void TinyGPSTime::setTime(const char *term) { newTime = (uint32_t)TinyGPSPlus::parseDecimal(term); @@ -487,6 +578,38 @@ void TinyGPSCustom::set(const char *term) strncpy(this->stagingBuffer, term, sizeof(this->stagingBuffer)); } +void TinyGPSPlus::parseSentenceType(const char *term) +{ + curSentenceType = GPS_SENTENCE_OTHER; + curSentenceSystem = GPS_SYSTEM_GPS; + size_t length = strlen(term); + if (length < 5 || term[0] != 'G') { + return; + } + switch (term[1]) { + case 'L': curSentenceSystem = GPS_SYSTEM_GLONASS; break; + case 'A': curSentenceSystem = GPS_SYSTEM_GALILEO; break; + case 'B': curSentenceSystem = GPS_SYSTEM_BEIDOU; break; + } + if (strcmp(&term[2], "RMC") == 0) + { + curSentenceType = GPS_SENTENCE_GPRMC; + } + else if (strcmp(&term[2], "GGA") == 0) + { + curSentenceType = GPS_SENTENCE_GPGGA; + } + else if (strcmp(&term[2], "GSA") == 0) + { + curSentenceType = GPS_SENTENCE_GPGSA; + } + else if (strcmp(&term[2], "GSV") == 0) + { + curSentenceType = GPS_SENTENCE_GPGSV; + } +} + + void TinyGPSPlus::insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int termNumber) { TinyGPSCustom **ppelt; diff --git a/src/TinyGPS++.h b/src/TinyGPS++.h index 60485cb..135da74 100644 --- a/src/TinyGPS++.h +++ b/src/TinyGPS++.h @@ -39,6 +39,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define _GPS_KM_PER_METER 0.001 #define _GPS_FEET_PER_METER 3.2808399 #define _GPS_MAX_FIELD_SIZE 15 +#define _GPS_MAX_NR_ACTIVE_SATELLITES 12 // NMEA allows upto 12, some receivers report more using GSV strings +//#define _GPS_MAX_NR_SYSTEMS 3 // GPS, GLONASS, GALILEO +#define _GPS_MAX_NR_SYSTEMS 2 // GPS, GLONASS +#define _GPS_MAX_ARRAY_LENGTH (_GPS_MAX_NR_ACTIVE_SATELLITES * _GPS_MAX_NR_SYSTEMS) struct RawDegrees { @@ -74,6 +78,47 @@ struct TinyGPSLocation void setLongitude(const char *term); }; +struct TinyGPSSatellites +{ + friend class TinyGPSPlus; +public: + bool isValid() const { return valid; } + bool isUpdated() const { return updated; } + uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } + uint8_t nrSatsUsed() const { return satsUsed; } + uint8_t getBestSNR() const { return bestSNR; } + + TinyGPSSatellites() : valid(false), updated(false), pos(-1), bestSNR(0), satsUsed(0), snrDataPresent(false) + {} + + uint8_t id[_GPS_MAX_ARRAY_LENGTH] = {0}; + uint8_t snr[_GPS_MAX_ARRAY_LENGTH] = {0}; + +private: + bool valid, updated; + /* + Satellite IDs: + - 01 ~ 32 are for GPS + - 33 ~ 64 are for SBAS (PRN minus 87) + - 65 ~ 96 are for GLONASS (64 plus slot numbers) + - 193 ~ 197 are for QZSS + - 01 ~ 37 are for Beidou (BD PRN). + GPS and Beidou satellites are differentiated by the GP and BD prefix. + */ + int8_t pos; // Use signed int, only increase pos to set satellite ID + uint8_t bestSNR; + uint8_t satsUsed; + bool snrDataPresent; + uint32_t lastCommitTime; + void commit(); + void setSatId(const char *term); + void setSatSNR(const char *term); + + // GSV messages form a sequence, so the initial position in the array must + // be set before inserting values. + void setMessageSeqNr(const char *term, uint8_t sentenceSystem); +}; + struct TinyGPSDate { friend class TinyGPSPlus; @@ -231,6 +276,7 @@ class TinyGPSPlus TinyGPSAltitude altitude; TinyGPSInteger satellites; TinyGPSHDOP hdop; + TinyGPSSatellites satellitesStats; static const char *libraryVersion() { return _GPS_VERSION; } @@ -247,13 +293,30 @@ class TinyGPSPlus uint32_t passedChecksum() const { return passedChecksumCount; } private: - enum {GPS_SENTENCE_GPGGA, GPS_SENTENCE_GPRMC, GPS_SENTENCE_OTHER}; + enum { + GPS_SENTENCE_GPGGA, // GGA - Global Positioning System Fix Data + GPS_SENTENCE_GPRMC, // RMC - Recommended Minimum Navigation Information + + GPS_SENTENCE_GPGSA, // GSA - GPS DOP and active satellites + GPS_SENTENCE_GPGSV, // GSV - Satellites in view + + GPS_SENTENCE_OTHER}; + + enum { + GPS_SYSTEM_GPS = 0, // GP: GPS, SBAS, QZSS & GN: Any combination of GNSS + GPS_SYSTEM_GLONASS, + GPS_SYSTEM_GALILEO, + GPS_SYSTEM_BEIDOU + }; + + void parseSentenceType(const char *term); // parsing state variables uint8_t parity; bool isChecksumTerm; char term[_GPS_MAX_FIELD_SIZE]; uint8_t curSentenceType; + uint8_t curSentenceSystem; uint8_t curTermNumber; uint8_t curTermOffset; bool sentenceHasFix; From 97d809e1d5c69ebbf18c43004d641cc67b960814 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 24 Jan 2019 14:52:30 +0100 Subject: [PATCH 2/2] Distinguish between tracked and visible satellites - Renamed 'used' to 'tracked' - Count nr sats with id <> 0 and SNR > 0 as being tracked - Count nr sats with id <> 0 as being visible. --- src/TinyGPS++.cpp | 14 +++++++++----- src/TinyGPS++.h | 11 +++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/TinyGPS++.cpp b/src/TinyGPS++.cpp index 026ef73..1248f55 100644 --- a/src/TinyGPS++.cpp +++ b/src/TinyGPS++.cpp @@ -408,14 +408,18 @@ double TinyGPSLocation::lng() void TinyGPSSatellites::commit() { - satsUsed = 0; + satsTracked = 0; + satsVisible = 0; bestSNR = 0; for (byte i = 0; i < _GPS_MAX_ARRAY_LENGTH; ++i) { - if ((id[i] != 0) && (snrDataPresent || (snr[i] != 0))) { - ++satsUsed; - if (snr[i] > bestSNR) { - bestSNR = snr[i]; + if (id[i] != 0) { + if (snr[i] != 0) { + ++satsTracked; + if (snr[i] > bestSNR) { + bestSNR = snr[i]; + } } + ++satsVisible; } } pos = -1; diff --git a/src/TinyGPS++.h b/src/TinyGPS++.h index 135da74..ea14e3d 100644 --- a/src/TinyGPS++.h +++ b/src/TinyGPS++.h @@ -39,7 +39,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define _GPS_KM_PER_METER 0.001 #define _GPS_FEET_PER_METER 3.2808399 #define _GPS_MAX_FIELD_SIZE 15 -#define _GPS_MAX_NR_ACTIVE_SATELLITES 12 // NMEA allows upto 12, some receivers report more using GSV strings +#define _GPS_MAX_NR_ACTIVE_SATELLITES 16 // NMEA allows upto 12, some receivers report more using GSV strings //#define _GPS_MAX_NR_SYSTEMS 3 // GPS, GLONASS, GALILEO #define _GPS_MAX_NR_SYSTEMS 2 // GPS, GLONASS #define _GPS_MAX_ARRAY_LENGTH (_GPS_MAX_NR_ACTIVE_SATELLITES * _GPS_MAX_NR_SYSTEMS) @@ -85,10 +85,11 @@ struct TinyGPSSatellites bool isValid() const { return valid; } bool isUpdated() const { return updated; } uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } - uint8_t nrSatsUsed() const { return satsUsed; } + uint8_t nrSatsTracked() const { return satsTracked; } + uint8_t nrSatsVisible() const { return satsVisible; } uint8_t getBestSNR() const { return bestSNR; } - TinyGPSSatellites() : valid(false), updated(false), pos(-1), bestSNR(0), satsUsed(0), snrDataPresent(false) + TinyGPSSatellites() : valid(false), updated(false), pos(-1), bestSNR(0), satsTracked(0), satsVisible(0), snrDataPresent(false) {} uint8_t id[_GPS_MAX_ARRAY_LENGTH] = {0}; @@ -107,9 +108,11 @@ struct TinyGPSSatellites */ int8_t pos; // Use signed int, only increase pos to set satellite ID uint8_t bestSNR; - uint8_t satsUsed; + uint8_t satsTracked; + uint8_t satsVisible; bool snrDataPresent; uint32_t lastCommitTime; + void commit(); void setSatId(const char *term); void setSatSNR(const char *term);