diff --git a/src/TinyGPS++.cpp b/src/TinyGPS++.cpp index e1ec777..3d11c5b 100644 --- a/src/TinyGPS++.cpp +++ b/src/TinyGPS++.cpp @@ -45,6 +45,7 @@ TinyGPSPlus::TinyGPSPlus() , sentencesWithFixCount(0) , failedChecksumCount(0) , passedChecksumCount(0) + , invalidDataCount(0) { term[0] = '\0'; } @@ -61,6 +62,7 @@ bool TinyGPSPlus::encode(char c) { case ',': // term terminators parity ^= (uint8_t)c; + // fall through case '\r': case '\n': case '*': @@ -131,22 +133,34 @@ int32_t TinyGPSPlus::parseDecimal(const char *term) // Parse degrees in that funny NMEA format DDMM.MMMM void TinyGPSPlus::parseDegrees(const char *term, RawDegrees °) { - uint32_t leftOfDecimal = (uint32_t)atol(term); - uint16_t minutes = (uint16_t)(leftOfDecimal % 100); - uint32_t multiplier = 10000000UL; - uint32_t tenMillionthsOfMinutes = minutes * multiplier; + deg.deg = 181; // Set to invalid value + if (!isdigit(*term) && *term != '.') { + // An invalid character + // TODO: Must check if the degree is allowed to start with a decimal point. + return; + } - deg.deg = (int16_t)(leftOfDecimal / 100); + const uint32_t leftOfDecimal = (uint32_t)atol(term); - while (isdigit(*term)) + while (isdigit(*term)) { ++term; + } - if (*term == '.') - while (isdigit(*++term)) - { - multiplier /= 10; - tenMillionthsOfMinutes += (*term - '0') * multiplier; - } + if (*term != '.') { + // Degree must have a decimal point + return; + } + + deg.deg = (int16_t)(leftOfDecimal / 100); + const uint16_t minutes = (uint16_t)(leftOfDecimal % 100); + uint32_t multiplier = 10000000UL; + uint32_t tenMillionthsOfMinutes = minutes * multiplier; + + while (isdigit(*++term)) + { + multiplier /= 10; + tenMillionthsOfMinutes += (*term - '0') * multiplier; + } deg.billionths = (5 * tenMillionthsOfMinutes + 1) / 3; deg.negative = false; @@ -173,22 +187,41 @@ bool TinyGPSPlus::endOfTermHandler() case GPS_SENTENCE_GPRMC: date.commit(); time.commit(); - if (sentenceHasFix) + if (sentenceHasFix && date.valid && time.valid) { location.commit(); speed.commit(); course.commit(); + if (!(location.valid && speed.valid && course.valid)) { + // one of them is invalid, so consider the entire sentence invalid + date.valid = false; + time.valid = false; + location.valid = false; + speed.valid = false; + course.valid = false; + ++invalidDataCount; + } } break; case GPS_SENTENCE_GPGGA: time.commit(); - if (sentenceHasFix) + satellites.commit(); + hdop.commit(); + if (sentenceHasFix && time.valid) { location.commit(); altitude.commit(); + if (!(satellites.valid && hdop.valid && location.valid && altitude.valid)) { + // one of them is invalid, so consider the entire sentence invalid + time.valid = false; + satellites.valid = false; + hdop.valid = false; + location.valid = false; + altitude.valid = false; + ++invalidDataCount; + } } - satellites.commit(); - hdop.commit(); + break; } @@ -336,10 +369,13 @@ const char *TinyGPSPlus::cardinal(double course) void TinyGPSLocation::commit() { - rawLatData = rawNewLatData; - rawLngData = rawNewLngData; - lastCommitTime = millis(); - valid = updated = true; + rawLatData = rawNewLatData; + rawLngData = rawNewLngData; + fixQuality = newFixQuality; + fixMode = newFixMode; + lastCommitTime = millis(); + updated = true; + valid = (rawNewLatData.deg <= 90 && rawNewLngData.deg <= 180); } void TinyGPSLocation::setLatitude(const char *term) @@ -368,16 +404,54 @@ double TinyGPSLocation::lng() void TinyGPSDate::commit() { + const uint32_t olddate = date; date = newDate; + valid = false; + + { + const uint16_t newyear = year(); + if (newyear < 2020) { + // Very unlikely the year of received date is before 2020, which is now. + date = olddate; + return; + } + } + { + const uint8_t newmonth = month(); + if (newmonth > 12 || newmonth == 0) { + date = olddate; + return; + } + } + { + const uint8_t newday = day(); + if (newday > 31 || newday == 0) { + // Day of month + date = olddate; + return; + } + } lastCommitTime = millis(); - valid = updated = true; + valid = true; + updated = true; } void TinyGPSTime::commit() { time = newTime; lastCommitTime = millis(); - valid = updated = true; + valid = false; + if (second() > 60) { + return; + } + if (minute() > 59) { + return; + } + if (hour() > 23) { + return; + } + updated = true; + valid = true; } void TinyGPSTime::setTime(const char *term) diff --git a/src/TinyGPS++.h b/src/TinyGPS++.h index 60485cb..205707b 100644 --- a/src/TinyGPS++.h +++ b/src/TinyGPS++.h @@ -50,6 +50,13 @@ struct RawDegrees {} }; +enum FixQuality { Invalid = 0, GPS = 1, DGPS = 2, PPS = 3, RTK = 4, FloatRTK = 5, Estimated = 6, Manual = 7, Simulated = 8 }; +enum FixMode { + N = 'N', // None + A = 'A', // Autonomous + D = 'D', // Differential + E = 'E'};// Dead Reckoning + struct TinyGPSLocation { friend class TinyGPSPlus; @@ -61,8 +68,10 @@ struct TinyGPSLocation const RawDegrees &rawLng() { updated = false; return rawLngData; } double lat(); double lng(); + FixQuality Quality() { /* updated = false; */ return fixQuality; } + FixMode Mode() { /* updated = false; */ return fixMode; } - TinyGPSLocation() : valid(false), updated(false) + TinyGPSLocation() : valid(false), updated(false), lastCommitTime(0), fixQuality(Invalid), newFixQuality(Invalid), fixMode(N), newFixMode(N) {} private: @@ -72,6 +81,8 @@ struct TinyGPSLocation void commit(); void setLatitude(const char *term); void setLongitude(const char *term); + FixQuality fixQuality, newFixQuality; + FixMode fixMode, newFixMode; }; struct TinyGPSDate @@ -87,7 +98,7 @@ struct TinyGPSDate uint8_t month(); uint8_t day(); - TinyGPSDate() : valid(false), updated(false), date(0) + TinyGPSDate() : valid(false), updated(false), date(0), newDate(0), lastCommitTime(0) {} private: @@ -112,7 +123,7 @@ struct TinyGPSTime uint8_t second(); uint8_t centisecond(); - TinyGPSTime() : valid(false), updated(false), time(0) + TinyGPSTime() : valid(false), updated(false), time(0), newTime(0), lastCommitTime(0) {} private: @@ -132,7 +143,7 @@ struct TinyGPSDecimal uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } int32_t value() { updated = false; return val; } - TinyGPSDecimal() : valid(false), updated(false), val(0) + TinyGPSDecimal() : valid(false), updated(false), lastCommitTime(0), val(0), newval(0) {} private: @@ -152,7 +163,7 @@ struct TinyGPSInteger uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } uint32_t value() { updated = false; return val; } - TinyGPSInteger() : valid(false), updated(false), val(0) + TinyGPSInteger() : valid(false), updated(false), lastCommitTime(0), val(0), newval(0) {} private: @@ -206,14 +217,14 @@ class TinyGPSCustom void commit(); void set(const char *term); - char stagingBuffer[_GPS_MAX_FIELD_SIZE + 1]; - char buffer[_GPS_MAX_FIELD_SIZE + 1]; - unsigned long lastCommitTime; - bool valid, updated; - const char *sentenceName; - int termNumber; + char stagingBuffer[_GPS_MAX_FIELD_SIZE + 1] = {0}; + char buffer[_GPS_MAX_FIELD_SIZE + 1] = {0}; + unsigned long lastCommitTime = 0; + bool valid, updated = false; + const char *sentenceName = nullptr; + int termNumber = 0; friend class TinyGPSPlus; - TinyGPSCustom *next; + TinyGPSCustom *next = nullptr; }; class TinyGPSPlus @@ -245,30 +256,33 @@ class TinyGPSPlus uint32_t sentencesWithFix() const { return sentencesWithFixCount; } uint32_t failedChecksum() const { return failedChecksumCount; } uint32_t passedChecksum() const { return passedChecksumCount; } + uint32_t invalidData() const { return invalidDataCount; } private: enum {GPS_SENTENCE_GPGGA, GPS_SENTENCE_GPRMC, GPS_SENTENCE_OTHER}; // parsing state variables - uint8_t parity; - bool isChecksumTerm; - char term[_GPS_MAX_FIELD_SIZE]; - uint8_t curSentenceType; - uint8_t curTermNumber; - uint8_t curTermOffset; - bool sentenceHasFix; + uint8_t parity = 0; + bool isChecksumTerm = false; + char term[_GPS_MAX_FIELD_SIZE] = {0}; + uint8_t curSentenceType = 0; + uint8_t curSentenceSystem = 0; + uint8_t curTermNumber = 0; + uint8_t curTermOffset = 0; + bool sentenceHasFix = false; // custom element support friend class TinyGPSCustom; - TinyGPSCustom *customElts; - TinyGPSCustom *customCandidates; + TinyGPSCustom *customElts = nullptr; + TinyGPSCustom *customCandidates = nullptr; void insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int index); // statistics - uint32_t encodedCharCount; - uint32_t sentencesWithFixCount; - uint32_t failedChecksumCount; - uint32_t passedChecksumCount; + uint32_t encodedCharCount = 0; + uint32_t sentencesWithFixCount = 0; + uint32_t failedChecksumCount = 0; + uint32_t passedChecksumCount = 0; + uint32_t invalidDataCount = 0; // internal utilities int fromHex(char a);