Skip to content

gps: ask u-blox receivers only for the signals and constellations they can run - #11965

Open
MrScothh wants to merge 2 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:fix/ublox-glonass-keys
Open

MrScothh wants to merge 2 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:fix/ublox-glonass-keys

Conversation

@MrScothh

@MrScothh MrScothh commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Two things keep a u-blox receiver from being configured with what it can actually
run, both in configureGNSS10() and both ending the same way: the receiver answers
NAK, applies none of the message, and INAV quietly rewrites the user's constellation
settings to their defaults without sending them.

1. The F10 is offered keys and signals it does not have

A u-blox F10 reports hwVersion 000A0000, the same string an M10 reports, so INAV correctly identifies it as protocol 27+ and configures it through CFG-VALSET. It then sends two things that receiver cannot accept.

Its configuration database has no Glonass group at all. The F10 interface description (UBX-23002975) shows the CFG-SIGNAL group jumping straight from CFG-SIGNAL-QZSS_ENA (0x10310024) to CFG-SIGNAL-NAVIC_ENA (0x10310026): key 0x10310025, which is CFG-SIGNAL-GLO_ENA on an M10, is not there, and neither is 0x10310018.

And its Beidou L1 signal is B1C, not B1I. configureGNSS10() picks B1I unless Glonass is on, which on an F10 both enables a signal it cannot receive and turns off the one it uses. The NEO-F10N integration manual is explicit about what happens then:

For constellations with dual-band support, it is not possible to disable one of the bands. Both bands must be enabled and disabled at once. [...] Unsupported combinations are rejected with a UBX-ACK-NAK message.

A CFG-VALSET is all or nothing, so either of those makes the receiver answer NAK and apply none of the message. SBAS, Galileo, BeiDou and QZSS go down with them, and INAV then resets gps_ublox_use_galileo, gps_ublox_use_beidou and gps_ublox_use_glonass to their defaults.

This is the whole F10 family: NEO-F10N, DAN-F10N and the boards built on them. What a real NEO-F10N answers to MON-VER:

swVersion  EXT SPGL1L5 6.00 (041e8a)
hwVersion  000A0000
           FWVER=SPGL1L5 6.00
           PROTVER=40.00
           MOD=NEO-F10N
           GPS;GAL;BDS
           SBAS;QZSS
           NAVIC

GPS;GAL;BDS, with no GLO, and SPGL1L5 for the dual band firmware.

What changes for the F10

Both pieces of information are already on hand.

INAV polls UBX-MON-GNSS before the configuration starts and keeps the supported-constellation mask, and gpsUbloxHasGlonass() is already there. The two Glonass keys are last in the array, so leaving them out makes the message shorter rather than different. They are only dropped when the mask says so: an empty mask means MON-GNSS never answered, nothing is known, and every key goes out as before.

The MON-VER extensions are already being walked for the constellation list and the protocol version, so the same walk now notices L1L5 and the Beidou choice follows it.

Nothing changes for a receiver that reports Glonass.

2. Any receiver can be asked for more constellations than it can run

UBX-MON-GNSS also says how many major constellations the receiver can track at once,
and INAV reads that number into ubx_capabilities.capMaxGnss without ever using it.
Ask for more and the answer is the same NAK as above. The u-blox 8 protocol
description says so for UBX-CFG-GNSS:

The receiver will respond to such a request with a UBX-ACK-ACK message if it can
support the requested configuration or a UBX-ACK-NAK message if not. [...] Otherwise
the receiver will reject the request, by issuing a UBX-ACK-NAK and continuing
operation with the previous configuration.

and the M10 and F10 integration manuals say it for the configuration interface:

Unsupported combinations are rejected with a UBX-ACK-NAK message, and the warning
"inv sig cfg" is sent via UBX-INF and NMEA-TXT messages (if enabled).

This is not hypothetical on the receivers INAV supports. A NEO-M8 tracks up to three
concurrent GNSS, so INAV's own defaults, GPS with Galileo and BeiDou, already fill it:
turning GLONASS on as well is refused, and today that loses the whole constellation
configuration rather than one constellation. The M10 platform tracks four, so nothing
is trimmed there.

What happens after the NAK is the part worth fixing. INAV resets the three settings to
their defaults in its own configuration but never sends them, so the receiver keeps
running whatever it had, the configurator shows the defaults instead of the choice
that was made, and the next save of any kind persists them.

What changes there

The constellations to ask for are now worked out once, from the selection, the
constellations the receiver reports and how many it says it can run:

  • what is not in the MON-GNSS supported mask is dropped, because it cannot work
    anyway;
  • if what is left does not fit the count, the constellations INAV turns off by default
    go first: GLONASS, then BeiDou, then Galileo;
  • the settings are not touched, so the same configuration gets everything it asked for
    on a receiver that can run it;
  • the count is only used next to a fresh mask, since it is not cleared between
    detection attempts.

Both paths use it, CFG-VALSET for M10 and later and CFG-GNSS for M8 and M9, so the
M8 case above stops being a refusal and becomes three constellations that work.

Testing

SITL on Windows against an emulated u-blox that answers MON-VER and MON-GNSS and streams NAV-PVT. The F10 emulator applies its manual's rules and NAKs the message if it sees a Glonass key, B1I enabled, or B1C turned off while B2a is on. What reaches the receiver in the constellation VALSET:

emulated receiver keys Glonass keys Beidou L1 NAK fix
M10 12 2 B1I 0 3D
M10, gps_ublox_use_glonass = ON 12 2 B1C 0 3D
F10 10 0 B1C 0 3D
F10, gps_ublox_use_glonass = ON 10 0 B1C 0 3D

Before the change the two F10 cases were rejected, the first for both reasons and the second for the Glonass keys alone, and no part of the constellation configuration reached the receiver. The two M10 cases are byte for byte what they were.

For the second fix, with the emulated receiver refusing anything past its reported
limit, as the manuals describe:

emulated receiver selected before after
M10 reporting 3, all four selected GPS+GAL+BDS+GLO NAK, gps_ublox_use_glonass rewritten to OFF runs GPS+GAL+BDS, settings untouched
M10 reporting 2, all four selected GPS+GAL+BDS+GLO NAK, settings rewritten runs GPS+GAL
M8 reporting 3, all four selected GPS+GAL+BDS+GLO NAK, settings rewritten runs GPS+GAL+BDS
M10 reporting 4, all four selected GPS+GAL+BDS+GLO all four all four, unchanged
F10 reporting 3 GPS+GAL+BDS+GLO (see table above) runs GPS+GAL+BDS, GLONASS was never possible

Also checked, because the capability state outlives a protocol restart: an M10 that goes quiet long enough for the state machine to restart, then comes back with its MON-GNSS reply lost, still gets all 12 keys, exactly as it does on maintenance-10.x.

Not tested on hardware yet. The emulators follow the published rules, but a real
NEO-F10N, and an M8 with all four constellations selected, would both be worth a
check.

@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

Avoid unsupported GLONASS keys on u-blox F10 receivers

🐞 Bug fix 🕐 Less than 10 minutes

Grey Divider

AI Description

• Omits Glonass CFG-VALSET keys when MON-GNSS confirms Glonass is unavailable.
• Preserves existing configuration when capabilities are unknown or Glonass is supported.
• Prevents F10 receivers rejecting unrelated SBAS, Galileo, BeiDou, and QZSS settings.
Diagram

graph TD
    A["MON-GNSS Poll"] --> B["Capability Mask"] --> C{"Glonass known absent?"}
    C -- Yes --> D["10-key VALSET"] --> F["Receiver Config"]
    C -- No --> E["12-key VALSET"] --> F
Loading
High-Level Assessment

The chosen approach is optimal: it reuses the authoritative MON-GNSS capability response and safely truncates trailing Glonass entries without duplicating payload definitions. Detecting F10 models from version strings would be more brittle, while always omitting Glonass would regress supported M10 receivers.

Files changed (1) +7 / -2

Bug fix (1) +7 / -2
gps_ublox.cExclude unsupported Glonass keys from CFG-VALSET +7/-2

Exclude unsupported Glonass keys from CFG-VALSET

• Checks the previously populated MON-GNSS capability state before configuring protocol 27+ receivers. When capabilities explicitly report no Glonass support, the payload omits the final two Glonass keys; otherwise, the existing 12-key behavior remains unchanged.

src/main/io/gps_ublox.c

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

qodo-free-for-open-source-projects Bot commented Sep 17, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Receiver restarts skip Glonass setup ✓ Resolved 🐞 Bug ≡ Correctness
Description
noGlonass treats a nonzero capMaxGnss as proof of a current capability response, but the
initialization path clears supported without clearing the previously cached capMaxGnss. After
communication loss restarts the protocol, the capability wait therefore completes immediately with
an empty support mask and configureGNSS10() omits both Glonass keys from a Glonass-capable
receiver.
Code

src/main/io/gps_ublox.c[478]

+        const bool noGlonass = (gpsUbloxMaxGnss() != 0) && !gpsUbloxHasGlonass();
Evidence
The protocol initialization clears only supported, enabledGnss, and defaultGnss, while its
wait condition uses capMaxGnss; gpsRestartUBLOX() reruns this initialization after the GPS state
machine detects lost communication. Since the added predicate combines the stale nonzero maximum
with the newly cleared support mask, it evaluates noGlonass as true before the fresh MON-GNSS
reply is processed.

src/main/io/gps_ublox.c[1269-1277]
src/main/io/gps_ublox.c[1319-1331]
src/main/io/gps.c[591-617]
src/main/io/gps_ublox.c[178-181]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Glonass-key decision can consume mismatched capability state after a GPS protocol restart because `supported` is cleared while `capMaxGnss` remains nonzero from the previous session. This makes configuration proceed before the new MON-GNSS response and incorrectly classify the receiver as lacking Glonass.
## Fix Focus Areas
- src/main/io/gps_ublox.c[1269-1277]
- src/main/io/gps_ublox.c[478-480]
## Recommended Fix
Reset `ubx_capabilities.capMaxGnss` alongside the three capability masks before polling MON-GNSS. This restores the intended wait for a fresh response while retaining the existing fallback that sends all keys if polling exhausts its retries.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review history

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/main/io/gps_ublox.c Outdated
@MrScothh
MrScothh force-pushed the fix/ublox-glonass-keys branch from cc20741 to 42e8c23 Compare September 17, 2026 21:19
@MrScothh

Copy link
Copy Markdown
Contributor Author

Good catch, and it reproduces.

Measured in SITL with an emulated M10 that answers everything, then goes silent long enough for the protocol to restart, then comes back but loses the one MON-GNSS reply. The constellation VALSET that reaches the receiver after the restart:

build keys of which Glonass
maintenance-10.x 12 2
this PR as first pushed 10 0

So the report is right. capMaxGnss survives the restart while the masks are cleared, the wait passes on the stale value, and the predicate then read an empty mask as "no Glonass".

I did not take the suggested fix, though. Clearing capMaxGnss with the masks does remove the mismatch, but it also changes what happens when that reply goes missing: the wait times out and the state machine restarts instead of configuring. Same scenario, measured:

build keys of which Glonass
this PR with capMaxGnss cleared 0, no constellation VALSET at all 0

That trades one regression for a worse one.

The decision now reads the support mask directly, which is cleared in the same place the other masks are:

const bool noGlonass = ubx_capabilities.supported && !gpsUbloxHasGlonass();

An empty mask means MON-GNSS never answered, so nothing is known and every key goes out, exactly as before this PR. No new state, no change to the wait, no change in timing.

After the change:

scenario keys of which Glonass
M10, MON-GNSS answers 12 2
F10, MON-GNSS without Glonass 10 0
F10 with gps_ublox_use_glonass = ON 10 0
restart, MON-GNSS answers 12 2
restart, MON-GNSS reply lost 12 2

Branch force pushed.

@MrScothh
MrScothh force-pushed the fix/ublox-glonass-keys branch from 42e8c23 to e8f77e1 Compare September 17, 2026 21:39
@MrScothh MrScothh changed the title gps: do not send Glonass keys to a receiver that has none gps: configure u-blox F10 receivers with the signals they have Sep 17, 2026
@MrScothh MrScothh changed the title gps: configure u-blox F10 receivers with the signals they have gps: ask u-blox receivers only for the signals and constellations they can run Sep 19, 2026
@sensei-hacker sensei-hacker added this to the 10.1 milestone Sep 20, 2026
An F10 reports the M10 hardware version, so INAV takes the CFG-VALSET path
for it, and then sends two things it cannot accept. Its configuration
database has no Glonass group at all, and its Beidou L1 signal is B1C, not
the B1I that INAV picks by default. Either one makes the receiver reject the
whole message, so SBAS, Galileo, BeiDou and QZSS never get applied.

UBX-MON-GNSS is already polled before the configuration starts and reports
which constellations the receiver has, so leave the Glonass keys out when it
says there are none. An empty mask means MON-GNSS never answered, and then
nothing is known, so every key goes out as before.

The dual band receivers name themselves in the MON-VER extensions, which are
already being read for the constellation list, so pick B1C there. It is what
they have, and their two bands cannot be configured apart.
UBX-MON-GNSS says how many major constellations the receiver can track at
once, and INAV read the number without using it. Asking for more gets the
configuration refused: the receiver answers NAK and keeps what it had, as the
M8 protocol description puts it for UBX-CFG-GNSS. INAV then reset Galileo,
BeiDou and GLONASS to their defaults in its own settings without sending them,
so the receiver ran one configuration, the configurator showed another, and
the next save of any kind replaced the user's choice with the defaults.

The constellations to enable are now worked out once, from the selection, the
constellations the receiver has and how many it can take. When the selection
does not fit, the ones INAV leaves off by default go first: GLONASS, then
BeiDou, then Galileo. The settings are not touched, so the same configuration
gets everything it asked for on a receiver that can run it. Both the
CFG-VALSET path for M10 and later and the CFG-GNSS path for M8 and M9 use it.

The count is only used next to a fresh MON-GNSS mask, since it is not cleared
between detection attempts.
@MrScothh
MrScothh force-pushed the fix/ublox-glonass-keys branch from df62a3e to 1e25a27 Compare September 20, 2026 09:20
@github-actions

Copy link
Copy Markdown

RAM / Flash usage vs. base commit 7e82f68 — commit 1e25a27

Target Flash Δ RAM Δ
MATEKF405 +248 B (+0.04%) CCM: ±0 B (±0.00%)
RAM: ±0 B (±0.00%)
MATEKF722 +200 B (+0.04%) ITCM_RAM: ±0 B (±0.00%)
RAM: +8 B (+0.01%)
TCM: ±0 B (±0.00%)
MATEKF765 +216 B (+0.03%) DTCM_RAM: ±0 B (±0.00%)
SRAM1: ±0 B (±0.00%)
MATEKH743 +216 B (+0.03%) 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

Copy link
Copy Markdown

Test firmware build ready — commit 1e25a27

Download firmware for PR #11965

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.

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