Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/kestrel/FrameParserKestrel.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ struct KestrelPhySts {

inline bool parse_physts_8852(const uint8_t *p, size_t len, bool is_8852c,
KestrelPhySts &out) {
/* Pure output param: the result depends only on this call's buffer, never
* on what a reused struct held before — including the failure return (an
* is_valid=0 stub, an absent IE, or a too-short buffer must all leave 0,
* not a previous blob's values), so the clear precedes the validation. */
out = KestrelPhySts{};
if (p == nullptr || len < 8)
return false;
out.rssi_avg = static_cast<uint8_t>(p[3] >> 1);
Expand Down
25 changes: 15 additions & 10 deletions src/kestrel/RtlKestrelDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,16 +338,21 @@ void RtlKestrelDevice::StartRxLoop(Action_ParsedRadioPacket packetProcessor) {
p.RxAtrib.snr[i] = _last_physts.snr[i];
p.RxAtrib.evm[i] = _last_physts.evm[i];
}
/* Feed the windowed RX-quality aggregate (passive rssi-snr floor +
* LinkHealth) with path-A RSSI + the IE01 average SNR (the
* all-paths quantity the passive floor was validated against;
* per-path SNR goes to _rxpaths instead). A frame with no IE_01
* SNR (snr_avg=0) still counts toward RSSI. */
if (_last_physts.rssi[0] > 0)
_rxq.add(_last_physts.rssi[0], _last_physts.snr_avg, 0);
/* Per-antenna window means (GetActiveRxPaths). Both dies are 2 RX
* chains; C/D read 0 and are excluded by n_chains. */
_rxpaths.add(p.RxAtrib.rssi, p.RxAtrib.snr, p.RxAtrib.evm, 2);
/* Window aggregates fold CRC-clean frames only (the Jaguar
* convention): a garbled frame's cached physts would bias the
* means and the active-chain classification.
*
* _rxq: passive rssi-snr floor + LinkHealth, fed path-A RSSI +
* the IE01 average SNR (the all-paths quantity the passive floor
* was validated against; per-path SNR goes to _rxpaths instead).
* A frame with no IE_01 SNR (snr_avg=0) still counts toward RSSI.
* _rxpaths: per-antenna window means (GetActiveRxPaths). Both
* dies are 2 RX chains; C/D read 0 and are excluded by n_chains. */
if (!f.crc_err) {
if (_last_physts.rssi[0] > 0)
_rxq.add(_last_physts.rssi[0], _last_physts.snr_avg, 0);
_rxpaths.add(p.RxAtrib.rssi, p.RxAtrib.snr, p.RxAtrib.evm, 2);
}
p.Data = std::span<uint8_t>(const_cast<uint8_t *>(f.payload),
f.payload_len);
packetProcessor(p);
Expand Down
14 changes: 12 additions & 2 deletions tests/kestrel_rxparse_selftest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ int main() {
CHECK(pb.evm[0] == -50 && pb.evm[1] == -30); /* EVM page read on both */
}

/* --- physts: is_valid=0 keeps header RSSI but skips the IE walk --- */
/* --- physts: is_valid=0 keeps header RSSI but skips the IE walk; a reused
* output struct is cleared (result depends only on this buffer) --- */
{
std::vector<uint8_t> b(8 + 32, 0);
b[0] = 0x01; /* no is_valid bit */
Expand All @@ -225,8 +226,12 @@ int main() {
b[8] = 1;
b[16] = 30;
KestrelPhySts ps;
ps.snr_avg = 44; /* stale garbage from a "previous parse" */
ps.snr[0] = 33;
ps.evm[1] = -40;
CHECK(parse_physts_8852(b.data(), b.size(), true, ps));
CHECK(ps.rssi[0] == 65 && ps.snr_avg == 0 && ps.snr[0] == 0);
CHECK(ps.rssi[0] == 65 && ps.snr_avg == 0 && ps.snr[0] == 0 &&
ps.evm[1] == 0);
}

/* --- physts: truncated / lying total-length is bounded by the buffer --- */
Expand All @@ -238,8 +243,13 @@ int main() {
KestrelPhySts ps;
CHECK(parse_physts_8852(b.data(), b.size(), true, ps));
CHECK(ps.snr_avg == 0); /* torn IE not parsed */
/* The failure return also clears a reused struct — the pure-output
* contract holds on every path, not just successful parses. */
KestrelPhySts p2;
p2.rssi[0] = 65;
p2.snr_avg = 44;
CHECK(!parse_physts_8852(b.data(), 4, true, p2)); /* below header size */
CHECK(p2.rssi[0] == 0 && p2.snr_avg == 0);
}

if (failures == 0)
Expand Down
Loading