Make the ground-station uplink configurable, and stop it starving video RX creating color blocks - #110
Open
kkbin505 wants to merge 1 commit into
Open
Conversation
The adapter is half duplex, so every uplink frame is a receive blackout.
The uplink was hardcoded to k=1/n=5 at MCS0 and Transmitter::sendPacket
emits a block's fragments back-to-back, measuring ~102 frames/s in bursts
of 10-12 inside 1-2 ms. A burst blinds the receiver long enough to drop
CONSECUTIVE video fragments, the one loss pattern the downlink FEC cannot
repair -- it sits pinned at its maximum setting and still fails, the
decoder gets partial frames, and the unwritten part of the output buffer
shows as colour blocks.
This was latent until devourer 5f099ee ("drop DEVOURER_TX_HT_MCS gate")
made the uplink actually reach the air: before it, fixed_rate stayed at
MGN_1M, and CCK is illegal on the 5 GHz channels this link runs on, so the
frames were never transmitted. The uplink was silently dead and the radio
was effectively receive-only.
Expose the uplink's airtime as settings rather than constants, because the
right trade-off depends on the airframe: a short-range quad can spend
uplink margin freely, a long-range wing cannot, and a ground station with a
separate RC/telemetry radio wants no uplink here at all. Adaptive ->
Uplink... offers enable/MCS/redundancy, stored in SharedPreferences and
applied on the next link start, matching the existing LDPC/STBC lifecycle.
Out-of-range values are rejected rather than clamped: transmitting at a
rate the operator did not choose is worse than keeping the previous one.
Defaults move one MCS step and leave redundancy alone. Measured at 5 GHz
ch161/20 MHz on an RTL8812AU, in fragments/s the downlink FEC could not
recover:
mcs 0, n 5 9.78 - 15.82 (previous hardcoded values)
mcs 1, n 5 2.18 <- new default
mcs 3, n 2 0.12
uplink off 0.00
~3 dB of uplink margin buys a 7x reduction. Redundancy is deliberately not
the knob turned by default -- losing mavlink is worse than losing video.
The gain is superlinear in airtime because shorter blackouts stop clipping
runs of fragments, and the downlink FEC absorbs isolated losses.
Spacing the five fragments 8 ms apart was tried and is worse: total lost
fragments fall (15.8 -> 10.4) but artefacts become more frequent, as the
same damage spreads over more downlink FEC blocks and one damaged block is
one visible glitch. The lever is total airtime, not smoother airtime.
Each measurement was validated as actually transmitting (TxFrame
countPIncoming > 0 plus the expected fixed_rate in the devourer log) before
its numbers were used; the uplink can die silently, which invalidates a run
that otherwise looks clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015LuniKiNRWAcTvaRV8ZhH8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Make the ground-station uplink configurable, and stop it starving video RX
Fixes the persistent colour-block corruption on RTL8812AU ground stations (see Issue #109 ).
The problem
The adapter is half duplex: every uplink frame is a receive blackout. The uplink was hardcoded to
k=1, n=5at MCS0, andTransmitter::sendPacketemits a block's fragments back-to-back, which measures as ~102 frames/s in bursts of 10-12 inside 1-2 ms. Those bursts blind the receiver long enough to drop video fragments — the one loss pattern the downlinkso the decoder gets partial frames and paints the rest of the buffer as colour blocks. That is the H.265 encoding, the compressor block is dynamic, so the colour blocks are random.
This was latent until devourer
5f099ee("drop DEVOURER_TX_HT_MCS gate") made the uplink actually reach the air. Before thatfixed_ratestayed atMGN_1M, and CCK is illegal on the 5 GHz channels this link runs on, so the frames were never transmitted — the uplink was silently dead on my device. (So there is no issue with the official release 0.23)The change
The uplink airtime is configurable through the menu rather than fixed as a hardcoded constant, because the optimal trade-off depends on the airframe. A short-range quad can afford to allocate more airtime to the uplink, whereas a long-range fixed-wing aircraft cannot. A ground station using a separate RC or telemetry radio may not need an uplink here at all. Until a dynamic optimization algorithm is implemented, this is the most practical compromise I have found.
Adaptive → Uplink… exposes three controls, stored in
SharedPreferencesand applied on the next link start (same lifecycle as the existing LDPC/STBC options):New JNI entry points
nativeSetUplinkEnabled/nativeSetUplinkMcs/nativeSetUplinkFecNmirror the existingnativeSetUseLdpcpattern. Out-of-range values are rejected rather than clamped — silently transmitting at a rate the operator did not choose is worse than keeping the previous one.Choice of default
Measured on an RTL8812AU at 5 GHz ch161/20 MHz. The metric is
lost_last_secondfrom the adaptive-link telemetry: video fragments the downlink FEC could not recover.The baseline was measured twice, 9.78 and 15.82, which is the honest spread for a config that is losing 5-15 fragments a second — the exact figure moves with how much video the drone is sending and with ambient traffic. Every other row is a single run. What is not ambiguous is the ordering: each step down in uplink airtime moves the loss by roughly an order of magnitude, and
muting the uplink removes it entirely.
T
Colour blocks were still occasionally visible at the default in my testing. It is a large improvement, not a complete fix — the honest end of the range is the uplink-off row, and everything between is a trade.
Note the improvement is superlinear in airtime — halving per-frame transmit time cut loss 7×, not 2×. Shorter blackouts stop clipping runs of fragments, and the downlink FEC absorbs isolated losses fine.
What I tried that did not work
Keeping the burst and spacing the five fragments 8 ms apart. Total lost fragments dropped (15.8 → 10.4) but the artefacts became visibly more frequent — the same damage spread over more downlink FEC blocks, and one damaged block is one visible glitch. Pacing is the wrong instinct here; the lever is total airtime.
Testing
Ground station: Samsung Galaxy S22 (SM-S901U1, Android 16), RTL8812AU (
0bda:881a), 5 GHz channel 161 @ 20 MHz. Each configuration run 30+ s, 200-1200 telemetry samples, statistics from logcat. Every run was validated as actually transmitting (TxFramecountPIncoming > 0and the expectedfixed ratecode in the devourer log) before its numbers were used — the uplink can die silently, which invalidates a run that otherwise looks clean.Also tested on Google Pixel 1, same issue and same fix.
Not tested at long range; the MCS default trades uplink margin, so that is the axis to check before trusting it on a distant airframe.
Fixes #109