Skip to content

Hide the RX downlink power element when the link does not report it - #11914

Merged
sensei-hacker merged 4 commits into
iNavFlight:maintenance-10.xfrom
Raffi1202:fix/crsf-downlink-tx-power
Nov 15, 2026
Merged

sensei-hacker merged 4 commits into
iNavFlight:maintenance-10.xfrom
Raffi1202:fix/crsf-downlink-tx-power

Conversation

@Raffi1202

@Raffi1202 Raffi1202 commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Problem

On an ExpressLRS receiver (BetaFPV SuperD 2.4, INAV 8.0.1, Matek H743 V3) the OSD element OSD_RX_POWER_DOWNLINK shows 0 mW although the receiver transmits telemetry at 100 mW. Fixes #11136.

Cause

osdDrawSingleElement() prints rxLinkStatistics.downlinkTXPower unconditionally. Only two paths write the field: MSP2_COMMON_SET_MSP_RC_INFO (fc_msp.c) and the mLRS radio link information when MAVLink is the serial receiver (fc_mavlink.c). Of the CRSF link statistics frames the parser handles only 0x14 (crsf.c), which carries the transmitter's uplink power but no receiver power, so on CRSF the field keeps its initial 0.

CRSF carries the receiver power in frame 0x1D (LINK_STATISTICS_TX, the source of the EdgeTX sensor RPWR). ExpressLRS sends neither 0x1C nor 0x1D (its crsf_protocol.h in 3.5.6 and 4.1.0 defines no such frame type), and INAV parses neither, so with ExpressLRS there is no value the element could show. Commit cd44927 names 0x1C; the correct frame is 0x1D.

Change

In the OSD_RX_POWER_DOWNLINK case, when downlinkTXPower is 0, the element blanks its six characters and returns true, like the other elements in osd.c that hide themselves at runtime (e.g. OSD_COURSE_HOLD_ERROR). The blank write (fe5ea81) removes a previously drawn value when an MSP or mLRS link reports 0. A value that stops arriving is not cleared, since rxLinkStatistics is never reset; that behaviour is unchanged.

On MSP, 0 means "not reported". On mLRS, mavlinkDbmToMilliwatts() maps both "not reported" (INT8_MAX) and 0 dBm or less to 0, so the element is also hidden at 0 dBm (1 mW) and below.

Test

Linux SITL, current maintenance-10.x (3931fcd) against the same tree with this PR merged. OSD over MSP DisplayPort (HDZERO) on UART2, element 160 enabled; the six element cells were read back from the DisplayPort stream.

CRSF receiver on UART3 (RC frames at 50 Hz, link statistics 0x14 at 10 Hz with uplink power index 3 and the downlink fields 0, as ExpressLRS sends them):

Case maintenance-10.x this PR
CRSF link up 0mW↓ blank
CRSF stream stopped (failsafe) mW↓ blank

OSD_TX_POWER_UPLINK showed 100mW in both builds.

Receiver MSP, RC via MSP_SET_RAW_RC at 50 Hz, receiver power set with MSP2_COMMON_SET_MSP_RC_INFO:

Case maintenance-10.x this PR
No RC info received 0mW↓ blank
Downlink power 100 100mW↓ 100mW↓
Then downlink power 0 0mW↓ blank, 100 overwritten
Power 100, RC feed stopped mW↓ mW↓
RC feed resumed, no new RC info 100mW↓ 100mW↓
Power 0, RC feed stopped mW↓ blank

With osd_msp_displayport_fullframe_interval = -1 the PR build gives the same results, so the blank write does not depend on the periodic full-frame refresh. Not tested on hardware or with mLRS.

Built locally with -DWARNINGS_AS_ERRORS=ON for MATEKF722, MATEKF405 and SITL, no warnings.

Flash / RAM

Against current maintenance-10.x (3931fcd), arm-none-eabi-size: MATEKF722 text +0 B (osdDrawSingleElement grows by 4 B, absorbed by alignment), MATEKF405 text +48 B, data and bss unchanged on both. MATEKF722 FLASH1 is at 97.54 %.

The size bot comment compares the CI merge build against the older base commit 76ee415, so its deltas show the maintenance-10.x changes since then, not this PR.

Docs

docs/OSD.md, row OSD_RX_POWER_DOWNLINK: "The element is hidden while the receiver transmit power is 0 mW, which is also the value when the RC link does not report it. MSP receivers that send MSP2_COMMON_SET_MSP_RC_INFO and mLRS as MAVLink receiver report it (mLRS at 0 dBm or less reads as 0 mW). INAV does not read it from CRSF links such as ExpressLRS."

Reference

EdgeTX v2.12.4 reads the receiver power (sensor RPWR) from frame 0x1D: radio/src/telemetry/crossfire.h:47 (LINK_TX_ID 0x1D), radio/src/telemetry/crossfire.cpp:359-363 (byte 7 → RX_RF_POWER_INDEX, sensor table entry at line 55 = STR_SENSOR_RX_RF_POWER, "RPWR").

The receiver transmit power is only written by the MSP and MAVLink RC
paths. The CRSF link statistics frame 0x14 has no such field and no
other CRSF frame is parsed for it, so with an ExpressLRS receiver the
element showed a permanent 0 mW.

ExpressLRS never sends the optional link statistics RX frame 0x1C that
would carry a power value, so parsing it would not populate the element
either. A power of 0 already means "not reported" on the other paths,
so skip drawing the element in that case.

Fixes iNavFlight#11136
@Raffi1202
Raffi1202 marked this pull request as ready for review September 11, 2026 15:45
@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

qodo-free-for-open-source-projects Bot commented Sep 11, 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. Stale receiver power remains after reporting stops ✓ Resolved
Description
osdDrawSingleElement() returns before writing or clearing the element when downlinkTXPower
becomes zero. After an MSP or MAVLink link previously reports a nonzero value and then loses that
field, normal OSD refreshes skip this element while displayClearScreen() is only used for
full-screen redraws, so the old power can remain visible.

Grey Divider

Tip of the day
💡 Did you know, you can reply 'qodo' on any finding to push back, ask questions, or dig deeper

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/main/io/osd.c Resolved
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Hide unavailable receiver downlink power from the OSD

🐞 Bug fix 📝 Documentation 🕐 10-20 Minutes

Grey Divider

AI Description

• Hide receiver downlink power when link statistics report an unknown zero value.
• Document that only MSP and MAVLink RC links provide receiver transmit power.
Diagram

graph TD
  MSP["MSP / MAVLink"] -->|"power value"| Stats["Link Statistics"] --> Check{"Power reported?"}
  CRSF["CRSF Link"] -->|"zero / unknown"| Stats
  Check -->|"yes"| Render["RX Power OSD"]
  Check -->|"no"| Skip["Skip element"]
Loading
High-Level Assessment

The guard is the appropriate minimal fix because zero is already the established unknown sentinel and the OSD scheduler already interprets false as not drawable. Parsing CRSF frame 0x1C would not help ExpressLRS users because that frame is not emitted, while introducing a separate capability flag would require unnecessary cross-protocol changes for the same result.

Files changed (2) +6 / -1

Bug fix (1) +5 / -0
osd.cSkip unavailable receiver power element +5/-0

Skip unavailable receiver power element

• Returns false from the receiver downlink power rendering case when downlinkTXPower is zero. This prevents unsupported links such as CRSF and ExpressLRS from permanently displaying 0 mW.

src/main/io/osd.c

Documentation (1) +1 / -1
OSD.mdDocument receiver downlink power availability +1/-1

Document receiver downlink power availability

• Explains that the element remains hidden unless the RC link reports receiver transmit power. Clarifies that MSP and MAVLink provide the value while CRSF does not.

docs/OSD.md

@sensei-hacker sensei-hacker added this to the 10.0 milestone Sep 20, 2026
Like the other OSD elements that hide themselves at runtime, the element
now takes its slot in the per-cycle draw budget after blanking its field.
No visible change.

The receiver transmit power is carried by CRSF frame 0x1D, not 0x1C.
INAV parses neither and ExpressLRS sends neither, so the docs no longer
claim that CRSF cannot carry the value, and name mLRS only as MAVLink
receiver.
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

RAM / Flash usage vs. base commit 76ee415 — commit 0da3929

Target Flash Δ RAM Δ
MATEKF405 ⚠️ +23156 B (+3.31%) -12636 B (-8.46%)
MATEKF722 ⚠️ +10340 B (+2.20%) -13256 B (-10.58%)
MATEKF765 ⚠️ +15740 B (+2.13%) -11512 B (-6.96%)
MATEKH743 ⚠️ +22564 B (+2.91%) -10688 B (-6.32%)

See RAM/flash optimization guide for techniques to reduce usage.

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Test firmware build ready — commit 0da3929

Download firmware for PR #11914

251 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.

Only MSP receivers that send MSP2_COMMON_SET_MSP_RC_INFO report the
receiver transmit power, and mLRS reports 0 dBm or less as 0 mW, so the
element is also hidden for mLRS at 1 mW and below. Say so in the OSD
element table.
@sensei-hacker sensei-hacker modified the milestones: 10.0, 10.1 Oct 4, 2026
@sensei-hacker sensei-hacker modified the milestones: 10.1, 10.0 Nov 15, 2026
@sensei-hacker

Copy link
Copy Markdown
Member

Thanks!

@sensei-hacker
sensei-hacker merged commit 318cdda into iNavFlight:maintenance-10.x Nov 15, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RX Power shows 0w.

2 participants