added a bscan extension
This commit is contained in:
@@ -52,6 +52,7 @@ def build_bscan_signature(
|
||||
floor_collection_id=floor_collection_id,
|
||||
)
|
||||
return (
|
||||
int(history_limit),
|
||||
str(live_config.bscan_axis),
|
||||
str(live_config.bscan_channel),
|
||||
float(live_config.bscan_cut_m),
|
||||
@@ -213,6 +214,7 @@ class AppWindowBscanPlotMixin:
|
||||
depth_max = float(np.max(depth_axis))
|
||||
depth_span = max(depth_max - depth_min, 1e-6)
|
||||
sweep_count = sweeps.shape[0]
|
||||
self._warn_if_bscan_exceeds_replay_window(sweep_count)
|
||||
sweep_width = float(max(sweep_count, 1))
|
||||
x_min = 0.5
|
||||
x_max = x_min + sweep_width
|
||||
@@ -236,9 +238,52 @@ class AppWindowBscanPlotMixin:
|
||||
)
|
||||
return True
|
||||
|
||||
def _warn_if_bscan_exceeds_replay_window(self, sweep_count: int) -> None:
|
||||
"""Warn once when the image reaches past what the C++ processor can replay.
|
||||
|
||||
Beyond that window a frame keeps the payload it was first computed with, so
|
||||
editing Gain / Cut / Max depth / Start-Stop MHz silently leaves the older
|
||||
columns on their previous settings — the image mixes two parameter sets.
|
||||
"""
|
||||
replay_window = int(self._bscan_cpp_replay_window)
|
||||
if sweep_count <= replay_window:
|
||||
return
|
||||
|
||||
stale_count = sweep_count - replay_window
|
||||
self._log_warning(
|
||||
f"B-scan shows {sweep_count} sweeps but the processor replays only the newest "
|
||||
f"{replay_window}; the older {stale_count} keep the settings they were captured with.",
|
||||
details=(
|
||||
"Changing Gain / Cut m / Max depth m / Start MHz / Stop MHz re-processes "
|
||||
f"only the newest {replay_window} sweeps.\n"
|
||||
f"Reduce 'Scans to show (stopped)' to {replay_window} for an image that is "
|
||||
"coherent across every column."
|
||||
),
|
||||
# Keyed on the operator-controlled window, not the live column count, so
|
||||
# that repeated "Remove Last" in stopped mode does not re-warn every click.
|
||||
once_key=(
|
||||
f"bscan_window_exceeds_replay_{replay_window}_"
|
||||
f"{self._bscan_display_window_scans()}"
|
||||
),
|
||||
)
|
||||
|
||||
def _bscan_display_window_scans(self) -> int:
|
||||
"""Return how many past sweeps the B-scan should render right now.
|
||||
|
||||
While acquisition runs the window stays at the C++ replay window: results
|
||||
arrive continuously, the whole history is rebuilt on every new one, and a
|
||||
1000-wide rebuild on the live path would cost ~20x per frame.
|
||||
|
||||
Once stopped, the operator reviews a frozen history, so the user-configured
|
||||
window applies and may reach back over the whole GUI result deque.
|
||||
"""
|
||||
if self._supervisor.is_running():
|
||||
return int(self._bscan_cpp_replay_window)
|
||||
return max(1, int(self._bscan_history_window.value()))
|
||||
|
||||
def _sync_bscan_history_from_results(self) -> None:
|
||||
"""Rebuild B-scan history cache when live params or inputs changed."""
|
||||
self._advance_bscan_floor_to_cpp_window()
|
||||
self._advance_bscan_floor_to_display_window()
|
||||
signature = self._bscan_signature()
|
||||
if signature == self._bscan_render_signature:
|
||||
return
|
||||
@@ -253,7 +298,7 @@ class AppWindowBscanPlotMixin:
|
||||
live_config=live_config,
|
||||
subtract_mean_ascan_enabled=bool(self._bscan_subtract_mean_ascan.isChecked()),
|
||||
result_history=result_history,
|
||||
history_limit=self._bscan_history_limit,
|
||||
history_limit=self._bscan_display_window_scans(),
|
||||
floor_collection_id=self._bscan_history_floor_collection_id,
|
||||
)
|
||||
|
||||
@@ -262,7 +307,7 @@ class AppWindowBscanPlotMixin:
|
||||
result_history = list(self._result_history)
|
||||
history_by_combo, depth_axis_by_combo = rebuild_bscan_history_from_results(
|
||||
result_history=result_history,
|
||||
history_limit=self._bscan_history_limit,
|
||||
history_limit=self._bscan_display_window_scans(),
|
||||
floor_collection_id=self._bscan_history_floor_collection_id,
|
||||
)
|
||||
self._bscan_history_by_combo = history_by_combo
|
||||
@@ -351,29 +396,35 @@ class AppWindowBscanPlotMixin:
|
||||
self._bscan_depth_axis_by_combo.clear()
|
||||
self._bscan_render_signature = None
|
||||
|
||||
def _advance_bscan_floor_to_cpp_window(self) -> None:
|
||||
"""Clamp B-scan source history to C++ available replay window."""
|
||||
if not self._result_history:
|
||||
def _advance_bscan_floor_to_display_window(self) -> None:
|
||||
"""Clamp B-scan source history to the active display window.
|
||||
|
||||
The window counts RETAINED ENTRIES, so the floor is read off the n-th
|
||||
newest entry rather than computed as `latest_id - window`. Collection ids
|
||||
are not dense: the results ring overwrites unread slots when the producer
|
||||
outruns the GUI poll loop, so the GUI keeps ids like 1..50, 81..130, ...
|
||||
Subtracting the window from the newest id would then span far fewer than
|
||||
`window` entries — asking for 150 sweeps yielded 87.
|
||||
|
||||
Recomputed unconditionally rather than ratcheted upwards: widening the
|
||||
window in stopped mode must be able to LOWER the floor and bring older
|
||||
frames back into view. A stale floor cannot survive this way either, so
|
||||
the previous special case for collection ids restarting on a new C++ run
|
||||
is no longer needed.
|
||||
"""
|
||||
history = self._result_history
|
||||
if not history:
|
||||
return
|
||||
|
||||
cpp_window_limit = min(
|
||||
int(self._defaults_config.rings.preprocessed.capacity),
|
||||
int(self._defaults_config.rings.results.capacity),
|
||||
)
|
||||
cpp_window_limit = max(1, cpp_window_limit)
|
||||
latest_collection_id = int(self._result_history[-1].collection_id)
|
||||
current_floor = int(self._bscan_history_floor_collection_id)
|
||||
|
||||
# Collection ids restart from 1 on new C++ run; release floor only while
|
||||
# acquisition is running, so manual "remove last" behavior in stopped mode
|
||||
# remains deterministic.
|
||||
if latest_collection_id < current_floor and self._supervisor.is_running():
|
||||
window = self._bscan_display_window_scans()
|
||||
if len(history) <= window:
|
||||
self._bscan_history_floor_collection_id = 0
|
||||
current_floor = 0
|
||||
return
|
||||
|
||||
floor_candidate = max(0, latest_collection_id - cpp_window_limit)
|
||||
if floor_candidate > current_floor:
|
||||
self._bscan_history_floor_collection_id = floor_candidate
|
||||
# `_result_tail` keeps entries with `collection_id > floor`, so sit the
|
||||
# floor one below the oldest entry that still fits in the window.
|
||||
oldest_visible = history[len(history) - window]
|
||||
self._bscan_history_floor_collection_id = max(0, int(oldest_visible.collection_id) - 1)
|
||||
|
||||
def _ensure_phase_view_box(self) -> pg.ViewBox:
|
||||
"""Create or return secondary right-axis ViewBox for phase curves."""
|
||||
|
||||
Reference in New Issue
Block a user