MultiDeviceLibreVnaService only exposes the 2x4 virtual combo matrix on its own USB transport. When a physical GPIO switch sits on the master stimulus and/or slave receiver path, the effective matrix is wider than that. SwitchedMatrixRadarService wraps the inner service and drives the extra switch(es) between acquire_collection calls, widening the combo matrix by the physical position counts (matrix_output_switch_positions / matrix_input_switch_positions in RunConfigModel). Combo-matrix construction was centralized into RunConfigModel.build_runtime_combos() so the GUI, workflows, and codec all derive the same widened matrix instead of each computing its own version of the virtual 2x4 layout.
88 lines
3.5 KiB
Python
88 lines
3.5 KiB
Python
"""Factory for matrix-mode (multi-port) radar acquisition services.
|
|
|
|
Matrix radars acquire the full virtual switch matrix per call via
|
|
``acquire_collection`` instead of one combo per ``acquire`` like single-radar
|
|
services. The supported models are LibreVNA in synchronized multi-device mode
|
|
and the PLANAR SN9000 multi-port analyzer.
|
|
|
|
Service implementations are imported lazily so a config that targets one
|
|
model does not pull in transport dependencies (libusb, pyvisa, etc.) needed
|
|
only by the other model.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Protocol
|
|
|
|
from python_app.models.dataset_model import SweepCollection
|
|
from python_app.models.run_config_model import RadarSweepModel, RunConfigModel
|
|
|
|
|
|
class MatrixRadarService(Protocol):
|
|
"""Common API for radars that emit a full combo matrix per acquisition."""
|
|
|
|
def open(self) -> None:
|
|
"""Open hardware connections."""
|
|
|
|
def close(self) -> None:
|
|
"""Close hardware connections."""
|
|
|
|
def configure(self, sweep: RadarSweepModel) -> None:
|
|
"""Apply sweep settings to the radar."""
|
|
|
|
def acquire_collection(self, collection_id: int = 1) -> SweepCollection:
|
|
"""Acquire one complete virtual switch matrix in canonical combo order."""
|
|
|
|
def recover(self) -> None:
|
|
"""Reconnect to the radar after a transient acquisition failure."""
|
|
|
|
|
|
def create_matrix_radar_service(config: RunConfigModel) -> MatrixRadarService:
|
|
"""Create the matrix radar service implementation for the active config."""
|
|
if not config.is_matrix_radar:
|
|
raise RuntimeError("matrix radar service factory requires a matrix-mode radar.model")
|
|
|
|
model = config.radar.model
|
|
if model == RunConfigModel.LIBREVNA_MULTI_MODEL:
|
|
from python_app.hardware_full.multi_device_service import MultiDeviceLibreVnaService
|
|
|
|
inner = MultiDeviceLibreVnaService(
|
|
master_serial=config.radar.serial,
|
|
slave_serials=list(config.radar.multi_device.slave_serials),
|
|
force_external_reference=config.radar.multi_device.force_external_reference,
|
|
recovery_attempts=config.radar.multi_device.recovery_attempts,
|
|
backend_mode=config.radar.driver_mode,
|
|
)
|
|
out_physical = config.matrix_output_switch_positions
|
|
in_physical = config.matrix_input_switch_positions
|
|
if out_physical <= 1 and in_physical <= 1:
|
|
return inner
|
|
|
|
from python_app.hardware_full.switched_matrix_radar_service import (
|
|
SwitchedMatrixRadarService,
|
|
build_physical_switch,
|
|
)
|
|
|
|
return SwitchedMatrixRadarService(
|
|
inner=inner,
|
|
output_switch=build_physical_switch(config.output_switch, out_physical, config.radar.driver_mode),
|
|
input_switch=build_physical_switch(config.input_switch, in_physical, config.radar.driver_mode),
|
|
inner_output_positions=RunConfigModel.MULTI_DEVICE_OUTPUT_POSITIONS,
|
|
inner_input_positions=RunConfigModel.MULTI_DEVICE_INPUT_POSITIONS,
|
|
settling_ms=config.runtime.settling_ms,
|
|
)
|
|
|
|
if model == RunConfigModel.SN9000_MODEL:
|
|
if config.radar.driver_mode != "native":
|
|
raise RuntimeError("SN9000 requires radar.driver_mode='native'")
|
|
from python_app.hardware_full.sn9000_service import Sn9000Service
|
|
|
|
visa_library = config.radar.visa_library or "@ivi"
|
|
return Sn9000Service(
|
|
host=config.radar.remote_host,
|
|
port=config.radar.remote_port,
|
|
visa_library=visa_library,
|
|
)
|
|
|
|
raise RuntimeError(f"Unsupported matrix radar model: {model}")
|