Skip to content

Fix: the GPS timeout ignores what the protocol asks for, so some receivers are never configured - #11977

Open
MrScothh wants to merge 1 commit into
iNavFlight:maintenance-10.xfrom
MrScothh:fix/gps-protocol-timeout
Open

MrScothh wants to merge 1 commit into
iNavFlight:maintenance-10.xfrom
MrScothh:fix/gps-protocol-timeout

Conversation

@MrScothh

Copy link
Copy Markdown
Contributor

What happens

gpsSetProtocolTimeout() takes a timeout and stores it in gpsState.timeoutMs. Nothing ever reads it. The check in gpsUpdate() compared against GPS_TIMEOUT until #8423 in 2022 and against gpsState.baseTimeoutMs since, so every phase gets one second, two for NMEA, whatever the driver asked for:

case GPS_RUNNING:
    gpsProviders[gpsState.gpsConfig->provider].protocol();

    // Check for GPS timeout
    if ((millis() - gpsState.lastMessageMs) > gpsState.baseTimeoutMs) {

The u-blox driver asks for more in two places, and has since the variable timeout was introduced in 2018:

gpsSetProtocolTimeout((GPS_BAUD_CHANGE_DELAY + 50) * (GPS_BAUDRATE_COUNT));   // autobaud sweep
gpsSetProtocolTimeout(MAX(GPS_TIMEOUT, ((GPS_VERSION_RETRY_TIMES + 3) * GPS_CFG_CMD_TIMEOUT_MS)));  // detection

Detection asks for three seconds because that is what it needs: three polls for UBX-MON-VER and three for UBX-MON-GNSS, half a second each. It gets one second, which is enough only when every poll is answered at once.

A receiver that does not answer UBX-MON-GNSS spends that second on two unanswered polls, the link is declared lost, and the state machine starts over from autobaud. It never reaches gpsConfigure(), so it is never configured at all: no rate, no dynamic model, no constellations, no message configuration. Against an emulated M10 that answers MON-VER but stays silent on MON-GNSS, in twelve seconds:

MON-VER polls MON-GNSS polls configuration messages
maintenance-10.x 6 12 0
with this change 1 5 5

The same receiver answering MON-GNSS is configured identically before and after, in one pass.

The change

One line: the check uses the timeout that was asked for, but never less than the base one.

if ((millis() - gpsState.lastMessageMs) > MAX(gpsState.timeoutMs, gpsState.baseTimeoutMs)) {

MAX() rather than the stored value alone on purpose. The configuration steps ask for GPS_SHORT_TIMEOUT, half a second, and have had a full second in practice for eight years. Honouring that literally would tighten them for the first time and could restart receivers that configure fine today, which is not what this fixes. Only the phases that ask for longer change: the autobaud sweep and detection.

Nothing else reads timeoutMs, so this is the whole change.

Testing

SITL on Windows, against an emulated u-blox that answers MON-VER, streams NAV-PVT, and is switched between answering MON-GNSS and staying silent. Message counts above are from that run, taken from the receiver side.

gps_null_port_unittest and gps_ublox_unittest pass.

Notes

gps.c is common to every GPS provider, so this is worth a careful look. In practice the only driver that asks for anything other than the base timeout is the u-blox one; NMEA, MSP, CRSF and DroneCAN always pass baseTimeoutMs, so for them the comparison is unchanged.

Independent of my other open GPS pull requests: they touch gps_ublox.c, this one touches gps.c.

@qodo-code-review

Copy link
Copy Markdown
Contributor

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Honor protocol-requested GPS setup timeouts

🐞 Bug fix 🕐 10-20 Minutes

Grey Divider

AI Description

• Honors provider-requested GPS timeouts during receiver setup and autobaud detection.
• Preserves the base timeout floor to avoid tightening existing configuration waits.
• Prevents u-blox receivers from repeatedly restarting before configuration.
Diagram

graph TD
    P["GPS Protocol"] -->|requests timeout| T["Timeout State"] --> U["GPS Update"] --> D{"Timeout exceeded?"} -->|Yes| L["Lost Communication"]
    D -->|No| R["Continue Running"]
Loading
High-Level Assessment

The PR's approach is optimal: using the greater of the requested and base timeouts activates the existing protocol-timeout contract while preserving historically tolerated configuration waits. Using the requested timeout directly was appropriately dismissed because short u-blox configuration timeouts could introduce regressions.

Files changed (1) +3 / -2

Bug fix (1) +3 / -2
gps.cHonor protocol timeout with a base timeout floor +3/-2

Honor protocol timeout with a base timeout floor

• Changes the GPS_RUNNING timeout check to use the maximum of the provider-requested timeout and the configured base timeout. This allows longer u-blox autobaud and detection phases to complete without shortening established configuration waits.

src/main/io/gps.c

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

RAM / Flash usage vs. base commit 7e82f68 — commit c351ce3

Target Flash Δ RAM Δ
MATEKF405 ±0 B (±0.00%) CCM: ±0 B (±0.00%)
RAM: ±0 B (±0.00%)
MATEKF722 ±0 B (±0.00%) ITCM_RAM: ±0 B (±0.00%)
RAM: ±0 B (±0.00%)
TCM: ±0 B (±0.00%)
MATEKF765 ±0 B (±0.00%) DTCM_RAM: ±0 B (±0.00%)
SRAM1: ±0 B (±0.00%)
MATEKH743 +16 B (+0.00%) D2_RAM: ±0 B (±0.00%)
DTCM_RAM: ±0 B (±0.00%)
ITCM_RAM: ±0 B (±0.00%)
RAM: ±0 B (±0.00%)

See RAM/flash optimization guide for techniques to reduce usage.

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

Test firmware build ready — commit c351ce3

Download firmware for PR #11977

249 targets built. Find your board's .hex file by name on that page (e.g. MATEKF405SE.hex). Files are individually downloadable — no GitHub login required.

Development build for testing only. Use Full Chip Erase when flashing.

gpsSetProtocolTimeout() stores the timeout it is given, but the check in
gpsUpdate() never read it: it compared against GPS_TIMEOUT until 2022 and
against baseTimeoutMs since, one second for every protocol but NMEA. The
longer waits the u-blox driver asks for, three seconds while it detects the
receiver and the full sweep during autobaud, were cut to that second.

Detection fits in a second only when every poll is answered at once. A
receiver that does not answer UBX-MON-GNSS uses up the second on two of the
three polls, times out, and starts over from autobaud, so it is never
configured at all: against an emulated M10 that stays silent on MON-GNSS,
INAV sent MON-VER six times and MON-GNSS eleven times in twelve seconds and
no configuration.

The check now uses the timeout that was asked for, but never less than the
base one. The configuration steps ask for GPS_SHORT_TIMEOUT, half a second,
and have always had a second in practice; tightening them now could restart
receivers that configure fine today, so they keep it. With the change the
silent receiver is detected once and configured like any other.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants