Skip to content

Runtime SetRxPathMask() API — motion-responsive RX-chain selection (adaptive-link diversity lever) #189

Description

@josephnef

Gap

The RX-chain enable mask (0x808 byte 0 — path A/B/C/D CCK+OFDM enable) is the adaptive-link's spatial-diversity lever: with the antennas decorrelated (RX motion), combining more chains buys MRC gain; when they're correlated it pays nothing. Today the only way to move it is the env var DEVOURER_RX_PATHS, either fixed at bring-up or cycled on a fixed timer (0xAA:0xBB@<ms> toggle-spec, src/jaguar1/RtlJaguarDevice.cpp:656-692, thread at :904). A fixed 300 ms toggle is a measurement convenience — it cannot respond to a measured fade/motion signal, which is what docs/adaptive-link-building-blocks.md describes the lever as being driven by.

This mirrors the motivation for the runtime TX-power API (#188): a lever the adaptive controller needs to move on a measured signal shouldn't be locked behind an env var and a demo-side timer.

Proposal

A tiny runtime setter on the concrete Jaguar1 device (the 4T4R 8814AU is where diversity actually has ≥2 chains to trade):

void SetRxPathMask(uint8_t mask);   // write 0x808[7:0]; 0x11=A, 0x33=A+B, 0x77=A+B+C, 0xFF=all
uint8_t GetRxPathMask();            // read-back

Cost is one byte write — no calibration, no thread. It supersedes the demo's timer thread for a controller that has its own motion signal (e.g. beamforming-variance from the sensing path), and it makes tests/mrc_mobility.py able to switch masks in lockstep with RX motion rather than on a blind clock.

Whether it belongs on IRtlDevice or stays Jaguar1-concrete is an open call: the 2T2R Jaguar2/3 parts have only A+B to trade and no 4-chain diversity story, so a J1-only concrete method (reached by dynamic_cast, like the existing research helpers) may be the honest scope. GetRxPathMask is trivially generation-agnostic if we do put it on the interface.

Constraints to document (pre-existing, not introduced here)

  • Reverts on channel switch: IQK saves/restores 0x808, and a later SetMonitorChannel reverts the mask to the table default (RtlJaguarDevice.cpp:663). A runtime setter is a single-channel knob unless it's re-applied after each channel set — the API doc must say so (the same stickiness question the TX-power offset answered by re-folding; here the honest answer is likely "re-apply after SetMonitorChannel if you want it sticky", since there's no per-rate table to re-fold against).
  • Not a gain knob: masking a chain drops its per-chain RSSI to the noise floor (validated on the 8814); it changes which chains combine, not their gain.

Validation

tests/mrc_mobility.py + docs/measuring-spatial-diversity.md: alternate a fixed chain vs all-4 in response to RX motion (not a fixed timer) over a marginal link, confirm the per-chain RSSI collapse on masked chains and the realised combining gain when they're enabled. Register-parity: 0x808 read-back == written mask; confirm a SetMonitorChannel reverts it (documents the single-channel contract).

Context

Falls out of the RX-vs-TX runtime-control research after #188 — the one RX knob that earns a runtime API (IGI/DIG and NBI/CSI-mask do not: RX gain is chip-closed, and the adaptive response to a spur is a channel hop, already runtime). See also the J3 DIG parity gap (separate issue).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions