check background

This commit is contained in:
awe
2026-03-24 19:37:11 +03:00
parent 3ab9f7ad21
commit fa4870c56c
2 changed files with 52 additions and 9 deletions

View File

@ -170,6 +170,30 @@ def resolve_visible_aux_curves(aux_curves: SweepAuxCurves, enabled: bool) -> Swe
return aux_1_arr, aux_2_arr
def compute_background_subtracted_bscan_levels(
disp_fft_lin: np.ndarray,
disp_fft: np.ndarray,
) -> Optional[Tuple[float, float]]:
"""Pick robust color levels from positive residuals after background subtraction."""
if disp_fft_lin.size == 0 or disp_fft.size == 0:
return None
positive_mask = np.isfinite(disp_fft_lin) & (disp_fft_lin > 0.0)
if np.count_nonzero(positive_mask) < 2:
return None
vals = np.asarray(disp_fft[positive_mask], dtype=np.float64).reshape(-1)
vals = vals[np.isfinite(vals)]
if vals.size < 2:
return None
try:
vmin = float(np.nanpercentile(vals, 15.0))
vmax = float(np.nanpercentile(vals, 99.7))
except Exception:
return None
if not (np.isfinite(vmin) and np.isfinite(vmax)) or vmin >= vmax:
return None
return (vmin, vmax)
def run_pyqtgraph(args) -> None:
"""Start the PyQtGraph GUI."""
peak_calibrate_mode = bool(getattr(args, "calibrate", False))
@ -1330,15 +1354,8 @@ def run_pyqtgraph(args) -> None:
levels = None
if active_background is not None:
try:
p5 = float(np.nanpercentile(disp_fft, 5))
p95 = float(np.nanpercentile(disp_fft, 95))
span = max(abs(p5), abs(p95))
if np.isfinite(span) and span > 0.0:
levels = (-span, span)
except Exception:
levels = None
else:
levels = compute_background_subtracted_bscan_levels(disp_fft_lin, disp_fft)
if levels is None:
try:
mean_spec = np.nanmean(disp_fft, axis=1)
vmin_v = float(np.nanmin(mean_spec))

View File

@ -9,6 +9,7 @@ from rfg_adc_plotter.constants import FFT_LEN, SWEEP_FREQ_MAX_GHZ, SWEEP_FREQ_MI
from rfg_adc_plotter.gui.pyqtgraph_backend import (
apply_working_range,
apply_working_range_to_aux_curves,
compute_background_subtracted_bscan_levels,
resolve_visible_aux_curves,
)
from rfg_adc_plotter.processing.calibration import (
@ -29,6 +30,7 @@ from rfg_adc_plotter.processing.fft import (
compute_distance_axis,
compute_fft_mag_row,
compute_fft_row,
fft_mag_to_db,
)
from rfg_adc_plotter.processing.normalization import (
build_calib_envelopes,
@ -180,6 +182,30 @@ class ProcessingTests(unittest.TestCase):
self.assertTrue(np.allclose(visible[0], aux[0]))
self.assertTrue(np.allclose(visible[1], aux[1]))
def test_background_subtracted_bscan_levels_ignore_zero_floor(self):
disp_fft_lin = np.zeros((4, 8), dtype=np.float32)
disp_fft_lin[1, 2:6] = np.asarray([0.05, 0.1, 0.5, 2.0], dtype=np.float32)
disp_fft_lin[2, 1:6] = np.asarray([0.08, 0.2, 0.7, 3.0, 9.0], dtype=np.float32)
disp_fft = fft_mag_to_db(disp_fft_lin)
levels = compute_background_subtracted_bscan_levels(disp_fft_lin, disp_fft)
self.assertIsNotNone(levels)
positive_vals = disp_fft[disp_fft_lin > 0.0]
self.assertAlmostEqual(levels[0], float(np.nanpercentile(positive_vals, 15.0)), places=5)
self.assertAlmostEqual(levels[1], float(np.nanpercentile(positive_vals, 99.7)), places=5)
zero_floor = disp_fft[disp_fft_lin == 0.0]
self.assertLess(float(np.nanmax(zero_floor)), levels[0])
def test_background_subtracted_bscan_levels_fallback_when_residuals_too_sparse(self):
disp_fft_lin = np.zeros((3, 4), dtype=np.float32)
disp_fft_lin[1, 2] = 1.0
disp_fft = fft_mag_to_db(disp_fft_lin)
levels = compute_background_subtracted_bscan_levels(disp_fft_lin, disp_fft)
self.assertIsNone(levels)
def test_fft_helpers_return_expected_shapes(self):
sweep = np.sin(np.linspace(0.0, 4.0 * np.pi, 128)).astype(np.float32)
freqs = np.linspace(3.3, 14.3, 128, dtype=np.float64)