Summary
Generalize the caller-supplied per-rate TX-power diff table (SetTxPowerRateDiffs) from the 8822E-only implementation to the other supported generations (Jaguar1, Jaguar2, Jaguar3-8822C, and ideally Kestrel). This is Realtek's "dynamic power by rate" lever — an efuse-shaped per-rate/per-path power offset (TxPwrByRateOffset[band][path][rate], PHY_GetTxPowerByRate* in the vendor hal_com_phycfg.c) — and it is the range-relevant power knob for a fixed-rate FPV link.
Motivation
- The link-relevant use is boosting CCK / low-MCS power (or trimming high-MCS) for range and PA headroom, independent of the descriptor rate — the natural companion to devourer's fixed-rate injection.
- The mechanism is already implemented and validated for the 8822E (
SetTxPowerRateDiffs(TxRateDiffsQdb), examples/txpower/ --rate-diffs, register-checked by tests/txpwr_rate_diffs_regcheck.sh). Every other generation's SetTxPowerRateDiffs currently just returns false, which is an artificial capability gap — the underlying per-rate power table exists on all Jaguar chips.
Current state
- 8822E (reference impl): a caller table (cck, legacy, mcs0..7, signed qdB) replaces the default
phy_reg_pg per-rate walk, folded on top of the active reference (efuse table or flat override). Sticks across SetMonitorChannel, FastRetune, and override set/clear. std::nullopt restores the default walk. See src/TxPower.h, the 8822E HAL, and docs/CLAUDE.md.
- All other generations:
SetTxPowerRateDiffs returns false (no-op). But the vendor per-rate power table (TxPwrByRate, phy_reg_pg) is a Jaguar-family-wide construct — the fold point exists; only the caller-override hook is missing.
Proposed work
- Lift the fold seam to the shared TX-power path. Factor the 8822E's "caller diff table replaces the default per-rate walk" logic into the generation-neutral
src/TxPower.* layer so each HAL only supplies its per-rate walk + register-write primitive. Preserve the exact stickiness contract (channel switch, FastRetune, override set/clear re-apply, nullopt = restore default).
- Per-generation wiring:
- Jaguar2 (8822B/8821C): TXAGC block is write-only → software shadow (
hw_readback=false), matching the existing 8822E/Jaguar2 power-state reporting.
- Jaguar1 (8812A/8821A/8814A): fold into the BB-swing / packed-port path; the 8814A packed port is write-only (shadow only).
- Jaguar3-8822C: shares the 8822E family fold; should be near-free.
- Kestrel: map onto the fixed-dBm BB target rewrite if it fits; otherwise explicitly out of scope for v1.
- Family step preserved via
GetTxPowerCaps (0.5 dB J1/J2, 0.25 dB J3).
- Extend the reference consumer + regression:
examples/txpower/ --rate-diffs cck,legacy,m0..m7|clear already exists — make it work on the newly-wired chips; extend tests/txpwr_rate_diffs_regcheck.sh to cover each generation (register/shadow validation).
- Caps: advertise support per generation (a
per_rate_txpwr_diffs flag or by making the API return true only where wired).
Validation
tests/txpwr_rate_diffs_regcheck.sh per generation (register-level or shadow, matching each chip's readback capability).
- One on-air confirmation per newly-wired chip that a CCK/low-MCS boost actually raises radiated power (SDR /
tests/txpkt_pwr_ofset_onair.sh style), since the diff table folds on top of the efuse reference.
Scope / caveats
- Preserve byte-identical behaviour for the 8822E path (it is on-air + register validated).
- Keep the neutral library env-free — this stays a construction/runtime setter, driven by the demo env mapping as today.
- Kestrel may be deferred if its fixed-dBm model doesn't map cleanly onto a per-rate diff table.
🤖 Filed with Claude Code after a code + vendor-reference investigation.
Summary
Generalize the caller-supplied per-rate TX-power diff table (
SetTxPowerRateDiffs) from the 8822E-only implementation to the other supported generations (Jaguar1, Jaguar2, Jaguar3-8822C, and ideally Kestrel). This is Realtek's "dynamic power by rate" lever — an efuse-shaped per-rate/per-path power offset (TxPwrByRateOffset[band][path][rate],PHY_GetTxPowerByRate*in the vendorhal_com_phycfg.c) — and it is the range-relevant power knob for a fixed-rate FPV link.Motivation
SetTxPowerRateDiffs(TxRateDiffsQdb),examples/txpower/ --rate-diffs, register-checked bytests/txpwr_rate_diffs_regcheck.sh). Every other generation'sSetTxPowerRateDiffscurrently just returnsfalse, which is an artificial capability gap — the underlying per-rate power table exists on all Jaguar chips.Current state
phy_reg_pgper-rate walk, folded on top of the active reference (efuse table or flat override). Sticks acrossSetMonitorChannel,FastRetune, and override set/clear.std::nulloptrestores the default walk. Seesrc/TxPower.h, the 8822E HAL, anddocs/CLAUDE.md.SetTxPowerRateDiffsreturnsfalse(no-op). But the vendor per-rate power table (TxPwrByRate,phy_reg_pg) is a Jaguar-family-wide construct — the fold point exists; only the caller-override hook is missing.Proposed work
src/TxPower.*layer so each HAL only supplies its per-rate walk + register-write primitive. Preserve the exact stickiness contract (channel switch, FastRetune, override set/clear re-apply,nullopt= restore default).hw_readback=false), matching the existing 8822E/Jaguar2 power-state reporting.GetTxPowerCaps(0.5 dB J1/J2, 0.25 dB J3).examples/txpower/ --rate-diffs cck,legacy,m0..m7|clearalready exists — make it work on the newly-wired chips; extendtests/txpwr_rate_diffs_regcheck.shto cover each generation (register/shadow validation).per_rate_txpwr_diffsflag or by making the API returntrueonly where wired).Validation
tests/txpwr_rate_diffs_regcheck.shper generation (register-level or shadow, matching each chip's readback capability).tests/txpkt_pwr_ofset_onair.shstyle), since the diff table folds on top of the efuse reference.Scope / caveats
🤖 Filed with Claude Code after a code + vendor-reference investigation.