This commit is contained in:
awe
2026-05-04 23:55:25 +03:00
parent 16146eda1e
commit d437ab1642
2 changed files with 95 additions and 36 deletions

View File

@ -74,6 +74,32 @@ TTY_RANGE_MIN_V = 1e-6
TTY_RANGE_MAX_V = 1_000_000.0
LOGDET_EXP_INPUT_LIMIT = 80.0
DO1_TAGGED_INFO_KEY = "_do1_tagged_payload"
DISPLAY_DISTANCE_ZERO_M = 9.0
def display_distance_value(distance_m: float, *, zero_at_m: float = DISPLAY_DISTANCE_ZERO_M) -> float:
"""Map physical distance to the displayed reversed distance axis."""
try:
distance_val = float(distance_m)
except Exception:
return float("nan")
if not np.isfinite(distance_val):
return float("nan")
return float(float(zero_at_m) - distance_val)
def display_distance_axis(
distance_axis: Optional[np.ndarray],
*,
zero_at_m: float = DISPLAY_DISTANCE_ZERO_M,
) -> np.ndarray:
"""Return display coordinates where physical ``zero_at_m`` is shown as 0 m."""
if distance_axis is None:
return np.zeros((0,), dtype=np.float64)
axis_arr = np.asarray(distance_axis, dtype=np.float64).reshape(-1)
if axis_arr.size <= 0:
return np.zeros((0,), dtype=np.float64)
return (float(zero_at_m) - axis_arr).astype(np.float64, copy=False)
def sanitize_curve_data_for_display(
@ -996,7 +1022,7 @@ def run_pyqtgraph(args) -> None:
fft_bg_line.setVisible(False)
fft_left_line.setVisible(False)
fft_right_line.setVisible(False)
p_fft.setLabel("bottom", "Расстояние, м")
p_fft.setLabel("bottom", "Расстояние, м (0 = 9 м)")
p_fft.setLabel("left", "Амплитуда" if complex_sweep_mode else "дБ")
if complex_sweep_mode:
try:
@ -1017,7 +1043,7 @@ def run_pyqtgraph(args) -> None:
p_spec.getAxis("bottom").setStyle(showValues=False)
except Exception:
pass
p_spec.setLabel("left", "Расстояние, м")
p_spec.setLabel("left", "Расстояние, м (0 = 9 м)")
img_fft = pg.ImageItem()
p_spec.addItem(img_fft)
spec_left_line = pg.InfiniteLine(angle=0, movable=False, pen=peak_pen)
@ -1472,7 +1498,7 @@ def run_pyqtgraph(args) -> None:
text = f"{fft_low_cut_percent:.1f}%"
cut_start = _active_distance_cut_start()
if cut_start is not None and np.isfinite(cut_start):
text = f"{text} (~{cut_start:.4g} м)"
text = f"{text} (~{display_distance_value(cut_start):.4g} м)"
if text == last_fft_low_cut_label_text:
return
try:
@ -1501,22 +1527,31 @@ def run_pyqtgraph(args) -> None:
distance_bounds = resolve_axis_bounds(runtime.ring.distance_axis)
if distance_bounds is not None:
d_min_full, d_max = distance_bounds
span = max(1e-9, float(d_max - d_min_full))
set_image_rect_if_changed("fft_waterfall_rect", img_fft, 0.0, d_min_full, float(max_sweeps), span)
display_axis_full = display_distance_axis(runtime.ring.distance_axis)
display_bounds = resolve_axis_bounds(display_axis_full)
if display_bounds is not None:
d_min_display, d_max_display = display_bounds
set_image_rect_if_changed(
"fft_waterfall_rect",
img_fft,
0.0,
d_min_display,
float(max_sweeps),
max(1e-9, float(d_max_display - d_min_display)),
)
set_xy_range_if_changed(
"fft_waterfall_range",
p_spec,
x_bounds=(0, max_sweeps - 1),
y_bounds=(d_min_full, d_max),
y_bounds=(d_min_display, d_max_display),
padding=0,
)
d_min_fft = d_min_full
d_cut = _active_distance_cut_start()
if d_cut is not None and np.isfinite(d_cut):
d_min_fft = max(float(d_min_fft), float(d_cut))
set_x_range_if_changed("fft_x", p_fft, d_min_fft, d_max, padding=0)
fft_axis_physical, _fft_keep = apply_distance_cut_to_axis(runtime.ring.distance_axis, d_cut)
fft_display_bounds = resolve_axis_bounds(display_distance_axis(fft_axis_physical))
if fft_display_bounds is not None:
set_x_range_if_changed("fft_x", p_fft, fft_display_bounds[0], fft_display_bounds[1], padding=0)
refresh_fft_low_cut_label()
def resolve_curve_xs(size: int) -> np.ndarray:
@ -2422,7 +2457,7 @@ def run_pyqtgraph(args) -> None:
if idx < len(peaks):
peak = peaks[idx]
lines.append(f"peak {idx + 1}:")
lines.append(f" X: {peak['x']:.4g} m")
lines.append(f" X: {display_distance_value(peak['x']):.4g} m")
lines.append(f" H: {peak['height']:.4g} dB")
lines.append(f" W: {peak['width']:.4g} m")
else:
@ -3151,7 +3186,8 @@ def run_pyqtgraph(args) -> None:
fft_complex_plot = fft_complex_plot[fft_keep_mask]
elif fft_complex_plot is not None:
fft_complex_plot = None
fft_x_bounds = resolve_axis_bounds(xs_fft)
xs_fft_display = display_distance_axis(xs_fft)
fft_x_bounds = resolve_axis_bounds(xs_fft_display)
if fft_x_bounds is not None:
set_x_range_if_changed("fft_x", p_fft, fft_x_bounds[0], fft_x_bounds[1], padding=0)
@ -3168,17 +3204,17 @@ def run_pyqtgraph(args) -> None:
show_imag=fft_imag_enabled,
)
if visible_abs is not None:
abs_x, abs_y = sanitize_curve_data_for_display(xs_fft[: visible_abs.size], visible_abs)
abs_x, abs_y = sanitize_curve_data_for_display(xs_fft_display[: visible_abs.size], visible_abs)
set_curve_data("fft_abs", curve_fft, abs_x, abs_y)
else:
clear_curve_if_needed("fft_abs", curve_fft)
if visible_real is not None:
real_x, real_y = sanitize_curve_data_for_display(xs_fft[: visible_real.size], visible_real)
real_x, real_y = sanitize_curve_data_for_display(xs_fft_display[: visible_real.size], visible_real)
set_curve_data("fft_real", curve_fft_real, real_x, real_y)
else:
clear_curve_if_needed("fft_real", curve_fft_real)
if visible_imag is not None:
imag_x, imag_y = sanitize_curve_data_for_display(xs_fft[: visible_imag.size], visible_imag)
imag_x, imag_y = sanitize_curve_data_for_display(xs_fft_display[: visible_imag.size], visible_imag)
set_curve_data("fft_imag", curve_fft_imag, imag_x, imag_y)
else:
clear_curve_if_needed("fft_imag", curve_fft_imag)
@ -3188,7 +3224,7 @@ def run_pyqtgraph(args) -> None:
finite_ref = np.isfinite(xs_fft) & np.isfinite(fft_ref_db)
if np.any(finite_ref):
fft_ref_lin = _db_to_linear_amplitude(fft_ref_db[finite_ref])
ref_x, ref_y = sanitize_curve_data_for_display(xs_fft[finite_ref], fft_ref_lin)
ref_x, ref_y = sanitize_curve_data_for_display(xs_fft_display[finite_ref], fft_ref_lin)
set_curve_data("fft_ref", curve_fft_ref, ref_x, ref_y)
set_item_visible_if_changed("fft_ref", curve_fft_ref, True)
ref_curve_for_range = fft_ref_lin
@ -3208,7 +3244,12 @@ def run_pyqtgraph(args) -> None:
set_curve_data(
f"fft_peak_box_{idx}",
box,
display_distance_axis(
np.asarray(
[peak["left"], peak["left"], peak["right"], peak["right"], peak["left"]],
dtype=np.float64,
)
),
y_box,
)
set_item_visible_if_changed(f"fft_peak_box_{idx}", box, True)
@ -3229,10 +3270,10 @@ def run_pyqtgraph(args) -> None:
markers = find_peak_width_markers(xs_fft, fft_vals_db)
if markers is not None:
fft_bg_line.setValue(float(_db_to_linear_amplitude(np.asarray([markers["background"]]))[0]))
fft_left_line.setValue(markers["left"])
fft_right_line.setValue(markers["right"])
spec_left_line.setValue(markers["left"])
spec_right_line.setValue(markers["right"])
fft_left_line.setValue(display_distance_value(markers["left"]))
fft_right_line.setValue(display_distance_value(markers["right"]))
spec_left_line.setValue(display_distance_value(markers["left"]))
spec_right_line.setValue(display_distance_value(markers["right"]))
set_peak_marker_visibility(True)
runtime.current_peak_width = markers["width"]
runtime.current_peak_amplitude = markers["amplitude"]
@ -3248,7 +3289,7 @@ def run_pyqtgraph(args) -> None:
clear_curve_if_needed("fft_real", curve_fft_real)
clear_curve_if_needed("fft_imag", curve_fft_imag)
if fft_abs_enabled:
fft_x, fft_y = sanitize_curve_data_for_display(xs_fft, fft_vals_db)
fft_x, fft_y = sanitize_curve_data_for_display(xs_fft_display, fft_vals_db)
set_curve_data("fft_abs", curve_fft, fft_x, fft_y)
else:
clear_curve_if_needed("fft_abs", curve_fft)
@ -3259,7 +3300,7 @@ def run_pyqtgraph(args) -> None:
fft_ref = rolling_median_ref(xs_fft, fft_vals_db, peak_ref_window)
finite_ref = np.isfinite(xs_fft) & np.isfinite(fft_ref)
if np.any(finite_ref):
ref_x, ref_y = sanitize_curve_data_for_display(xs_fft[finite_ref], fft_ref[finite_ref])
ref_x, ref_y = sanitize_curve_data_for_display(xs_fft_display[finite_ref], fft_ref[finite_ref])
set_curve_data("fft_ref", curve_fft_ref, ref_x, ref_y)
set_item_visible_if_changed("fft_ref", curve_fft_ref, True)
y_for_range = np.concatenate((y_for_range, fft_ref[finite_ref]))
@ -3273,7 +3314,12 @@ def run_pyqtgraph(args) -> None:
set_curve_data(
f"fft_peak_box_{idx}",
box,
display_distance_axis(
np.asarray(
[peak["left"], peak["left"], peak["right"], peak["right"], peak["left"]],
dtype=np.float64,
)
),
[peak["ref"], peak["peak_y"], peak["peak_y"], peak["ref"], peak["ref"]],
)
set_item_visible_if_changed(f"fft_peak_box_{idx}", box, True)
@ -3303,10 +3349,10 @@ def run_pyqtgraph(args) -> None:
markers = find_peak_width_markers(xs_fft, fft_vals_db)
if markers is not None:
fft_bg_line.setValue(markers["background"])
fft_left_line.setValue(markers["left"])
fft_right_line.setValue(markers["right"])
spec_left_line.setValue(markers["left"])
spec_right_line.setValue(markers["right"])
fft_left_line.setValue(display_distance_value(markers["left"]))
fft_right_line.setValue(display_distance_value(markers["right"]))
spec_left_line.setValue(display_distance_value(markers["left"]))
spec_right_line.setValue(display_distance_value(markers["right"]))
set_peak_marker_visibility(True)
runtime.current_peak_width = markers["width"]
runtime.current_peak_amplitude = markers["amplitude"]
@ -3494,7 +3540,11 @@ def run_pyqtgraph(args) -> None:
and runtime.ring.y_min_fft != runtime.ring.y_max_fft
):
levels = (runtime.ring.y_min_fft, runtime.ring.y_max_fft)
distance_bounds = resolve_axis_bounds(disp_fft_axis)
disp_fft_display_axis = display_distance_axis(disp_fft_axis)
if disp_fft_display_axis.size == disp_fft.shape[0]:
disp_fft = disp_fft[::-1, :]
disp_fft_display_axis = disp_fft_display_axis[::-1]
distance_bounds = resolve_axis_bounds(disp_fft_display_axis)
if distance_bounds is not None:
d_min, d_max = distance_bounds
set_image_rect_if_changed("fft_waterfall_rect", img_fft, 0.0, d_min, float(max_sweeps), max(1e-9, d_max - d_min))

View File

@ -20,6 +20,8 @@ from rfg_adc_plotter.gui.pyqtgraph_backend import (
convert_tty_i16_to_voltage,
decimate_bscan_rows_for_display,
decimate_curve_for_display,
display_distance_axis,
display_distance_value,
fft_bscan_image_to_db,
is_short_sweep,
resolve_axis_bounds,
@ -546,6 +548,13 @@ class ProcessingTests(unittest.TestCase):
self.assertTrue(bool(keep_mask[-1]))
self.assertAlmostEqual(float(cut_axis[0]), 3.0, places=6)
def test_display_distance_axis_zero_is_at_nine_meters(self):
axis = np.asarray([0.0, 4.5, 9.0], dtype=np.float64)
display_axis = display_distance_axis(axis)
np.testing.assert_allclose(display_axis, np.asarray([9.0, 4.5, 0.0], dtype=np.float64))
self.assertAlmostEqual(display_distance_value(9.0), 0.0, places=12)
def test_resolve_initial_window_size_stays_within_small_screen(self):
width, height = resolve_initial_window_size(800, 480)