Skip to content

Commit

Permalink
Merge pull request letscontrolit#5087 from TD-er/feature/improve_GPS_…
Browse files Browse the repository at this point in the history
…time_accuracy

[GPS] Improve GPS time stability
  • Loading branch information
TD-er authored Jul 15, 2024
2 parents c44c85c + a22c51b commit 3d7f299
Show file tree
Hide file tree
Showing 10 changed files with 855 additions and 490 deletions.
727 changes: 365 additions & 362 deletions lib/TinyGPSPlus-1.0.2/src/TinyGPS++.h

Large diffs are not rendered by default.

23 changes: 7 additions & 16 deletions src/_P082_GPS.ino
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@



// Must use volatile declared variable (which will end up in iRAM)
volatile unsigned long P082_pps_time = 0;
void Plugin_082_interrupt() IRAM_ATTR;

boolean Plugin_082(uint8_t function, struct EventStruct *event, String& string) {
boolean success = false;

Expand Down Expand Up @@ -329,14 +325,10 @@ boolean Plugin_082(uint8_t function, struct EventStruct *event, String& string)
return success;
}

if (P082_data->init(port, serial_rx, serial_tx)) {
if (P082_data->init(port, serial_rx, serial_tx, pps_pin)) {
success = true;
serialHelper_log_GpioDescription(port, serial_rx, serial_tx);

if (validGpio(pps_pin)) {
// pinMode(pps_pin, INPUT_PULLUP);
attachInterrupt(pps_pin, Plugin_082_interrupt, RISING);
}
# ifdef P082_USE_U_BLOX_SPECIFIC
P082_data->setPowerMode(static_cast<P082_PowerMode>(P082_POWER_MODE));
P082_data->setDynamicModel(static_cast<P082_DynamicModel>(P082_DYNAMIC_MODEL));
Expand Down Expand Up @@ -751,6 +743,12 @@ void P082_html_show_stats(struct EventStruct *event) {
chksumStats += P082_data->gps->invalidData();
addHtml(chksumStats);
}
#ifndef BUILD_NO_DEBUG
/*
addRowLabel(F("SW PPS stats"));
addHtml(P082_data->getPPSStats());
*/
#endif
}

void P082_setSystemTime(struct EventStruct *event) {
Expand All @@ -761,14 +759,7 @@ void P082_setSystemTime(struct EventStruct *event) {
return;
}

P082_data->_pps_time = P082_pps_time; // Must copy the interrupt gathered time first.
P082_pps_time = 0;

P082_data->tryUpdateSystemTime();
}

void Plugin_082_interrupt() {
P082_pps_time = millis();
}

#endif // USES_P082
18 changes: 13 additions & 5 deletions src/src/DataTypes/ESPEasyTimeSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const __FlashStringHelper* toString(timeSource_t timeSource)
switch (timeSource) {
case timeSource_t::GPS_PPS_time_source: return F("GPS PPS");
case timeSource_t::GPS_time_source: return F("GPS");
case timeSource_t::GPS_time_source_no_fix: return F("GPS no Fix");
case timeSource_t::NTP_time_source: return F("NTP");
case timeSource_t::Manual_set: return F("Manual");
case timeSource_t::ESP_now_peer: return F(ESPEASY_NOW_NAME " peer");
Expand All @@ -27,6 +28,7 @@ bool isExternalTimeSource(timeSource_t timeSource)
switch (timeSource) {
case timeSource_t::GPS_PPS_time_source:
case timeSource_t::GPS_time_source:
case timeSource_t::GPS_time_source_no_fix:
case timeSource_t::NTP_time_source:
case timeSource_t::External_RTC_time_source:
case timeSource_t::Manual_set:
Expand All @@ -36,9 +38,9 @@ bool isExternalTimeSource(timeSource_t timeSource)
}
}

// Typical time wander for ESP nodes is 0.04 ms/sec
// Meaning per 25 sec, the time may wander 1 msec.
#define TIME_WANDER_FACTOR 25000
// Typical time wander for ESP nodes should be less than 10 ppm
// Meaning per hour, the time should wander less than 36 msec.
#define TIME_WANDER_FACTOR 100000

uint32_t updateExpectedWander(
int32_t current_wander,
Expand Down Expand Up @@ -68,6 +70,12 @@ uint32_t computeExpectedWander(timeSource_t timeSource,
expectedWander_ms += 10;
break;
}
case timeSource_t::GPS_time_source_no_fix:
{
// When the GPS has no fix, the reported time may differ quite a bit
expectedWander_ms += 2000;
break;
}
case timeSource_t::NTP_time_source:
{
// Typical time needed to perform a NTP request to an online NTP server
Expand All @@ -78,8 +86,8 @@ uint32_t computeExpectedWander(timeSource_t timeSource,
case timeSource_t::ESP_now_peer:
case timeSource_t::ESPEASY_p2p_UDP:
{
// expected wander is 144 per hour.
// Using a 'penalty' of 1000 makes it only preferrable over NTP after +/- 7 hour.
// expected wander is 36 per hour.
// Using a 'penalty' of 1000 makes it only preferrable over NTP after +/- 28 hour.
expectedWander_ms += 1000;
break;
}
Expand Down
3 changes: 2 additions & 1 deletion src/src/DataTypes/ESPEasyTimeSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class String;

#define EXT_TIME_SOURCE_MIN_UPDATE_INTERVAL_MSEC 3600000
#define EXT_TIME_SOURCE_MIN_UPDATE_INTERVAL_MSEC 1800000
#define EXT_TIME_SOURCE_MIN_UPDATE_INTERVAL_SEC 3600

// Time Source type, sort by priority.
Expand All @@ -28,6 +28,7 @@ enum class timeSource_t : uint8_t {
ESP_now_peer = 40, // < 5 msec accuracy between nodes, but time on the whole network may drift
ESPEASY_p2p_UDP = 41,
External_RTC_time_source = 45, // Typically +/- 500 msec off.
GPS_time_source_no_fix = 46, // Typically 500 - 1000 msec off.

Restore_RTC_time_source = 50, // > 1 sec difference per reboot
No_time_source = 255 // No time set
Expand Down
1 change: 1 addition & 0 deletions src/src/Helpers/ESPEasy_time.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ bool ESPEasy_time::systemTimePresent() const {
case timeSource_t::External_RTC_time_source:
case timeSource_t::GPS_time_source:
case timeSource_t::GPS_PPS_time_source:
case timeSource_t::GPS_time_source_no_fix:
case timeSource_t::ESP_now_peer:
case timeSource_t::ESPEASY_p2p_UDP:
case timeSource_t::Manual_set:
Expand Down
2 changes: 2 additions & 0 deletions src/src/Helpers/ESPEasy_time.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class ESPEasy_time {
void restoreLastKnownUnixTime(unsigned long lastSysTime,
uint8_t deepSleepState);

// Set external time source
// Wander is expected error in msec
bool setExternalTimeSource_withTimeWander(double new_time,
timeSource_t new_timeSource,
int32_t wander,
Expand Down
146 changes: 146 additions & 0 deletions src/src/Helpers/ModuloOversamplingHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#ifndef HELPERS_MODULOOVERSAMPLINGHELPER_H
#define HELPERS_MODULOOVERSAMPLINGHELPER_H

#include "../../ESPEasy_common.h"
#include <limits>
#include "../Helpers/OversamplingHelper.h"

template<class T, class SUM_VALUE_TYPE = float>
class ModuloOversamplingHelper {
public:

// When working with cyclical values which may 'overflow',
// it is hard to compute an average.
// For example with some angle in degrees, 359 and 1 degree are very close to eachother.
// The expected average would be 0, however the numerical average is 180.
//
// Typical use cases:
// - Compute direction in degrees (range 0 .. 359)
// - Compute 'jitter' in some periodical signal
// - Phase shift calculations
ModuloOversamplingHelper() {
// Just set some value for _modulo
// so there will be no overflow when applying the offset
// And we have a default constructor.
// However it is highly unlikely this is a practical value,
// so should not be run with this default value
_modulo = std::numeric_limits<T>::max() / 2;
}

ModuloOversamplingHelper(T modulo) : _modulo(modulo) {}

void setModulo(T modulo) {
_modulo = modulo;
reset();
}

// Add new sample
void add(T currentValue) {
// Make sure the value is in range 0 ... (_modulo - 1)
currentValue %= _modulo;

if (_oversampling.getCount() == 0) {
// Work with values centered around the middle of the modulo range.
// Since the template type T can be unsigned,
// must make sure the offset is positive.
// N.B. _offset is always less than or equal to (_modulo / 2)
const T modulo_halve = (_modulo / 2);

if (currentValue > modulo_halve) {
_offset = currentValue - modulo_halve;
} else {
_offset = modulo_halve - currentValue;
}
}

T shiftedValue = currentValue + _offset; // FIXME TD-er: Could overflow

if (shiftedValue > _modulo) {
shiftedValue -= _modulo;
}

_oversampling.add(shiftedValue);
}

// Get current oversampling value without reset.
// @param value Value will only be updated if there were samples available
bool peek(SUM_VALUE_TYPE& value) const {
if (!_oversampling.peek(value)) {
return false;
}

if (value < _offset) {
value += _modulo;
}
value -= _offset;
return true;
}

// Get current oversampling value and reset.
// @param value Value will only be updated if there were samples available
bool get(SUM_VALUE_TYPE& value) {
// Must call functions of this class and not just return _oversampling.get()
// This way we are sure the _offset is properly applied
if (peek(value)) {
reset();
return true;
}
return false;
}

// Return number of used samples
uint32_t getCount() const {
return _oversampling.getCount();
}

// Clear all oversampling values
void reset() {
_oversampling.reset();
}

// Clear all oversampling values and add last average if there were samples available.
SUM_VALUE_TYPE resetKeepLast() {
SUM_VALUE_TYPE value{};

// Must call functions of this class and not just return _oversampling.resetKeepLast()
// This way we are sure the _offset is properly applied
if (get(value)) {
add(static_cast<T>(value));
}
return value;
}

// Clear all oversampling values and add last average if there were samples available.
// Last value will be added with a weight according to the given ratio of the last count.
// @param countRatio New weight will be previous nr of samples / countRatio
void resetKeepLastWeighted(int countRatio) {
SUM_VALUE_TYPE value{};
const uint32_t count = getCount();

// Must call functions of this class and not just return _oversampling.resetKeepLast()
// This way we are sure the _offset is properly applied
if (get(value)) {
if (count > countRatio) {
const uint32_t weight = (count + (countRatio / 2)) / countRatio;

for (uint32_t i = 0; i < weight; ++i) {
add(static_cast<T>(value));
}
} else {
add(static_cast<T>(value));
}
}
}

void setFilterPeaks(bool enable) {
_oversampling.setFilterPeaks(enable);
}

private:

OversamplingHelper<T, SUM_VALUE_TYPE>_oversampling;
T _modulo;
T _offset{};
};

#endif // ifndef HELPERS_MODULOOVERSAMPLINGHELPER_H
Empty file.
Loading

0 comments on commit 3d7f299

Please sign in to comment.