Add RFG UDP source to rph_radar

This commit is contained in:
Ayzen
2026-10-06 19:36:00 +03:00
parent a185e61495
commit cfe81f782b
29 changed files with 817 additions and 279 deletions
+57 -5
View File
@@ -4,6 +4,7 @@ import json
import os
from pathlib import Path
import pty
import socket
import subprocess
import sys
import tempfile
@@ -16,14 +17,15 @@ from unittest import mock
import numpy as np
from python_app.hardware_full.rph_radar.service import RphRadarService
from python_app.hardware_full.rph_radar.tty_reader import RphRadarTtyReader
from python_app.hardware_full.rph_radar import stream_reader
from python_app.hardware_full.rph_radar.stream_reader import RphRadarStreamReader
from python_app.hardware_full.rph_radar.protocol import RphRadarStreamParser
from python_app.hardware_full.librevna_driver.models import SweepResult
from python_app.models.dataset_model import ComboKey
from python_app.models.run_config_schema import RunConfigModel
from python_app.orchestration.process_supervisor import ProcessSupervisor
from python_app.scripts import rph_radar_raw_producer as producer
from python_app.tests.test_rph_radar_protocol import packet
from python_app.tests.test_rph_radar_protocol import packet, rfg_datagram
def config():
@@ -35,11 +37,61 @@ def config():
})
class RphRadarTtyReaderTest(unittest.TestCase):
class RfgStreamServiceTest(unittest.TestCase):
def test_udp_stream_delivers_complete_sweeps_of_the_laser_task_length(self):
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as probe:
probe.bind(("127.0.0.1", 0))
port = probe.getsockname()[1]
rfg = RunConfigModel.from_dict({
"radar": {"model": "rph_radar", "driver_mode": "native",
"rph_radar": {"source": "rfg_udp", "udp_bind": f"127.0.0.1:{port}"},
"laser_control": {"mode": "variation", "variation": {
"min_value": 30.0, "max_value": 30.15, "step": 0.05, "time_step": 50}}},
"switches": {"port1": {"positions": 1}, "port2": {"positions": 1}},
"run": {"continuous": False, "combos": [{"input": 0, "output": 0}]},
})
service = RphRadarService(rfg)
service.open()
self.addCleanup(service.close)
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as board:
for datagram in (rfg_datagram(7, 2, 3), rfg_datagram(8, 0, 1, 2, 3), rfg_datagram(9, 0)):
board.sendto(datagram, ("127.0.0.1", port))
raw = service.read_raw_sweep()
self.assertEqual(raw.steps.tolist(), [0, 1, 2, 3]) # ceil((9879 - 9830) / 16) DAC codes
self.assertFalse(raw.dirty)
self.assertEqual(raw.capture_end_ns - raw.capture_start_ns, 4 * 50_000)
class RfgClockJumpTest(unittest.TestCase):
def test_a_realtime_step_drops_queued_datagrams_and_the_sweep_in_progress(self):
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as probe:
probe.bind(("127.0.0.1", 0))
port = probe.getsockname()[1]
assembler = mock.Mock(feed=mock.Mock(return_value=[]))
reader = RphRadarStreamReader(f"127.0.0.1:{port}", assembler)
real_offset, step = stream_reader._clock_offset_ns, [0]
with mock.patch.object(stream_reader, "_clock_offset_ns", lambda: real_offset() + step[0]):
reader.open()
self.addCleanup(reader.close)
step[0] = 2_000_000 # REALTIME stepped 2 ms after bind (stamps stay real, so forward)
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as board:
board.sendto(rfg_datagram(1, 0), ("127.0.0.1", port))
deadline = time.monotonic() + 1
while not assembler.reset.called and time.monotonic() < deadline:
time.sleep(.005)
assembler.feed.assert_not_called()
board.sendto(rfg_datagram(1, 1), ("127.0.0.1", port))
while not assembler.feed.called and time.monotonic() < deadline:
time.sleep(.005)
assembler.reset.assert_called_once()
assembler.feed.assert_called_once()
class RphRadarStreamReaderTest(unittest.TestCase):
def test_pty_delivers_latest_complete_clean_sweeps_by_pair(self):
master, slave = pty.openpty()
tty.setraw(slave)
reader = RphRadarTtyReader(os.ttyname(slave))
reader = RphRadarStreamReader(os.ttyname(slave))
reader.open()
try:
os.write(master, packet(source_id=1, combo=(0, 0))
@@ -216,7 +268,7 @@ class RphRadarConfigTest(unittest.TestCase):
with mock.patch("python_app.hardware_full.rph_radar.service._prepare_tty_path_for_collector") as unlink, \
mock.patch.object(RphRadarService, "_start_process") as start, \
mock.patch.object(RphRadarService, "_wait_for_tty"), \
mock.patch("python_app.hardware_full.rph_radar.service.RphRadarTtyReader"):
mock.patch("python_app.hardware_full.rph_radar.service.RphRadarStreamReader"):
with self.assertRaisesRegex(RuntimeError, "did not stop"):
service.open()
unlink.assert_not_called()