ch1 ch2 new

This commit is contained in:
awe
2026-05-29 17:15:32 +03:00
parent 5591e80c53
commit 08dc6b3a1f
5 changed files with 328 additions and 27 deletions

View File

@ -286,6 +286,28 @@ class LegacyBinaryParser:
)
)
def _emit_secondary_point(
self,
events: List[ParserEvent],
step: int,
ch_1_word: int,
ch_2_word: int,
) -> None:
self._mode = "bin"
self._current_signal_kind = self._current_signal_kind or "bin_iq"
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=0.0,
aux=(float(ch_1), float(ch_2)),
signal_kind="bin_iq",
is_secondary=True,
)
)
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))
@ -310,8 +332,9 @@ class LegacyBinaryParser:
w0 = words[:, 0]
w1 = words[:, 1]
is_tty_point = (w0 == 0x000A) & (w1 != 0xFFFF)
is_sec_point = (w0 == 0x00A8) & (w1 != 0xFFFF)
is_legacy_point = (raw[:, 6] == 0x0A) & (w0 != 0xFFFF)
valid = is_tty_point
valid = is_tty_point | is_sec_point
if require_not_legacy:
valid = valid & (~is_legacy_point)
if valid.size <= 0 or not bool(valid[0]):
@ -321,7 +344,15 @@ class LegacyBinaryParser:
if valid_count <= 0:
return False
steps = words[:valid_count, 1].astype(np.int64, copy=True)
primary_mask = is_tty_point[:valid_count]
secondary_mask = is_sec_point[:valid_count]
primary_indices = np.nonzero(primary_mask)[0]
if primary_indices.size <= 0:
# No primary records in this block — cannot batch
return False
primary_steps = words[:valid_count, 1][primary_mask].astype(np.int64, copy=True)
if self._current_signal_kind != "bin_iq":
if self._seen_points:
events.append(StartEvent(ch=0, signal_kind="bin_iq"))
@ -330,44 +361,72 @@ class LegacyBinaryParser:
self._current_signal_kind = "bin_iq"
self._reset_tagged_steps()
if self._seen_points and self._last_step is not None and steps[0] <= int(self._last_step):
if self._seen_points and self._last_step is not None and primary_steps[0] <= int(self._last_step):
events.append(StartEvent(ch=0, signal_kind="bin_iq"))
self._last_step = None
self._seen_points = False
self._reset_tagged_steps()
reset_idx = np.nonzero(np.diff(steps) <= 0)[0]
take_count = int(reset_idx[0] + 1) if reset_idx.size > 0 else int(steps.size)
reset_idx = np.nonzero(np.diff(primary_steps) <= 0)[0]
if reset_idx.size > 0:
reset_primary_pos = int(reset_idx[0] + 1)
if reset_primary_pos < primary_indices.size:
take_count = int(primary_indices[reset_primary_pos])
else:
take_count = valid_count
else:
take_count = valid_count
if take_count <= 0:
return False
primary_mask_take = is_tty_point[:take_count]
secondary_mask_take = is_sec_point[:take_count]
batch_words = words[:take_count].copy()
xs = batch_words[:, 1].astype(np.int64, copy=False)
ch_1 = batch_words[:, 2].astype(np.uint16, copy=False).view(np.int16)
ch_2 = batch_words[:, 3].astype(np.uint16, copy=False).view(np.int16)
del raw
del words
del w0
del w1
del self._buf[: take_count * 8]
ch_1_i64 = ch_1.astype(np.int64)
ch_2_i64 = ch_2.astype(np.int64)
ys = ((ch_1_i64 * ch_1_i64) + (ch_2_i64 * ch_2_i64)).astype(np.float32)
self._mode = "bin"
self._seen_points = True
self._last_step = int(xs[-1])
self._current_signal_kind = "bin_iq"
self._reset_tagged_steps()
events.append(
BatchPointEvent(
ch=0,
xs=xs,
ys=ys,
aux=(ch_1.astype(np.float32), ch_2.astype(np.float32)),
signal_kind="bin_iq",
if np.any(primary_mask_take):
p_words = batch_words[primary_mask_take]
p_xs = p_words[:, 1].astype(np.int64, copy=False)
p_ch1 = p_words[:, 2].astype(np.uint16, copy=False).view(np.int16)
p_ch2 = p_words[:, 3].astype(np.uint16, copy=False).view(np.int16)
p_ch1_i64 = p_ch1.astype(np.int64)
p_ch2_i64 = p_ch2.astype(np.int64)
p_ys = ((p_ch1_i64 * p_ch1_i64) + (p_ch2_i64 * p_ch2_i64)).astype(np.float32)
self._mode = "bin"
self._seen_points = True
self._last_step = int(p_xs[-1])
self._current_signal_kind = "bin_iq"
self._reset_tagged_steps()
events.append(
BatchPointEvent(
ch=0,
xs=p_xs,
ys=p_ys,
aux=(p_ch1.astype(np.float32), p_ch2.astype(np.float32)),
signal_kind="bin_iq",
)
)
)
if np.any(secondary_mask_take):
s_words = batch_words[secondary_mask_take]
s_xs = s_words[:, 1].astype(np.int64, copy=False)
s_ch1 = s_words[:, 2].astype(np.uint16, copy=False).view(np.int16)
s_ch2 = s_words[:, 3].astype(np.uint16, copy=False).view(np.int16)
events.append(
BatchPointEvent(
ch=0,
xs=s_xs,
ys=np.zeros(s_xs.size, dtype=np.float32),
aux=(s_ch1.astype(np.float32), s_ch2.astype(np.float32)),
signal_kind="bin_iq",
is_secondary=True,
)
)
return True
def feed(self, data: bytes) -> List[ParserEvent]:
@ -387,6 +446,7 @@ class LegacyBinaryParser:
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)
is_secondary_point = (w0 == 0x00A8 and w1 != 0xFFFF)
if is_legacy_start:
self._emit_legacy_start(events, ch=int(self._buf[7]))
@ -404,7 +464,7 @@ class LegacyBinaryParser:
continue
if self._mode == "legacy":
if is_tty_point and (not is_legacy_point) and self._try_emit_tty_batch(events, require_not_legacy=True):
if (is_tty_point or is_secondary_point) and (not is_legacy_point) and self._try_emit_tty_batch(events, require_not_legacy=True):
continue
if is_legacy_point:
self._emit_legacy_point(
@ -440,11 +500,15 @@ class LegacyBinaryParser:
)
del self._buf[:8]
continue
if is_secondary_point and (not is_legacy_point):
self._emit_secondary_point(events, step=int(w1), ch_1_word=int(w2), ch_2_word=int(w3))
del self._buf[:8]
continue
del self._buf[:1]
continue
if self._mode == "bin":
if is_tty_point and self._try_emit_tty_batch(events, require_not_legacy=False):
if (is_tty_point or is_secondary_point) and self._try_emit_tty_batch(events, require_not_legacy=False):
continue
if is_tty_point:
self._emit_tty_point(events, step=int(w1), ch_1_word=int(w2), ch_2_word=int(w3))
@ -470,6 +534,10 @@ class LegacyBinaryParser:
)
del self._buf[:8]
continue
if is_secondary_point:
self._emit_secondary_point(events, step=int(w1), ch_1_word=int(w2), ch_2_word=int(w3))
del self._buf[:8]
continue
if is_legacy_point and (not is_tty_point):
self._emit_legacy_point(
events,
@ -485,9 +553,13 @@ class LegacyBinaryParser:
# Mode is still unknown. Accept only unambiguous point shapes to avoid
# jumping between tty and legacy interpretations on coincidental bytes.
if is_tty_point and (not is_legacy_point):
if (is_tty_point or is_secondary_point) and (not is_legacy_point):
if self._try_emit_tty_batch(events, require_not_legacy=True):
continue
if is_secondary_point:
self._emit_secondary_point(events, step=int(w1), ch_1_word=int(w2), ch_2_word=int(w3))
del self._buf[:8]
continue
self._emit_tty_point(events, step=int(w1), ch_1_word=int(w2), ch_2_word=int(w3))
del self._buf[:8]
continue
@ -514,6 +586,11 @@ class LegacyBinaryParser:
del self._buf[:8]
continue
if is_secondary_point and (not is_legacy_point):
self._emit_secondary_point(events, step=int(w1), ch_1_word=int(w2), ch_2_word=int(w3))
del self._buf[:8]
continue
if is_legacy_point and (not is_tty_point):
self._emit_legacy_point(
events,
@ -737,10 +814,18 @@ class SweepAssembler:
self._tagged_high_ys: list[float] = []
self._tagged_high_aux_1: list[float] = []
self._tagged_high_aux_2: list[float] = []
self._secondary_xs: list[int] = []
self._secondary_aux_1: list[float] = []
self._secondary_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_secondary_current(self) -> None:
self._secondary_xs.clear()
self._secondary_aux_1.clear()
self._secondary_aux_2.clear()
def _reset_tagged_current(self) -> None:
self._tagged_low_xs.clear()
self._tagged_low_ys.clear()
@ -757,6 +842,7 @@ class SweepAssembler:
self._aux_1.clear()
self._aux_2.clear()
self._reset_tagged_current()
self._reset_secondary_current()
self._cur_channel = None
self._cur_signal_kind = None
self._cur_channels.clear()
@ -870,6 +956,30 @@ class SweepAssembler:
self._aux_2.extend(aux_2_arr[:aux_width].tolist())
return packet
def _consume_secondary_point(self, event: PointEvent) -> None:
self._secondary_xs.append(int(event.x))
if event.aux is not None:
self._secondary_aux_1.append(float(event.aux[0]))
self._secondary_aux_2.append(float(event.aux[1]))
def _consume_secondary_batch(self, event: BatchPointEvent) -> None:
xs_arr = np.asarray(event.xs, dtype=np.int64).reshape(-1)
width = xs_arr.size
if width <= 0:
return
self._secondary_xs.extend(xs_arr.tolist())
if event.aux is not None:
try:
aux_1, aux_2 = event.aux
aux_1_arr = np.asarray(aux_1, dtype=np.float32).reshape(-1)
aux_2_arr = np.asarray(aux_2, dtype=np.float32).reshape(-1)
aux_width = min(width, aux_1_arr.size, aux_2_arr.size)
except Exception:
aux_width = 0
if aux_width > 0:
self._secondary_aux_1.extend(aux_1_arr[:aux_width].tolist())
self._secondary_aux_2.extend(aux_2_arr[:aux_width].tolist())
def consume(self, event: ParserEvent) -> Optional[SweepPacket]:
if isinstance(event, StartEvent):
packet = self.finalize_current()
@ -879,8 +989,15 @@ class SweepAssembler:
self._cur_signal_kind = event.signal_kind
return packet
if isinstance(event, BatchPointEvent):
if event.is_secondary:
self._consume_secondary_batch(event)
return None
return self._consume_batch(event)
if isinstance(event, PointEvent) and event.is_secondary:
self._consume_secondary_point(event)
return None
point_ch = int(event.ch)
point_signal_kind = event.signal_kind
packet: Optional[SweepPacket] = None
@ -1015,4 +1132,14 @@ class SweepAssembler:
}
if do1_tagged_payload is not None:
info["_do1_tagged_payload"] = do1_tagged_payload
if (
self._secondary_xs
and self._secondary_aux_1
and self._secondary_aux_2
and len(self._secondary_aux_1) == len(self._secondary_xs)
):
info["_secondary_payload"] = {
"ch1": self._scatter(self._secondary_xs, self._secondary_aux_1, target_width),
"ch2": self._scatter(self._secondary_xs, self._secondary_aux_2, target_width),
}
return (sweep, info, aux_curves)