try new synchro method
This commit is contained in:
@ -55,6 +55,11 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
default="pg",
|
||||
help="Совместимый флаг. Поддерживаются только auto и pg; mpl удален.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--opengl",
|
||||
action="store_true",
|
||||
help="Включить OpenGL-ускорение для PyQtGraph. По умолчанию используется CPU-отрисовка.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--norm-type",
|
||||
choices=["projector", "simple"],
|
||||
|
||||
@ -310,7 +310,7 @@ def run_pyqtgraph(args) -> None:
|
||||
)
|
||||
|
||||
pg.setConfigOptions(
|
||||
useOpenGL=not peak_calibrate_mode,
|
||||
useOpenGL=bool(getattr(args, "opengl", False)),
|
||||
antialias=False,
|
||||
imageAxisOrder="row-major",
|
||||
)
|
||||
|
||||
@ -17,7 +17,57 @@ from rfg_adc_plotter.io.sweep_parser_core import (
|
||||
ParserTestStreamParser,
|
||||
SweepAssembler,
|
||||
)
|
||||
from rfg_adc_plotter.types import SweepPacket
|
||||
from rfg_adc_plotter.types import ParserEvent, PointEvent, SweepPacket
|
||||
|
||||
_PARSER_16_BIT_X2_PROBE_BYTES = 64 * 1024
|
||||
_LEGACY_STREAM_MIN_RECORDS = 32
|
||||
_LEGACY_STREAM_MIN_MATCH_RATIO = 0.95
|
||||
|
||||
|
||||
def _u16le_at(data: bytes, offset: int) -> int:
|
||||
return int(data[offset]) | (int(data[offset + 1]) << 8)
|
||||
|
||||
|
||||
def _looks_like_legacy_8byte_stream(data: bytes) -> bool:
|
||||
"""Heuristically detect the legacy 8-byte stream on an arbitrary byte offset."""
|
||||
buf = bytes(data)
|
||||
for offset in range(8):
|
||||
blocks = (len(buf) - offset) // 8
|
||||
if blocks < _LEGACY_STREAM_MIN_RECORDS:
|
||||
continue
|
||||
min_matches = max(_LEGACY_STREAM_MIN_RECORDS, int(blocks * _LEGACY_STREAM_MIN_MATCH_RATIO))
|
||||
matched_steps: list[int] = []
|
||||
for block_idx in range(blocks):
|
||||
base = offset + (block_idx * 8)
|
||||
if (_u16le_at(buf, base + 6) & 0x00FF) != 0x000A:
|
||||
continue
|
||||
matched_steps.append(_u16le_at(buf, base))
|
||||
if len(matched_steps) < min_matches:
|
||||
continue
|
||||
monotonic_or_reset = 0
|
||||
for prev_step, next_step in zip(matched_steps, matched_steps[1:]):
|
||||
if next_step == (prev_step + 1) or next_step <= prev_step:
|
||||
monotonic_or_reset += 1
|
||||
if monotonic_or_reset >= max(4, len(matched_steps) - 4):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _is_valid_parser_16_bit_x2_probe(events: list[ParserEvent]) -> bool:
|
||||
"""Accept only plausible complex streams and ignore resync noise."""
|
||||
point_steps: list[int] = []
|
||||
for event in events:
|
||||
if isinstance(event, PointEvent):
|
||||
point_steps.append(int(event.x))
|
||||
|
||||
if len(point_steps) < 3:
|
||||
return False
|
||||
|
||||
monotonic_or_small_reset = 0
|
||||
for prev_step, next_step in zip(point_steps, point_steps[1:]):
|
||||
if next_step == (prev_step + 1) or next_step <= 2:
|
||||
monotonic_or_small_reset += 1
|
||||
return monotonic_or_small_reset >= max(2, len(point_steps) - 3)
|
||||
|
||||
|
||||
class SweepReader(threading.Thread):
|
||||
@ -40,7 +90,7 @@ class SweepReader(threading.Thread):
|
||||
self._port_path = port_path
|
||||
self._baud = int(baud)
|
||||
self._queue = out_queue
|
||||
self._stop = stop_event
|
||||
self._stop_event = stop_event
|
||||
self._fancy = bool(fancy)
|
||||
self._bin_mode = bool(bin_mode)
|
||||
self._logscale = bool(logscale)
|
||||
@ -62,6 +112,42 @@ class SweepReader(threading.Thread):
|
||||
return LegacyBinaryParser(), SweepAssembler(fancy=self._fancy, apply_inversion=True)
|
||||
return AsciiSweepParser(), SweepAssembler(fancy=self._fancy, apply_inversion=True)
|
||||
|
||||
@staticmethod
|
||||
def _consume_events(assembler: SweepAssembler, events) -> list[SweepPacket]:
|
||||
packets: list[SweepPacket] = []
|
||||
for event in events:
|
||||
packet = assembler.consume(event)
|
||||
if packet is not None:
|
||||
packets.append(packet)
|
||||
return packets
|
||||
|
||||
def _probe_parser_16_bit_x2(self, chunk_reader: SerialChunkReader):
|
||||
parser = LogScale16BitX2BinaryParser()
|
||||
probe_buf = bytearray()
|
||||
probe_events: list[ParserEvent] = []
|
||||
|
||||
while not self._stop_event.is_set() and len(probe_buf) < _PARSER_16_BIT_X2_PROBE_BYTES:
|
||||
data = chunk_reader.read_available()
|
||||
if not data:
|
||||
time.sleep(0.0005)
|
||||
continue
|
||||
probe_buf += data
|
||||
probe_events.extend(parser.feed(data))
|
||||
if _is_valid_parser_16_bit_x2_probe(probe_events):
|
||||
assembler = SweepAssembler(fancy=self._fancy, apply_inversion=False)
|
||||
probe_packets = self._consume_events(assembler, probe_events)
|
||||
return parser, assembler, probe_packets
|
||||
|
||||
if probe_buf and _looks_like_legacy_8byte_stream(bytes(probe_buf)):
|
||||
sys.stderr.write("[info] parser_16_bit_x2: fallback -> legacy\n")
|
||||
parser = LegacyBinaryParser()
|
||||
assembler = SweepAssembler(fancy=self._fancy, apply_inversion=True)
|
||||
probe_packets = self._consume_events(assembler, parser.feed(bytes(probe_buf)))
|
||||
return parser, assembler, probe_packets
|
||||
|
||||
assembler = SweepAssembler(fancy=self._fancy, apply_inversion=False)
|
||||
return parser, assembler, []
|
||||
|
||||
def _enqueue(self, packet: SweepPacket) -> None:
|
||||
try:
|
||||
self._queue.put_nowait(packet)
|
||||
@ -83,19 +169,24 @@ class SweepReader(threading.Thread):
|
||||
sys.stderr.write(f"[error] {exc}\n")
|
||||
return
|
||||
|
||||
parser, assembler = self._build_parser()
|
||||
|
||||
try:
|
||||
chunk_reader = SerialChunkReader(self._src)
|
||||
while not self._stop.is_set():
|
||||
if self._parser_16_bit_x2:
|
||||
parser, assembler, pending_packets = self._probe_parser_16_bit_x2(chunk_reader)
|
||||
else:
|
||||
parser, assembler = self._build_parser()
|
||||
pending_packets = []
|
||||
|
||||
for packet in pending_packets:
|
||||
self._enqueue(packet)
|
||||
|
||||
while not self._stop_event.is_set():
|
||||
data = chunk_reader.read_available()
|
||||
if not data:
|
||||
time.sleep(0.0005)
|
||||
continue
|
||||
for event in parser.feed(data):
|
||||
packet = assembler.consume(event)
|
||||
if packet is not None:
|
||||
self._enqueue(packet)
|
||||
for packet in self._consume_events(assembler, parser.feed(data)):
|
||||
self._enqueue(packet)
|
||||
packet = assembler.finalize_current()
|
||||
if packet is not None:
|
||||
self._enqueue(packet)
|
||||
|
||||
Reference in New Issue
Block a user