Files
wadamesh/test/test_gps_time_sync.cpp
Kaj SchittecatandClaude Opus 5.5 1ee32105de touch(clock): Central Africa and 24 more zones, GPS wake no longer sets a stale time, every SNTP sync reaches the clock
Discord (gadgeteerza, ThinkNode M9, beta_100): no Central Africa zone, the
clock often half an hour behind on a Custom UTC offset, and "Sync clock from
system" with Wi-Fi on not always setting it.

Zones: the saved setting is an index into the zone table, and Custom used to
be "one past the list", so appending zones would have moved it. Custom now
keeps its id (16) forever and the new zones (Central/South/East/West Africa,
Egypt, Eastern/Western Europe, Moscow, Turkey, Arabia, Iran, Gulf, Pakistan,
Nepal, Bangladesh, Indochina, Singapore, Perth, Korea, Adelaide, Brisbane,
New Zealand, Argentina, Mexico City, Newfoundland) sit after it. The picker
shows its own west-to-east order and opens on the current zone.

Half an hour behind: not the offset. WadaNmeaLocationProvider::begin() kept
the parser's pre-rest sentence, so after a GPS saver rest the first loop pass
saw a "valid" fix, the 30-minute re-sync was due, and the clock was set to
the moment the receiver went to rest. begin() now clears the parser, and
UITask::setGpsPower(true) calls syncTime() so the core provider (V4 and
others) gets the same. test_gps_time_sync runs the real provider against a
receiver sending true UTC: the pre-fix provider set the clock 1801 s behind
after a 30-minute rest; now every set is within 1 s.

Sync from system: on the M9 the system clock is the device clock, so the
button copied a clock onto itself. And the Wi-Fi loop wrote only the FIRST
SNTP result into the mesh clock: SNTP's hourly re-syncs set the system clock
only, the PCF8563 kept its old value, and the hourly chip resync (#583)
pulled the clock back to it. Every completed sync is now written through
(SntpClockPush.h, test_sntp_clock_push), and the button with Wi-Fi up
restarts SNTP and says "Clock synced" when the answer lands.

test_time_zones: the legacy ids keep their zones, every zone's offset in
January/July/October, Egypt's DST edges, and all 65536 saved
(tz_zone, time_offs) byte pairs through the exact prefs path to localtime().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 08:19:39 +01:00

91 lines
4.1 KiB
C++

// SPDX-License-Identifier: GPL-3.0-or-later
//
// Host test for the GPS clock set across a GPS saver rest (Discord, ThinkNode M9 on beta_100:
// "the clock is often behind by a half hour"). The real src/helpers/WadaNmeaLocationProvider.h
// runs against a simulated receiver that sends one RMC a second with the TRUE UTC time while it
// is awake (test/gps_time_fakes), and a clock that records every set.
//
// The bug: begin() kept the parser's pre-rest sentence, so the first loop pass after a wake
// saw a "valid" fix with the 30-minute re-sync due and set the clock to the moment the receiver
// went to rest, before the receiver had said anything new (the pre-fix provider, run through
// this test, set the clock 1801 s behind after a 30-minute rest). The rule checked here: every clock
// set the provider makes is the true time (within the 1 s an RMC is old at most), whatever the
// rest length, both through begin() alone (the provider's own fix) and with the UI's
// syncTime() on wake (UITask::setGpsPower, the fix that also covers the core provider).
//
// c++ -std=c++17 -Wall -Wextra -DESP32 -I src -I test/gps_time_fakes -I test/gps_probe_fakes \
// test/test_gps_time_sync.cpp -o /tmp/test_gps_time_sync && /tmp/test_gps_time_sync
#include <assert.h>
#include <stdio.h>
#include "helpers/WadaNmeaLocationProvider.h"
uint32_t g_test_ms = 0;
uint32_t g_utc_at_ms0 = 0;
// The provider's prefs calls (only the UART-probe path uses them; this test has no probe).
uint32_t touchPrefsGetGpsBaud(uint32_t fallback) { return fallback; }
bool touchPrefsGpsBaudChosen() { return false; }
void touchPrefsSetGpsBaudFound(uint32_t) {}
bool touchPrefsSetGpsBaud(uint32_t) { return true; }
struct RecordingClock : mesh::RTCClock {
int sets = 0, stale_sets = 0;
long worst_lag = 0; // seconds the most-behind set was behind the true time
void setCurrentTime(uint32_t t) override {
++sets;
const long lag = (long)trueUtc() - (long)t;
if (lag > worst_lag) worst_lag = lag;
if (lag > 1 || lag < -1) ++stale_sets;
}
};
// The sensor manager calls loop() only while the GPS is on (EnvironmentSensorManager::loop,
// gps_active); the UI loop passes every 10 ms here.
static void run(WadaNmeaLocationProvider& gps, uint32_t ms) {
const uint32_t end = g_test_ms + ms;
while ((int32_t)(g_test_ms - end) < 0) { gps.loop(); g_test_ms += 10; }
}
static void scenario(bool ui_sync_on_wake) {
g_test_ms = 100000;
g_utc_at_ms0 = 1793017200u - 100; // 2026-10-26 12:20Z at the start of the run
HardwareSerial uart;
RecordingClock clk;
WadaNmeaLocationProvider gps(uart, &clk, -1, -1);
// Boot: the receiver needs a moment, then reports every second.
uart.wake(1500);
gps.begin();
run(gps, 60000);
assert(clk.sets == 1 && clk.stale_sets == 0); // the boot sync, at the true time
// The GPS saver: settled fix -> rest for the chosen interval -> wake, again and again.
// Every interval the UI offers (2, 5, 15, 30 min), and a long one.
const uint32_t rests_min[] = { 30, 2, 5, 15, 30, 31, 45, 30 };
for (uint32_t rest : rests_min) {
uart.sleep();
gps.stop(); // stop_gps(): no more loop() calls while it rests
g_test_ms += rest * 60000u;
uart.wake(4000); // a waking receiver is silent for a few seconds
gps.begin(); // start_gps()
gps.reset();
if (ui_sync_on_wake) gps.syncTime(); // UITask::setGpsPower(true)
// The saver keeps it awake until the fix has held 30 s (s_gps_fix_since + 30000).
run(gps, 40000);
assert(clk.stale_sets == 0);
}
// The re-syncs did happen (the 30-minute one is always due after a rest that long), just
// from fresh sentences.
assert(clk.sets >= 4);
printf(" %s: %d clock sets, all within 1 s of true UTC (worst lag %ld s)\n",
ui_sync_on_wake ? "begin()+syncTime()" : "begin() only ", clk.sets, clk.worst_lag);
}
int main() {
scenario(false); // the provider's own begin() fix
scenario(true); // plus the UI's syncTime() on wake (what the core provider relies on)
printf("test_gps_time_sync: all passed\n");
return 0;
}