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_PLOT_MAX_POINTS = 4096
|
||||||
RAW_WATERFALL_MAX_POINTS = 2048
|
RAW_WATERFALL_MAX_POINTS = 2048
|
||||||
|
BSCAN_MAX_POINTS = 512
|
||||||
UI_MAX_PACKETS_PER_TICK = 8
|
UI_MAX_PACKETS_PER_TICK = 8
|
||||||
DEBUG_FRAME_LOG_EVERY = 10
|
DEBUG_FRAME_LOG_EVERY = 10
|
||||||
UI_BACKLOG_TAIL_THRESHOLD_MULTIPLIER = 1
|
UI_BACKLOG_TAIL_THRESHOLD_MULTIPLIER = 1
|
||||||
@ -197,6 +198,19 @@ def resolve_heavy_refresh_stride(
|
|||||||
return 1
|
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]:
|
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."""
|
"""Fit the initial window to the current screen without assuming desktop-sized geometry."""
|
||||||
width_in = int(max(0, available_width))
|
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]
|
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(
|
def coalesce_packets_for_ui(
|
||||||
packets: Sequence[SweepPacket],
|
packets: Sequence[SweepPacket],
|
||||||
*,
|
*,
|
||||||
@ -1103,6 +1149,7 @@ def run_pyqtgraph(args) -> None:
|
|||||||
last_queue_backlog = 0
|
last_queue_backlog = 0
|
||||||
last_backlog_skipped = 0
|
last_backlog_skipped = 0
|
||||||
last_heavy_refresh_stride = 1
|
last_heavy_refresh_stride = 1
|
||||||
|
last_bscan_refresh_stride = 1
|
||||||
expected_sweep_width = 0
|
expected_sweep_width = 0
|
||||||
base_freqs_cache: Dict[int, np.ndarray] = {}
|
base_freqs_cache: Dict[int, np.ndarray] = {}
|
||||||
last_packet_processed_at: Optional[float] = None
|
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)
|
runtime.current_fft_db = fft_mag_to_db(runtime.current_fft_mag)
|
||||||
|
|
||||||
def drain_queue() -> int:
|
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
|
nonlocal expected_sweep_width, base_freqs_cache, last_packet_processed_at
|
||||||
pending_packets: List[SweepPacket] = []
|
pending_packets: List[SweepPacket] = []
|
||||||
while True:
|
while True:
|
||||||
@ -2123,6 +2171,7 @@ def run_pyqtgraph(args) -> None:
|
|||||||
if drained <= 0:
|
if drained <= 0:
|
||||||
last_backlog_skipped = 0
|
last_backlog_skipped = 0
|
||||||
last_heavy_refresh_stride = 1
|
last_heavy_refresh_stride = 1
|
||||||
|
last_bscan_refresh_stride = 1
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
pending_packets, skipped_packets = coalesce_packets_for_ui(
|
pending_packets, skipped_packets = coalesce_packets_for_ui(
|
||||||
@ -2131,6 +2180,7 @@ def run_pyqtgraph(args) -> None:
|
|||||||
)
|
)
|
||||||
last_backlog_skipped = skipped_packets
|
last_backlog_skipped = skipped_packets
|
||||||
last_heavy_refresh_stride = resolve_heavy_refresh_stride(drained)
|
last_heavy_refresh_stride = resolve_heavy_refresh_stride(drained)
|
||||||
|
last_bscan_refresh_stride = resolve_bscan_refresh_stride(drained)
|
||||||
ui_frames_skipped += skipped_packets
|
ui_frames_skipped += skipped_packets
|
||||||
if skipped_packets > 0:
|
if skipped_packets > 0:
|
||||||
log_debug_event(
|
log_debug_event(
|
||||||
@ -2272,6 +2322,11 @@ def run_pyqtgraph(args) -> None:
|
|||||||
or last_heavy_refresh_stride <= 1
|
or last_heavy_refresh_stride <= 1
|
||||||
or (update_ticks % last_heavy_refresh_stride) == 0
|
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:
|
if redraw_needed:
|
||||||
refresh_signal_mode_labels()
|
refresh_signal_mode_labels()
|
||||||
@ -2717,10 +2772,10 @@ def run_pyqtgraph(args) -> None:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
if redraw_needed and runtime.ring.ring_fft is not None:
|
if redraw_needed and runtime.ring.ring_fft is not None:
|
||||||
if not refresh_heavy_views:
|
if not refresh_bscan_views:
|
||||||
log_debug_event(
|
log_debug_event(
|
||||||
"suppressed_fft_image_refresh",
|
"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:
|
else:
|
||||||
disp_fft_lin = runtime.ring.get_display_fft_linear()
|
disp_fft_lin = runtime.ring.get_display_fft_linear()
|
||||||
@ -2735,6 +2790,11 @@ def run_pyqtgraph(args) -> None:
|
|||||||
if keep_mask.size > 0:
|
if keep_mask.size > 0:
|
||||||
disp_fft_lin = disp_fft_lin[keep_mask, :]
|
disp_fft_lin = disp_fft_lin[keep_mask, :]
|
||||||
disp_fft_axis = axis_arr
|
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:
|
if spec_mean_sec > 0.0:
|
||||||
disp_times = runtime.ring.get_display_times()
|
disp_times = runtime.ring.get_display_times()
|
||||||
if disp_times is not None:
|
if disp_times is not None:
|
||||||
|
|||||||
@ -16,9 +16,11 @@ from rfg_adc_plotter.gui.pyqtgraph_backend import (
|
|||||||
compute_background_subtracted_bscan_levels,
|
compute_background_subtracted_bscan_levels,
|
||||||
compute_aux_phase_curve,
|
compute_aux_phase_curve,
|
||||||
convert_tty_i16_to_voltage,
|
convert_tty_i16_to_voltage,
|
||||||
|
decimate_bscan_rows_for_display,
|
||||||
decimate_curve_for_display,
|
decimate_curve_for_display,
|
||||||
is_short_sweep,
|
is_short_sweep,
|
||||||
resolve_axis_bounds,
|
resolve_axis_bounds,
|
||||||
|
resolve_bscan_refresh_stride,
|
||||||
resolve_heavy_refresh_stride,
|
resolve_heavy_refresh_stride,
|
||||||
resolve_initial_window_size,
|
resolve_initial_window_size,
|
||||||
resolve_distance_cut_start,
|
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(8, max_packets=8), 2)
|
||||||
self.assertEqual(resolve_heavy_refresh_stride(16, max_packets=8), 4)
|
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):
|
def test_update_expected_sweep_width_initializes_from_first_valid_sweep(self):
|
||||||
self.assertEqual(update_expected_sweep_width(0, 411), 411)
|
self.assertEqual(update_expected_sweep_width(0, 411), 411)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user