gps time sync improvement

This commit is contained in:
liquidraver
2026-10-12 19:51:56 +02:00
parent 448ff91590
commit ec55eb0f4c
5 changed files with 46 additions and 10 deletions
+5 -3
View File
@@ -802,7 +802,9 @@ Loading also range-checks `freq`/`sf`/`bw` and falls back to the compile-time de
### 7.3 GPS (`adapters/gps/`)
- State machine: OFF → ACQUIRING → STANDBY (with warm standby on supported hardware)
- 3 consecutive good fixes (≥4 satellites) required before reporting
- 5 consecutive good fixes required before reporting. A good fix is a satellite solution (GGA quality
GNSS or DGNSS) with ≥4 satellites; an estimated fix (quality 6, dead reckoning) counts as no fix,
because its time is the module's own clock and its position the last one
- Multi-constellation: GPS+GLONASS+Galileo+BeiDou with fallback
- T1000-E / MeshTracker X1 (AG3335): multi-GPIO power sequencing; standby is the module's RTC backup sleep
(`$PAIR650,0` before `GPS_EN` is cut, VRTC kept, a `GPS_RTC_INT` pulse wakes it)
@@ -820,8 +822,8 @@ Loading also range-checks `freq`/`sf`/`bw` and falls back to the compile-time de
`gps_wake_interval_ms` (initialised from `prefs.gps_interval`) controls the mode:
- **Duty cycling** (`gps_wake_interval_ms > 0`): after acquiring 3 good fixes the GPS powers down; the state machine wakes it again after the configured standby interval. The fix callback fires and then the GPS sleeps.
- **Always-on** (`gps_wake_interval_ms == 0`): the GPS never powers down. `consecutive_good_fixes` is reset after each promotion so the 3-fix gate cycles continuously, streaming fresh positions. Flash writes and fix callbacks are rate-limited to once per `gps_acquire_timeout_ms` to avoid hammering storage.
- **Duty cycling** (`gps_wake_interval_ms > 0`): after acquiring 5 good fixes the GPS powers down; the state machine wakes it again after the configured standby interval. The fix callback fires and then the GPS sleeps.
- **Always-on** (`gps_wake_interval_ms == 0`): the GPS never powers down. `consecutive_good_fixes` is reset after each promotion so the 5-fix gate cycles continuously, streaming fresh positions. Flash writes and fix callbacks are rate-limited to once per `gps_acquire_timeout_ms` to avoid hammering storage.
`gps_set_poll_interval_sec(0)` switches to always-on live; persisted via `prefs.gps_interval` (set by `set gps duty 0`).
@@ -55,3 +55,26 @@ first, which a long press cannot meet: every long press asked for the confirmati
The confirmation is a second **hold**. A short press pages on instead, which on the Power Off page
goes back to the first page.
## GPS: a fix could set the clock to an old time
On a repeater with a GPS, a fix could set the clock hours back. The next fix usually put it right
again, but until then the node's adverts and replies carried the old time, and mesh time sync was not
allowed to correct it.
Right after waking, a GPS receiver can report an estimated position for a few seconds: its last
position and its own clock, before any satellite has confirmed them. These were accepted as a fix.
If the receiver's clock had not kept time while it slept, that old time was written to the node.
- **Only positions solved from satellites count as a fix**, and five in a row are needed (was three).
A fix takes about two seconds longer.
- This applies to every board with a GPS.
It was reported on a SenseCAP Solar P1 with `set gps standby` raised to 48 hours, which keeps the
receiver powered while it sleeps. With the default (one hour) a repeater switches the receiver off
between its 48-hour fixes.
Good to know: a telemetry request from a logged-in client (anything above guest) wakes a repeater's
GPS for a fresh fix, so the GPS can run more often than the `gps duty` interval.
Thanks to **Innocent Killer 101** for the report and the measurements.
+1 -1
View File
@@ -950,7 +950,7 @@ config ZEPHCORE_GPS_FIX_TIMEOUT_SEC
GPS will be powered off after this timeout.
Default 120s — 30s was too aggressive for Air530Z / MAX-7Q warm
starts in marginal sky conditions; the acquire loop would time out
before the 3-consecutive-good-fix gate could promote. Applies to
before the consecutive-good-fix gate could promote. Applies to
wake cycles where the module kept its state; the first acquisition
and every wake after a full power-off use
ZEPHCORE_GPS_FIRST_FIX_TIMEOUT_SEC.
+16 -5
View File
@@ -71,17 +71,20 @@ enum gps_state {
static enum gps_state gps_current_state = GPS_STATE_OFF;
static uint8_t consecutive_good_fixes = 0;
static bool first_fix_acquired = false; /* True after first 3-good-fix cycle since enable. Cleared on gps_enable(false) and at boot. */
static bool first_fix_acquired = false; /* True after the first validated fix since enable. Cleared on gps_enable(false) and at boot. */
static bool first_acquire_used = false; /* True once the one-time long cold-start window has ended (fix or timeout). Cleared on gps_enable(false) and at boot. */
static bool standby_powered_off = false; /* The last standby was the full power-off (interval past gps_standby_max_sec), so the wake after it is a cold start. Set on every standby, cleared on gps_enable(false). */
static bool gps_time_synced = false; /* True after GPS syncs RTC. Starts false at boot (RTC reset),
* set true after 3 good fixes, cleared when GPS disabled. */
* set true by a validated fix, cleared when GPS disabled. */
static int64_t last_fix_uptime_ms = 0; /* k_uptime when last validated fix was acquired */
static int64_t standby_start_ms = 0; /* k_uptime when standby started (for next-wake calc) */
static uint64_t standby_interval_ms = 0; /* How long standby lasts (for next-wake calc) */
#define GPS_GOOD_FIX_COUNT 3 /* Need 3 consecutive good fixes */
#define GPS_MIN_SATELLITES 4 /* Minimum satellites for valid fix */
/* A validated fix is GPS_GOOD_FIX_COUNT consecutive good fixes. A good fix is
* a satellite solution (see gps_fix_is_real()) with GPS_MIN_SATELLITES. Why:
* devdocs/lld/10-peripherals-power.md, "Fix validation". */
#define GPS_GOOD_FIX_COUNT 5
#define GPS_MIN_SATELLITES 4
/* Acquire windows (Kconfig) and the duty interval (prefs, set at boot). */
static const uint32_t gps_acquire_timeout_ms = CONFIG_ZEPHCORE_GPS_FIX_TIMEOUT_SEC * 1000U;
@@ -223,6 +226,14 @@ static void gps_post_fix_locked(const struct gnss_data *data)
atomic_or(&pending_gps_actions, GPS_ACTION_FIX);
}
/* True for a position and time solved from satellites. An estimated fix (GGA
* quality 6, dead reckoning) is the module's last position and its own clock,
* so it counts as no fix. */
static bool gps_fix_is_real(enum gnss_fix_status status)
{
return status == GNSS_FIX_STATUS_GNSS_FIX || status == GNSS_FIX_STATUS_DGNSS_FIX;
}
/* GNSS callback - called when new fix data is available */
static void gnss_data_cb(const struct device *dev, const struct gnss_data *data)
{
@@ -244,7 +255,7 @@ static void gnss_data_cb(const struct device *dev, const struct gnss_data *data)
k_mutex_lock(&gps_mutex, K_FOREVER);
if (data->info.fix_status >= GNSS_FIX_STATUS_GNSS_FIX) {
if (gps_fix_is_real(data->info.fix_status)) {
/* Reject (0,0): the parser can publish a valid fix status with lat/lon not
* yet filled (GGA ahead of RMC). It is never a real fix. */
if (data->nav_data.latitude == 0 && data->nav_data.longitude == 0) {
+1 -1
View File
@@ -57,7 +57,7 @@ void gps_format_diagnostics(char *out, size_t out_size);
typedef void (*gps_enable_callback_t)(bool enabled);
void gps_set_enable_callback(gps_enable_callback_t cb);
/* GPS fix callback - called when GPS acquires a validated fix (3 consecutive good fixes)
/* GPS fix callback - called when GPS acquires a validated fix (5 consecutive satellite fixes)
* Parameters: latitude (degrees), longitude (degrees), utc_time (Unix timestamp,
* 0 if the fix carried no usable date). Runs on the main thread, from
* gps_process_event(), so it may set the clock and prefs directly. */