ch1 ch2 new
This commit is contained in:
@ -74,6 +74,7 @@ TTY_RANGE_MIN_V = 1e-6
|
|||||||
TTY_RANGE_MAX_V = 1_000_000.0
|
TTY_RANGE_MAX_V = 1_000_000.0
|
||||||
LOGDET_EXP_INPUT_LIMIT = 80.0
|
LOGDET_EXP_INPUT_LIMIT = 80.0
|
||||||
DO1_TAGGED_INFO_KEY = "_do1_tagged_payload"
|
DO1_TAGGED_INFO_KEY = "_do1_tagged_payload"
|
||||||
|
SECONDARY_INFO_KEY = "_secondary_payload"
|
||||||
DISPLAY_DISTANCE_ZERO_M = 9.0
|
DISPLAY_DISTANCE_ZERO_M = 9.0
|
||||||
|
|
||||||
|
|
||||||
@ -944,6 +945,8 @@ def run_pyqtgraph(args) -> None:
|
|||||||
curve = p_line.plot(pen=pg.mkPen((80, 120, 255), width=1))
|
curve = p_line.plot(pen=pg.mkPen((80, 120, 255), width=1))
|
||||||
curve_raw_low = p_line.plot(pen=pg.mkPen((255, 90, 90), width=1))
|
curve_raw_low = p_line.plot(pen=pg.mkPen((255, 90, 90), width=1))
|
||||||
curve_raw_high = p_line.plot(pen=pg.mkPen((90, 220, 255), width=1))
|
curve_raw_high = p_line.plot(pen=pg.mkPen((90, 220, 255), width=1))
|
||||||
|
curve_secondary_ch1 = p_line.plot(pen=pg.mkPen((0, 200, 100), width=1))
|
||||||
|
curve_secondary_ch2 = p_line.plot(pen=pg.mkPen((200, 100, 200), width=1))
|
||||||
p_line_aux_vb = None
|
p_line_aux_vb = None
|
||||||
if bin_iq_power_mode:
|
if bin_iq_power_mode:
|
||||||
p_line_aux_vb = pg.ViewBox()
|
p_line_aux_vb = pg.ViewBox()
|
||||||
@ -1889,6 +1892,8 @@ def run_pyqtgraph(args) -> None:
|
|||||||
runtime.current_do1_tagged_raw_high = None
|
runtime.current_do1_tagged_raw_high = None
|
||||||
runtime.current_do1_tagged_aux_low = None
|
runtime.current_do1_tagged_aux_low = None
|
||||||
runtime.current_do1_tagged_aux_high = None
|
runtime.current_do1_tagged_aux_high = None
|
||||||
|
runtime.current_secondary_ch1 = None
|
||||||
|
runtime.current_secondary_ch2 = None
|
||||||
runtime.current_sweep_norm = None
|
runtime.current_sweep_norm = None
|
||||||
runtime.current_fft_mag = None
|
runtime.current_fft_mag = None
|
||||||
runtime.current_fft_db = None
|
runtime.current_fft_db = None
|
||||||
@ -1952,6 +1957,27 @@ def run_pyqtgraph(args) -> None:
|
|||||||
runtime.current_do1_tagged_aux_low = None
|
runtime.current_do1_tagged_aux_low = None
|
||||||
runtime.current_do1_tagged_aux_high = None
|
runtime.current_do1_tagged_aux_high = None
|
||||||
|
|
||||||
|
if runtime.full_secondary_ch1 is not None:
|
||||||
|
runtime.current_secondary_ch1 = apply_working_range_to_signal(
|
||||||
|
runtime.full_current_freqs,
|
||||||
|
runtime.full_current_sweep_raw,
|
||||||
|
runtime.full_secondary_ch1,
|
||||||
|
runtime.range_min_ghz,
|
||||||
|
runtime.range_max_ghz,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
runtime.current_secondary_ch1 = None
|
||||||
|
if runtime.full_secondary_ch2 is not None:
|
||||||
|
runtime.current_secondary_ch2 = apply_working_range_to_signal(
|
||||||
|
runtime.full_current_freqs,
|
||||||
|
runtime.full_current_sweep_raw,
|
||||||
|
runtime.full_secondary_ch2,
|
||||||
|
runtime.range_min_ghz,
|
||||||
|
runtime.range_max_ghz,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
runtime.current_secondary_ch2 = None
|
||||||
|
|
||||||
if runtime.current_sweep_raw.size == 0:
|
if runtime.current_sweep_raw.size == 0:
|
||||||
if push_to_ring:
|
if push_to_ring:
|
||||||
reset_ring_buffers()
|
reset_ring_buffers()
|
||||||
@ -1965,6 +1991,8 @@ def run_pyqtgraph(args) -> None:
|
|||||||
runtime.current_do1_tagged_raw_high = None
|
runtime.current_do1_tagged_raw_high = None
|
||||||
runtime.current_do1_tagged_aux_low = None
|
runtime.current_do1_tagged_aux_low = None
|
||||||
runtime.current_do1_tagged_aux_high = None
|
runtime.current_do1_tagged_aux_high = None
|
||||||
|
runtime.current_secondary_ch1 = None
|
||||||
|
runtime.current_secondary_ch2 = None
|
||||||
runtime.current_sweep_norm = None
|
runtime.current_sweep_norm = None
|
||||||
runtime.current_fft_mag = None
|
runtime.current_fft_mag = None
|
||||||
runtime.current_fft_db = None
|
runtime.current_fft_db = None
|
||||||
@ -2722,6 +2750,8 @@ def run_pyqtgraph(args) -> None:
|
|||||||
runtime.full_do1_tagged_aux_high = None
|
runtime.full_do1_tagged_aux_high = None
|
||||||
runtime.full_do1_tagged_aux_low_codes = None
|
runtime.full_do1_tagged_aux_low_codes = None
|
||||||
runtime.full_do1_tagged_aux_high_codes = None
|
runtime.full_do1_tagged_aux_high_codes = None
|
||||||
|
runtime.full_secondary_ch1 = None
|
||||||
|
runtime.full_secondary_ch2 = None
|
||||||
signal_kind = get_signal_kind(info)
|
signal_kind = get_signal_kind(info)
|
||||||
if signal_kind == "bin_iq_do1_tagged":
|
if signal_kind == "bin_iq_do1_tagged":
|
||||||
calibrated = calibrate_freqs(
|
calibrated = calibrate_freqs(
|
||||||
@ -2845,6 +2875,20 @@ def run_pyqtgraph(args) -> None:
|
|||||||
runtime.full_current_aux_curves = None
|
runtime.full_current_aux_curves = None
|
||||||
runtime.full_current_aux_curves_codes = None
|
runtime.full_current_aux_curves_codes = None
|
||||||
runtime.full_current_sweep_codes = None
|
runtime.full_current_sweep_codes = None
|
||||||
|
secondary_payload = info.get(SECONDARY_INFO_KEY) if isinstance(info, dict) else None
|
||||||
|
if isinstance(secondary_payload, dict):
|
||||||
|
sec_ch1 = secondary_payload.get("ch1")
|
||||||
|
sec_ch2 = secondary_payload.get("ch2")
|
||||||
|
if sec_ch1 is not None:
|
||||||
|
runtime.full_secondary_ch1 = np.asarray(
|
||||||
|
calibrate_freqs({"F": base_freqs, "I": sec_ch1})["I"],
|
||||||
|
dtype=np.float32,
|
||||||
|
)
|
||||||
|
if sec_ch2 is not None:
|
||||||
|
runtime.full_secondary_ch2 = np.asarray(
|
||||||
|
calibrate_freqs({"F": base_freqs, "I": sec_ch2})["I"],
|
||||||
|
dtype=np.float32,
|
||||||
|
)
|
||||||
refresh_current_window(push_to_ring=True)
|
refresh_current_window(push_to_ring=True)
|
||||||
processed_frames += 1
|
processed_frames += 1
|
||||||
last_packet_processed_at = time.time()
|
last_packet_processed_at = time.time()
|
||||||
@ -2999,6 +3043,21 @@ def run_pyqtgraph(args) -> None:
|
|||||||
clear_curve_if_needed("raw_low", curve_raw_low)
|
clear_curve_if_needed("raw_low", curve_raw_low)
|
||||||
clear_curve_if_needed("raw_high", curve_raw_high)
|
clear_curve_if_needed("raw_high", curve_raw_high)
|
||||||
|
|
||||||
|
if runtime.current_secondary_ch1 is not None:
|
||||||
|
sec_width = min(xs.size, runtime.current_secondary_ch1.size)
|
||||||
|
sec_x1, sec_y1 = decimate_curve_for_display(xs[:sec_width], runtime.current_secondary_ch1[:sec_width])
|
||||||
|
sec_x1, sec_y1 = sanitize_curve_data_for_display(sec_x1, sec_y1)
|
||||||
|
set_curve_data("secondary_ch1", curve_secondary_ch1, sec_x1, sec_y1, autoDownsample=False)
|
||||||
|
else:
|
||||||
|
clear_curve_if_needed("secondary_ch1", curve_secondary_ch1)
|
||||||
|
if runtime.current_secondary_ch2 is not None:
|
||||||
|
sec_width2 = min(xs.size, runtime.current_secondary_ch2.size)
|
||||||
|
sec_x2, sec_y2 = decimate_curve_for_display(xs[:sec_width2], runtime.current_secondary_ch2[:sec_width2])
|
||||||
|
sec_x2, sec_y2 = sanitize_curve_data_for_display(sec_x2, sec_y2)
|
||||||
|
set_curve_data("secondary_ch2", curve_secondary_ch2, sec_x2, sec_y2, autoDownsample=False)
|
||||||
|
else:
|
||||||
|
clear_curve_if_needed("secondary_ch2", curve_secondary_ch2)
|
||||||
|
|
||||||
if active_do1_tagged:
|
if active_do1_tagged:
|
||||||
if displayed_tagged_aux_low is not None:
|
if displayed_tagged_aux_low is not None:
|
||||||
aux_low_1, aux_low_2 = displayed_tagged_aux_low
|
aux_low_1, aux_low_2 = displayed_tagged_aux_low
|
||||||
|
|||||||
@ -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:
|
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")
|
self._prepare_bin_point(events, step=int(step), signal_kind="bin_logdet")
|
||||||
value = u16_to_i16(int(value_word))
|
value = u16_to_i16(int(value_word))
|
||||||
@ -310,8 +332,9 @@ class LegacyBinaryParser:
|
|||||||
w0 = words[:, 0]
|
w0 = words[:, 0]
|
||||||
w1 = words[:, 1]
|
w1 = words[:, 1]
|
||||||
is_tty_point = (w0 == 0x000A) & (w1 != 0xFFFF)
|
is_tty_point = (w0 == 0x000A) & (w1 != 0xFFFF)
|
||||||
|
is_sec_point = (w0 == 0x00A8) & (w1 != 0xFFFF)
|
||||||
is_legacy_point = (raw[:, 6] == 0x0A) & (w0 != 0xFFFF)
|
is_legacy_point = (raw[:, 6] == 0x0A) & (w0 != 0xFFFF)
|
||||||
valid = is_tty_point
|
valid = is_tty_point | is_sec_point
|
||||||
if require_not_legacy:
|
if require_not_legacy:
|
||||||
valid = valid & (~is_legacy_point)
|
valid = valid & (~is_legacy_point)
|
||||||
if valid.size <= 0 or not bool(valid[0]):
|
if valid.size <= 0 or not bool(valid[0]):
|
||||||
@ -321,7 +344,15 @@ class LegacyBinaryParser:
|
|||||||
if valid_count <= 0:
|
if valid_count <= 0:
|
||||||
return False
|
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._current_signal_kind != "bin_iq":
|
||||||
if self._seen_points:
|
if self._seen_points:
|
||||||
events.append(StartEvent(ch=0, signal_kind="bin_iq"))
|
events.append(StartEvent(ch=0, signal_kind="bin_iq"))
|
||||||
@ -330,44 +361,72 @@ class LegacyBinaryParser:
|
|||||||
self._current_signal_kind = "bin_iq"
|
self._current_signal_kind = "bin_iq"
|
||||||
self._reset_tagged_steps()
|
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"))
|
events.append(StartEvent(ch=0, signal_kind="bin_iq"))
|
||||||
self._last_step = None
|
self._last_step = None
|
||||||
self._seen_points = False
|
self._seen_points = False
|
||||||
self._reset_tagged_steps()
|
self._reset_tagged_steps()
|
||||||
|
|
||||||
reset_idx = np.nonzero(np.diff(steps) <= 0)[0]
|
reset_idx = np.nonzero(np.diff(primary_steps) <= 0)[0]
|
||||||
take_count = int(reset_idx[0] + 1) if reset_idx.size > 0 else int(steps.size)
|
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:
|
if take_count <= 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
primary_mask_take = is_tty_point[:take_count]
|
||||||
|
secondary_mask_take = is_sec_point[:take_count]
|
||||||
batch_words = words[:take_count].copy()
|
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 raw
|
||||||
del words
|
del words
|
||||||
del w0
|
del w0
|
||||||
del w1
|
del w1
|
||||||
del self._buf[: take_count * 8]
|
del self._buf[: take_count * 8]
|
||||||
|
|
||||||
ch_1_i64 = ch_1.astype(np.int64)
|
if np.any(primary_mask_take):
|
||||||
ch_2_i64 = ch_2.astype(np.int64)
|
p_words = batch_words[primary_mask_take]
|
||||||
ys = ((ch_1_i64 * ch_1_i64) + (ch_2_i64 * ch_2_i64)).astype(np.float32)
|
p_xs = p_words[:, 1].astype(np.int64, copy=False)
|
||||||
self._mode = "bin"
|
p_ch1 = p_words[:, 2].astype(np.uint16, copy=False).view(np.int16)
|
||||||
self._seen_points = True
|
p_ch2 = p_words[:, 3].astype(np.uint16, copy=False).view(np.int16)
|
||||||
self._last_step = int(xs[-1])
|
p_ch1_i64 = p_ch1.astype(np.int64)
|
||||||
self._current_signal_kind = "bin_iq"
|
p_ch2_i64 = p_ch2.astype(np.int64)
|
||||||
self._reset_tagged_steps()
|
p_ys = ((p_ch1_i64 * p_ch1_i64) + (p_ch2_i64 * p_ch2_i64)).astype(np.float32)
|
||||||
events.append(
|
self._mode = "bin"
|
||||||
BatchPointEvent(
|
self._seen_points = True
|
||||||
ch=0,
|
self._last_step = int(p_xs[-1])
|
||||||
xs=xs,
|
self._current_signal_kind = "bin_iq"
|
||||||
ys=ys,
|
self._reset_tagged_steps()
|
||||||
aux=(ch_1.astype(np.float32), ch_2.astype(np.float32)),
|
events.append(
|
||||||
signal_kind="bin_iq",
|
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
|
return True
|
||||||
|
|
||||||
def feed(self, data: bytes) -> List[ParserEvent]:
|
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_low_point = (w0 == 0x00A3 and w1 != 0xFFFF)
|
||||||
is_tty_tagged_high_point = (w0 == 0x00A4 and w1 != 0xFFFF)
|
is_tty_tagged_high_point = (w0 == 0x00A4 and w1 != 0xFFFF)
|
||||||
is_logdet_point = (w0 == 0x001A and w3 == 0x0000)
|
is_logdet_point = (w0 == 0x001A and w3 == 0x0000)
|
||||||
|
is_secondary_point = (w0 == 0x00A8 and w1 != 0xFFFF)
|
||||||
|
|
||||||
if is_legacy_start:
|
if is_legacy_start:
|
||||||
self._emit_legacy_start(events, ch=int(self._buf[7]))
|
self._emit_legacy_start(events, ch=int(self._buf[7]))
|
||||||
@ -404,7 +464,7 @@ class LegacyBinaryParser:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if self._mode == "legacy":
|
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
|
continue
|
||||||
if is_legacy_point:
|
if is_legacy_point:
|
||||||
self._emit_legacy_point(
|
self._emit_legacy_point(
|
||||||
@ -440,11 +500,15 @@ class LegacyBinaryParser:
|
|||||||
)
|
)
|
||||||
del self._buf[:8]
|
del self._buf[:8]
|
||||||
continue
|
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]
|
del self._buf[:1]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self._mode == "bin":
|
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
|
continue
|
||||||
if is_tty_point:
|
if is_tty_point:
|
||||||
self._emit_tty_point(events, step=int(w1), ch_1_word=int(w2), ch_2_word=int(w3))
|
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]
|
del self._buf[:8]
|
||||||
continue
|
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):
|
if is_legacy_point and (not is_tty_point):
|
||||||
self._emit_legacy_point(
|
self._emit_legacy_point(
|
||||||
events,
|
events,
|
||||||
@ -485,9 +553,13 @@ class LegacyBinaryParser:
|
|||||||
|
|
||||||
# Mode is still unknown. Accept only unambiguous point shapes to avoid
|
# Mode is still unknown. Accept only unambiguous point shapes to avoid
|
||||||
# jumping between tty and legacy interpretations on coincidental bytes.
|
# 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):
|
if self._try_emit_tty_batch(events, require_not_legacy=True):
|
||||||
continue
|
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))
|
self._emit_tty_point(events, step=int(w1), ch_1_word=int(w2), ch_2_word=int(w3))
|
||||||
del self._buf[:8]
|
del self._buf[:8]
|
||||||
continue
|
continue
|
||||||
@ -514,6 +586,11 @@ class LegacyBinaryParser:
|
|||||||
del self._buf[:8]
|
del self._buf[:8]
|
||||||
continue
|
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):
|
if is_legacy_point and (not is_tty_point):
|
||||||
self._emit_legacy_point(
|
self._emit_legacy_point(
|
||||||
events,
|
events,
|
||||||
@ -737,10 +814,18 @@ class SweepAssembler:
|
|||||||
self._tagged_high_ys: list[float] = []
|
self._tagged_high_ys: list[float] = []
|
||||||
self._tagged_high_aux_1: list[float] = []
|
self._tagged_high_aux_1: list[float] = []
|
||||||
self._tagged_high_aux_2: 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_channel: Optional[int] = None
|
||||||
self._cur_signal_kind: Optional[SignalKind] = None
|
self._cur_signal_kind: Optional[SignalKind] = None
|
||||||
self._cur_channels: set[int] = set()
|
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:
|
def _reset_tagged_current(self) -> None:
|
||||||
self._tagged_low_xs.clear()
|
self._tagged_low_xs.clear()
|
||||||
self._tagged_low_ys.clear()
|
self._tagged_low_ys.clear()
|
||||||
@ -757,6 +842,7 @@ class SweepAssembler:
|
|||||||
self._aux_1.clear()
|
self._aux_1.clear()
|
||||||
self._aux_2.clear()
|
self._aux_2.clear()
|
||||||
self._reset_tagged_current()
|
self._reset_tagged_current()
|
||||||
|
self._reset_secondary_current()
|
||||||
self._cur_channel = None
|
self._cur_channel = None
|
||||||
self._cur_signal_kind = None
|
self._cur_signal_kind = None
|
||||||
self._cur_channels.clear()
|
self._cur_channels.clear()
|
||||||
@ -870,6 +956,30 @@ class SweepAssembler:
|
|||||||
self._aux_2.extend(aux_2_arr[:aux_width].tolist())
|
self._aux_2.extend(aux_2_arr[:aux_width].tolist())
|
||||||
return packet
|
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]:
|
def consume(self, event: ParserEvent) -> Optional[SweepPacket]:
|
||||||
if isinstance(event, StartEvent):
|
if isinstance(event, StartEvent):
|
||||||
packet = self.finalize_current()
|
packet = self.finalize_current()
|
||||||
@ -879,8 +989,15 @@ class SweepAssembler:
|
|||||||
self._cur_signal_kind = event.signal_kind
|
self._cur_signal_kind = event.signal_kind
|
||||||
return packet
|
return packet
|
||||||
if isinstance(event, BatchPointEvent):
|
if isinstance(event, BatchPointEvent):
|
||||||
|
if event.is_secondary:
|
||||||
|
self._consume_secondary_batch(event)
|
||||||
|
return None
|
||||||
return self._consume_batch(event)
|
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_ch = int(event.ch)
|
||||||
point_signal_kind = event.signal_kind
|
point_signal_kind = event.signal_kind
|
||||||
packet: Optional[SweepPacket] = None
|
packet: Optional[SweepPacket] = None
|
||||||
@ -1015,4 +1132,14 @@ class SweepAssembler:
|
|||||||
}
|
}
|
||||||
if do1_tagged_payload is not None:
|
if do1_tagged_payload is not None:
|
||||||
info["_do1_tagged_payload"] = do1_tagged_payload
|
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)
|
return (sweep, info, aux_curves)
|
||||||
|
|||||||
@ -30,6 +30,8 @@ class RuntimeState:
|
|||||||
full_do1_tagged_aux_high: SweepAuxCurves = None
|
full_do1_tagged_aux_high: SweepAuxCurves = None
|
||||||
full_do1_tagged_aux_low_codes: SweepAuxCurves = None
|
full_do1_tagged_aux_low_codes: SweepAuxCurves = None
|
||||||
full_do1_tagged_aux_high_codes: SweepAuxCurves = None
|
full_do1_tagged_aux_high_codes: SweepAuxCurves = None
|
||||||
|
full_secondary_ch1: Optional[np.ndarray] = None
|
||||||
|
full_secondary_ch2: Optional[np.ndarray] = None
|
||||||
current_freqs: Optional[np.ndarray] = None
|
current_freqs: Optional[np.ndarray] = None
|
||||||
current_distances: Optional[np.ndarray] = None
|
current_distances: Optional[np.ndarray] = None
|
||||||
current_sweep_raw: Optional[np.ndarray] = None
|
current_sweep_raw: Optional[np.ndarray] = None
|
||||||
@ -41,6 +43,8 @@ class RuntimeState:
|
|||||||
current_do1_tagged_raw_high: Optional[np.ndarray] = None
|
current_do1_tagged_raw_high: Optional[np.ndarray] = None
|
||||||
current_do1_tagged_aux_low: SweepAuxCurves = None
|
current_do1_tagged_aux_low: SweepAuxCurves = None
|
||||||
current_do1_tagged_aux_high: SweepAuxCurves = None
|
current_do1_tagged_aux_high: SweepAuxCurves = None
|
||||||
|
current_secondary_ch1: Optional[np.ndarray] = None
|
||||||
|
current_secondary_ch2: Optional[np.ndarray] = None
|
||||||
current_sweep_norm: Optional[np.ndarray] = None
|
current_sweep_norm: Optional[np.ndarray] = None
|
||||||
current_fft_mag: Optional[np.ndarray] = None
|
current_fft_mag: Optional[np.ndarray] = None
|
||||||
current_fft_db: Optional[np.ndarray] = None
|
current_fft_db: Optional[np.ndarray] = None
|
||||||
|
|||||||
@ -31,6 +31,7 @@ class PointEvent:
|
|||||||
aux: Optional[Tuple[float, float]] = None
|
aux: Optional[Tuple[float, float]] = None
|
||||||
signal_kind: Optional[SignalKind] = None
|
signal_kind: Optional[SignalKind] = None
|
||||||
do1_level: Optional[Do1Level] = None
|
do1_level: Optional[Do1Level] = None
|
||||||
|
is_secondary: bool = False
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@ -41,6 +42,7 @@ class BatchPointEvent:
|
|||||||
aux: Optional[Tuple[np.ndarray, np.ndarray]] = None
|
aux: Optional[Tuple[np.ndarray, np.ndarray]] = None
|
||||||
signal_kind: Optional[SignalKind] = None
|
signal_kind: Optional[SignalKind] = None
|
||||||
do1_level: Optional[Do1Level] = None
|
do1_level: Optional[Do1Level] = None
|
||||||
|
is_secondary: bool = False
|
||||||
|
|
||||||
|
|
||||||
ParserEvent: TypeAlias = Union[StartEvent, PointEvent, BatchPointEvent]
|
ParserEvent: TypeAlias = Union[StartEvent, PointEvent, BatchPointEvent]
|
||||||
|
|||||||
@ -588,5 +588,114 @@ class SweepParserCoreTests(unittest.TestCase):
|
|||||||
self.assertAlmostEqual(float(sweep_2[1]), 20.0, places=6)
|
self.assertAlmostEqual(float(sweep_2[1]), 20.0, places=6)
|
||||||
|
|
||||||
|
|
||||||
|
def test_legacy_binary_parser_accepts_secondary_0xa8_stream(self):
|
||||||
|
parser = LegacyBinaryParser()
|
||||||
|
stream = b"".join(
|
||||||
|
[
|
||||||
|
_pack_tty_start(),
|
||||||
|
_pack_tty_point(1, 5, 0),
|
||||||
|
_pack_tty_tagged_point(0x00A8, 1, 0xFFCD, 0xFFDC),
|
||||||
|
_pack_tty_point(2, 0xFFF8, 1),
|
||||||
|
_pack_tty_tagged_point(0x00A8, 2, 0xFFCE, 0xFFE9),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
events = parser.feed(stream)
|
||||||
|
|
||||||
|
start_events = [e for e in events if isinstance(e, StartEvent)]
|
||||||
|
self.assertEqual(len(start_events), 1)
|
||||||
|
|
||||||
|
point_events = [e for e in events if isinstance(e, PointEvent)]
|
||||||
|
primary = [e for e in point_events if not e.is_secondary]
|
||||||
|
secondary = [e for e in point_events if e.is_secondary]
|
||||||
|
|
||||||
|
self.assertEqual(len(primary), 2)
|
||||||
|
self.assertEqual(len(secondary), 2)
|
||||||
|
self.assertEqual(secondary[0].x, 1)
|
||||||
|
self.assertEqual(secondary[0].aux, (-51.0, -36.0))
|
||||||
|
self.assertTrue(secondary[0].is_secondary)
|
||||||
|
self.assertEqual(secondary[1].x, 2)
|
||||||
|
self.assertEqual(secondary[1].aux, (-50.0, -23.0))
|
||||||
|
|
||||||
|
def test_secondary_0xa8_does_not_trigger_sweep_reset(self):
|
||||||
|
parser = LegacyBinaryParser()
|
||||||
|
stream = b"".join(
|
||||||
|
[
|
||||||
|
_pack_tty_start(),
|
||||||
|
_pack_tty_point(1, 5, 0),
|
||||||
|
_pack_tty_tagged_point(0x00A8, 1, 100, 200),
|
||||||
|
_pack_tty_point(2, 6, 0),
|
||||||
|
_pack_tty_tagged_point(0x00A8, 2, 110, 210),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
events = parser.feed(stream)
|
||||||
|
start_events = [e for e in events if isinstance(e, StartEvent)]
|
||||||
|
self.assertEqual(len(start_events), 1)
|
||||||
|
|
||||||
|
def test_legacy_binary_parser_batch_handles_interleaved_secondary(self):
|
||||||
|
parser = LegacyBinaryParser(batch_events=True)
|
||||||
|
stream = b"".join(
|
||||||
|
[
|
||||||
|
_pack_tty_start(),
|
||||||
|
_pack_tty_point(1, 5, 0),
|
||||||
|
_pack_tty_tagged_point(0x00A8, 1, 100, 200),
|
||||||
|
_pack_tty_point(2, 6, 0),
|
||||||
|
_pack_tty_tagged_point(0x00A8, 2, 110, 210),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
events = parser.feed(stream)
|
||||||
|
|
||||||
|
batch_events = [e for e in events if isinstance(e, BatchPointEvent)]
|
||||||
|
primary_batches = [e for e in batch_events if not e.is_secondary]
|
||||||
|
secondary_batches = [e for e in batch_events if e.is_secondary]
|
||||||
|
|
||||||
|
self.assertTrue(len(primary_batches) >= 1)
|
||||||
|
self.assertTrue(len(secondary_batches) >= 1)
|
||||||
|
|
||||||
|
pb = primary_batches[0]
|
||||||
|
self.assertTrue(np.array_equal(pb.xs, np.array([1, 2], dtype=np.int64)))
|
||||||
|
self.assertFalse(pb.is_secondary)
|
||||||
|
|
||||||
|
sb = secondary_batches[0]
|
||||||
|
self.assertTrue(np.array_equal(sb.xs, np.array([1, 2], dtype=np.int64)))
|
||||||
|
self.assertTrue(sb.is_secondary)
|
||||||
|
|
||||||
|
def test_sweep_assembler_packages_secondary_payload(self):
|
||||||
|
assembler = SweepAssembler(fancy=False, apply_inversion=False)
|
||||||
|
assembler.consume(StartEvent(ch=0, signal_kind="bin_iq"))
|
||||||
|
assembler.consume(PointEvent(ch=0, x=1, y=25.0, aux=(5.0, 0.0), signal_kind="bin_iq"))
|
||||||
|
assembler.consume(
|
||||||
|
PointEvent(ch=0, x=1, y=0.0, aux=(-51.0, -36.0), signal_kind="bin_iq", is_secondary=True)
|
||||||
|
)
|
||||||
|
assembler.consume(PointEvent(ch=0, x=2, y=65.0, aux=(-8.0, 1.0), signal_kind="bin_iq"))
|
||||||
|
assembler.consume(
|
||||||
|
PointEvent(ch=0, x=2, y=0.0, aux=(-50.0, -23.0), signal_kind="bin_iq", is_secondary=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
sweep, info, aux = assembler.finalize_current()
|
||||||
|
|
||||||
|
self.assertEqual(info["signal_kind"], "bin_iq")
|
||||||
|
self.assertAlmostEqual(float(sweep[1]), 25.0, places=6)
|
||||||
|
self.assertAlmostEqual(float(sweep[2]), 65.0, places=6)
|
||||||
|
|
||||||
|
payload = info.get("_secondary_payload")
|
||||||
|
self.assertIsNotNone(payload)
|
||||||
|
self.assertIn("ch1", payload)
|
||||||
|
self.assertIn("ch2", payload)
|
||||||
|
self.assertAlmostEqual(float(payload["ch1"][1]), -51.0, places=6)
|
||||||
|
self.assertAlmostEqual(float(payload["ch2"][1]), -36.0, places=6)
|
||||||
|
self.assertAlmostEqual(float(payload["ch1"][2]), -50.0, places=6)
|
||||||
|
self.assertAlmostEqual(float(payload["ch2"][2]), -23.0, places=6)
|
||||||
|
|
||||||
|
def test_sweep_assembler_secondary_absent_when_no_0xa8_data(self):
|
||||||
|
assembler = SweepAssembler(fancy=False, apply_inversion=False)
|
||||||
|
assembler.consume(StartEvent(ch=0, signal_kind="bin_iq"))
|
||||||
|
assembler.consume(PointEvent(ch=0, x=1, y=25.0, aux=(5.0, 0.0), signal_kind="bin_iq"))
|
||||||
|
assembler.consume(PointEvent(ch=0, x=2, y=65.0, aux=(-8.0, 1.0), signal_kind="bin_iq"))
|
||||||
|
|
||||||
|
sweep, info, aux = assembler.finalize_current()
|
||||||
|
|
||||||
|
self.assertNotIn("_secondary_payload", info)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user