test new variant

This commit is contained in:
awe
2026-04-28 19:32:10 +03:00
parent ffb7dc3f25
commit 9ff97bf737
11 changed files with 813 additions and 33 deletions

View File

@ -5,12 +5,13 @@ from __future__ import annotations
import math
import time
from collections import deque
from typing import List, Optional, Sequence, Set
from typing import Dict, List, Optional, Sequence, Set
import numpy as np
from rfg_adc_plotter.constants import DATA_INVERSION_THRESHOLD, LOG_BASE, LOG_EXP_LIMIT, LOG_POSTSCALER, LOG_SCALER
from rfg_adc_plotter.types import (
Do1Level,
ParserEvent,
PointEvent,
SignalKind,
@ -162,6 +163,14 @@ class LegacyBinaryParser:
self._seen_points = False
self._mode: Optional[str] = None
self._current_signal_kind: Optional[SignalKind] = None
self._last_tagged_step_by_level: Dict[Do1Level, Optional[int]] = {
"low": None,
"high": None,
}
def _reset_tagged_steps(self) -> None:
self._last_tagged_step_by_level["low"] = None
self._last_tagged_step_by_level["high"] = None
@staticmethod
def _u16_at(buf: bytearray, offset: int) -> int:
@ -172,6 +181,7 @@ class LegacyBinaryParser:
self._last_step = None
self._seen_points = False
self._current_signal_kind = None
self._reset_tagged_steps()
events.append(StartEvent(ch=int(ch)))
def _emit_bin_start(self, events: List[ParserEvent], signal_kind: SignalKind) -> None:
@ -179,6 +189,7 @@ class LegacyBinaryParser:
self._last_step = None
self._seen_points = False
self._current_signal_kind = signal_kind
self._reset_tagged_steps()
events.append(StartEvent(ch=0, signal_kind=signal_kind))
def _emit_tty_start(self, events: List[ParserEvent]) -> None:
@ -187,6 +198,7 @@ class LegacyBinaryParser:
def _emit_legacy_point(self, events: List[ParserEvent], step: int, value_word_hi: int, value_word_lo: int, ch: int) -> None:
self._mode = "legacy"
self._current_signal_kind = None
self._reset_tagged_steps()
if self._seen_points and self._last_step is not None and step <= self._last_step:
events.append(StartEvent(ch=int(ch)))
self._seen_points = True
@ -194,7 +206,13 @@ class LegacyBinaryParser:
value = u32_to_i32((int(value_word_hi) << 16) | int(value_word_lo))
events.append(PointEvent(ch=int(ch), x=int(step), y=float(value)))
def _prepare_bin_point(self, events: List[ParserEvent], step: int, signal_kind: SignalKind) -> None:
def _prepare_bin_point(
self,
events: List[ParserEvent],
step: int,
signal_kind: SignalKind,
do1_level: Optional[Do1Level] = None,
) -> None:
self._mode = "bin"
if self._current_signal_kind != signal_kind:
if self._seen_points:
@ -202,12 +220,28 @@ class LegacyBinaryParser:
self._last_step = None
self._seen_points = False
self._current_signal_kind = signal_kind
self._reset_tagged_steps()
if signal_kind == "bin_iq_do1_tagged":
level: Do1Level = "high" if do1_level == "high" else "low"
last_level_step = self._last_tagged_step_by_level[level]
if self._seen_points and last_level_step is not None and step <= last_level_step:
events.append(StartEvent(ch=0, signal_kind=signal_kind))
self._last_step = None
self._seen_points = False
self._reset_tagged_steps()
self._seen_points = True
self._last_tagged_step_by_level[level] = int(step)
self._last_step = int(step)
return
if self._seen_points and self._last_step is not None and step <= self._last_step:
events.append(StartEvent(ch=0, signal_kind=signal_kind))
self._last_step = None
self._seen_points = False
self._seen_points = True
self._last_step = int(step)
self._reset_tagged_steps()
def _emit_tty_point(self, events: List[ParserEvent], step: int, ch_1_word: int, ch_2_word: int) -> None:
self._prepare_bin_point(events, step=int(step), signal_kind="bin_iq")
@ -223,6 +257,33 @@ class LegacyBinaryParser:
)
)
def _emit_tty_tagged_point(
self,
events: List[ParserEvent],
step: int,
ch_1_word: int,
ch_2_word: int,
do1_level: Do1Level,
) -> None:
self._prepare_bin_point(
events,
step=int(step),
signal_kind="bin_iq_do1_tagged",
do1_level=do1_level,
)
ch_1 = u16_to_i16(int(ch_1_word))
ch_2 = u16_to_i16(int(ch_2_word))
events.append(
PointEvent(
ch=0,
x=int(step),
y=tty_ch_pair_to_sweep(ch_1, ch_2),
aux=(float(ch_1), float(ch_2)),
signal_kind="bin_iq_do1_tagged",
do1_level=do1_level,
)
)
def _emit_logdet_point(self, events: List[ParserEvent], step: int, value_word: int) -> None:
self._prepare_bin_point(events, step=int(step), signal_kind="bin_logdet")
value = u16_to_i16(int(value_word))
@ -249,6 +310,8 @@ class LegacyBinaryParser:
is_tty_start = (w0 == 0x000A and w1 == 0xFFFF and w2 == 0xFFFF and w3 == 0xFFFF)
is_legacy_point = (self._buf[6] == 0x0A and w0 != 0xFFFF)
is_tty_point = (w0 == 0x000A and w1 != 0xFFFF)
is_tty_tagged_low_point = (w0 == 0x00A3 and w1 != 0xFFFF)
is_tty_tagged_high_point = (w0 == 0x00A4 and w1 != 0xFFFF)
is_logdet_point = (w0 == 0x001A and w3 == 0x0000)
if is_legacy_start:
@ -281,6 +344,26 @@ class LegacyBinaryParser:
self._emit_tty_point(events, step=int(w1), ch_1_word=int(w2), ch_2_word=int(w3))
del self._buf[:8]
continue
if is_tty_tagged_low_point and (not is_legacy_point):
self._emit_tty_tagged_point(
events,
step=int(w1),
ch_1_word=int(w2),
ch_2_word=int(w3),
do1_level="low",
)
del self._buf[:8]
continue
if is_tty_tagged_high_point and (not is_legacy_point):
self._emit_tty_tagged_point(
events,
step=int(w1),
ch_1_word=int(w2),
ch_2_word=int(w3),
do1_level="high",
)
del self._buf[:8]
continue
del self._buf[:1]
continue
@ -289,6 +372,26 @@ class LegacyBinaryParser:
self._emit_tty_point(events, step=int(w1), ch_1_word=int(w2), ch_2_word=int(w3))
del self._buf[:8]
continue
if is_tty_tagged_low_point:
self._emit_tty_tagged_point(
events,
step=int(w1),
ch_1_word=int(w2),
ch_2_word=int(w3),
do1_level="low",
)
del self._buf[:8]
continue
if is_tty_tagged_high_point:
self._emit_tty_tagged_point(
events,
step=int(w1),
ch_1_word=int(w2),
ch_2_word=int(w3),
do1_level="high",
)
del self._buf[:8]
continue
if is_legacy_point and (not is_tty_point):
self._emit_legacy_point(
events,
@ -309,6 +412,28 @@ class LegacyBinaryParser:
del self._buf[:8]
continue
if is_tty_tagged_low_point and (not is_legacy_point):
self._emit_tty_tagged_point(
events,
step=int(w1),
ch_1_word=int(w2),
ch_2_word=int(w3),
do1_level="low",
)
del self._buf[:8]
continue
if is_tty_tagged_high_point and (not is_legacy_point):
self._emit_tty_tagged_point(
events,
step=int(w1),
ch_1_word=int(w2),
ch_2_word=int(w3),
do1_level="high",
)
del self._buf[:8]
continue
if is_legacy_point and (not is_tty_point):
self._emit_legacy_point(
events,
@ -524,15 +649,34 @@ class SweepAssembler:
self._ys: list[float] = []
self._aux_1: list[float] = []
self._aux_2: list[float] = []
self._tagged_low_xs: list[int] = []
self._tagged_low_ys: list[float] = []
self._tagged_low_aux_1: list[float] = []
self._tagged_low_aux_2: list[float] = []
self._tagged_high_xs: list[int] = []
self._tagged_high_ys: list[float] = []
self._tagged_high_aux_1: list[float] = []
self._tagged_high_aux_2: list[float] = []
self._cur_channel: Optional[int] = None
self._cur_signal_kind: Optional[SignalKind] = None
self._cur_channels: set[int] = set()
def _reset_tagged_current(self) -> None:
self._tagged_low_xs.clear()
self._tagged_low_ys.clear()
self._tagged_low_aux_1.clear()
self._tagged_low_aux_2.clear()
self._tagged_high_xs.clear()
self._tagged_high_ys.clear()
self._tagged_high_aux_1.clear()
self._tagged_high_aux_2.clear()
def _reset_current(self) -> None:
self._xs.clear()
self._ys.clear()
self._aux_1.clear()
self._aux_2.clear()
self._reset_tagged_current()
self._cur_channel = None
self._cur_signal_kind = None
self._cur_channels.clear()
@ -567,6 +711,24 @@ class SweepAssembler:
if last_idx < series.size - 1:
series[last_idx + 1 :] = series[last_idx]
@staticmethod
def _nanmean_pair(primary: np.ndarray, secondary: np.ndarray) -> np.ndarray:
width = min(primary.size, secondary.size)
if width <= 0:
return np.zeros((0,), dtype=np.float32)
first = np.asarray(primary[:width], dtype=np.float32)
second = np.asarray(secondary[:width], dtype=np.float32)
out = np.full((width,), np.nan, dtype=np.float32)
first_valid = np.isfinite(first)
second_valid = np.isfinite(second)
both_valid = first_valid & second_valid
only_first = first_valid & (~second_valid)
only_second = second_valid & (~first_valid)
out[only_first] = first[only_first]
out[only_second] = second[only_second]
out[both_valid] = (first[both_valid] + second[both_valid]) * 0.5
return out
def consume(self, event: ParserEvent) -> Optional[SweepPacket]:
if isinstance(event, StartEvent):
packet = self.finalize_current()
@ -598,7 +760,21 @@ class SweepAssembler:
self._cur_channels.add(point_ch)
self._xs.append(int(event.x))
self._ys.append(float(event.y))
if event.aux is not None:
if self._cur_signal_kind == "bin_iq_do1_tagged":
level = "high" if event.do1_level == "high" else "low"
if level == "low":
self._tagged_low_xs.append(int(event.x))
self._tagged_low_ys.append(float(event.y))
if event.aux is not None:
self._tagged_low_aux_1.append(float(event.aux[0]))
self._tagged_low_aux_2.append(float(event.aux[1]))
else:
self._tagged_high_xs.append(int(event.x))
self._tagged_high_ys.append(float(event.y))
if event.aux is not None:
self._tagged_high_aux_1.append(float(event.aux[0]))
self._tagged_high_aux_2.append(float(event.aux[1]))
elif event.aux is not None:
self._aux_1.append(float(event.aux[0]))
self._aux_2.append(float(event.aux[1]))
return packet
@ -613,13 +789,37 @@ class SweepAssembler:
self._max_width = max(self._max_width, width)
target_width = self._max_width if self._fancy else width
sweep = self._scatter(self._xs, self._ys, target_width)
aux_curves: SweepAuxCurves = None
if self._aux_1 and self._aux_2 and len(self._aux_1) == len(self._xs):
aux_curves = (
self._scatter(self._xs, self._aux_1, target_width),
self._scatter(self._xs, self._aux_2, target_width),
)
do1_tagged_payload = None
if self._cur_signal_kind == "bin_iq_do1_tagged":
raw_low = self._scatter(self._tagged_low_xs, self._tagged_low_ys, target_width)
raw_high = self._scatter(self._tagged_high_xs, self._tagged_high_ys, target_width)
sweep = self._nanmean_pair(raw_low, raw_high)
aux_low = None
if self._tagged_low_aux_1 and self._tagged_low_aux_2 and len(self._tagged_low_aux_1) == len(self._tagged_low_xs):
aux_low = (
self._scatter(self._tagged_low_xs, self._tagged_low_aux_1, target_width),
self._scatter(self._tagged_low_xs, self._tagged_low_aux_2, target_width),
)
aux_high = None
if self._tagged_high_aux_1 and self._tagged_high_aux_2 and len(self._tagged_high_aux_1) == len(self._tagged_high_xs):
aux_high = (
self._scatter(self._tagged_high_xs, self._tagged_high_aux_1, target_width),
self._scatter(self._tagged_high_xs, self._tagged_high_aux_2, target_width),
)
do1_tagged_payload = {
"raw_low": raw_low,
"raw_high": raw_high,
"aux_low": aux_low,
"aux_high": aux_high,
}
else:
sweep = self._scatter(self._xs, self._ys, target_width)
if self._aux_1 and self._aux_2 and len(self._aux_1) == len(self._xs):
aux_curves = (
self._scatter(self._xs, self._aux_1, target_width),
self._scatter(self._xs, self._aux_2, target_width),
)
n_valid_cur = int(np.count_nonzero(np.isfinite(sweep)))
@ -670,4 +870,6 @@ class SweepAssembler:
"std": std,
"dt_ms": dt_ms,
}
if do1_tagged_payload is not None:
info["_do1_tagged_payload"] = do1_tagged_payload
return (sweep, info, aux_curves)

View File

@ -50,7 +50,7 @@ def _looks_like_legacy_8byte_stream(data: bytes) -> bool:
w0 = _u16le_at(buf, base)
w1 = _u16le_at(buf, base + 2)
w3 = _u16le_at(buf, base + 6)
if w0 == 0x000A and w1 != 0xFFFF:
if w0 in {0x000A, 0x00A3, 0x00A4} and w1 != 0xFFFF:
matched_steps_tty.append(w1)
elif w0 == 0x001A and w3 == 0x0000:
matched_steps_logdet.append(w1)
@ -232,7 +232,8 @@ class SweepReader(threading.Thread):
)
)
sys.stderr.write(
"[hint] parser_16_bit_x2: if source is 8-byte tty CH1/CH2 stream (0x000A,step,ch1,ch2), try --bin\n"
"[hint] parser_16_bit_x2: if source is 8-byte tty CH1/CH2 stream "
"(0x000A/0x00A3/0x00A4,step,ch1,ch2), try --bin\n"
)
assembler = SweepAssembler(fancy=self._fancy, apply_inversion=False)
return parser, assembler, []
@ -358,7 +359,8 @@ class SweepReader(threading.Thread):
and (now_s - self._started_at) >= _NO_PACKET_HINT_AFTER_S
):
sys.stderr.write(
"[hint] parser_16_bit_x2 still has no sweeps; if source is tty CH1/CH2, rerun with --bin\n"
"[hint] parser_16_bit_x2 still has no sweeps; "
"if source is tty CH1/CH2 (0x000A/0x00A3/0x00A4), rerun with --bin\n"
)
parser_hint_emitted = True
time.sleep(0.0005)