From 744f38ca8ebb6c41c2f828b1bef851f8e8454c9c Mon Sep 17 00:00:00 2001 From: raphaelhunziker1202-stack <250872901+raphaelhunziker1202-stack@users.noreply.github.com> Date: Wed, 2 Sep 2026 11:22:45 +0200 Subject: [PATCH 1/2] CRSF telemetry: selectable altitude source for the GPS frame (crsf_gps_alt_source) The altitude field of the CRSF GPS frame (0x02) so far carried INAV's estimated altitude relative to the arming point, while the CRSF specification intends this field to carry the GPS altitude (MSL), which is also what Betaflight sends and what the GAlt sensor on EdgeTX/OpenTX radios is expected to show. Add a new setting `crsf_gps_alt_source`: - ESTIMATED (default): estimated altitude above the arming point, identical to the previous behaviour - MSL: raw GNSS altitude above mean sea level The relative altitude remains available via the barometer/vario frame (Alt sensor) regardless of this setting. PG_TELEMETRY_CONFIG version bumped 8 -> 9. --- docs/Settings.md | 10 ++++++++++ src/main/fc/settings.yaml | 9 +++++++++ src/main/telemetry/crsf.c | 10 +++++++++- src/main/telemetry/telemetry.c | 3 ++- src/main/telemetry/telemetry.h | 6 ++++++ 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/docs/Settings.md b/docs/Settings.md index 3d7351dc371..e2a8b1cf9b1 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -592,6 +592,16 @@ Blackbox logging rate numerator. Use num/denom settings to decide if a frame sho --- +### crsf_gps_alt_source + +CRSF telemetry only: Altitude source for the GPS frame (GAlt sensor on EdgeTX/OpenTX radios). ESTIMATED sends INAV's estimated altitude relative to the arming point (legacy behaviour), MSL sends the raw GNSS altitude above mean sea level as intended by the CRSF specification. [ESTIMATED/MSL] + +| Default | Min | Max | +| --- | --- | --- | +| ESTIMATED | | | + +--- + ### cruise_power Power draw at cruise throttle used for remaining flight time/distance estimation in 0.01W unit diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 4e8affb0221..8cac4fcab01 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -204,6 +204,9 @@ tables: - name: mavlink_autopilot_type values: ["GENERIC", "ARDUPILOT"] enum: mavlinkAutopilotType_e + - name: crsf_gps_alt_source + values: ["ESTIMATED", "MSL"] + enum: crsfGpsAltSource_e - name: default_altitude_source values: ["GPS", "BARO", "GPS_ONLY", "BARO_ONLY"] enum: navDefaultAltitudeSensor_e @@ -3168,6 +3171,12 @@ groups: field: ltmUpdateRate condition: USE_TELEMETRY_LTM table: ltm_rates + - name: crsf_gps_alt_source + description: "CRSF telemetry only: Altitude source for the GPS frame (GAlt sensor on EdgeTX/OpenTX radios). ESTIMATED sends INAV's estimated altitude relative to the arming point (legacy behaviour), MSL sends the raw GNSS altitude above mean sea level as intended by the CRSF specification. [ESTIMATED/MSL]" + default_value: "ESTIMATED" + field: crsfGpsAltSource + table: crsf_gps_alt_source + type: uint8_t - name: sim_ground_station_number description: "Number of phone that is used to communicate with SIM module. Messages / calls from other numbers are ignored. If undefined, can be set by calling or sending a message to the module." default_value: "" diff --git a/src/main/telemetry/crsf.c b/src/main/telemetry/crsf.c index e8214fc8c1f..ab4894e5c26 100755 --- a/src/main/telemetry/crsf.c +++ b/src/main/telemetry/crsf.c @@ -239,7 +239,15 @@ static void crsfFrameGps(sbuf_t *dst) crsfSerialize32(dst, gpsSol.llh.lon); crsfSerialize16(dst, (gpsSol.groundSpeed * 36 + 50) / 100); // gpsSol.groundSpeed is in cm/s crsfSerialize16(dst, DECIDEGREES_TO_CENTIDEGREES(gpsSol.groundCourse)); // gpsSol.groundCourse is 0.1 degrees, need 0.01 deg - const uint16_t altitude = (getEstimatedActualPosition(Z) / 100) + 1000; + // Altitude source is selectable: the estimated altitude above the arming point (legacy + // behaviour) or the raw GNSS altitude above mean sea level, as the CRSF spec intends + int32_t altitudeCm; + if (telemetryConfig()->crsfGpsAltSource == CRSF_GPS_ALT_MSL) { + altitudeCm = gpsSol.llh.alt; + } else { + altitudeCm = lrintf(getEstimatedActualPosition(Z)); + } + const uint16_t altitude = (altitudeCm / 100) + 1000; crsfSerialize16(dst, altitude); crsfSerialize8(dst, gpsSol.numSat); } diff --git a/src/main/telemetry/telemetry.c b/src/main/telemetry/telemetry.c index 2c896945843..7d2992e0ff7 100644 --- a/src/main/telemetry/telemetry.c +++ b/src/main/telemetry/telemetry.c @@ -56,7 +56,7 @@ #include "telemetry/ghst.h" -PG_REGISTER_WITH_RESET_TEMPLATE(telemetryConfig_t, telemetryConfig, PG_TELEMETRY_CONFIG, 8); +PG_REGISTER_WITH_RESET_TEMPLATE(telemetryConfig_t, telemetryConfig, PG_TELEMETRY_CONFIG, 9); PG_RESET_TEMPLATE(telemetryConfig_t, telemetryConfig, .telemetry_switch = SETTING_TELEMETRY_SWITCH_DEFAULT, @@ -73,6 +73,7 @@ PG_RESET_TEMPLATE(telemetryConfig_t, telemetryConfig, #endif .ibusTelemetryType = SETTING_IBUS_TELEMETRY_TYPE_DEFAULT, .ltmUpdateRate = SETTING_LTM_UPDATE_RATE_DEFAULT, + .crsfGpsAltSource = SETTING_CRSF_GPS_ALT_SOURCE_DEFAULT, #ifdef USE_TELEMETRY_SIM .simTransmitInterval = SETTING_SIM_TRANSMIT_INTERVAL_DEFAULT, diff --git a/src/main/telemetry/telemetry.h b/src/main/telemetry/telemetry.h index 7fb26781c11..419622045e6 100644 --- a/src/main/telemetry/telemetry.h +++ b/src/main/telemetry/telemetry.h @@ -53,6 +53,11 @@ typedef enum { SMARTPORT_FUEL_UNIT_MWH } smartportFuelUnit_e; +typedef enum { + CRSF_GPS_ALT_ESTIMATED, // Estimated altitude above the arming point (legacy behaviour) + CRSF_GPS_ALT_MSL // Raw GNSS altitude above mean sea level +} crsfGpsAltSource_e; + typedef struct telemetryConfig_s { uint8_t telemetry_switch; // Use aux channel to change serial output & baudrate( MSP / Telemetry ). It disables automatic switching to Telemetry when armed. uint8_t telemetry_inverted; // Flip the default inversion of the protocol - Same as serialrx_inverted in rx.c, but for telemetry. @@ -64,6 +69,7 @@ typedef struct telemetryConfig_s { smartportFuelUnit_e smartportFuelUnit; uint8_t ibusTelemetryType; uint8_t ltmUpdateRate; + uint8_t crsfGpsAltSource; #ifdef USE_TELEMETRY_SIM int16_t simLowAltitude; From 9eb30e9694f1e27052dcb753a81d84db96510e3c Mon Sep 17 00:00:00 2001 From: raphaelhunziker1202-stack <250872901+raphaelhunziker1202-stack@users.noreply.github.com> Date: Wed, 2 Sep 2026 11:29:28 +0200 Subject: [PATCH 2/2] Keep exact legacy conversion for ESTIMATED; clarify MSL wording The intermediate integer-centimeter step rounded negative estimated altitudes differently than the previous float expression (by up to 1 m). Use the original expression verbatim for the ESTIMATED path so the default behaviour is unchanged, and drop raw from the MSL description since gpsSol may carry the estimated fix when GPS fix estimation is enabled. --- docs/Settings.md | 2 +- src/main/fc/settings.yaml | 2 +- src/main/telemetry/crsf.c | 9 ++++----- src/main/telemetry/telemetry.h | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/Settings.md b/docs/Settings.md index e2a8b1cf9b1..9e4364cea88 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -594,7 +594,7 @@ Blackbox logging rate numerator. Use num/denom settings to decide if a frame sho ### crsf_gps_alt_source -CRSF telemetry only: Altitude source for the GPS frame (GAlt sensor on EdgeTX/OpenTX radios). ESTIMATED sends INAV's estimated altitude relative to the arming point (legacy behaviour), MSL sends the raw GNSS altitude above mean sea level as intended by the CRSF specification. [ESTIMATED/MSL] +CRSF telemetry only: Altitude source for the GPS frame (GAlt sensor on EdgeTX/OpenTX radios). ESTIMATED sends INAV's estimated altitude relative to the arming point (legacy behaviour), MSL sends the GNSS altitude above mean sea level as intended by the CRSF specification. [ESTIMATED/MSL] | Default | Min | Max | | --- | --- | --- | diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 8cac4fcab01..7ef0812d407 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -3172,7 +3172,7 @@ groups: condition: USE_TELEMETRY_LTM table: ltm_rates - name: crsf_gps_alt_source - description: "CRSF telemetry only: Altitude source for the GPS frame (GAlt sensor on EdgeTX/OpenTX radios). ESTIMATED sends INAV's estimated altitude relative to the arming point (legacy behaviour), MSL sends the raw GNSS altitude above mean sea level as intended by the CRSF specification. [ESTIMATED/MSL]" + description: "CRSF telemetry only: Altitude source for the GPS frame (GAlt sensor on EdgeTX/OpenTX radios). ESTIMATED sends INAV's estimated altitude relative to the arming point (legacy behaviour), MSL sends the GNSS altitude above mean sea level as intended by the CRSF specification. [ESTIMATED/MSL]" default_value: "ESTIMATED" field: crsfGpsAltSource table: crsf_gps_alt_source diff --git a/src/main/telemetry/crsf.c b/src/main/telemetry/crsf.c index ab4894e5c26..0bbb344815a 100755 --- a/src/main/telemetry/crsf.c +++ b/src/main/telemetry/crsf.c @@ -240,14 +240,13 @@ static void crsfFrameGps(sbuf_t *dst) crsfSerialize16(dst, (gpsSol.groundSpeed * 36 + 50) / 100); // gpsSol.groundSpeed is in cm/s crsfSerialize16(dst, DECIDEGREES_TO_CENTIDEGREES(gpsSol.groundCourse)); // gpsSol.groundCourse is 0.1 degrees, need 0.01 deg // Altitude source is selectable: the estimated altitude above the arming point (legacy - // behaviour) or the raw GNSS altitude above mean sea level, as the CRSF spec intends - int32_t altitudeCm; + // behaviour) or the GNSS altitude above mean sea level, as the CRSF spec intends + uint16_t altitude; if (telemetryConfig()->crsfGpsAltSource == CRSF_GPS_ALT_MSL) { - altitudeCm = gpsSol.llh.alt; + altitude = (gpsSol.llh.alt / 100) + 1000; // gpsSol.llh.alt is in cm } else { - altitudeCm = lrintf(getEstimatedActualPosition(Z)); + altitude = (getEstimatedActualPosition(Z) / 100) + 1000; } - const uint16_t altitude = (altitudeCm / 100) + 1000; crsfSerialize16(dst, altitude); crsfSerialize8(dst, gpsSol.numSat); } diff --git a/src/main/telemetry/telemetry.h b/src/main/telemetry/telemetry.h index 419622045e6..239eff8a833 100644 --- a/src/main/telemetry/telemetry.h +++ b/src/main/telemetry/telemetry.h @@ -55,7 +55,7 @@ typedef enum { typedef enum { CRSF_GPS_ALT_ESTIMATED, // Estimated altitude above the arming point (legacy behaviour) - CRSF_GPS_ALT_MSL // Raw GNSS altitude above mean sea level + CRSF_GPS_ALT_MSL // GNSS altitude above mean sea level } crsfGpsAltSource_e; typedef struct telemetryConfig_s {