[GPS] Apply sanity check on received data.

This commit is contained in:
Gijs Noorlander
2020-08-09 09:55:21 +02:00
parent d1a6deade4
commit 1616b8df1d
3 changed files with 136 additions and 55 deletions
+99 -27
View File
@@ -41,6 +41,7 @@ TinyGPSPlus::TinyGPSPlus()
, sentencesWithFixCount(0)
, failedChecksumCount(0)
, passedChecksumCount(0)
, invalidDataCount(0)
{
term[0] = '\0';
}
@@ -57,6 +58,7 @@ bool TinyGPSPlus::encode(char c)
{
case ',': // term terminators
parity ^= (uint8_t)c;
// fall through
case '\r':
case '\n':
case '*':
@@ -81,14 +83,14 @@ bool TinyGPSPlus::encode(char c)
curSentenceSystem = GPS_SYSTEM_GPS;
isChecksumTerm = false;
sentenceHasFix = false;
return false;
break;
default: // ordinary characters
if (curTermOffset < sizeof(term) - 1)
term[curTermOffset++] = c;
if (!isChecksumTerm)
parity ^= c;
return false;
break;
}
return false;
@@ -128,22 +130,34 @@ int32_t TinyGPSPlus::parseDecimal(const char *term)
// Parse degrees in that funny NMEA format DDMM.MMMM
void TinyGPSPlus::parseDegrees(const char *term, RawDegrees &deg)
{
uint32_t leftOfDecimal = (uint32_t)atol(term);
uint16_t minutes = (uint16_t)(leftOfDecimal % 100);
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;
}
const uint32_t leftOfDecimal = (uint32_t)atol(term);
while (isdigit(*term)) {
++term;
}
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;
deg.deg = (int16_t)(leftOfDecimal / 100);
while (isdigit(*term))
++term;
if (*term == '.')
while (isdigit(*++term))
{
multiplier /= 10;
tenMillionthsOfMinutes += (*term - '0') * multiplier;
}
while (isdigit(*++term))
{
multiplier /= 10;
tenMillionthsOfMinutes += (*term - '0') * multiplier;
}
deg.billionths = (5 * tenMillionthsOfMinutes + 1) / 3;
deg.negative = false;
@@ -170,22 +184,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;
case GPS_SENTENCE_GPGSV:
satellitesStats.commit();
@@ -376,12 +409,13 @@ const char *TinyGPSPlus::cardinal(double course)
void TinyGPSLocation::commit()
{
rawLatData = rawNewLatData;
rawLngData = rawNewLngData;
fixQuality = newFixQuality;
fixMode = newFixMode;
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)
@@ -431,16 +465,54 @@ void TinyGPSSatellites::commit()
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 TinyGPSSatellites::setSatId(const char *term)
+29 -27
View File
@@ -75,7 +75,7 @@ public:
FixQuality Quality() { /* updated = false; */ return fixQuality; }
FixMode Mode() { /* updated = false; */ return fixMode; }
TinyGPSLocation() : valid(false), updated(false), fixQuality(Invalid), newFixQuality(Invalid), fixMode(N), newFixMode(N)
TinyGPSLocation() : valid(false), updated(false), lastCommitTime(0), fixQuality(Invalid), newFixQuality(Invalid), fixMode(N), newFixMode(N)
{}
private:
@@ -100,7 +100,7 @@ public:
uint8_t nrSatsVisible() const { return satsVisible; }
uint8_t getBestSNR() const { return bestSNR; }
TinyGPSSatellites() : valid(false), updated(false), pos(-1), bestSNR(0), satsTracked(0), satsVisible(0), snrDataPresent(false)
TinyGPSSatellites() : valid(false), updated(false), pos(-1), bestSNR(0), satsTracked(0), satsVisible(0), snrDataPresent(false), lastCommitTime(0)
{}
uint8_t id[_GPS_MAX_ARRAY_LENGTH] = {0};
@@ -146,7 +146,7 @@ public:
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:
@@ -171,7 +171,7 @@ public:
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:
@@ -191,7 +191,7 @@ public:
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:
@@ -211,7 +211,7 @@ public:
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:
@@ -265,14 +265,14 @@ private:
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
@@ -305,6 +305,7 @@ public:
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 {
@@ -326,26 +327,27 @@ private:
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;
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);
+8 -1
View File
@@ -184,6 +184,9 @@ struct P082_data_struct : public PluginTaskData_base {
if (gps->date.age() > P082_TIMESTAMP_AGE) {
return false;
}
if (!gps->date.isValid() || !gps->time.isValid()) {
return false;
}
dateTime.tm_year = gps->date.year() - 1970;
dateTime.tm_mon = gps->date.month();
dateTime.tm_mday = gps->date.day();
@@ -606,6 +609,8 @@ void P082_logStats(struct EventStruct *event) {
log += P082_data->gps->passedChecksum();
log += '/';
log += P082_data->gps->failedChecksum();
log += F(" invalid: ");
log += P082_data->gps->invalidData();
addLog(LOG_LEVEL_DEBUG, log);
}
@@ -723,11 +728,13 @@ void P082_html_show_stats(struct EventStruct *event) {
addHtml(F("-"));
}
addRowLabel(F("Checksum (pass/fail)"));
addRowLabel(F("Checksum (pass/fail/invalid)"));
String chksumStats;
chksumStats = P082_data->gps->passedChecksum();
chksumStats += '/';
chksumStats += P082_data->gps->failedChecksum();
chksumStats += '/';
chksumStats += P082_data->gps->invalidData();
addHtml(chksumStats);
}