"""Data models for sweep and processing payload collections.""" from __future__ import annotations from dataclasses import dataclass, field import numpy as np def _empty_f32_array() -> np.ndarray: """Return empty float32 array used by payload defaults.""" return np.array([], dtype=np.float32) def _empty_c64_array() -> np.ndarray: """Return empty complex64 array used by payload defaults.""" return np.array([], dtype=np.complex64) def _empty_f32_matrix() -> np.ndarray: """Return empty 2D float32 matrix used by payload defaults.""" return np.zeros((0, 0), dtype=np.float32) @dataclass(frozen=True, slots=True) class ComboKey: """Switch combination key: input position + output position.""" input: int output: int @dataclass(slots=True) class TraceData: """One frequency-domain trace set for a specific switch combination. ``capture_start_ns``/``capture_end_ns`` bound the monotonic window in which THIS trace's sweep was measured, excluding the switch drive and settling that preceded it. In switched modes a collection is assembled combo by combo over many milliseconds, so the collection-level window says nothing about when any individual combo was measured — these do. Zero on both means the producer did not report per-trace timing. """ combo: ComboKey frequency_hz: np.ndarray s11: np.ndarray s21: np.ndarray capture_start_ns: int = 0 capture_end_ns: int = 0 @dataclass(slots=True) class SweepCollection: """Raw or preprocessed collection containing multiple combo traces.""" collection_id: int monotonic_ns: int traces: list[TraceData] = field(default_factory=list) capture_start_ns: int = 0 capture_end_ns: int = 0 @dataclass(slots=True) class ResultPayload: """Processed payload item (trace-like or scalar) produced by processor stage.""" processing_name: str kind: int frequency_hz: np.ndarray = field(default_factory=_empty_f32_array) trace: np.ndarray = field(default_factory=_empty_c64_array) scalar_value: float = 0.0 image_x_axis: np.ndarray = field(default_factory=_empty_f32_array) image_y_axis: np.ndarray = field(default_factory=_empty_f32_array) image: np.ndarray = field(default_factory=_empty_f32_matrix) table: np.ndarray = field(default_factory=_empty_f32_matrix) @dataclass(slots=True) class ResultBlock: """Result payload block grouped by one switch combination.""" combo: ComboKey payloads: list[ResultPayload] = field(default_factory=list) @dataclass(slots=True) class ResultCollection: """Collection of processed payload blocks for one acquisition cycle.""" collection_id: int monotonic_ns: int # Wall-clock nanoseconds spent by the data_processor on `process_collection` # for this collection. Zero means the producer did not report a measurement. processing_duration_ns: int = 0 collection_payloads: list[ResultPayload] = field(default_factory=list) blocks: list[ResultBlock] = field(default_factory=list)