check background
This commit is contained in:
@ -170,6 +170,30 @@ def resolve_visible_aux_curves(aux_curves: SweepAuxCurves, enabled: bool) -> Swe
|
|||||||
return aux_1_arr, aux_2_arr
|
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:
|
def run_pyqtgraph(args) -> None:
|
||||||
"""Start the PyQtGraph GUI."""
|
"""Start the PyQtGraph GUI."""
|
||||||
peak_calibrate_mode = bool(getattr(args, "calibrate", False))
|
peak_calibrate_mode = bool(getattr(args, "calibrate", False))
|
||||||
@ -1330,15 +1354,8 @@ def run_pyqtgraph(args) -> None:
|
|||||||
|
|
||||||
levels = None
|
levels = None
|
||||||
if active_background is not None:
|
if active_background is not None:
|
||||||
try:
|
levels = compute_background_subtracted_bscan_levels(disp_fft_lin, disp_fft)
|
||||||
p5 = float(np.nanpercentile(disp_fft, 5))
|
if levels is None:
|
||||||
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:
|
|
||||||
try:
|
try:
|
||||||
mean_spec = np.nanmean(disp_fft, axis=1)
|
mean_spec = np.nanmean(disp_fft, axis=1)
|
||||||
vmin_v = float(np.nanmin(mean_spec))
|
vmin_v = float(np.nanmin(mean_spec))
|
||||||
|
|||||||
@ -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 (
|
from rfg_adc_plotter.gui.pyqtgraph_backend import (
|
||||||
apply_working_range,
|
apply_working_range,
|
||||||
apply_working_range_to_aux_curves,
|
apply_working_range_to_aux_curves,
|
||||||
|
compute_background_subtracted_bscan_levels,
|
||||||
resolve_visible_aux_curves,
|
resolve_visible_aux_curves,
|
||||||
)
|
)
|
||||||
from rfg_adc_plotter.processing.calibration import (
|
from rfg_adc_plotter.processing.calibration import (
|
||||||
@ -29,6 +30,7 @@ from rfg_adc_plotter.processing.fft import (
|
|||||||
compute_distance_axis,
|
compute_distance_axis,
|
||||||
compute_fft_mag_row,
|
compute_fft_mag_row,
|
||||||
compute_fft_row,
|
compute_fft_row,
|
||||||
|
fft_mag_to_db,
|
||||||
)
|
)
|
||||||
from rfg_adc_plotter.processing.normalization import (
|
from rfg_adc_plotter.processing.normalization import (
|
||||||
build_calib_envelopes,
|
build_calib_envelopes,
|
||||||
@ -180,6 +182,30 @@ class ProcessingTests(unittest.TestCase):
|
|||||||
self.assertTrue(np.allclose(visible[0], aux[0]))
|
self.assertTrue(np.allclose(visible[0], aux[0]))
|
||||||
self.assertTrue(np.allclose(visible[1], aux[1]))
|
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):
|
def test_fft_helpers_return_expected_shapes(self):
|
||||||
sweep = np.sin(np.linspace(0.0, 4.0 * np.pi, 128)).astype(np.float32)
|
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)
|
freqs = np.linspace(3.3, 14.3, 128, dtype=np.float64)
|
||||||
|
|||||||
Reference in New Issue
Block a user