Files
scanengine-3/core/config.py
Thomas Ales ef8c0feb91 Expose burst acquisition in the app; refresh scan_format acquisition docs
Wires the burst_mode flag through QtScanController to ScanEngine and adds a
checkbox to the scan panel. The setting persists via ScanDefaults like the
other scan fields, defaulting to off — per-row acquisition stays the default
path until burst mode has run on the rig and the gate-off preflight has
settled which TRIGOUT value idles the pin low.

scan_format.md — the acquisition settings table had drifted from the code it
claimed to describe: it attributed the settings to sc3_aui_app.py (they moved
to core/scope_sras.py in the Phase 2 extraction), listed a 1.24 V trigger
level and 0 % offset where the code sets 0.500 V and HORizontal:POSition 30,
and did not mention the logic-AND scan trigger at all. Corrected, pointed at
the module that actually owns them, and noted that none of it affects byte
layout — only where the acoustic packet lands inside a frame.

Added an acquisition-paths section: the two paths write byte-identical files
and the choice is a runtime flag that is not recorded in the file, so a
reader never needs to care which produced it. Documents where row boundaries
come from in a burst and that either path squares rows up to n_frames.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 12:18:23 -05:00

49 lines
1.7 KiB
Python

"""Persisted user defaults (ports, scope IP, save directory).
One flat JSON file, one dataclass. ``save()`` always writes every field, so
a partial UI update can never silently drop another field's saved value
(which is exactly what the old dict-based writer did to helios_port).
"""
from __future__ import annotations
import json
import logging
from dataclasses import asdict, dataclass, fields
from pathlib import Path
logger = logging.getLogger(__name__)
DEFAULTS_PATH = Path(__file__).resolve().parent.parent / "aui_defaults.json"
@dataclass
class ScanDefaults:
t3r_port: str = "/dev/ttyUSB0"
bbd_port: str = "/dev/ttyUSB1"
oscope_ip: str = "192.168.0.1"
save_dir: str = str(DEFAULTS_PATH.parent / "scans")
helios_port: str = "/dev/ttyUSB2"
burst_mode: bool = False
@classmethod
def load(cls, path: Path = DEFAULTS_PATH) -> "ScanDefaults":
"""Load defaults, tolerating a missing/corrupt file and unknown keys."""
if path.exists():
try:
with open(path) as f:
data = json.load(f)
known = {f.name for f in fields(cls)}
return cls(**{k: v for k, v in data.items() if k in known})
except (OSError, ValueError, TypeError) as e:
logger.warning("Could not load %s (%s); using fallback defaults", path, e)
inst = cls()
inst.save(path)
return inst
def save(self, path: Path = DEFAULTS_PATH) -> None:
try:
with open(path, "w") as f:
json.dump(asdict(self), f, indent=2)
except OSError as e:
logger.warning("Could not save defaults to %s: %s", path, e)