fix upd speed waterflow
This commit is contained in:
@ -48,6 +48,7 @@ from rfg_adc_plotter.types import SweepAuxCurves, SweepInfo, SweepPacket
|
||||
|
||||
RAW_PLOT_MAX_POINTS = 4096
|
||||
RAW_WATERFALL_MAX_POINTS = 2048
|
||||
BSCAN_MAX_POINTS = 512
|
||||
UI_MAX_PACKETS_PER_TICK = 8
|
||||
DEBUG_FRAME_LOG_EVERY = 10
|
||||
UI_BACKLOG_TAIL_THRESHOLD_MULTIPLIER = 1
|
||||
@ -197,6 +198,19 @@ def resolve_heavy_refresh_stride(
|
||||
return 1
|
||||
|
||||
|
||||
def resolve_bscan_refresh_stride(
|
||||
backlog_packets: int,
|
||||
*,
|
||||
max_packets: int = UI_MAX_PACKETS_PER_TICK,
|
||||
) -> int:
|
||||
"""Keep B-scan responsive by limiting suppression to every other frame."""
|
||||
base = max(1, int(max_packets))
|
||||
backlog = max(0, int(backlog_packets))
|
||||
if backlog >= (base * UI_BACKLOG_LATEST_ONLY_THRESHOLD_MULTIPLIER):
|
||||
return 2
|
||||
return 1
|
||||
|
||||
|
||||
def resolve_initial_window_size(available_width: int, available_height: int) -> Tuple[int, int]:
|
||||
"""Fit the initial window to the current screen without assuming desktop-sized geometry."""
|
||||
width_in = int(max(0, available_width))
|
||||
@ -572,6 +586,38 @@ def decimate_curve_for_display(
|
||||
return x_arr[display_idx], y_arr[display_idx]
|
||||
|
||||
|
||||
def decimate_bscan_rows_for_display(
|
||||
axis: Optional[np.ndarray],
|
||||
data: np.ndarray,
|
||||
*,
|
||||
max_points: int = BSCAN_MAX_POINTS,
|
||||
) -> Tuple[Optional[np.ndarray], np.ndarray]:
|
||||
"""Reduce B-scan rows to keep waterfall rendering responsive."""
|
||||
data_arr = np.asarray(data, dtype=np.float32)
|
||||
if data_arr.ndim != 2:
|
||||
return axis, data_arr
|
||||
row_count = int(data_arr.shape[0])
|
||||
limit = max(1, int(max_points))
|
||||
if row_count <= limit:
|
||||
return axis, data_arr
|
||||
|
||||
row_idx = np.linspace(0, row_count - 1, limit, dtype=np.int64)
|
||||
row_idx = np.unique(row_idx)
|
||||
decimated = data_arr[row_idx, :]
|
||||
if axis is None:
|
||||
return None, decimated
|
||||
|
||||
axis_arr = np.asarray(axis, dtype=np.float64).reshape(-1)
|
||||
if axis_arr.size <= 0:
|
||||
return None, decimated
|
||||
take = min(axis_arr.size, row_count)
|
||||
axis_arr = axis_arr[:take]
|
||||
valid_idx = row_idx[row_idx < axis_arr.size]
|
||||
if valid_idx.size != row_idx.size:
|
||||
decimated = data_arr[valid_idx, :]
|
||||
return axis_arr[valid_idx], decimated
|
||||
|
||||
|
||||
def coalesce_packets_for_ui(
|
||||
packets: Sequence[SweepPacket],
|
||||
*,
|
||||
@ -1103,6 +1149,7 @@ def run_pyqtgraph(args) -> None:
|
||||
last_queue_backlog = 0
|
||||
last_backlog_skipped = 0
|
||||
last_heavy_refresh_stride = 1
|
||||
last_bscan_refresh_stride = 1
|
||||
expected_sweep_width = 0
|
||||
base_freqs_cache: Dict[int, np.ndarray] = {}
|
||||
last_packet_processed_at: Optional[float] = None
|
||||
@ -2110,7 +2157,8 @@ def run_pyqtgraph(args) -> None:
|
||||
runtime.current_fft_db = fft_mag_to_db(runtime.current_fft_mag)
|
||||
|
||||
def drain_queue() -> int:
|
||||
nonlocal processed_frames, ui_frames_skipped, last_queue_backlog, last_backlog_skipped, last_heavy_refresh_stride
|
||||
nonlocal processed_frames, ui_frames_skipped, last_queue_backlog, last_backlog_skipped
|
||||
nonlocal last_heavy_refresh_stride, last_bscan_refresh_stride
|
||||
nonlocal expected_sweep_width, base_freqs_cache, last_packet_processed_at
|
||||
pending_packets: List[SweepPacket] = []
|
||||
while True:
|
||||
@ -2123,6 +2171,7 @@ def run_pyqtgraph(args) -> None:
|
||||
if drained <= 0:
|
||||
last_backlog_skipped = 0
|
||||
last_heavy_refresh_stride = 1
|
||||
last_bscan_refresh_stride = 1
|
||||
return 0
|
||||
|
||||
pending_packets, skipped_packets = coalesce_packets_for_ui(
|
||||
@ -2131,6 +2180,7 @@ def run_pyqtgraph(args) -> None:
|
||||
)
|
||||
last_backlog_skipped = skipped_packets
|
||||
last_heavy_refresh_stride = resolve_heavy_refresh_stride(drained)
|
||||
last_bscan_refresh_stride = resolve_bscan_refresh_stride(drained)
|
||||
ui_frames_skipped += skipped_packets
|
||||
if skipped_packets > 0:
|
||||
log_debug_event(
|
||||
@ -2272,6 +2322,11 @@ def run_pyqtgraph(args) -> None:
|
||||
or last_heavy_refresh_stride <= 1
|
||||
or (update_ticks % last_heavy_refresh_stride) == 0
|
||||
)
|
||||
refresh_bscan_views = (
|
||||
runtime.plot_dirty
|
||||
or last_bscan_refresh_stride <= 1
|
||||
or (update_ticks % last_bscan_refresh_stride) == 0
|
||||
)
|
||||
|
||||
if redraw_needed:
|
||||
refresh_signal_mode_labels()
|
||||
@ -2717,10 +2772,10 @@ def run_pyqtgraph(args) -> None:
|
||||
pass
|
||||
|
||||
if redraw_needed and runtime.ring.ring_fft is not None:
|
||||
if not refresh_heavy_views:
|
||||
if not refresh_bscan_views:
|
||||
log_debug_event(
|
||||
"suppressed_fft_image_refresh",
|
||||
f"ui FFT waterfall refresh suppressed stride:{last_heavy_refresh_stride}",
|
||||
f"ui FFT waterfall refresh suppressed stride:{last_bscan_refresh_stride}",
|
||||
)
|
||||
else:
|
||||
disp_fft_lin = runtime.ring.get_display_fft_linear()
|
||||
@ -2735,6 +2790,11 @@ def run_pyqtgraph(args) -> None:
|
||||
if keep_mask.size > 0:
|
||||
disp_fft_lin = disp_fft_lin[keep_mask, :]
|
||||
disp_fft_axis = axis_arr
|
||||
disp_fft_axis, disp_fft_lin = decimate_bscan_rows_for_display(
|
||||
disp_fft_axis,
|
||||
disp_fft_lin,
|
||||
max_points=BSCAN_MAX_POINTS,
|
||||
)
|
||||
if spec_mean_sec > 0.0:
|
||||
disp_times = runtime.ring.get_display_times()
|
||||
if disp_times is not None:
|
||||
|
||||
@ -16,9 +16,11 @@ from rfg_adc_plotter.gui.pyqtgraph_backend import (
|
||||
compute_background_subtracted_bscan_levels,
|
||||
compute_aux_phase_curve,
|
||||
convert_tty_i16_to_voltage,
|
||||
decimate_bscan_rows_for_display,
|
||||
decimate_curve_for_display,
|
||||
is_short_sweep,
|
||||
resolve_axis_bounds,
|
||||
resolve_bscan_refresh_stride,
|
||||
resolve_heavy_refresh_stride,
|
||||
resolve_initial_window_size,
|
||||
resolve_distance_cut_start,
|
||||
@ -377,6 +379,31 @@ class ProcessingTests(unittest.TestCase):
|
||||
self.assertEqual(resolve_heavy_refresh_stride(8, max_packets=8), 2)
|
||||
self.assertEqual(resolve_heavy_refresh_stride(16, max_packets=8), 4)
|
||||
|
||||
def test_resolve_bscan_refresh_stride_limits_suppression(self):
|
||||
self.assertEqual(resolve_bscan_refresh_stride(0, max_packets=8), 1)
|
||||
self.assertEqual(resolve_bscan_refresh_stride(8, max_packets=8), 1)
|
||||
self.assertEqual(resolve_bscan_refresh_stride(16, max_packets=8), 2)
|
||||
|
||||
def test_decimate_bscan_rows_for_display_keeps_shape_consistent(self):
|
||||
axis = np.linspace(0.0, 1.0, 10, dtype=np.float64)
|
||||
data = np.arange(50, dtype=np.float32).reshape(10, 5)
|
||||
|
||||
dec_axis, dec_data = decimate_bscan_rows_for_display(axis, data, max_points=4)
|
||||
|
||||
self.assertEqual(dec_data.shape, (4, 5))
|
||||
self.assertIsNotNone(dec_axis)
|
||||
self.assertEqual(dec_axis.shape, (4,))
|
||||
self.assertAlmostEqual(float(dec_axis[0]), 0.0, places=12)
|
||||
self.assertAlmostEqual(float(dec_axis[-1]), 1.0, places=12)
|
||||
|
||||
def test_decimate_bscan_rows_for_display_handles_missing_axis(self):
|
||||
data = np.arange(32, dtype=np.float32).reshape(8, 4)
|
||||
|
||||
dec_axis, dec_data = decimate_bscan_rows_for_display(None, data, max_points=3)
|
||||
|
||||
self.assertIsNone(dec_axis)
|
||||
self.assertEqual(dec_data.shape, (3, 4))
|
||||
|
||||
def test_update_expected_sweep_width_initializes_from_first_valid_sweep(self):
|
||||
self.assertEqual(update_expected_sweep_width(0, 411), 411)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user