some kamil_adc fixes

This commit is contained in:
Ayzen
2026-07-02 17:44:24 +03:00
parent 42532c9868
commit 3efe968dd1
10 changed files with 644 additions and 42 deletions
+30 -1
View File
@@ -29,6 +29,12 @@ import numpy as np
FRAME_BYTES = 8
MAIN_MARKER = 0x000A
REFERENCE_MARKER = 0x00A8
# Combo tag — ``0x00C0, input_pos, output_pos, dirty``. Emitted by the switch-aware
# collector right after a sweep boundary to label the upcoming sweep with the RF
# switch combination it was captured under (``dirty != 0`` means the sweep straddled
# a switch transition and must be dropped). Absent in the standalone/calibration
# collector, where every sweep carries no combo (``combo is None``).
COMBO_MARKER = 0x00C0
_BOUNDARY_STEP = 0xFFFF
# marker (u16), step (u16), ch1 (i16), ch2 (i16) — point frames carry signed I/Q.
@@ -52,6 +58,8 @@ class RawSweep:
steps: np.ndarray
main: np.ndarray
reference: np.ndarray
combo: tuple[int, int] | None = None
dirty: bool = False
@property
def size(self) -> int:
@@ -66,13 +74,17 @@ class KamilAdcStreamParser:
but holds no I/O and is cheap to unit-test.
"""
__slots__ = ("_buffer", "_aligned", "_main", "_reference")
__slots__ = ("_buffer", "_aligned", "_main", "_reference", "_pending_combo", "_pending_dirty")
def __init__(self) -> None:
self._buffer = bytearray()
self._aligned = False
self._main: dict[int, complex] = {}
self._reference: dict[int, complex] = {}
# Combo tag for the sweep currently being accumulated (set by the combo
# frame right after each boundary; ``None`` in non-switch collector modes).
self._pending_combo: tuple[int, int] | None = None
self._pending_dirty = False
def feed(self, data: bytes) -> list[RawSweep]:
"""Append ``data`` and return any sweeps completed by it."""
@@ -95,6 +107,10 @@ class KamilAdcStreamParser:
self._main[step] = complex(real, imag)
elif marker == REFERENCE_MARKER:
self._reference[step] = complex(real, imag)
elif marker == COMBO_MARKER:
# step = input_pos, real = output_pos, imag = dirty flag.
self._pending_combo = (int(step), int(real))
self._pending_dirty = imag != 0
else:
raise ValueError(
f"Kamil ADC protocol violation: unexpected frame marker 0x{marker:04x}"
@@ -107,6 +123,8 @@ class KamilAdcStreamParser:
self._aligned = False
self._main.clear()
self._reference.clear()
self._pending_combo = None
self._pending_dirty = False
def _align(self) -> bool:
"""Discard pre-roll up to and including the first sweep boundary.
@@ -122,6 +140,8 @@ class KamilAdcStreamParser:
del self._buffer[: index + FRAME_BYTES]
self._main.clear()
self._reference.clear()
self._pending_combo = None
self._pending_dirty = False
self._aligned = True
return True
@@ -130,12 +150,21 @@ class KamilAdcStreamParser:
shared = sorted(self._main.keys() & self._reference.keys())
main = self._main
reference = self._reference
combo = self._pending_combo
dirty = self._pending_dirty
self._main = {}
self._reference = {}
# The next sweep's combo is set by its own combo frame (right after this
# boundary); clear so a sweep without one reports combo=None rather than
# inheriting a stale tag.
self._pending_combo = None
self._pending_dirty = False
if not shared:
return None
return RawSweep(
steps=np.asarray(shared, dtype=np.int32),
main=np.asarray([main[step] for step in shared], dtype=np.complex64),
reference=np.asarray([reference[step] for step in shared], dtype=np.complex64),
combo=combo,
dirty=dirty,
)