Skip to content

Adaptive video link: energy-min controller + SVC-HEVC UEP pipeline#115

Merged
josephnef merged 2 commits into
masterfrom
adaptive-video-link
Jun 28, 2026
Merged

Adaptive video link: energy-min controller + SVC-HEVC UEP pipeline#115
josephnef merged 2 commits into
masterfrom
adaptive-video-link

Conversation

@josephnef

Copy link
Copy Markdown
Collaborator

What

An energy-minimizing adaptive link for the drone VTX → ground VRX video downlink — the dual of OpenIPC's alink. Where alink maximizes video quality within a power budget, this minimizes energy-per-delivered-bit subject to a per-layer UEP delivery SLA (base/IDR ≥ 99 %, enhancement best-effort). It rides the highest MCS the link bears (short airtime) and spends only the minimum TX power that clears it — the opposite reflex of a throughput-maximizer.

All userspace: policy in Python (tools/precoder/), reusing the already-merged fused-FEC modules (#113/#114); the C++ demo gains only mechanical control hooks.

Pieces

Control stack (tools/precoder/)

  • energy_model / link_model — airtime×power E_bit, SNR→P_deliver; documented nominal calibration with a metered-JSON hook ("model now, meter later").
  • op_table / controller — argmin-e_bit over SLA-feasible rows, TXAGC = minimum that clears the MCS, path-loss (not SNR) asymmetric EWMA, slow-up/fast-down hysteresis, MAX_RANGE failsafe. SvcController bank adds per-temporal-layer targets, enhancement shedding, one shared PA power.
  • rc_proto / score / rendezvous — CRC16-guarded RCF/DISC codec, post-FEC residual-loss scoring, receiver-initiated low-duty discovery.
  • adaptive_link.py--role vtx|vrx orchestrator driving StreamDuplexDemo.

SVC-HEVC pipeline

  • tests/gen_svc_nals.py — importable synthetic HEVC source (realistic per-layer VBR sizes; 4:8:16 T0/T1/T2 + IDR access units).
  • svc_uep_fec.py — opt-in NAL fragmentation/reassembly so real-sized NALs ride symbol-sized FEC packets (default off; existing behavior byte-identical).
  • svc_pipeline.py — end-to-end encode → per-layer (MCS,SNR) sub-block corruption → SBI salvage → decode, plus a closed-loop adaptive variant driven by SvcController.

C++StreamDuplexDemo gains a stdin control-opcode escape (SET_PWR/SET_RATE/SET_CHAN) so the Python policy moves the knobs with no USB churn or restart.

Results

  • tests/sim_loop.py headline (fly-out-and-back): 34 % energy/bit saved vs the best static energy-min profile, 53 % vs an over-provisioned robust profile, delivery 0.999, 2 operating-point changes over 200 ticks (no flapping).
  • SVC pipeline shows the graceful-degradation staircase end-to-end (T2 sheds first, base/IDR last) and SBI sub-block salvage beating whole-frame erasure.
  • New .github/workflows/precoder-tests.yml runs the whole precoder suite (uv + swif build + pytest) headlessly in CI — 205 tests pass.
  • On-air harness tests/adaptive_onair.sh (8812 VTX ↔ 8821 VRX + B210 interferer), witnessed by the peer's own rate=/rssi=.

Docs

docs/adaptive-link.md — the design plus a competitive comparison and feature matrix across wfb-ng, OpenIPC alink, RubyFPV, OpenHD, DJI OcuSync. The two axes unique to this link: it makes energy the objective and does per-temporal-layer UEP (PHY-MCS ⊕ FEC ladder + shedding) + corrupt-frame salvage.

Scope notes

  • Energy is modeled, not metered — relative savings are valid on the nominal calibration; an absolute Joule figure needs the DC-meter anchor the calibration JSON accepts.
  • On-air SVC today is non-adaptive (SvcTxDemo); the adaptive SVC path (VRX retuning the per-layer ladder live via per-frame layer-tagged radiotap) is the named integration point.

🤖 Generated with Claude Code

josephnef and others added 2 commits June 28, 2026 07:50
Energy-minimizing adaptive link for the drone VTX -> ground VRX video
downlink — the dual of OpenIPC's alink: instead of maximizing quality
within a power budget, it minimizes energy-per-delivered-bit subject to a
per-layer UEP delivery SLA (base/IDR >= 99%, enhancement best-effort).

Control stack (tools/precoder/, Python, reuses the committed fused-FEC
modules):
- energy_model / link_model: airtime x power E_bit and SNR -> P_deliver,
  with a documented nominal calibration ("model now, meter later").
- op_table / controller: argmin-e_bit over SLA-feasible rows; TXAGC chosen
  as the minimum that clears the MCS; path-loss (not SNR) asymmetric EWMA;
  slow-up/fast-down hysteresis; MAX_RANGE failsafe. SvcController bank adds
  per-temporal-layer targets + enhancement shedding + one shared PA power.
- rc_proto / score / rendezvous: CRC16-guarded RCF/DISC codec, post-FEC
  residual-loss scoring, receiver-initiated low-duty discovery.
- adaptive_link.py: --role vtx|vrx orchestrator driving StreamDuplexDemo.

SVC-HEVC pipeline:
- tests/gen_svc_nals.py: importable synthetic HEVC source (realistic VBR
  per-layer sizes, 4:8:16 T0/T1/T2 + IDR access units).
- svc_uep_fec.py: opt-in NAL fragmentation/reassembly so real-sized NALs
  ride symbol-sized FEC packets (default off; existing behavior unchanged).
- svc_pipeline.py: end-to-end encode -> per-layer (MCS,SNR) sub-block
  corruption -> SBI salvage -> decode, plus a closed-loop adaptive variant
  driven by SvcController.

C++: StreamDuplexDemo gains a stdin control-opcode escape (SET_PWR /
SET_RATE / SET_CHAN) so the Python policy moves the knobs with no USB
churn or restart.

Verification:
- tests/sim_loop.py headline: 34% energy/bit saved vs the best static
  energy-min profile, 53% vs an over-provisioned robust profile, delivery
  0.999, no flapping.
- New .github/workflows/precoder-tests.yml runs the whole precoder suite
  (uv + swif build + pytest) headlessly in CI; 205 tests pass.
- On-air harness tests/adaptive_onair.sh (8812 VTX <-> 8821 VRX + B210
  interferer), witnessed by the peer's own rate=/rssi=.

Docs: docs/adaptive-link.md — the design plus a competitive comparison
(wfb-ng, OpenIPC alink, RubyFPV, OpenHD, DJI OcuSync) and feature matrix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Replace hardcoded ~/git/devourer absolute paths with __file__-relative
  ones (sim_loop, calibrate_energy, test_controller, adaptive_link --duplex
  default) so the code works regardless of clone location; test_controller
  no longer relies on another test's sys.path side-effect.
- Rewrite docs/adaptive-link.md as a balanced comparison of the field
  (wfb-ng, OpenIPC alink, RubyFPV, OpenHD, DJI) rather than an alink-centric
  piece; drop implementation details (file paths, APIs, config constants);
  fix the inaccurate "video link is one-way" framing — the video is one-way
  broadcast but the link is two-way (RC up / telemetry down, feedback rides
  the return channel).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@josephnef josephnef merged commit 1fbf7d9 into master Jun 28, 2026
7 checks passed
@josephnef josephnef deleted the adaptive-video-link branch June 28, 2026 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant