per-point stimulus switching: host side
Матрица 4x4 за одну развёртку: каждый порт мастера измеряется на каждой частоте дважды, с двумя положениями выходного ключа. Ключ дёргает контроллер в триггерной петле, поэтому расбери переключателями больше не управляет — SwitchedMatrixRadarService уходит из тракта, все 16 комбинаций снимаются в пределах одной точки вместо разброса между тремя развёртками. Выходы нумеруются позиция-мажорно, стадии порт-мажорно (старшие биты счётчика стадий выбирают порт, младший — положение ключа). Из этого следует, что положение ключа = младший бит номера стадии, а период 2 нацело делит 4 стадии — поэтому контроллеру достаточно инвертировать ногу на каждый цикл триггера, без абсолютного счётчика. Выходы 0 и 1 при этом сохраняют прежний смысл. Приёмники слейвов пронумерованы от числа выходов + 1, а не от фиксированного 3: иначе при 4 выходах прямой член «приёмник 3 <- выход 3» назывался бы так же, как отражение выхода 3. При двух выходах даёт прежние порты 3..6. Слейвам порт-стадии выставляются в 7: стоковый приём «одна за последней стадией» перестаёт работать, когда порт выбирается старшими битами. Всё за флагом multi_device.stimulus_switch_positions (1 = прежнее поведение, байт в байт). Спецификация правок FPGA/STM в device_firmware/per_point_switching.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ from unittest import mock
|
||||
|
||||
import numpy as np
|
||||
|
||||
from python_app.hardware_full.matrix_radar_service import create_matrix_radar_service
|
||||
from python_app.hardware_full.multi_device_service import MultiDeviceLibreVnaService
|
||||
from python_app.hardware_full.switched_matrix_radar_service import SwitchedMatrixRadarService
|
||||
from python_app.models.dataset_model import ComboKey, SweepCollection, TraceData
|
||||
@@ -213,5 +214,40 @@ class ManualComboCaptureUsesTargetedAcquisitionTest(unittest.TestCase):
|
||||
self.assertEqual(inner_acquire.call_count, 2)
|
||||
|
||||
|
||||
class PerPointStimulusSwitchingConfigTest(unittest.TestCase):
|
||||
"""Per-point stimulus switching widens the matrix without a host-driven switch."""
|
||||
|
||||
@staticmethod
|
||||
def _config(stimulus_switch_positions: int) -> RunConfigModel:
|
||||
config = RunConfigModel()
|
||||
config.radar.model = RunConfigModel.LIBREVNA_MULTI_MODEL
|
||||
config.radar.driver_mode = "mock"
|
||||
config.radar.multi_device.slave_serials = ["SLAVE_A", "SLAVE_B"]
|
||||
config.radar.multi_device.stimulus_switch_positions = stimulus_switch_positions
|
||||
config.apply_device_model_constraints()
|
||||
return config
|
||||
|
||||
def test_two_positions_double_the_output_axis(self) -> None:
|
||||
config = self._config(2)
|
||||
self.assertEqual(config.matrix_virtual_output_positions, 4)
|
||||
self.assertEqual(config.output_switch.positions, 4)
|
||||
self.assertEqual(config.input_switch.positions, 4)
|
||||
self.assertEqual(len(config.combos), 16)
|
||||
|
||||
def test_host_does_not_drive_any_switch(self) -> None:
|
||||
# The switch is stepped by the controller in the hardware trigger loop, so the
|
||||
# host must acquire the whole 4x4 matrix in ONE sweep and never wrap the
|
||||
# service in the sweep-per-step widener.
|
||||
service = create_matrix_radar_service(self._config(2))
|
||||
self.assertIsInstance(service, MultiDeviceLibreVnaService)
|
||||
self.assertNotIsInstance(service, SwitchedMatrixRadarService)
|
||||
self.assertEqual(service.output_position_count, 4)
|
||||
|
||||
def test_disabled_switching_keeps_the_two_output_matrix(self) -> None:
|
||||
config = self._config(1)
|
||||
self.assertEqual(config.matrix_virtual_output_positions, 2)
|
||||
self.assertEqual(len(config.combos), 8)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user