feat(python): host-side decode for ADR-018 byte 18-19 (ADR-110)

Python ESP32BinaryParser was using struct format '<IBBHIIBB2x' — the
'2x' skipped bytes 18-19 as reserved. After the Rust-side decoder was
extended to surface PPDU type + flags, the Python pipeline (which
archive/v1 still uses for testing + the proof verifier) needs the same
update so its consumers see the HE metadata too.

csi_extractor.py:
- HEADER_FMT now '<IBBHIIBBBB' (captures bytes 18-19)
- New metadata fields: ppdu_type ('ht_legacy'|'he_su'|'he_mu'|'he_tb'|'unknown'),
  ppdu_type_raw, he_capable, bw40, stbc, ldpc, ieee802154_sync_valid,
  adr018_flags_raw
- Class constants PPDU_HT_LEGACY..PPDU_UNKNOWN mirror the firmware

test_esp32_binary_parser.py:
- build_binary_frame() takes optional ppdu_byte + flags_byte (default 0)
- New TestAdr110ByteEncoding class with 5 tests:
  - Pre-ADR-110 zeros decode as 'ht_legacy' + all-flags-false
  - HE-SU / HE-MU / HE-TB decode correctly
  - 0xFF decodes as 'unknown'
  - All-flags-set round-trip (0x1D)

11/11 parser tests pass (6 existing + 5 new). Backwards compat verified.

Pairs with the Rust-side decoder in commit 3959fabf3. Both pipelines now
read the same wire format produced by the C6 firmware's
CONFIG_CSI_FRAME_HE_TAGGING path.

Ref: ruvnet/RuView#762, draft PR #764

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruv
2026-05-22 22:46:37 -04:00
parent 3959fabf31
commit 8eaa92cf21
2 changed files with 84 additions and 5 deletions
@@ -23,7 +23,10 @@ from hardware.csi_extractor import (
# ADR-018 constants
MAGIC = 0xC5110001
HEADER_FMT = '<IBBHIIBB2x'
# ADR-110: bytes 18-19 are now PPDU type + flags (used to be `2x` reserved).
# Pre-ADR-110 firmware sends zeros for both, which round-trip as
# ('ht_legacy', flags=all-false) — fully backwards compatible.
HEADER_FMT = '<IBBHIIBBBB'
HEADER_SIZE = 20
@@ -36,6 +39,8 @@ def build_binary_frame(
rssi: int = -50,
noise_floor: int = -90,
iq_pairs: list = None,
ppdu_byte: int = 0, # ADR-110: default 0 = HT/legacy (pre-ADR-110 behavior)
flags_byte: int = 0, # ADR-110: default 0 = no flags set
) -> bytes:
"""Build an ADR-018 binary frame for testing."""
if iq_pairs is None:
@@ -54,6 +59,8 @@ def build_binary_frame(
sequence,
rssi_u8,
noise_u8,
ppdu_byte,
flags_byte,
)
iq_data = b''
@@ -63,6 +70,52 @@ def build_binary_frame(
return header + iq_data
class TestAdr110ByteEncoding:
"""ADR-110: byte 18 = PPDU type, byte 19 = flags."""
def setup_method(self):
self.parser = ESP32BinaryParser()
def test_pre_adr110_zeros_decode_as_ht_legacy(self):
"""Pre-ADR-110 firmware sends zeros → must surface as HT/legacy + no flags."""
frame = build_binary_frame() # ppdu_byte=0, flags_byte=0 default
csi = self.parser.parse(frame)
assert csi.metadata['ppdu_type'] == 'ht_legacy'
assert csi.metadata['ppdu_type_raw'] == 0
assert csi.metadata['he_capable'] is False
assert csi.metadata['bw40'] is False
assert csi.metadata['stbc'] is False
assert csi.metadata['ldpc'] is False
assert csi.metadata['ieee802154_sync_valid'] is False
def test_he_su_decodes(self):
frame = build_binary_frame(ppdu_byte=1)
csi = self.parser.parse(frame)
assert csi.metadata['ppdu_type'] == 'he_su'
assert csi.metadata['he_capable'] is True
def test_he_mu_and_he_tb_decode(self):
for byte, expected in [(2, 'he_mu'), (3, 'he_tb')]:
csi = self.parser.parse(build_binary_frame(ppdu_byte=byte))
assert csi.metadata['ppdu_type'] == expected
assert csi.metadata['he_capable'] is True
def test_unknown_ppdu_byte(self):
csi = self.parser.parse(build_binary_frame(ppdu_byte=0xFF))
assert csi.metadata['ppdu_type'] == 'unknown'
assert csi.metadata['ppdu_type_raw'] == 0xFF
assert csi.metadata['he_capable'] is False
def test_all_flags_set_round_trip(self):
# bw40 (0x01) + STBC (0x04) + LDPC (0x08) + 15.4-sync (0x10) = 0x1D
csi = self.parser.parse(build_binary_frame(ppdu_byte=1, flags_byte=0x1D))
assert csi.metadata['bw40'] is True
assert csi.metadata['stbc'] is True
assert csi.metadata['ldpc'] is True
assert csi.metadata['ieee802154_sync_valid'] is True
assert csi.metadata['adr018_flags_raw'] == 0x1D
class TestESP32BinaryParser:
"""Tests for ESP32BinaryParser."""