From 472ac3450ee2958a8e5368e1361ddb25d7cf4afb Mon Sep 17 00:00:00 2001 From: MrScothh <167884257+MrScothh@users.noreply.github.com> Date: Fri, 18 Sep 2026 00:31:42 +0200 Subject: [PATCH 1/5] gps: report the receiver's constellations and name over MSP 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. --- src/main/fc/fc_msp.c | 16 +++++++++++++ src/main/io/gps_ublox.c | 50 +++++++++++++++++++++++++++++++++++++++++ src/main/io/gps_ublox.h | 14 ++++++++++++ 3 files changed, 80 insertions(+) diff --git a/src/main/fc/fc_msp.c b/src/main/fc/fc_msp.c index d00984f2d85..ebb34700237 100644 --- a/src/main/fc/fc_msp.c +++ b/src/main/fc/fc_msp.c @@ -1070,6 +1070,22 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF sbufWriteU16(dst, gpsSol.eph); sbufWriteU16(dst, gpsSol.epv); sbufWriteU8(dst, gpsState.hwVersion); + // Which constellations the receiver has, and which of them are running, so the + // configurator can stop offering the ones that are not there. Zero means unknown + sbufWriteU8(dst, isGpsUblox() ? gpsUbloxSupportedGnss() : 0); + sbufWriteU8(dst, isGpsUblox() ? gpsUbloxEnabledGnss() : 0); + // SBAS, QZSS and NavIC, which MON-GNSS does not report, and how many major + // constellations the receiver can run at once + sbufWriteU8(dst, isGpsUblox() ? gpsUbloxExtendedGnss() : 0); + sbufWriteU8(dst, isGpsUblox() ? gpsUbloxMaxGnss() : 0); + // The module's own name, as it reports it, so the configurator can say which + // receiver this is instead of guessing from the hardware version + { + const char * module = isGpsUblox() ? gpsUbloxModuleName() : ""; + const uint8_t len = strlen(module); + sbufWriteU8(dst, len); + sbufWriteData(dst, module, len); + } break; #endif case MSP2_ADSB_VEHICLE_LIST: diff --git a/src/main/io/gps_ublox.c b/src/main/io/gps_ublox.c index 703242d10dd..b51fad8cecb 100755 --- a/src/main/io/gps_ublox.c +++ b/src/main/io/gps_ublox.c @@ -87,6 +87,12 @@ static const char * baudInitDataNMEA[GPS_BAUDRATE_COUNT] = { static ubx_nav_sig_info satelites[UBLOX_MAX_SIGNALS] = {}; +// The module name from the MON-VER extensions, as in MOD=NEO-F10N. Empty when not reported +static char ubxModuleName[UBLOX_MODULE_NAME_LEN] = ""; + +// SBAS, QZSS and NavIC, which the receiver lists in MON-VER but not in MON-GNSS +static uint8_t ubxExtendedGnss = 0; + // MON-RF noise value (noisePerMS) reported by UBX-MON-RF as U2 at payload offset 0x10 static uint16_t monRfNoisePerMs = 0; static uint16_t monAgcCount = 0; @@ -215,6 +221,27 @@ uint8_t gpsUbloxMaxGnss(void) return ubx_capabilities.capMaxGnss; } +// The MON-GNSS masks as the receiver reports them: bit 0 GPS, 1 Glonass, 2 Beidou, 3 Galileo +uint8_t gpsUbloxSupportedGnss(void) +{ + return ubx_capabilities.supported; +} + +uint8_t gpsUbloxEnabledGnss(void) +{ + return ubx_capabilities.enabledGnss; +} + +const char * gpsUbloxModuleName(void) +{ + return ubxModuleName; +} + +uint8_t gpsUbloxExtendedGnss(void) +{ + return ubxExtendedGnss; +} + timeMs_t gpsUbloxCapLastUpdate(void) { return gpsState.lastCapaUpdMs; @@ -765,6 +792,27 @@ static bool gpsParseFrameUBLOX(void) break; } } + + for (int j = 40; j < _payload_length; j += 30) { + const char * line = (const char *)(_buffer.bytes + j); + + 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'; + } + + // The augmentation and regional systems, which MON-GNSS does not carry + if (strnstr(line, "SBAS", 30)) { + ubxExtendedGnss |= UBLOX_EXT_GNSS_SBAS; + } + if (strnstr(line, "QZSS", 30)) { + ubxExtendedGnss |= UBLOX_EXT_GNSS_QZSS; + } + if (strnstr(line, "NAVIC", 30)) { + ubxExtendedGnss |= UBLOX_EXT_GNSS_NAVIC; + } + } } } break; @@ -1250,6 +1298,8 @@ STATIC_PROTOTHREAD(gpsProtocolStateThread) // Attempt to detect GPS hw version gpsState.hwVersion = UBX_HW_VERSION_UNKNOWN; + ubxModuleName[0] = '\0'; + ubxExtendedGnss = 0; gpsState.autoConfigStep = 0; // Configure GPS module if enabled diff --git a/src/main/io/gps_ublox.h b/src/main/io/gps_ublox.h index 75f10901035..549de66831b 100644 --- a/src/main/io/gps_ublox.h +++ b/src/main/io/gps_ublox.h @@ -535,7 +535,21 @@ typedef enum { NAV_STATUS_FIX_VALID = 1 } ubx_nav_status_bits_t; +// Long enough for the names u-blox ships, such as NEO-F10N and ZED-F9P +#define UBLOX_MODULE_NAME_LEN 16 + +/* The augmentation and regional systems a receiver lists in its MON-VER extensions. + * UBX-MON-GNSS only reports the four major constellations, so these come from the + * version strings instead, where the list reads SBAS;QZSS and NAVIC. */ +#define UBLOX_EXT_GNSS_SBAS (1 << 0) +#define UBLOX_EXT_GNSS_QZSS (1 << 1) +#define UBLOX_EXT_GNSS_NAVIC (1 << 2) + uint8_t gpsUbloxMaxGnss(void); +uint8_t gpsUbloxSupportedGnss(void); +uint8_t gpsUbloxEnabledGnss(void); +const char * gpsUbloxModuleName(void); +uint8_t gpsUbloxExtendedGnss(void); timeMs_t gpsUbloxCapLastUpdate(void); bool gpsUbloxHasGalileo(void); From e45e1644d0b6204505388c7c82e764228807e0db Mon Sep 17 00:00:00 2001 From: MrScothh <167884257+MrScothh@users.noreply.github.com> Date: Sat, 19 Sep 2026 16:43:03 +0200 Subject: [PATCH 2/5] gps: let NavIC be switched on where the receiver has it 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. --- docs/Settings.md | 10 ++++++++++ src/main/fc/settings.yaml | 5 +++++ src/main/io/gps.c | 3 ++- src/main/io/gps.h | 1 + src/main/io/gps_ublox.c | 20 ++++++++++++++++++++ src/main/io/gps_ublox.h | 4 ++++ 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/Settings.md b/docs/Settings.md index 6d7169b7c5b..ec0f68785e2 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -2048,6 +2048,16 @@ Enable use of Glonass satellites. This is at the expense of other regional const --- +### gps_ublox_use_navic + +Enable use of NavIC satellites, the Indian regional system, on receivers that have it, such as the u-blox F10. They are only visible over India and the region around it. Receivers without NavIC ignore this setting [OFF/ON]. + +| Default | Min | Max | +| --- | --- | --- | +| OFF | OFF | ON | + +--- + ### ground_test_mode For developer ground test use. Disables motors, sets heading status = Trusted on FW. diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index f805e60d658..84c0299fd3a 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -1874,6 +1874,11 @@ groups: default_value: OFF field: ubloxUseGlonass type: bool + - name: gps_ublox_use_navic + description: "Enable use of NavIC satellites, the Indian regional system, on receivers that have it, such as the u-blox F10. They are only visible over India and the region around it. Receivers without NavIC ignore this setting [OFF/ON]." + default_value: OFF + field: ubloxUseNavic + type: bool - name: gps_min_sats description: "Minimum number of GPS satellites in view to acquire GPS_FIX and consider GPS position valid. Some GPS receivers appeared to be very inaccurate with low satellite count." default_value: 6 diff --git a/src/main/io/gps.c b/src/main/io/gps.c index 3d2afe754b4..1e06eb5c6ef 100755 --- a/src/main/io/gps.c +++ b/src/main/io/gps.c @@ -146,7 +146,8 @@ PG_RESET_TEMPLATE(gpsConfig_t, gpsConfig, .ubloxUseBeidou = SETTING_GPS_UBLOX_USE_BEIDOU_DEFAULT, .ubloxUseGlonass = SETTING_GPS_UBLOX_USE_GLONASS_DEFAULT, .ubloxNavHz = SETTING_GPS_UBLOX_NAV_HZ_DEFAULT, - .autoBaudMax = SETTING_GPS_AUTO_BAUD_MAX_SUPPORTED_DEFAULT + .autoBaudMax = SETTING_GPS_AUTO_BAUD_MAX_SUPPORTED_DEFAULT, + .ubloxUseNavic = SETTING_GPS_UBLOX_USE_NAVIC_DEFAULT ); int gpsBaudRateToInt(gpsBaudRate_e baudrate) diff --git a/src/main/io/gps.h b/src/main/io/gps.h index ec531a5c6ba..b5c79008c7d 100755 --- a/src/main/io/gps.h +++ b/src/main/io/gps.h @@ -106,6 +106,7 @@ typedef struct gpsConfig_s { uint8_t gpsMinSats; uint8_t ubloxNavHz; gpsBaudRate_e autoBaudMax; + bool ubloxUseNavic; } gpsConfig_t; PG_DECLARE(gpsConfig_t, gpsConfig); diff --git a/src/main/io/gps_ublox.c b/src/main/io/gps_ublox.c index b51fad8cecb..533e81a528b 100755 --- a/src/main/io/gps_ublox.c +++ b/src/main/io/gps_ublox.c @@ -472,6 +472,18 @@ static int configureGNSS_GLONASS(ubx_gnss_element_t * gnss_block) return 1; } +// NavIC goes out on its own. Its keys exist only on receivers that have it, and +// one key a receiver does not know makes it refuse the whole message +static void configureNAVIC(void) +{ + ubx_config_data8_payload_t navicValues[] = { + {UBLOX_CFG_NAVIC_ENA, gpsState.gpsConfig->ubloxUseNavic}, + {UBLOX_CFG_NAVIC_L5_ENA, gpsState.gpsConfig->ubloxUseNavic} + }; + + ubloxSendSetCfgBytes(navicValues, 2); +} + static void configureGNSS10(void) { ubx_config_data8_payload_t gnssConfigValues[] = { @@ -1219,6 +1231,14 @@ STATIC_PROTOTHREAD(gpsConfigure) gpsConfigMutable()->ubloxUseBeidou = SETTING_GPS_UBLOX_USE_BEIDOU_DEFAULT; gpsConfigMutable()->ubloxUseGlonass = SETTING_GPS_UBLOX_USE_GLONASS_DEFAULT; } + + // Only a receiver that lists NavIC gets its keys, and only through the + // configuration interface, which is where those keys live + if (ubloxVersionGT(23, 1) && (ubxExtendedGnss & UBLOX_EXT_GNSS_NAVIC)) { + gpsSetProtocolTimeout(GPS_SHORT_TIMEOUT); + configureNAVIC(); + ptWaitTimeout((_ack_state == UBX_ACK_GOT_ACK || _ack_state == UBX_ACK_GOT_NAK), GPS_CFG_CMD_TIMEOUT_MS); + } } for(int i = 0; i < UBLOX_MAX_SIGNALS; ++i) diff --git a/src/main/io/gps_ublox.h b/src/main/io/gps_ublox.h index 549de66831b..bd6b4669db8 100644 --- a/src/main/io/gps_ublox.h +++ b/src/main/io/gps_ublox.h @@ -119,6 +119,10 @@ STATIC_ASSERT(MAX_UBLOX_PAYLOAD_SIZE >= 256, ubx_size_too_small); #define UBLOX_CFG_GLO_ENA 0x10310025 // U1 default off - may conflict with other constelations #define UBLOX_CFG_GLO_L1_ENA 0x10310018 // U1 default off +// Only on receivers that list NAVIC in their MON-VER extensions, such as the F10 +#define UBLOX_CFG_NAVIC_ENA 0x10310026 // U1 default off +#define UBLOX_CFG_NAVIC_L5_ENA 0x1031001d // U1 default off + #define UBLOX_CFG_SBAS_PRNSCANMASK 0x50360006 // 0 = auto // X8 #define UBLOX_SBAS_ALL 0x0000000000000000 //Enable search for all SBAS PRNs #define UBLOX_SBAS_PRN120 0x0000000000000001 //Enable search for SBAS PRN120 From ba0d9c427261b0aab93f583972495b0d267d16b6 Mon Sep 17 00:00:00 2001 From: MrScothh <167884257+MrScothh@users.noreply.github.com> Date: Sun, 20 Sep 2026 00:41:05 +0200 Subject: [PATCH 3/5] docs: describe the constellation fields added to MSP_GPSSTATISTICS 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. --- docs/development/msp/README.md | 6 ++++ docs/development/msp/msp_messages.json | 41 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/docs/development/msp/README.md b/docs/development/msp/README.md index 42634e951db..53e53b58bdb 100644 --- a/docs/development/msp/README.md +++ b/docs/development/msp/README.md @@ -2333,6 +2333,12 @@ When the MSP JSON specification changes, bump `msp_messages.json` version: | `eph` | `uint16_t` | 2 | cm | Estimated Horizontal Position Accuracy (`gpsSol.eph`) | | `epv` | `uint16_t` | 2 | cm | Estimated Vertical Position Accuracy (`gpsSol.epv`) | | `hwVersion` | `uint8_t` | 1 | - | GPS hardware version bit-field: bits[7:6]=series (0b01=u-blox Neo/M), bits[5:0]=generation. E.g. 0x48=M8, 0x49=M9, 0x4A=M10, 0=unknown. | +| `supportedGnss` | `uint8_t` | 1 | Bitmask | 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 | +| `enabledGnss` | `uint8_t` | 1 | Bitmask | Bitmask: which of those constellations the receiver says are running. Same bit order as `supportedGnss` | +| `extendedGnss` | `uint8_t` | 1 | Bitmask | Bitmask: augmentation and regional systems listed in the UBX-MON-VER extensions, which UBX-MON-GNSS does not carry: Bit 0=SBAS, Bit 1=QZSS, Bit 2=NavIC | +| `maxGnss` | `uint8_t` | 1 | Count | How many major constellations the receiver can track at the same time, as reported by UBX-MON-GNSS. 0 if unknown | +| `moduleNameLength` | `uint8_t` | 1 | - | Length of the module name string that follows. 0 if the receiver does not report one | +| `moduleName` | `char[]` | array | - | Module name as the receiver reports it in the UBX-MON-VER extensions (e.g., "NEO-M10N"). Length given by previous field | **Notes:** Requires `USE_GPS`. diff --git a/docs/development/msp/msp_messages.json b/docs/development/msp/msp_messages.json index 97a8ab9f185..2dd1a64113a 100644 --- a/docs/development/msp/msp_messages.json +++ b/docs/development/msp/msp_messages.json @@ -4923,6 +4923,47 @@ "ctype": "uint8_t", "desc": "GPS hardware version bit-field: bits[7:6]=series (0b01=u-blox Neo/M), bits[5:0]=generation. E.g. 0x48=M8, 0x49=M9, 0x4A=M10, 0=unknown.", "units": "" + }, + { + "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", + "units": "Bitmask", + "bitmask": true + }, + { + "name": "enabledGnss", + "ctype": "uint8_t", + "desc": "Bitmask: which of those constellations the receiver says are running. Same bit order as `supportedGnss`", + "units": "Bitmask", + "bitmask": true + }, + { + "name": "extendedGnss", + "ctype": "uint8_t", + "desc": "Bitmask: augmentation and regional systems listed in the UBX-MON-VER extensions, which UBX-MON-GNSS does not carry: Bit 0=SBAS, Bit 1=QZSS, Bit 2=NavIC", + "units": "Bitmask", + "bitmask": true + }, + { + "name": "maxGnss", + "ctype": "uint8_t", + "desc": "How many major constellations the receiver can track at the same time, as reported by UBX-MON-GNSS. 0 if unknown", + "units": "Count" + }, + { + "name": "moduleNameLength", + "ctype": "uint8_t", + "desc": "Length of the module name string that follows. 0 if the receiver does not report one", + "units": "" + }, + { + "name": "moduleName", + "desc": "Module name as the receiver reports it in the UBX-MON-VER extensions (e.g., \"NEO-M10N\"). Length given by previous field", + "ctype": "char", + "array": true, + "array_size": 0, + "units": "" } ] }, From 1065184185c0a541ce9dc33fd8a74558449bd300 Mon Sep 17 00:00:00 2001 From: MrScothh <167884257+MrScothh@users.noreply.github.com> Date: Sun, 20 Sep 2026 01:11:20 +0200 Subject: [PATCH 4/5] gps: keep the capability read inside the frame, and forget it with the 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. --- src/main/io/gps_ublox.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/io/gps_ublox.c b/src/main/io/gps_ublox.c index 533e81a528b..9851404b50e 100755 --- a/src/main/io/gps_ublox.c +++ b/src/main/io/gps_ublox.c @@ -805,12 +805,16 @@ static bool gpsParseFrameUBLOX(void) } } - for (int j = 40; j < _payload_length; j += 30) { + // Whole extensions only: a frame whose length is not a round number + // of them would have its last line read past the payload + for (int j = 40; j + 30 <= _payload_length; j += 30) { const char * line = (const char *)(_buffer.bytes + j); const char * mod = strnstr(line, "MOD=", 30); if (mod) { - strncpy(ubxModuleName, mod + 4, UBLOX_MODULE_NAME_LEN - 1); + const char * name = mod + 4; + const size_t room = (size_t)(line + 30 - name); + strncpy(ubxModuleName, name, MIN(room, (size_t)(UBLOX_MODULE_NAME_LEN - 1))); ubxModuleName[UBLOX_MODULE_NAME_LEN - 1] = '\0'; } @@ -1331,7 +1335,10 @@ STATIC_PROTOTHREAD(gpsProtocolStateThread) } while(gpsState.autoConfigStep < GPS_VERSION_RETRY_TIMES && gpsState.hwVersion == UBX_HW_VERSION_UNKNOWN); gpsState.autoConfigStep = 0; + // The limit goes with them: left over from a receiver that has been swapped out + // it would end the poll below before the new one has answered ubx_capabilities.supported = ubx_capabilities.enabledGnss = ubx_capabilities.defaultGnss = 0; + ubx_capabilities.capMaxGnss = 0; // M7 and earlier will never get pass this step, so skip it (#9440). // UBLOX documents that this is M8N and later if (gpsState.hwVersion > UBX_HW_VERSION_UBLOX7) { From bb1360a222e0ed11293037463fe30a2de02df30b Mon Sep 17 00:00:00 2001 From: MrScothh <167884257+MrScothh@users.noreply.github.com> Date: Sun, 20 Sep 2026 09:57:28 +0200 Subject: [PATCH 5/5] docs: bump the message registry to 2.2.0 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. --- docs/development/msp/msp_messages.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/development/msp/msp_messages.json b/docs/development/msp/msp_messages.json index 2dd1a64113a..4d1ad0688fb 100644 --- a/docs/development/msp/msp_messages.json +++ b/docs/development/msp/msp_messages.json @@ -1,8 +1,8 @@ { "version": { "major": 2, - "minor": 1, - "patch": 1 + "minor": 2, + "patch": 0 }, "messages": { "MSP_API_VERSION": {