fix(gui): GUI-S1 — structural validation in find_packet_boundaries

The packet-boundary scanner only checked header + footer bytes, so any
payload byte that happened to be 0xAA (or 0xBB) and which lined up with
a 0x55 at offset+10 (or +25) was accepted as a packet. A single corrupt
byte could permanently shift the binning until the next frame_start
re-sync.

Added two structural sentinel checks against fixed bits the FPGA
emitter always drives to known values:
  - data byte 9   = {frame_start, 6'b0, cfar_detection} -> bits[6:1]==0
  - status byte 1 = high byte of status_words[0]        -> 0xFF

Combined with the existing footer check, false-match probability drops
from ~1/256 to ~1/16384 (data) and ~1/65536 (status). Mock generators
already produce conformant bit patterns, so existing parser/mock-read
tests pass unchanged.

New tests:
  - test_find_boundaries_rejects_false_data_header   (forged 0xAA...0x55)
  - test_find_boundaries_rejects_false_status_header (forged 0xBB...0x55)
  - test_find_boundaries_recovers_after_byte_drop    (single-byte loss)

Tests: GUI 96/96 (was 93), test_v7 83/83, MCU 75/75, ruff clean.
No RTL change -- wire format is unchanged; this hardens the parser only.
This commit is contained in:
Jason
2026-04-27 13:26:24 +05:45
parent 760288037f
commit e9e301dc50
2 changed files with 63 additions and 9 deletions
+21 -9
View File
@@ -267,28 +267,40 @@ class RadarProtocol:
"""
Scan buffer for packet start markers (0xAA data, 0xBB status).
Returns list of (start_idx, expected_end_idx, packet_type).
GUI-S1: in addition to header+footer, validate fixed structural
bytes the FPGA always emits in known patterns. This rejects false
starts where a payload byte happens to be 0xAA/0xBB and the byte
DATA/STATUS_PACKET_SIZE later happens to be 0x55:
- data byte 9 = {frame_start, 6'b0, cfar_detection} → bits[6:1]==0
- status byte 1 = high byte of status_words[0] → 0xFF
Drops false-match probability from 1/256 to ~1/16384 (data) /
~1/65536 (status).
"""
packets = []
i = 0
while i < len(buf):
n = len(buf)
while i < n:
if buf[i] == HEADER_BYTE:
end = i + DATA_PACKET_SIZE
if end <= len(buf) and buf[end - 1] == FOOTER_BYTE:
if end > n:
break # partial packet at end — leave for residual
if (buf[end - 1] == FOOTER_BYTE and
(buf[i + 9] & 0x7E) == 0):
packets.append((i, end, "data"))
i = end
else:
if end > len(buf):
break # partial packet at end — leave for residual
i += 1 # footer mismatch — skip this false header
i += 1 # structural mismatch — skip this false header
elif buf[i] == STATUS_HEADER_BYTE:
end = i + STATUS_PACKET_SIZE
if end <= len(buf) and buf[end - 1] == FOOTER_BYTE:
if end > n:
break # partial status packet — leave for residual
if (buf[end - 1] == FOOTER_BYTE and
buf[i + 1] == 0xFF):
packets.append((i, end, "status"))
i = end
else:
if end > len(buf):
break # partial status packet — leave for residual
i += 1 # footer mismatch — skip
i += 1
else:
i += 1
return packets