mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-27 19:57:38 +00:00
[GPS] Apply sanity check on received data.
This commit is contained in:
@@ -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 °)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user