improved logging

This commit is contained in:
Ayzen
2026-06-06 00:52:52 +03:00
parent af6005d68f
commit aea49f6128
65 changed files with 1206 additions and 240 deletions
@@ -2,6 +2,8 @@
from __future__ import annotations
import logging
from python_app.models.dataset_model import SweepCollection
from python_app.models.run_config_model import RunConfigModel
from python_app.storage.npz_store import NpzStore
@@ -10,6 +12,8 @@ from python_app.workflows.sequential_capture_workflow import (
SequentialCaptureSession,
)
logger = logging.getLogger(__name__)
def capture_calibration_set(
config: RunConfigModel,
@@ -19,6 +23,7 @@ def capture_calibration_set(
median_sweep_count: int = DEFAULT_CALIBRATION_MEDIAN_SWEEP_COUNT,
) -> tuple[str, SweepCollection]:
"""Capture all switch combinations and persist them as calibration set."""
logger.info("Starting one-shot calibration capture: set=%s", set_name)
if config.is_matrix_radar:
raise RuntimeError(
"Matrix-radar S21 through calibration is not supported by this one-shot full-set helper. "
@@ -2,6 +2,7 @@
from __future__ import annotations
import logging
import time
import numpy as np
@@ -9,12 +10,19 @@ import numpy as np
from python_app.models.dataset_model import ComboKey, SweepCollection, TraceData
from python_app.models.run_config_model import ComboModel, RunConfigModel
logger = logging.getLogger(__name__)
def build_kamil_adc_neutral_s21_sets(
config: RunConfigModel,
point_count: int,
) -> tuple[SweepCollection, SweepCollection]:
"""Build S21 calibration/reference collections that leave input S21 unchanged."""
"""Build neutral S21 calibration/reference collections for the Kamil ADC radar.
The calibration uses unit S21 (1+0j) and the reference uses zero S21 across
every configured combo, so applying them in the preprocessing pipeline leaves
the input S21 unchanged. Returns the ``(calibration, reference)`` collections.
"""
if not config.is_kamil_adc:
raise ValueError("Neutral Kamil ADC sets require radar.model='kamil_adc'")
@@ -47,6 +55,7 @@ def build_kamil_adc_neutral_s21_sets(
s21_value=np.complex64(0.0 + 0.0j),
monotonic_ns=now_ns,
)
logger.info("Built neutral Kamil ADC S21 sets: combos=%d points=%d", len(combos), points)
return calibration, reference
@@ -4,6 +4,7 @@ from __future__ import annotations
from contextlib import suppress
from dataclasses import dataclass
import logging
import time
import numpy as np
@@ -23,6 +24,8 @@ from python_app.workflows.sequential_capture_workflow import (
select_trace_for_combo,
)
logger = logging.getLogger(__name__)
@dataclass(frozen=True, slots=True)
class MultiRadarCaptureBatch:
@@ -97,6 +100,15 @@ class MultiRadarSequentialCaptureSession:
self._next_index = 0
self._opened = False
logger.info(
"Multi-radar capture session created: kind=%s set=%s combos=%d variants=%d matrix_radar=%s",
self._kind,
self._set_name,
len(self._combos),
len(self._radar_variants),
self._is_matrix_radar,
)
if self._is_matrix_radar:
self._radar: MatrixRadarService = create_matrix_radar_service(base_config)
self._input_switch = None
@@ -148,12 +160,17 @@ class MultiRadarSequentialCaptureSession:
self._input_switch.open()
if self._output_switch is not None:
self._output_switch.open()
logger.info("Multi-radar capture session opened (kind=%s set=%s)", self._kind, self._set_name)
except Exception:
logger.exception(
"Failed to open multi-radar capture session (kind=%s set=%s)", self._kind, self._set_name
)
self.close()
raise
def close(self) -> None:
"""Close all opened hardware resources."""
was_open = self._opened
with suppress(Exception):
if self._output_switch is not None:
self._output_switch.close()
@@ -163,6 +180,8 @@ class MultiRadarSequentialCaptureSession:
with suppress(Exception):
self._radar.close()
self._opened = False
if was_open:
logger.info("Multi-radar capture session closed (kind=%s set=%s)", self._kind, self._set_name)
def state(self) -> SequentialCaptureState:
"""Return current progress snapshot."""
@@ -258,6 +277,14 @@ class MultiRadarSequentialCaptureSession:
self._next_index = len(self._combos)
else:
self._next_index += 1
logger.debug(
"Captured combo input=%d output=%d across %d variant(s) (%d/%d)",
combo.input,
combo.output,
len(variant_labels),
self._next_index,
len(self._combos),
)
return batch
def undo_last_capture(self) -> MultiRadarCaptureBatch:
@@ -336,6 +363,12 @@ class MultiRadarSequentialCaptureSession:
trace_count=len(traces),
)
)
logger.info(
"Finalized multi-radar capture kind=%s set=%s into %d variant set(s)",
self._kind,
self._set_name,
len(saved_sets),
)
return saved_sets
def _current_combo(self) -> ComboModel | None:
@@ -4,11 +4,14 @@ from __future__ import annotations
from dataclasses import dataclass
import json
import logging
from pathlib import Path
from python_app.models.run_config_model import RunConfigModel
from python_app.storage.npz_store import radar_key_from_config
logger = logging.getLogger(__name__)
_RADAR_SWEEP_KEYS = (
"start_hz",
"stop_hz",
@@ -59,6 +62,7 @@ def scan_radar_config_variants(
directory = Path(normalized_path).expanduser()
if not directory.exists():
logger.warning("Radar config variant directory does not exist: %s", directory)
return [], RadarConfigScanSummary(
directory_path=str(directory),
json_file_count=0,
@@ -68,6 +72,7 @@ def scan_radar_config_variants(
issues=(f"Directory does not exist: {directory}",),
)
if not directory.is_dir():
logger.warning("Radar config variant path is not a directory: %s", directory)
return [], RadarConfigScanSummary(
directory_path=str(directory),
json_file_count=0,
@@ -88,16 +93,30 @@ def scan_radar_config_variants(
variant = _load_radar_config_variant(path, base_config=base_config)
except Exception as exc: # noqa: BLE001
issues.append(f"{path.name}: {type(exc).__name__}: {exc}")
logger.warning("Skipping radar config variant %s: %s: %s", path.name, type(exc).__name__, exc)
continue
if variant.radar_key in seen_radar_keys:
duplicate_variant_count += 1
issues.append(
f"{path.name}: duplicate radar variant key {variant.radar_key}; keeping the first matching file only"
)
logger.warning(
"Skipping duplicate radar config variant %s (radar_key=%s already seen)",
path.name,
variant.radar_key,
)
continue
seen_radar_keys.add(variant.radar_key)
variants.append(variant)
logger.info(
"Scanned radar config variants in %s: json=%d valid=%d duplicates=%d",
directory,
len(json_paths),
len(variants),
duplicate_variant_count,
)
return variants, RadarConfigScanSummary(
directory_path=str(directory),
json_file_count=len(json_paths),
@@ -2,6 +2,8 @@
from __future__ import annotations
import logging
from python_app.models.dataset_model import SweepCollection
from python_app.models.run_config_model import RunConfigModel
from python_app.storage.npz_store import NpzStore
@@ -10,6 +12,8 @@ from python_app.workflows.sequential_capture_workflow import (
SequentialCaptureSession,
)
logger = logging.getLogger(__name__)
def capture_reference_set(
config: RunConfigModel,
@@ -19,6 +23,7 @@ def capture_reference_set(
median_sweep_count: int = DEFAULT_CALIBRATION_MEDIAN_SWEEP_COUNT,
) -> tuple[str, SweepCollection]:
"""Capture all switch combinations and persist them as reference set."""
logger.info("Starting one-shot reference capture: set=%s", set_name)
session = SequentialCaptureSession(
config=config,
kind="s21_reference",
@@ -4,6 +4,7 @@ from __future__ import annotations
from contextlib import suppress
from dataclasses import dataclass
import logging
import time
import numpy as np
@@ -15,13 +16,15 @@ from python_app.models.dataset_model import ComboKey, SweepCollection, TraceData
from python_app.models.run_config_model import ComboModel, RunConfigModel
from python_app.storage.npz_store import NpzStore, radar_key_from_config
logger = logging.getLogger(__name__)
MATRIX_RADAR_MANUAL_CAPTURE_KINDS = frozenset({"s21_calibration", "s11_open", "s11_short", "s11_load"})
DEFAULT_CALIBRATION_MEDIAN_SWEEP_COUNT = 5
@dataclass(slots=True)
class SequentialCaptureState:
"""Immutable view of sequential capture progress."""
"""Snapshot of sequential capture progress for the GUI/controller."""
kind: str
set_name: str
@@ -73,6 +76,15 @@ class SequentialCaptureSession:
self._next_index = 0
self._opened = False
logger.info(
"Sequential capture session created: kind=%s set=%s combos=%d matrix_radar=%s median_sweeps=%d",
self._kind,
self._set_name,
len(self._combos),
self._is_matrix_radar,
self._median_sweep_count,
)
if self._is_matrix_radar:
self._radar: MatrixRadarService = create_matrix_radar_service(config)
self._input_switch = None
@@ -124,12 +136,15 @@ class SequentialCaptureSession:
self._input_switch.open()
if self._output_switch is not None:
self._output_switch.open()
logger.info("Sequential capture session opened (kind=%s set=%s)", self._kind, self._set_name)
except Exception:
logger.exception("Failed to open sequential capture session (kind=%s set=%s)", self._kind, self._set_name)
self.close()
raise
def close(self) -> None:
"""Close all opened hardware resources."""
was_open = self._opened
with suppress(Exception):
if self._output_switch is not None:
self._output_switch.close()
@@ -139,6 +154,8 @@ class SequentialCaptureSession:
with suppress(Exception):
self._radar.close()
self._opened = False
if was_open:
logger.info("Sequential capture session closed (kind=%s set=%s)", self._kind, self._set_name)
def state(self) -> SequentialCaptureState:
"""Return current progress snapshot."""
@@ -174,11 +191,13 @@ class SequentialCaptureSession:
trace = combine_traces_via_median(per_sweep_traces)
self._traces.append(trace)
self._next_index += 1
logger.debug("Captured matrix combo input=%d output=%d", combo.input, combo.output)
return trace
combined_collection = combine_collections_via_median(collections)
self._traces.extend(combined_collection.traces)
self._next_index = len(self._combos)
logger.info("Captured full matrix combo set (%d traces)", len(combined_collection.traces))
return combined_collection.traces[-1]
if self._input_switch is None or self._output_switch is None:
@@ -202,6 +221,13 @@ class SequentialCaptureSession:
trace = combine_traces_via_median(sweep_traces)
self._traces.append(trace)
self._next_index += 1
logger.debug(
"Captured combo input=%d output=%d (%d/%d)",
combo.input,
combo.output,
self._next_index,
len(self._combos),
)
return trace
def undo_last_capture(self) -> TraceData:
@@ -217,6 +243,7 @@ class SequentialCaptureSession:
removed_trace = self._traces[-1]
self._traces.clear()
self._next_index = 0
logger.info("Undid matrix combo set capture (kind=%s set=%s)", self._kind, self._set_name)
return removed_trace
expected_combo = self._combos[self._next_index - 1]
@@ -228,6 +255,13 @@ class SequentialCaptureSession:
raise RuntimeError("Capture session state is inconsistent; last trace does not match rewind combo")
self._next_index -= 1
self._traces.pop()
logger.debug(
"Undid combo capture input=%d output=%d (%d/%d remaining)",
expected_combo.input,
expected_combo.output,
self._next_index,
len(self._combos),
)
return removed_trace
def last_captured_trace(self) -> TraceData | None:
@@ -265,6 +299,13 @@ class SequentialCaptureSession:
extra_serials=self._config.radar_key_extra_parts() or None,
)
store.save_set(self._kind, radar_key, self._set_name, collection)
logger.info(
"Finalized capture set kind=%s set=%s radar_key=%s traces=%d",
self._kind,
self._set_name,
radar_key,
len(collection.traces),
)
return radar_key, collection
def _current_combo(self) -> ComboModel | None: