Skip to content

GPS: report what the receiver actually has, and allow NavIC where it exists - #11979

Open
MrScothh wants to merge 5 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:feature/gps-report-constellations
Open

MrScothh wants to merge 5 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:feature/gps-report-constellations

Conversation

@MrScothh

Copy link
Copy Markdown
Contributor

What happens

The u-blox driver already asks every receiver what it is and what it has. UBX-MON-GNSS gives the major constellations it supports, which of them are running, and how many it can track at once; UBX-MON-VER gives the firmware, the module name and the list of augmentation systems. INAV keeps the first three in ubx_capabilities and throws the rest away, and none of it leaves the flight controller.

So the configurator has one byte to work with, the hardware version, and has to offer the same three switches to everyone. A NEO-F10N has no GLONASS at all, yet the GLONASS switch is there; NavIC exists on the F10 and on nothing else INAV supports, so it is offered to no one; and the tab can only say "u-blox M10" when the receiver has been telling us it is a NEO-M10N all along.

The changes

MSP_GPSSTATISTICS says what the receiver reported. Five fields appended after hwVersion: the supported and enabled MON-GNSS masks, the augmentation systems from the MON-VER extensions, how many constellations the receiver can run at once, and the module name as a length-prefixed string. Appended, so anything reading the old layout keeps working. Zero everywhere when the provider is not u-blox or the receiver has not answered yet, which the configurator reads as "unknown" rather than "not supported".

The MON-VER extensions are parsed. Each 30-byte extension line is scanned for MOD=, which is the module's own name, and for the SBAS;QZSS;NAVIC style list of augmentation systems, which UBX-MON-GNSS does not carry. Both are cleared when the protocol thread restarts, so a receiver swapped on a bench does not inherit the previous one's answers.

gps_ublox_use_navic. NavIC is the Indian regional system, visible over India and the region around it. Its keys, CFG-SIGNAL-NAVIC_ENA and CFG-SIGNAL-NAVIC_L5_ENA, exist on the F10 and not on the M10, and a single unknown key makes a receiver reject the whole CFG-VALSET, so NavIC goes out in a message of its own and only to a receiver that has listed NAVIC in its MON-VER extensions. Default off, as the F10 ships it.

Testing

SITL on Windows against emulated M8, M10 and F10 receivers that reject unknown keys the way the real ones do, reading what INAV writes to the wire:

receiver gps_ublox_use_navic what goes out keys rejected
F10 ON NAVIC_ENA 1, NAVIC_L5_ENA 1, in their own VALSET none
F10 OFF NAVIC_ENA 0, NAVIC_L5_ENA 0 none
M10 ON nothing, the receiver never listed NavIC none
M8 ON nothing, no configuration interface none

The constellation VALSET goes out unchanged in all four, which is the point of sending NavIC separately.

gps_ublox_unittest and gps_null_port_unittest pass.

Not tried on a real F10 yet.

Notes

The configurator side, which uses these fields to offer only the constellations the receiver has and to put the module name in the tab title, is a separate pull request; it degrades to today's behaviour against a flight controller without this change, since the fields come back as zero.

@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

Report u-blox capabilities and add capability-gated NavIC support

✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Exposes receiver-reported constellations, limits, and module identity through backward-compatible
 MSP fields.
• Parses MON-VER extensions and resets discovered capabilities when the GPS protocol restarts.
• Adds optional NavIC configuration only for receivers explicitly reporting support.
Diagram

sequenceDiagram
    participant Receiver as u-blox Receiver
    participant Driver as UBX Driver
    participant Config as GPS Config
    participant MSP as MSP Statistics
    participant UI as Configurator
    Receiver-->>Driver: MON-GNSS and MON-VER
    Driver->>Driver: Store reported capabilities
    Config->>Driver: NavIC preference
    Driver->>Receiver: Gated NavIC VALSET
    Driver->>MSP: Capability metadata
    MSP-->>UI: Additive statistics fields
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Static receiver model allowlist
  • ➕ Avoids parsing capability strings from MON-VER extensions.
  • ➕ Can encode known behavior for validated receiver models.
  • ➖ Hardware version cannot reliably distinguish F10 from M10 receivers.
  • ➖ Requires firmware updates whenever new modules or variants appear.
  • ➖ Risks enabling unsupported keys when model detection is incomplete.
2. Include NavIC in the existing VALSET
  • ➕ Uses one fewer configuration transaction.
  • ➕ Keeps all constellation settings in one message.
  • ➖ Receivers reject the entire VALSET when any key is unknown.
  • ➖ Could prevent otherwise valid constellation settings from applying.
  • ➖ Would make enabling the preference unsafe on M10-class hardware.

Recommendation: Keep the PR's capability-driven approach. Parsing receiver-reported support avoids brittle model assumptions, while a separate NavIC VALSET isolates unsupported keys and preserves existing constellation configuration. Appending MSP fields is also the safest backward-compatible protocol extension.

Files changed (9) +169 / -1

Enhancement (3) +104 / -0
fc_msp.cSerialize receiver-reported capabilities over MSP +16/-0

Serialize receiver-reported capabilities over MSP

• Appends u-blox constellation masks, concurrency, and module identity to MSP_GPSSTATISTICS. Non-u-blox and undiscovered values serialize as zero or an empty string for backward-compatible unknown handling.

src/main/fc/fc_msp.c

gps_ublox.cDiscover receiver capabilities and configure NavIC safely +70/-0

Discover receiver capabilities and configure NavIC safely

• Parses MON-VER extension records for the module name, SBAS, QZSS, and NavIC while exposing existing MON-GNSS capability data. Sends NavIC keys in an isolated VALSET only when the receiver advertises support, and clears discovered extension data when the protocol restarts.

src/main/io/gps_ublox.c

gps_ublox.hDefine NavIC keys and capability interfaces +18/-0

Define NavIC keys and capability interfaces

• Adds u-blox NavIC configuration keys, extended-constellation masks, module-name limits, and public accessors for receiver-reported metadata.

src/main/io/gps_ublox.h

Documentation (3) +57 / -0
Settings.mdDocument the NavIC receiver setting +10/-0

Document the NavIC receiver setting

• Documents the new 'gps_ublox_use_navic' option, its geographic scope, receiver compatibility, and default-off behavior.

docs/Settings.md

README.mdDocument appended GPS capability statistics +6/-0

Document appended GPS capability statistics

• Extends the MSP_GPSSTATISTICS layout documentation with supported, enabled, and extended GNSS masks, concurrency limits, and the length-prefixed module name.

docs/development/msp/README.md

msp_messages.jsonAdd GPS capability fields to the MSP specification +41/-0

Add GPS capability fields to the MSP specification

• Adds machine-readable definitions for the four capability bytes and variable-length module name appended to MSP_GPSSTATISTICS.

docs/development/msp/msp_messages.json

Other (3) +8 / -1
settings.yamlRegister the NavIC configuration setting +5/-0

Register the NavIC configuration setting

• Defines the default-off 'gps_ublox_use_navic' boolean setting and maps it to the GPS configuration structure.

src/main/fc/settings.yaml

gps.cInitialize NavIC configuration defaults +2/-1

Initialize NavIC configuration defaults

• Adds the generated NavIC default to the GPS parameter-group reset template.

src/main/io/gps.c

gps.hStore the NavIC preference in GPS configuration +1/-0

Store the NavIC preference in GPS configuration

• Extends 'gpsConfig_t' with the persistent 'ubloxUseNavic' boolean.

src/main/io/gps.h

@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


Remediation recommended

1. Swapped receivers report stale limits ✓ Resolved 🐞 Bug ≡ Correctness
Description
gpsUbloxMaxGnss() exposes ubx_capabilities.capMaxGnss, but the protocol restart reset clears the
other capability fields without clearing this value. After a receiver previously supplied MON-GNSS,
a restart or receiver swap continues returning its concurrency limit until another valid MON-GNSS
response overwrites it, including when the replacement never answers.
Code

src/main/fc/fc_msp.c[1076]

+        sbufWriteU8(dst, isGpsUblox() ? gpsUbloxMaxGnss() : 0);
Evidence
The MSP addition serializes the getter directly, while the getter returns persistent static state.
MON-GNSS is the only assignment to capMaxGnss, and the restart initialization explicitly resets
adjacent capability members but omits it before restarting receiver discovery.

src/main/fc/fc_msp.c[1071-1076]
src/main/io/gps_ublox.c[218-227]
src/main/io/gps_ublox.c[831-839]
src/main/io/gps_ublox.c[1318-1342]
src/main/io/gps_ublox.c[1384-1396]

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 newly exposed maximum-constellation value can belong to a receiver used before the current protocol restart because `capMaxGnss` is not reset with the other capability fields.
## Fix Focus Areas
- src/main/io/gps_ublox.c[1318-1342]
- src/main/fc/fc_msp.c[1071-1076]
## Recommended Fix
Clear `capMaxGnss` together with every other receiver capability at the beginning of each protocol-thread initialization, before any version or capability polling. Ensure MSP continues reporting zero until the current receiver supplies a valid MON-GNSS response.

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


2. Malformed GPS replies read past memory ✓ Resolved 🐞 Bug ☼ Reliability
Description
The new MON-VER loop accepts any remaining byte as an extension start but passes a fixed 30-byte
range to strnstr() and may copy another 15 bytes from the returned position. A checksum-valid
payload whose length does not contain complete 30-byte records can therefore make the final
iteration read beyond _buffer.bytes, especially near the accepted maximum payload size.
Code

src/main/io/gps_ublox.c[R811-814]

+                    const char * mod = strnstr(line, "MOD=", 30);
+                    if (mod) {
+                        strncpy(ubxModuleName, mod + 4, UBLOX_MODULE_NAME_LEN - 1);
+                        ubxModuleName[UBLOX_MODULE_NAME_LEN - 1] = '\0';
Evidence
The parser permits payloads up to the exact byte-array capacity and does not append a terminator.
The added loop only checks j < _payload_length, while the added searches inspect up to 30 bytes
and the module copy can inspect 15 more bytes from within that range.

src/main/io/gps_ublox.c[150-164]
src/main/io/gps_ublox.c[807-826]
src/main/io/gps_ublox.c[950-979]
src/main/io/gps_ublox.h[35-38]

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

## Issue description
MON-VER extension parsing can inspect bytes beyond the received payload and receive buffer when the payload ends with an incomplete 30-byte extension record.
## Fix Focus Areas
- src/main/io/gps_ublox.c[808-825]
- src/main/io/gps_ublox.h[35-38]
## Recommended Fix
Iterate only while a complete extension record remains, using a condition equivalent to `j + 30 <= _payload_length`. Bound module-name extraction to the bytes remaining within that same record, copy no more than the destination capacity minus one, and terminate the destination explicitly.

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


3. The message schema keeps an old version ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
msp_messages.json adds six payload entries while retaining its top-level 2.1.0 version. The
repository classifies this as a backward-compatible schema extension requiring a minor-version
increment, so schema snapshots cannot distinguish the new layout from the preceding 2.1.0
definition.
Code

docs/development/msp/msp_messages.json[R4927-4930]

+                    {
+                        "name": "supportedGnss",
+                        "ctype": "uint8_t",
+                        "desc": "Bitmask: major constellations the receiver reports in UBX-MON-GNSS: Bit 0=GPS, Bit 1=GLONASS, Bit 2=BeiDou, Bit 3=Galileo. 0 if the receiver has not reported them, or the provider is not u-blox",
Evidence
The schema remains version 2.1.0 while the PR adds several fields to an existing reply. The
repository's adjacent development documentation explicitly requires incrementing the minor version
for a backward-compatible schema extension.

docs/development/msp/msp_messages.json[1-6]
docs/development/msp/msp_messages.json[4926-4967]
docs/development/msp/README.md[20-26]

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 MSP JSON schema gained backward-compatible payload fields without the version increment required by its documented versioning process.
## Fix Focus Areas
- docs/development/msp/msp_messages.json[2-6]
- docs/development/msp/msp_messages.json[4926-4966]
- docs/development/msp/README.md[20-26]
## Recommended Fix
Increment the schema minor version and reset its patch component according to the documented rule, changing 2.1.0 to 2.2.0.

ⓘ 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/fc/fc_msp.c
Comment thread src/main/io/gps_ublox.c
Comment thread docs/development/msp/msp_messages.json
@MrScothh

Copy link
Copy Markdown
Contributor Author

Thanks, two of the three were real and are fixed in the push above.

The concurrency limit outliving a receiver. Correct, and it is worse than a stale MSP field: capMaxGnss is also the exit condition of the poll loop right below the reset, so a non-zero leftover ended that loop before the new receiver had answered anything. It is now cleared with the other capabilities.

Reading past the payload. Also correct. The loop now takes whole extensions only, j + 30 <= _payload_length, and the module name is copied with the room left in its own record rather than a fixed 15 bytes.

The schema version. Bumped, to 2.1.1. The rule in docs/development/msp/README.md asks for a minor bump on a backward-compatible schema extension and a patch bump for a "message/content/docs-only update inside current schema". Nothing about the schema changed here: the entries added are ordinary payload fields using the keys the format already defines, so this is content inside the current schema. Happy to make it 2.2.0 instead if the maintainers read that line the other way.

@MrScothh

Copy link
Copy Markdown
Contributor Author

The configurator side is open as iNavFlight/inav-configurator#2778.

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

Copy link
Copy Markdown
Contributor Author

Taking the stricter reading on the registry version, as offered above: it is 2.2.0 now rather than 2.1.1. New payload fields do extend what the registry describes while leaving anything reading the old layout working, and on reflection that is what the minor bump in the rule is for, otherwise nothing would ever qualify for one.

@github-actions

github-actions Bot commented Sep 20, 2026

Copy link
Copy Markdown

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

Target Flash Δ RAM Δ
MATEKF405 +1040 B (+0.15%) CCM: ±0 B (±0.00%)
RAM: +16 B (+0.01%)
MATEKF722 +480 B (+0.10%) ITCM_RAM: ±0 B (±0.00%)
RAM: +16 B (+0.02%)
TCM: ±0 B (±0.00%)
MATEKF765 +1056 B (+0.14%) DTCM_RAM: ±0 B (±0.00%)
SRAM1: +16 B (+0.01%)
MATEKH743 +1040 B (+0.13%) 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 d651af6

Download firmware for PR #11979

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.

The configurator has no way of knowing what the receiver in front of it can
actually do. It shows the same list of settings for every u-blox module, so a
NEO-F10N is offered Glonass it does not have, and the hardware version alone
cannot tell an F10 from an M10 because both report 000A0000.

INAV already asks the receiver: UBX-MON-GNSS says which constellations are
present, which are running and how many can run at once, and the MON-VER
extensions carry the module name and the augmentation systems. None of it left
the flight controller.

MSP_GPSSTATISTICS now carries it, appended after the hardware version so older
configurators keep reading the fields they know.
INAV never configured NavIC, so a receiver that has it, such as the u-blox
F10, kept whatever it was shipped with, which is off. There was no setting to
change that short of writing the receiver's flash from u-center.

gps_ublox_use_navic, off by default, now sets CFG-SIGNAL-NAVIC_ENA and
NAVIC_L5_ENA. The keys go only to receivers that list NAVIC in their MON-VER
extensions, and in a CFG-VALSET of their own: a receiver refuses a whole
message over one key it does not know, so they cannot share one with the
constellations every receiver gets.

The new field goes at the end of gpsConfig_t, where pgLoad gives it its
default on an existing configuration, as was done for autoBaudMax.
The reply now carries what the receiver reported about itself: the major
constellations it has and which of them are running, the augmentation systems
that only MON-VER lists, how many it can track at once, and its module name.
README.md regenerated from the registry with gen_msp_md.py.
…e receiver

Two things came out of review. The MON-VER extension loop took any remaining
byte as the start of a record and then read a fixed thirty from it, so a frame
whose length is not a round number of records had its last line read past the
payload; it now takes whole records only, and the module name is copied with the
room left in its own record. And capMaxGnss survived a protocol restart while
the masks beside it were cleared, which both reported a swapped receiver's limit
and ended the capability poll before the new receiver had answered.

The message registry gets the patch bump its own rule asks for.
New payload fields extend what the registry describes while leaving anything
reading the old layout working, which is the minor bump its own rule asks for.
@MrScothh
MrScothh force-pushed the feature/gps-report-constellations branch from 307b3ea to d651af6 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