Skip to content

Fix: the SBAS service choice never reaches M10 and F10 receivers - #11978

Open
MrScothh wants to merge 2 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:fix/ublox-m10-sbas-scanmask
Open

MrScothh wants to merge 2 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:fix/ublox-m10-sbas-scanmask

Conversation

@MrScothh

@MrScothh MrScothh commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

What happens

gps_sbas_mode picks which SBAS service the receiver should look for, and INAV sends that choice with UBX-CFG-SBAS. On u-blox 10 and later that message does not exist. The M10 and F10 interface descriptions list, of the whole CFG class, only UBX-CFG-CFG, UBX-CFG-RST, UBX-CFG-VALDEL, UBX-CFG-VALGET and UBX-CFG-VALSET; everything else moved to the configuration interface.

So on an M10 or an F10 the choice is written to a message the receiver answers NAK to, and the receiver goes on searching the PRN list it was shipped with: 0x00072bc8 on the M10 platform, 0x0003ab88 on the F10. Selecting EGNOS, WAAS, MSAS, GAGAN or leaving it on AUTO all give the same behaviour, and picking a service does not stop the receiver from locking onto a satellite of another one. Only NONE works today, and that is because SBAS is switched off elsewhere, through CFG-SIGNAL-SBAS_ENA in the VALSET that configures the constellations.

The change

Those receivers take the same choice through CFG-SBAS-PRNSCANMASK (0x50360006), an 8-byte bitfield where bit 0 is PRN 120, each bit up to PRN 183, and all zeroes means scan every PRN. That is the layout INAV's ubloxScanMode1 table is already written in, the one it passes to UBX-CFG-SBAS as scanmode1, so the table is sent as is:

if (ubloxVersionGTE(34, 0)) {
    ubx_config_data64_payload_t scanValues[] = {
        {UBLOX_CFG_SBAS_PRNSCANMASK, ubloxScanMode1[gpsState.gpsConfig->sbasMode]}
    };
    ubloxSendSetCfgU8(scanValues, 1);
    return;
}

The branch is taken on the protocol version, because that is what says whether the old message exists, and it draws the line exactly where it belongs:

product protocol version UBX-CFG-SBAS
u-blox F9 HPG 1.32 27.31 yes
u-blox M9 SPG 4.04 32.01 yes
u-blox M10 SPG 5.10 34.10 no
u-blox F10 SPG 6.00 40.00 no

So an M8, an M9 or an F9 keeps the message it has, untouched, and nothing here depends on the hardware version being recognised.

The key is 8 bytes wide and the driver only had VALSET helpers for 1-byte and 2-byte values, so this adds ubloxCfgFillU8() next to ubloxCfgFillU1() and ubloxCfgFillU2(), same shape, same RAM-only layer, same checksum path.

Worth knowing before merging

On an M10 or F10, AUTO now means what it already means on an M8: search every SBAS PRN. Until now it meant the list the module happened to ship with, which is narrower. Anyone who had picked a service and was getting a satellite of a different one will now get the one they picked, and anyone on AUTO in a region the shipped mask left out will start seeing SBAS. The default is NONE, so this only reaches setups where SBAS was deliberately turned on.

maxSBAS has no equivalent key in the configuration interface, so the number of SBAS satellites is left to the receiver, which is what it already does on these platforms.

Testing

Emulated receivers on SITL, reading the bytes off the wire. The emulated M10 and F10 answer NAK to UBX-CFG-SBAS, since the interface description does not list it for those platforms:

receiver gps_sbas_mode before after
M10 AUTO CFG-SBAS, rejected PRNSCANMASK 0x0
M10 EGNOS CFG-SBAS, rejected PRNSCANMASK 0x4001004A
M10 WAAS CFG-SBAS, rejected PRNSCANMASK 0x4A800
M10 NONE CFG-SBAS, rejected PRNSCANMASK 0x0, SBAS signal off as before
F10 GAGAN CFG-SBAS, rejected PRNSCANMASK 0x1180
M8 AUTO CFG-SBAS, scanmode1 0x0 unchanged
M8 EGNOS CFG-SBAS, scanmode1 0x4001004A unchanged
M9 AUTO CFG-SBAS, scanmode1 0x0 unchanged
M9 EGNOS CFG-SBAS, scanmode1 0x4001004A unchanged

gps_ublox_unittest gets a case that builds the frame and compares all 24 bytes, header to checksum, against the message the interface description specifies.

Not tried on hardware yet.

Notes

Independent of my other open pull request on this file: that one is in configureGNSS10(), this one is in configureSBAS(). They can go in in either order; if both land, git merges them without help.

@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

Send SBAS scan masks to u-blox M10 and F10 receivers

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Routes M10/F10 SBAS service masks through supported CFG-VALSET configuration.
• Preserves legacy UBX-CFG-SBAS behavior for M8 and M9 receivers.
• Adds 64-bit VALSET serialization with frame and capacity tests.
Diagram

graph TD
    A["SBAS Choice"] --> B["configureSBAS"] --> C{"Modern u-blox?"}
    C -->|Yes| D["64-bit VALSET"] --> E["M10 or F10"]
    C -->|No| F["CFG-SBAS"] --> G["M8 or M9"]
Loading
High-Level Assessment

The targeted 64-bit VALSET helper is appropriate because it reuses the existing scan-mask table and checksum path while preserving legacy receiver behavior. A generic mixed-width VALSET serializer could reduce helper duplication, but it would broaden this compatibility fix and increase regression risk without improving the immediate SBAS behavior.

Files changed (5) +95 / -0

Enhancement (3) +47 / -0
gps_ublox.hDefine 64-bit u-blox configuration payloads +17/-0

Define 64-bit u-blox configuration payloads

• Introduces packed key/value and message structures for eight-byte CFG-VALSET items. The message capacity is limited to four entries with checksum storage included.

src/main/io/gps_ublox.h

gps_ublox_utils.cSerialize eight-byte CFG-VALSET entries +29/-0

Serialize eight-byte CFG-VALSET entries

• Adds a RAM-layer CFG-VALSET builder for 64-bit values, including entry clamping, packed payload copying, message sizing, and checksum generation.

src/main/io/gps_ublox_utils.c

gps_ublox_utils.hExpose the 64-bit CFG-VALSET builder +1/-0

Expose the 64-bit CFG-VALSET builder

• Declares the new ubloxCfgFillU8 utility for use by the GPS driver and unit tests.

src/main/io/gps_ublox_utils.h

Bug fix (1) +23 / -0
gps_ublox.cRoute modern SBAS configuration through CFG-VALSET +23/-0

Route modern SBAS configuration through CFG-VALSET

• Adds a sender for 64-bit CFG-VALSET values. M10 and newer hardware now receive CFG-SBAS-PRNSCANMASK, while older receivers continue using UBX-CFG-SBAS unchanged.

src/main/io/gps_ublox.c

Tests (1) +25 / -0
gps_ublox_unittest.ccVerify 64-bit SBAS VALSET frame generation +25/-0

Verify 64-bit SBAS VALSET frame generation

• Checks the complete EGNOS PRN scan-mask frame byte-for-byte, including little-endian encoding and checksum. Also verifies that oversized entry counts are clamped to prevent buffer overruns.

src/test/unit/gps_ublox_unittest.cc

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

qodo-free-for-open-source-projects Bot commented Sep 19, 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. F10 receivers still ignore the choice ✓ Resolved 🐞 Bug ≡ Correctness
Description
configureSBAS() requires gpsState.hwVersion >= UBX_HW_VERSION_UBLOX10, but the hardware-version
model only represents the N/M series and its decoder recognizes only the M10 hardware string. When
an F10 is classified as unknown, it falls through to the unsupported legacy SBAS message, so every
enabled service selection still leaves that receiver's scan mask unchanged.
Code

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

+    if (ubloxVersionGT(23, 1) && gpsState.hwVersion >= UBX_HW_VERSION_UBLOX10) {
Evidence
The new branch is entered only for hardware values ordered at or above the N/M-series M10 constant.
The hardware encoding explicitly reserves other series such as F-series receivers for future
representation, while ubloxParseHardwareVersion() recognizes M8, M9, and the exact M10 string
before returning UBX_HW_VERSION_UNKNOWN for everything else; therefore the current model provides
no F10 value capable of satisfying the added gate.

src/main/io/gps_ublox.c[582-591]
src/main/io/gps_ublox.c[634-650]
src/main/io/gps_ublox.h[67-83]

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 new SBAS configuration branch is gated by an N/M-series M10 hardware value, while the hardware model and decoder have no F10 representation. Consequently, an F10 classified as unknown continues receiving the unsupported legacy SBAS message.
## Fix Focus Areas
- src/main/io/gps_ublox.c[585-591]
- src/main/io/gps_ublox.c[620-650]
- src/main/io/gps_ublox.h[67-83]
## Recommended Fix
Add an F-series/F10 hardware classification and decode its reported hardware version, then route both M10 and F10 by an explicit configuration-interface capability predicate rather than an ordering comparison tied to the N/M series. Add a configureSBAS-level test using the F10 hardware identity to verify that it emits CFG-VALSET rather than UBX-CFG-SBAS.

ⓘ 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

Copy link
Copy Markdown
Contributor Author

An F10 does satisfy that gate, because it reports the u-blox 10 hardware version.

The F/M distinction is not in the hardware version at all. The F10 SPG 6.00 interface description prints the hardware line of UBX-MON-VER as HW UBX 10, the same family the M10 SPG 5.10 interface description prints as HW UBX 10 000A0000, and gpsDecodeHardwareVersion() maps that string to UBX_HW_VERSION_UBLOX10. What tells an F10 from an M10 is the firmware: EXT SPGL1L5 6.00 rather than ROM SPG 5.10, PROTVER=40.00 rather than 34, and MOD=NEO-F10N among the version extensions. That is also why this branch checks ubloxVersionGT(23, 1) next to the hardware version: the protocol version is what actually says the configuration interface is there.

On the bench the emulated F10, which answers exactly what those documents specify, is detected as 0x4A and does take the new branch: with gps_sbas_mode = GAGAN it receives CFG-SBAS-PRNSCANMASK 0x1180 and no legacy CFG-SBAS at all. Before this change the same receiver got the legacy message and answered NAK, which is the bug.

I have not had a physical NEO-F10N in hand, so the hardware version there comes from the documents rather than from a receiver on my desk. If anyone has one connected, a UBX-MON-VER dump would settle it for good.

@sensei-hacker sensei-hacker added this to the 10.1 milestone Sep 19, 2026
@MrScothh

Copy link
Copy Markdown
Contributor Author

Pushed a follow-up that removes the hardware version from this decision entirely.

What actually decides whether UBX-CFG-SBAS exists is the protocol version, and it draws the line exactly where it belongs:

product protocol version UBX-CFG-SBAS
u-blox F9 HPG 1.32 27.31 yes
u-blox M9 SPG 4.04 32.01 yes
u-blox M10 SPG 5.10 34.10 no
u-blox F10 SPG 6.00 40.00 no

So the branch is now ubloxVersionGTE(34, 0). An M9 or an F9 keeps the message it has, an M10 or an F10 gets the key, and nothing depends on gpsDecodeHardwareVersion() recognising anything.

The bench grew two cases for the receiver in the middle, the M9, which knows CFG-VALSET and still has UBX-CFG-SBAS: it must keep getting the old message. Nine cases now, all passing, and gps_ublox_unittest still checks the frame byte for byte.

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

RAM / Flash usage vs. base commit 7e82f68 — commit 5d5675b

Target Flash Δ RAM Δ
MATEKF405 +208 B (+0.03%) CCM: ±0 B (±0.00%)
RAM: ±0 B (±0.00%)
MATEKF722 +176 B (+0.04%) ITCM_RAM: ±0 B (±0.00%)
RAM: ±0 B (±0.00%)
TCM: ±0 B (±0.00%)
MATEKF765 +192 B (+0.03%) DTCM_RAM: ±0 B (±0.00%)
SRAM1: ±0 B (±0.00%)
MATEKH743 +192 B (+0.02%) 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 5d5675b

Download firmware for PR #11978

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.

INAV picks the SBAS service with UBX-CFG-SBAS, which M10 and F10 receivers
do not have: their interface descriptions list only CFG-CFG, CFG-RST and the
three CFG-VAL messages. The selection never reached them, and they kept
searching the PRN list they were shipped with whether EGNOS, WAAS or AUTO was
chosen.

On those receivers the same scan mask now goes out as CFG-SBAS-PRNSCANMASK
through CFG-VALSET. The bit layout is the one scanmode1 already uses, PRN120
in bit 0, so the existing table is sent unchanged, and zero keeps meaning
every PRN. M8 and M9 receivers still get UBX-CFG-SBAS as before.

The key is eight bytes wide, so this adds an eight byte variant of the
CFG-VALSET helpers, with a unit test that checks the frame byte for byte.
The message this replaces disappears with protocol 34, which is where the CFG
class was reduced to the configuration interface. Keying on that says exactly
what is meant, and does not depend on the hardware version being recognised:
the M10 platform reports 34, the F10 40, while the M9 reports 32 and the F9 27,
and both of those still have UBX-CFG-SBAS and keep using it.
@MrScothh
MrScothh force-pushed the fix/ublox-m10-sbas-scanmask branch from f45c42f to 5d5675b Compare September 20, 2026 09:20
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