init commit

This commit is contained in:
Ayzen
2026-03-05 14:42:33 +03:00
commit fd4618b20d
964 changed files with 325114 additions and 0 deletions
@@ -0,0 +1,9 @@
"""Public controller classes."""
from .config import ConfigController
from .vna import VNAController
__all__ = [
"ConfigController",
"VNAController",
]
@@ -0,0 +1,54 @@
"""Device configuration controller."""
from __future__ import annotations
import logging
from ..enums import PacketType
from ..exceptions import ParseError
from ..models import DeviceConfigVariant, Packet
from ..protocol import parse_device_config
from ..session import LibreVNASession
logger = logging.getLogger(__name__)
class ConfigController:
"""Device configuration read/write/reset operations."""
def __init__(self, session: LibreVNASession) -> None:
"""Bind controller to an active session."""
self._session = session
def get(self, *, timeout_s: float = 1.0) -> DeviceConfigVariant:
"""Read configuration block for active hardware family."""
packet = self._session.request(
PacketType.REQUEST_DEVICE_CONFIGURATION,
PacketType.DEVICE_CONFIGURATION,
timeout_s=timeout_s,
)
if not isinstance(packet.payload, (bytes, bytearray, memoryview)):
raise ParseError("DeviceConfiguration payload has unexpected type")
cfg = parse_device_config(bytes(packet.payload), self._session.hardware_family)
logger.info(
"Loaded device configuration for family=%s fields=%d",
cfg.family.name,
len(cfg.values),
)
return cfg
def set(self, cfg: DeviceConfigVariant, *, timeout_s: float = 1.0) -> None:
"""Write configuration block for active hardware family."""
if cfg.family != self._session.hardware_family:
raise ParseError("DeviceConfigVariant family does not match connected hardware family")
logger.info(
"Writing device configuration for family=%s fields=%d",
cfg.family.name,
len(cfg.values),
)
self._session.send(Packet(PacketType.DEVICE_CONFIGURATION, cfg), require_ack=True, timeout_s=timeout_s)
def reset(self, *, timeout_s: float = 1.0) -> None:
"""Reset device configuration to firmware defaults."""
logger.warning("Resetting device configuration to firmware defaults")
self._session.send(Packet(PacketType.RESET_DEVICE_CONFIGURATION), require_ack=True, timeout_s=timeout_s)
@@ -0,0 +1,99 @@
"""VNA high-level controller for direct protocol packets."""
from __future__ import annotations
from dataclasses import replace
import logging
from typing import Callable
from ..enums import PacketType
from ..exceptions import ParseError
from ..models import Packet, StreamHandle, SweepResult, VNADatapointPacket, VNARawPoint, VNASweepSettings
from ..session import LibreVNASession
from ..sweep.assembler import assemble_vna_sweep, datapoint_to_raw_point
logger = logging.getLogger(__name__)
class VNAController:
"""VNA operations built on direct packet protocol."""
def __init__(self, session: LibreVNASession) -> None:
"""Bind VNA controller to active session."""
self._session = session
self._settings: VNASweepSettings | None = None
def configure(self, settings: VNASweepSettings) -> None:
"""Send `SweepSettings` packet to configure VNA operation."""
effective = settings
if effective.sync_mode is None:
effective = replace(effective, sync_mode=self._session.default_sync_mode)
self._settings = effective
self._session.clear_queue(PacketType.VNA_DATAPOINT)
self._session.send(Packet(PacketType.SWEEP_SETTINGS, effective), require_ack=True)
logger.info(
"VNA configured: kind=%s start=%.3fHz stop=%.3fHz points=%d ifbw=%.3fHz ports=%s",
effective.kind.value,
effective.f_start_hz,
effective.f_stop_hz,
effective.points,
effective.if_bandwidth_hz,
effective.excited_ports,
)
def acquire(self, *, expected_points: int | None = None, timeout_s: float = 10.0) -> SweepResult:
"""Acquire one complete sweep and return assembled complex traces."""
if self._settings is None:
raise ParseError("VNA is not configured. Call vna.configure() before acquire().")
settings = self._settings
points_target = expected_points if expected_points is not None else settings.points
if points_target <= 0:
raise ValueError("expected_points must be > 0")
logger.info(
"VNA acquire start: expected_points=%d timeout=%.2fs standby=%s",
points_target,
timeout_s,
settings.standby,
)
self._session.clear_queue(PacketType.VNA_DATAPOINT)
if settings.standby:
self._session.send(Packet(PacketType.INITIATE_SWEEP), require_ack=True)
ordered = self._session.collect_indexed_payloads(
packet_type=PacketType.VNA_DATAPOINT,
expected_points=points_target,
timeout_s=timeout_s,
payload_type=VNADatapointPacket,
payload_error="Expected decoded VNADatapointPacket payload",
)
device_info = self._session.get_device_info()
result = assemble_vna_sweep(
ordered,
settings,
num_ports=device_info.num_ports,
expected_points=points_target,
)
logger.info("VNA acquire complete: received_points=%d", len(result.x))
return result
def stream(self, callback: Callable[[VNARawPoint], None]) -> StreamHandle:
"""Subscribe callback for every incoming VNA datapoint packet."""
if self._settings is None:
raise ParseError("VNA is not configured. Call vna.configure() before stream().")
settings = self._settings
num_ports = self._session.get_device_info().num_ports
def _on_packet(packet: Packet) -> None:
"""Decode datapoint packet and forward mapped raw point to callback."""
payload = packet.payload
if not isinstance(payload, VNADatapointPacket):
raise ParseError("Expected decoded VNADatapointPacket payload")
callback(datapoint_to_raw_point(payload, settings, num_ports=num_ports))
logger.info("VNA stream subscription started")
return self._session.subscribe(PacketType.VNA_DATAPOINT, _on_packet)