something working new format
This commit is contained in:
@ -13,11 +13,13 @@ from rfg_adc_plotter.processing.pipeline import SweepPreprocessor
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SAMPLE_BG = ROOT / "sample_data" / "empty"
|
||||
SAMPLE_CALIB = ROOT / "sample_data" / "no_antennas_35dB_attenuators"
|
||||
SAMPLE_NEW_FMT = ROOT / "sample_data" / "new_format" / "attenuators_50dB"
|
||||
|
||||
|
||||
def test_detect_reference_file_format_for_sample_capture():
|
||||
assert detect_reference_file_format(str(SAMPLE_BG)) == "bin_capture"
|
||||
assert detect_reference_file_format(str(SAMPLE_CALIB)) == "bin_capture"
|
||||
assert detect_reference_file_format(str(SAMPLE_NEW_FMT)) == "bin_capture"
|
||||
|
||||
|
||||
def test_load_capture_sweeps_parses_binary_capture():
|
||||
@ -33,6 +35,22 @@ def test_load_capture_sweeps_parses_binary_capture():
|
||||
assert channels == {0}
|
||||
|
||||
|
||||
def test_load_capture_sweeps_parses_new_format_logdetector_capture():
|
||||
sweeps = load_capture_sweeps(str(SAMPLE_NEW_FMT), fancy=False, logscale=False)
|
||||
assert len(sweeps) > 900
|
||||
|
||||
widths = [int(s.size) for s, _info in sweeps]
|
||||
dominant_width = max(set(widths), key=widths.count)
|
||||
# Должно совпадать с ожидаемой шириной свипа из штатных capture.
|
||||
assert dominant_width in (758, 759)
|
||||
|
||||
channels = set()
|
||||
for _s, info in sweeps:
|
||||
chs = info.get("chs", [info.get("ch", 0)])
|
||||
channels.update(int(v) for v in chs)
|
||||
assert channels == {0}
|
||||
|
||||
|
||||
def test_aggregate_capture_reference_filters_incomplete_sweeps():
|
||||
sweeps = load_capture_sweeps(str(SAMPLE_BG), fancy=False, logscale=False)
|
||||
vector, summary = aggregate_capture_reference(sweeps, channel=0, method="median", path=str(SAMPLE_BG))
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import numpy as np
|
||||
|
||||
from rfg_adc_plotter.processing.fourier import compute_ifft_profile_from_sweep
|
||||
from rfg_adc_plotter.state.ring_buffer import RingBuffer
|
||||
|
||||
|
||||
@ -8,6 +9,7 @@ def test_ring_buffer_allocates_fft_buffers_from_first_push():
|
||||
ring.ensure_init(64)
|
||||
|
||||
sweep = np.linspace(-1.0, 1.0, 64, dtype=np.float32)
|
||||
depth_expected, vals_expected = compute_ifft_profile_from_sweep(sweep, complex_mode="arccos")
|
||||
ring.push(sweep)
|
||||
|
||||
assert ring.ring_fft is not None
|
||||
@ -16,14 +18,7 @@ def test_ring_buffer_allocates_fft_buffers_from_first_push():
|
||||
assert ring.fft_bins == ring.ring_fft.shape[1]
|
||||
assert ring.fft_bins == ring.fft_depth_axis_m.size
|
||||
assert ring.fft_bins == ring.last_fft_vals.size
|
||||
assert ring.last_fft_third_axes_m != (None, None, None)
|
||||
assert ring.last_fft_third_vals != (None, None, None)
|
||||
for axis, vals in zip(ring.last_fft_third_axes_m, ring.last_fft_third_vals):
|
||||
assert axis is not None
|
||||
assert vals is not None
|
||||
assert axis.dtype == np.float32
|
||||
assert vals.dtype == np.float32
|
||||
assert axis.size == vals.size
|
||||
assert ring.fft_bins == min(depth_expected.size, vals_expected.size)
|
||||
# Legacy alias kept for compatibility with existing GUI code paths.
|
||||
assert ring.fft_time_axis is ring.fft_depth_axis_m
|
||||
|
||||
@ -56,8 +51,6 @@ def test_ring_buffer_mode_switch_resets_fft_buffers_only():
|
||||
assert ring.ring is not None
|
||||
assert ring.ring_fft is not None
|
||||
raw_before = ring.ring.copy()
|
||||
assert ring.last_fft_third_axes_m != (None, None, None)
|
||||
assert ring.last_fft_third_vals != (None, None, None)
|
||||
|
||||
changed = ring.set_fft_complex_mode("diff")
|
||||
assert changed is True
|
||||
@ -67,35 +60,22 @@ def test_ring_buffer_mode_switch_resets_fft_buffers_only():
|
||||
assert ring.ring_fft is None
|
||||
assert ring.fft_depth_axis_m is None
|
||||
assert ring.last_fft_vals is None
|
||||
assert ring.last_fft_third_axes_m == (None, None, None)
|
||||
assert ring.last_fft_third_vals == (None, None, None)
|
||||
assert ring.fft_bins == 0
|
||||
|
||||
ring.push(np.linspace(-1.0, 1.0, 128, dtype=np.float32))
|
||||
assert ring.ring_fft is not None
|
||||
assert ring.fft_depth_axis_m is not None
|
||||
assert ring.last_fft_vals is not None
|
||||
assert ring.last_fft_third_axes_m != (None, None, None)
|
||||
assert ring.last_fft_third_vals != (None, None, None)
|
||||
for axis, vals in zip(ring.last_fft_third_axes_m, ring.last_fft_third_vals):
|
||||
assert axis is not None
|
||||
assert vals is not None
|
||||
assert axis.dtype == np.float32
|
||||
assert vals.dtype == np.float32
|
||||
assert axis.size == vals.size
|
||||
|
||||
|
||||
def test_ring_buffer_short_sweeps_keep_third_profiles_well_formed():
|
||||
def test_ring_buffer_short_sweeps_keep_fft_profile_well_formed():
|
||||
for n in (1, 2, 3):
|
||||
ring = RingBuffer(max_sweeps=4)
|
||||
ring.ensure_init(n)
|
||||
ring.push(np.linspace(-1.0, 1.0, n, dtype=np.float32))
|
||||
|
||||
assert ring.last_fft_third_axes_m != (None, None, None)
|
||||
assert ring.last_fft_third_vals != (None, None, None)
|
||||
for axis, vals in zip(ring.last_fft_third_axes_m, ring.last_fft_third_vals):
|
||||
assert axis is not None
|
||||
assert vals is not None
|
||||
assert axis.dtype == np.float32
|
||||
assert vals.dtype == np.float32
|
||||
assert axis.size == vals.size
|
||||
assert ring.fft_depth_axis_m is not None
|
||||
assert ring.last_fft_vals is not None
|
||||
assert ring.fft_depth_axis_m.dtype == np.float32
|
||||
assert ring.last_fft_vals.dtype == np.float32
|
||||
assert ring.fft_depth_axis_m.size == ring.last_fft_vals.size
|
||||
|
||||
110
tests/test_sweep_parser_core_binary_protocols.py
Normal file
110
tests/test_sweep_parser_core_binary_protocols.py
Normal file
@ -0,0 +1,110 @@
|
||||
import math
|
||||
|
||||
from rfg_adc_plotter.io.sweep_parser_core import BinaryRecordStreamParser
|
||||
|
||||
|
||||
def _u16le(word: int) -> bytes:
|
||||
w = int(word) & 0xFFFF
|
||||
return bytes((w & 0xFF, (w >> 8) & 0xFF))
|
||||
|
||||
|
||||
def _pack_signed_words_be(value: int, words: int) -> list[int]:
|
||||
bits = 16 * int(words)
|
||||
v = int(value)
|
||||
if v < 0:
|
||||
v = (1 << bits) + v
|
||||
out: list[int] = []
|
||||
for i in range(words):
|
||||
shift = (words - 1 - i) * 16
|
||||
out.append((v >> shift) & 0xFFFF)
|
||||
return out
|
||||
|
||||
|
||||
def _pack_legacy_start(ch: int) -> bytes:
|
||||
return b"\xff\xff" * 3 + bytes((0x0A, int(ch) & 0xFF))
|
||||
|
||||
|
||||
def _pack_legacy_point(ch: int, step: int, value_i32: int) -> bytes:
|
||||
v = int(value_i32) & 0xFFFF_FFFF
|
||||
return b"".join(
|
||||
[
|
||||
_u16le(step),
|
||||
_u16le((v >> 16) & 0xFFFF),
|
||||
_u16le(v & 0xFFFF),
|
||||
bytes((0x0A, int(ch) & 0xFF)),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def _pack_log_start(ch: int) -> bytes:
|
||||
return b"\xff\xff" * 5 + bytes((0x0A, int(ch) & 0xFF))
|
||||
|
||||
|
||||
def _pack_log_point(step: int, avg1: int, avg2: int, pair_words: int, ch: int = 0) -> bytes:
|
||||
words = [int(step) & 0xFFFF]
|
||||
words.extend(_pack_signed_words_be(avg1, pair_words))
|
||||
words.extend(_pack_signed_words_be(avg2, pair_words))
|
||||
words.append(((int(ch) & 0xFF) << 8) | 0x000A)
|
||||
return b"".join(_u16le(w) for w in words)
|
||||
|
||||
|
||||
def _log_pair_to_linear(avg1: int, avg2: int) -> float:
|
||||
exp1 = max(-300.0, min(300.0, float(avg1) * 0.001))
|
||||
exp2 = max(-300.0, min(300.0, float(avg2) * 0.001))
|
||||
return (math.pow(10.0, exp1) - math.pow(10.0, exp2)) * 1000.0
|
||||
|
||||
|
||||
def test_binary_parser_parses_legacy_8_byte_records():
|
||||
parser = BinaryRecordStreamParser()
|
||||
stream = b"".join(
|
||||
[
|
||||
_pack_legacy_start(3),
|
||||
_pack_legacy_point(3, 1, -2),
|
||||
_pack_legacy_point(3, 2, 123456),
|
||||
]
|
||||
)
|
||||
|
||||
events = []
|
||||
events.extend(parser.feed(stream[:5]))
|
||||
events.extend(parser.feed(stream[5:17]))
|
||||
events.extend(parser.feed(stream[17:]))
|
||||
|
||||
assert events[0] == ("start", 3)
|
||||
assert events[1] == ("point", 3, 1, -2.0)
|
||||
assert events[2] == ("point", 3, 2, 123456.0)
|
||||
|
||||
|
||||
def test_binary_parser_parses_logdetector_32bit_pair_records():
|
||||
parser = BinaryRecordStreamParser()
|
||||
stream = b"".join(
|
||||
[
|
||||
_pack_log_start(0),
|
||||
_pack_log_point(1, 1500, 700, pair_words=2, ch=0),
|
||||
_pack_log_point(2, 1510, 710, pair_words=2, ch=0),
|
||||
]
|
||||
)
|
||||
|
||||
events = parser.feed(stream)
|
||||
assert events[0] == ("start", 0)
|
||||
assert events[1][0:3] == ("point", 0, 1)
|
||||
assert events[2][0:3] == ("point", 0, 2)
|
||||
assert abs(float(events[1][3]) - _log_pair_to_linear(1500, 700)) < 1e-6
|
||||
assert abs(float(events[2][3]) - _log_pair_to_linear(1510, 710)) < 1e-6
|
||||
|
||||
|
||||
def test_binary_parser_parses_logdetector_128bit_pair_records():
|
||||
parser = BinaryRecordStreamParser()
|
||||
stream = b"".join(
|
||||
[
|
||||
_pack_log_start(5),
|
||||
_pack_log_point(7, 1600, 800, pair_words=8, ch=5),
|
||||
_pack_log_point(8, 1610, 810, pair_words=8, ch=5),
|
||||
]
|
||||
)
|
||||
|
||||
events = parser.feed(stream)
|
||||
assert events[0] == ("start", 5)
|
||||
assert events[1][0:3] == ("point", 5, 7)
|
||||
assert events[2][0:3] == ("point", 5, 8)
|
||||
assert abs(float(events[1][3]) - _log_pair_to_linear(1600, 800)) < 1e-6
|
||||
assert abs(float(events[2][3]) - _log_pair_to_linear(1610, 810)) < 1e-6
|
||||
Reference in New Issue
Block a user