From e671436d03057fbb8deb6a5d7f64b3dd3a78cffd Mon Sep 17 00:00:00 2001 From: MrScothh <167884257+MrScothh@users.noreply.github.com> Date: Thu, 17 Sep 2026 23:19:39 +0200 Subject: [PATCH 1/2] gps: configure u-blox F10 receivers with the signals they have 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. --- src/main/io/gps_ublox.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/io/gps_ublox.c b/src/main/io/gps_ublox.c index 703242d10dd..75a90202821 100755 --- a/src/main/io/gps_ublox.c +++ b/src/main/io/gps_ublox.c @@ -87,6 +87,9 @@ static const char * baudInitDataNMEA[GPS_BAUDRATE_COUNT] = { static ubx_nav_sig_info satelites[UBLOX_MAX_SIGNALS] = {}; +// Set from the MON-VER extensions, where a dual band receiver names itself, as in FWVER=SPGL1L5 +static bool ubxDualBand = false; + // 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; @@ -447,6 +450,10 @@ static int configureGNSS_GLONASS(ubx_gnss_element_t * gnss_block) static void configureGNSS10(void) { + // A dual band receiver has no B1I: its Beidou L1 signal is B1C, and it rejects a + // configuration that would leave one of the two bands enabled without the other + const bool useB1C = ubxDualBand || gpsState.gpsConfig->ubloxUseGlonass; + ubx_config_data8_payload_t gnssConfigValues[] = { // SBAS {UBLOX_CFG_SIGNAL_SBAS_ENA, gpsState.gpsConfig->sbasMode == SBAS_NONE ? 0 : 1}, @@ -459,20 +466,25 @@ static void configureGNSS10(void) // Beidou // M10 can't use BDS_B1I and Glonass together. Instead, use BDS_B1C {UBLOX_CFG_SIGNAL_BDS_ENA, gpsState.gpsConfig->ubloxUseBeidou}, - {UBLOX_CFG_SIGNAL_BDS_B1_ENA, gpsState.gpsConfig->ubloxUseBeidou && ! gpsState.gpsConfig->ubloxUseGlonass}, - {UBLOX_CFG_SIGNAL_BDS_B1C_ENA, gpsState.gpsConfig->ubloxUseBeidou && gpsState.gpsConfig->ubloxUseGlonass}, + {UBLOX_CFG_SIGNAL_BDS_B1_ENA, gpsState.gpsConfig->ubloxUseBeidou && !useB1C}, + {UBLOX_CFG_SIGNAL_BDS_B1C_ENA, gpsState.gpsConfig->ubloxUseBeidou && useB1C}, // Should be enabled with GPS {UBLOX_CFG_QZSS_ENA, 1}, {UBLOX_CFG_QZSS_L1CA_ENA, 1}, {UBLOX_CFG_QZSS_L1S_ENA, 1}, - // Glonass + // Glonass, must stay last so it can be left out {UBLOX_CFG_GLO_ENA, gpsState.gpsConfig->ubloxUseGlonass}, {UBLOX_CFG_GLO_L1_ENA, gpsState.gpsConfig->ubloxUseGlonass} }; - ubloxSendSetCfgBytes(gnssConfigValues, 12); + // A receiver without Glonass has no Glonass keys either, and one unknown key makes it + // reject the whole message, taking SBAS, Galileo, BeiDou and QZSS down with it. An + // empty mask means MON-GNSS never answered and nothing is known, so send them all. + const bool noGlonass = ubx_capabilities.supported && !gpsUbloxHasGlonass(); + + ubloxSendSetCfgBytes(gnssConfigValues, noGlonass ? 10 : 12); } static void configureGNSS(void) @@ -765,6 +777,13 @@ static bool gpsParseFrameUBLOX(void) break; } } + + for (int j = 40; j < _payload_length; j += 30) { + if (strnstr((const char *)(_buffer.bytes + j), "L1L5", 30)) { + ubxDualBand = true; + break; + } + } } } break; @@ -1250,6 +1269,7 @@ STATIC_PROTOTHREAD(gpsProtocolStateThread) // Attempt to detect GPS hw version gpsState.hwVersion = UBX_HW_VERSION_UNKNOWN; + ubxDualBand = false; gpsState.autoConfigStep = 0; // Configure GPS module if enabled From 1e25a2770119abafa103c514e17d0d370e374385 Mon Sep 17 00:00:00 2001 From: MrScothh <167884257+MrScothh@users.noreply.github.com> Date: Sat, 19 Sep 2026 17:47:02 +0200 Subject: [PATCH 2/2] gps: never ask a u-blox receiver for more constellations than it can run 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. --- src/main/io/gps_ublox.c | 70 ++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/src/main/io/gps_ublox.c b/src/main/io/gps_ublox.c index 75a90202821..167d8ae1b2e 100755 --- a/src/main/io/gps_ublox.c +++ b/src/main/io/gps_ublox.c @@ -358,6 +358,49 @@ static int configureGNSS_SBAS(ubx_gnss_element_t * gnss_block) return 1; } +/* + * The major constellations to ask the receiver for: the ones selected, less any it + * does not have, cut down to as many as it says it can track at once. A receiver + * refuses a configuration it cannot run and carries on with the previous one, so a + * selection that does not fit would be lost whole. When it does not fit, the + * constellations INAV leaves off by default go first. The settings are left as they + * are, and apply in full on a receiver that can take them. + */ +static uint8_t ubloxGnssToEnable(void) +{ + static const uint8_t leaveOutFirst[] = { + UBX_MON_GNSS_GLONASS_MASK, UBX_MON_GNSS_BEIDOU_MASK, UBX_MON_GNSS_GALILEO_MASK + }; + + uint8_t gnss = UBX_MON_GNSS_GPS_MASK; + if (gpsState.gpsConfig->ubloxUseGalileo) { + gnss |= UBX_MON_GNSS_GALILEO_MASK; + } + if (gpsState.gpsConfig->ubloxUseBeidou) { + gnss |= UBX_MON_GNSS_BEIDOU_MASK; + } + if (gpsState.gpsConfig->ubloxUseGlonass) { + gnss |= UBX_MON_GNSS_GLONASS_MASK; + } + + // Until MON-GNSS answers nothing is known, and the selection goes out as it is. The + // count is only trusted next to a fresh mask: it is not cleared between attempts + if (!ubx_capabilities.supported) { + return gnss; + } + + gnss &= ubx_capabilities.supported; + + for (unsigned i = 0; i < ARRAYLEN(leaveOutFirst); i++) { + if (ubx_capabilities.capMaxGnss <= 0 || __builtin_popcount(gnss) <= ubx_capabilities.capMaxGnss) { + break; + } + gnss &= ~leaveOutFirst[i]; + } + + return gnss; +} + static int configureGNSS_GALILEO(ubx_gnss_element_t * gnss_block) { if (!gpsUbloxHasGalileo()) { @@ -371,7 +414,7 @@ static int configureGNSS_GALILEO(ubx_gnss_element_t * gnss_block) // 0x10 = Galileo E5a // off by default // 0x20 = Galileo E5b // off by default gnss_block->sigCfgMask = 0x01; - if (gpsState.gpsConfig->ubloxUseGalileo) { + if (ubloxGnssToEnable() & UBX_MON_GNSS_GALILEO_MASK) { gnss_block->enabled = 1; gnss_block->resTrkCh = 4; } else { @@ -395,7 +438,7 @@ static int configureGNSS_BEIDOU(ubx_gnss_element_t * gnss_block) // 0x10 = BeiDou B2I // off by default // 0x80 = BeiDou B2A // off by default gnss_block->sigCfgMask = 0x01; - if (gpsState.gpsConfig->ubloxUseBeidou) { + if (ubloxGnssToEnable() & UBX_MON_GNSS_BEIDOU_MASK) { gnss_block->enabled = 1; gnss_block->resTrkCh = 4; } else { @@ -437,7 +480,7 @@ static int configureGNSS_GLONASS(ubx_gnss_element_t * gnss_block) // 0x01 = GLONASS L1 // 0x10 = GLONASS L2 // off by default gnss_block->sigCfgMask = 0x01; - if (gpsState.gpsConfig->ubloxUseGlonass) { + if (ubloxGnssToEnable() & UBX_MON_GNSS_GLONASS_MASK) { gnss_block->enabled = 1; gnss_block->resTrkCh = 4; } else { @@ -450,9 +493,14 @@ static int configureGNSS_GLONASS(ubx_gnss_element_t * gnss_block) static void configureGNSS10(void) { + const uint8_t gnss = ubloxGnssToEnable(); + const bool useGalileo = gnss & UBX_MON_GNSS_GALILEO_MASK; + const bool useBeidou = gnss & UBX_MON_GNSS_BEIDOU_MASK; + const bool useGlonass = gnss & UBX_MON_GNSS_GLONASS_MASK; + // A dual band receiver has no B1I: its Beidou L1 signal is B1C, and it rejects a // configuration that would leave one of the two bands enabled without the other - const bool useB1C = ubxDualBand || gpsState.gpsConfig->ubloxUseGlonass; + const bool useB1C = ubxDualBand || useGlonass; ubx_config_data8_payload_t gnssConfigValues[] = { // SBAS @@ -460,14 +508,14 @@ static void configureGNSS10(void) {UBLOX_CFG_SIGNAL_SBAS_L1CA_ENA, gpsState.gpsConfig->sbasMode == SBAS_NONE ? 0 : 1}, // Galileo - {UBLOX_CFG_SIGNAL_GAL_ENA, gpsState.gpsConfig->ubloxUseGalileo}, - {UBLOX_CFG_SIGNAL_GAL_E1_ENA, gpsState.gpsConfig->ubloxUseGalileo}, + {UBLOX_CFG_SIGNAL_GAL_ENA, useGalileo}, + {UBLOX_CFG_SIGNAL_GAL_E1_ENA, useGalileo}, // Beidou // M10 can't use BDS_B1I and Glonass together. Instead, use BDS_B1C - {UBLOX_CFG_SIGNAL_BDS_ENA, gpsState.gpsConfig->ubloxUseBeidou}, - {UBLOX_CFG_SIGNAL_BDS_B1_ENA, gpsState.gpsConfig->ubloxUseBeidou && !useB1C}, - {UBLOX_CFG_SIGNAL_BDS_B1C_ENA, gpsState.gpsConfig->ubloxUseBeidou && useB1C}, + {UBLOX_CFG_SIGNAL_BDS_ENA, useBeidou}, + {UBLOX_CFG_SIGNAL_BDS_B1_ENA, useBeidou && !useB1C}, + {UBLOX_CFG_SIGNAL_BDS_B1C_ENA, useBeidou && useB1C}, // Should be enabled with GPS {UBLOX_CFG_QZSS_ENA, 1}, @@ -475,8 +523,8 @@ static void configureGNSS10(void) {UBLOX_CFG_QZSS_L1S_ENA, 1}, // Glonass, must stay last so it can be left out - {UBLOX_CFG_GLO_ENA, gpsState.gpsConfig->ubloxUseGlonass}, - {UBLOX_CFG_GLO_L1_ENA, gpsState.gpsConfig->ubloxUseGlonass} + {UBLOX_CFG_GLO_ENA, useGlonass}, + {UBLOX_CFG_GLO_L1_ENA, useGlonass} }; // A receiver without Glonass has no Glonass keys either, and one unknown key makes it