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,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)