cut the range feature
This commit is contained in:
@ -65,6 +65,48 @@ def _visible_levels_pyqtgraph(data: np.ndarray, plot_item) -> Optional[Tuple[flo
|
||||
return (vmin, vmax)
|
||||
|
||||
|
||||
def apply_working_range(
|
||||
freqs: Optional[np.ndarray],
|
||||
sweep: Optional[np.ndarray],
|
||||
range_min_ghz: float,
|
||||
range_max_ghz: float,
|
||||
) -> Tuple[np.ndarray, np.ndarray]:
|
||||
"""Crop sweep data to the active working frequency range."""
|
||||
if freqs is None or sweep is None:
|
||||
return (
|
||||
np.zeros((0,), dtype=np.float64),
|
||||
np.zeros((0,), dtype=np.float32),
|
||||
)
|
||||
|
||||
freq_arr = np.asarray(freqs, dtype=np.float64).reshape(-1)
|
||||
sweep_arr = np.asarray(sweep, dtype=np.float32).reshape(-1)
|
||||
width = min(freq_arr.size, sweep_arr.size)
|
||||
if width <= 0:
|
||||
return (
|
||||
np.zeros((0,), dtype=np.float64),
|
||||
np.zeros((0,), dtype=np.float32),
|
||||
)
|
||||
|
||||
freq_arr = freq_arr[:width]
|
||||
sweep_arr = sweep_arr[:width]
|
||||
valid = (
|
||||
np.isfinite(freq_arr)
|
||||
& np.isfinite(sweep_arr)
|
||||
& (freq_arr >= float(range_min_ghz))
|
||||
& (freq_arr <= float(range_max_ghz))
|
||||
)
|
||||
if not np.any(valid):
|
||||
return (
|
||||
np.zeros((0,), dtype=np.float64),
|
||||
np.zeros((0,), dtype=np.float32),
|
||||
)
|
||||
|
||||
return (
|
||||
freq_arr[valid].astype(np.float64, copy=False),
|
||||
sweep_arr[valid].astype(np.float32, copy=False),
|
||||
)
|
||||
|
||||
|
||||
def run_pyqtgraph(args) -> None:
|
||||
"""Start the PyQtGraph GUI."""
|
||||
peak_calibrate_mode = bool(getattr(args, "calibrate", False))
|
||||
@ -98,7 +140,11 @@ def run_pyqtgraph(args) -> None:
|
||||
fft_bins = FFT_LEN // 2 + 1
|
||||
spec_clip = parse_spec_clip(getattr(args, "spec_clip", None))
|
||||
spec_mean_sec = float(getattr(args, "spec_mean_sec", 0.0))
|
||||
runtime = RuntimeState(ring=RingBuffer(max_sweeps))
|
||||
runtime = RuntimeState(
|
||||
ring=RingBuffer(max_sweeps),
|
||||
range_min_ghz=float(SWEEP_FREQ_MIN_GHZ),
|
||||
range_max_ghz=float(SWEEP_FREQ_MAX_GHZ),
|
||||
)
|
||||
|
||||
pg.setConfigOptions(
|
||||
useOpenGL=not peak_calibrate_mode,
|
||||
@ -205,6 +251,24 @@ def run_pyqtgraph(args) -> None:
|
||||
bg_compute_cb = QtWidgets.QCheckBox("расчет фона")
|
||||
bg_subtract_cb = QtWidgets.QCheckBox("вычет фона")
|
||||
fft_bg_subtract_cb = QtWidgets.QCheckBox("FFT вычет фона")
|
||||
range_group = QtWidgets.QGroupBox("Рабочий диапазон")
|
||||
range_group_layout = QtWidgets.QFormLayout(range_group)
|
||||
range_group_layout.setContentsMargins(6, 6, 6, 6)
|
||||
range_group_layout.setSpacing(6)
|
||||
range_min_spin = QtWidgets.QDoubleSpinBox()
|
||||
range_max_spin = QtWidgets.QDoubleSpinBox()
|
||||
for spin in (range_min_spin, range_max_spin):
|
||||
spin.setDecimals(6)
|
||||
spin.setRange(0.0, 100.0)
|
||||
spin.setSingleStep(0.1)
|
||||
try:
|
||||
spin.setSuffix(" GHz")
|
||||
except Exception:
|
||||
pass
|
||||
range_min_spin.setValue(runtime.range_min_ghz)
|
||||
range_max_spin.setValue(runtime.range_max_ghz)
|
||||
range_group_layout.addRow("f min", range_min_spin)
|
||||
range_group_layout.addRow("f max", range_max_spin)
|
||||
fft_mode_label = QtWidgets.QLabel("IFFT режим")
|
||||
fft_mode_combo = QtWidgets.QComboBox()
|
||||
fft_mode_combo.addItem("Обычный", "direct")
|
||||
@ -240,6 +304,7 @@ def run_pyqtgraph(args) -> None:
|
||||
settings_layout.addWidget(QtWidgets.QLabel("Настройки"))
|
||||
except Exception:
|
||||
pass
|
||||
settings_layout.addWidget(range_group)
|
||||
settings_layout.addWidget(calib_group)
|
||||
settings_layout.addWidget(bg_compute_cb)
|
||||
settings_layout.addWidget(bg_subtract_cb)
|
||||
@ -258,6 +323,7 @@ def run_pyqtgraph(args) -> None:
|
||||
fft_mode = "symmetric"
|
||||
status_note = ""
|
||||
status_dirty = True
|
||||
range_change_in_progress = False
|
||||
fixed_ylim: Optional[Tuple[float, float]] = None
|
||||
if args.ylim:
|
||||
try:
|
||||
@ -272,14 +338,21 @@ def run_pyqtgraph(args) -> None:
|
||||
changed = runtime.ring.ensure_init(sweep_width)
|
||||
if not changed:
|
||||
return
|
||||
f_min = float(runtime.range_min_ghz)
|
||||
f_max = float(runtime.range_max_ghz)
|
||||
if runtime.current_freqs is not None and runtime.current_freqs.size > 0:
|
||||
finite_f = runtime.current_freqs[np.isfinite(runtime.current_freqs)]
|
||||
if finite_f.size > 0:
|
||||
f_min = float(np.min(finite_f))
|
||||
f_max = float(np.max(finite_f))
|
||||
img.setImage(runtime.ring.get_display_raw(), autoLevels=False)
|
||||
img.setRect(0, SWEEP_FREQ_MIN_GHZ, max_sweeps, SWEEP_FREQ_MAX_GHZ - SWEEP_FREQ_MIN_GHZ)
|
||||
img.setRect(0, f_min, max_sweeps, max(1e-9, f_max - f_min))
|
||||
p_img.setRange(
|
||||
xRange=(0, max_sweeps - 1),
|
||||
yRange=(SWEEP_FREQ_MIN_GHZ, SWEEP_FREQ_MAX_GHZ),
|
||||
yRange=(f_min, f_max),
|
||||
padding=0,
|
||||
)
|
||||
p_line.setXRange(SWEEP_FREQ_MIN_GHZ, SWEEP_FREQ_MAX_GHZ, padding=0)
|
||||
p_line.setXRange(f_min, f_max, padding=0)
|
||||
img_fft.setImage(runtime.ring.get_display_fft_linear(), autoLevels=False)
|
||||
img_fft.setRect(0, 0.0, max_sweeps, 1.0)
|
||||
p_spec.setRange(xRange=(0, max_sweeps - 1), yRange=(0.0, 1.0), padding=0)
|
||||
@ -295,6 +368,15 @@ def run_pyqtgraph(args) -> None:
|
||||
f_max = f_min + 1.0
|
||||
img.setRect(0, f_min, max_sweeps, f_max - f_min)
|
||||
p_img.setRange(xRange=(0, max_sweeps - 1), yRange=(f_min, f_max), padding=0)
|
||||
p_line.setXRange(f_min, f_max, padding=0)
|
||||
else:
|
||||
f_min = float(runtime.range_min_ghz)
|
||||
f_max = float(runtime.range_max_ghz)
|
||||
if f_max <= f_min:
|
||||
f_max = f_min + 1.0
|
||||
img.setRect(0, f_min, max_sweeps, f_max - f_min)
|
||||
p_img.setRange(xRange=(0, max_sweeps - 1), yRange=(f_min, f_max), padding=0)
|
||||
p_line.setXRange(f_min, f_max, padding=0)
|
||||
|
||||
distance_axis = runtime.ring.distance_axis
|
||||
if distance_axis is not None and distance_axis.size > 0:
|
||||
@ -317,6 +399,13 @@ def run_pyqtgraph(args) -> None:
|
||||
size,
|
||||
dtype=np.float64,
|
||||
)
|
||||
if runtime.range_max_ghz > runtime.range_min_ghz:
|
||||
return np.linspace(
|
||||
float(runtime.range_min_ghz),
|
||||
float(runtime.range_max_ghz),
|
||||
size,
|
||||
dtype=np.float64,
|
||||
)
|
||||
if runtime.ring.x_shared is not None and size <= runtime.ring.x_shared.size:
|
||||
return runtime.ring.x_shared[:size]
|
||||
return np.arange(size, dtype=np.float32)
|
||||
@ -333,14 +422,69 @@ def run_pyqtgraph(args) -> None:
|
||||
path = ""
|
||||
return path or "calibration_envelope.npy"
|
||||
|
||||
def reset_ring_buffers() -> None:
|
||||
runtime.ring.reset()
|
||||
runtime.current_distances = None
|
||||
runtime.current_fft_db = None
|
||||
runtime.bg_spec_cache = None
|
||||
runtime.current_peak_width = None
|
||||
runtime.current_peak_amplitude = None
|
||||
runtime.peak_candidates = []
|
||||
img.setImage(runtime.ring.get_display_raw(), autoLevels=False)
|
||||
img_fft.setImage(runtime.ring.get_display_fft_linear(), autoLevels=False)
|
||||
update_physical_axes()
|
||||
|
||||
def refresh_current_window(push_to_ring: bool = False, *, reset_ring: bool = False) -> None:
|
||||
if reset_ring:
|
||||
reset_ring_buffers()
|
||||
|
||||
if runtime.full_current_freqs is None or runtime.full_current_sweep_raw is None:
|
||||
runtime.current_freqs = None
|
||||
runtime.current_sweep_raw = None
|
||||
runtime.current_sweep_norm = None
|
||||
runtime.current_fft_db = None
|
||||
runtime.current_distances = runtime.ring.distance_axis
|
||||
return
|
||||
|
||||
current_freqs, current_sweep = apply_working_range(
|
||||
runtime.full_current_freqs,
|
||||
runtime.full_current_sweep_raw,
|
||||
runtime.range_min_ghz,
|
||||
runtime.range_max_ghz,
|
||||
)
|
||||
runtime.current_freqs = current_freqs
|
||||
runtime.current_sweep_raw = current_sweep
|
||||
|
||||
if runtime.current_sweep_raw.size == 0:
|
||||
if push_to_ring:
|
||||
reset_ring_buffers()
|
||||
runtime.current_freqs = None
|
||||
runtime.current_sweep_raw = None
|
||||
runtime.current_sweep_norm = None
|
||||
runtime.current_fft_db = None
|
||||
runtime.current_distances = None
|
||||
set_status_note("диапазон: нет точек в выбранном окне")
|
||||
return
|
||||
|
||||
recompute_current_processed_sweep(push_to_ring=push_to_ring)
|
||||
|
||||
def recompute_current_processed_sweep(push_to_ring: bool = False) -> None:
|
||||
if runtime.current_sweep_raw is not None and calib_enabled and runtime.calib_envelope is not None:
|
||||
if (
|
||||
runtime.current_sweep_raw is not None
|
||||
and runtime.current_sweep_raw.size > 0
|
||||
and calib_enabled
|
||||
and runtime.calib_envelope is not None
|
||||
):
|
||||
runtime.current_sweep_norm = normalize_by_envelope(runtime.current_sweep_raw, runtime.calib_envelope)
|
||||
else:
|
||||
runtime.current_sweep_norm = None
|
||||
|
||||
runtime.current_fft_db = None
|
||||
if not push_to_ring or runtime.current_sweep_raw is None:
|
||||
if (
|
||||
not push_to_ring
|
||||
or runtime.current_sweep_raw is None
|
||||
or runtime.current_sweep_raw.size == 0
|
||||
):
|
||||
return
|
||||
|
||||
sweep_for_processing = runtime.current_sweep_norm if runtime.current_sweep_norm is not None else runtime.current_sweep_raw
|
||||
@ -360,6 +504,36 @@ def run_pyqtgraph(args) -> None:
|
||||
recompute_current_processed_sweep(push_to_ring=False)
|
||||
runtime.mark_dirty()
|
||||
|
||||
def restore_range_controls() -> None:
|
||||
nonlocal range_change_in_progress
|
||||
range_change_in_progress = True
|
||||
try:
|
||||
range_min_spin.setValue(float(runtime.range_min_ghz))
|
||||
range_max_spin.setValue(float(runtime.range_max_ghz))
|
||||
finally:
|
||||
range_change_in_progress = False
|
||||
|
||||
def set_working_range() -> None:
|
||||
nonlocal range_change_in_progress
|
||||
if range_change_in_progress:
|
||||
return
|
||||
try:
|
||||
new_min = float(range_min_spin.value())
|
||||
new_max = float(range_max_spin.value())
|
||||
except Exception:
|
||||
restore_range_controls()
|
||||
return
|
||||
if (not np.isfinite(new_min)) or (not np.isfinite(new_max)) or new_min >= new_max:
|
||||
set_status_note("диапазон: f_min должен быть меньше f_max")
|
||||
restore_range_controls()
|
||||
runtime.mark_dirty()
|
||||
return
|
||||
runtime.range_min_ghz = new_min
|
||||
runtime.range_max_ghz = new_max
|
||||
refresh_current_window(push_to_ring=True, reset_ring=True)
|
||||
set_status_note(f"диапазон: {new_min:.6g}..{new_max:.6g} GHz")
|
||||
runtime.mark_dirty()
|
||||
|
||||
def pick_calib_file() -> None:
|
||||
start_path = get_calib_file_path()
|
||||
try:
|
||||
@ -472,10 +646,13 @@ def run_pyqtgraph(args) -> None:
|
||||
fft_mode_combo.setCurrentIndex(1)
|
||||
except Exception:
|
||||
pass
|
||||
restore_range_controls()
|
||||
set_bg_compute_enabled()
|
||||
set_fft_mode()
|
||||
|
||||
try:
|
||||
range_min_spin.valueChanged.connect(lambda _v: set_working_range())
|
||||
range_max_spin.valueChanged.connect(lambda _v: set_working_range())
|
||||
calib_cb.stateChanged.connect(lambda _v: set_calib_enabled())
|
||||
calib_pick_btn.clicked.connect(lambda _checked=False: pick_calib_file())
|
||||
calib_save_btn.clicked.connect(lambda _checked=False: save_current_calibration())
|
||||
@ -691,11 +868,11 @@ def run_pyqtgraph(args) -> None:
|
||||
"I": sweep,
|
||||
}
|
||||
)
|
||||
runtime.current_freqs = calibrated["F"]
|
||||
runtime.current_sweep_raw = calibrated["I"]
|
||||
runtime.full_current_freqs = np.asarray(calibrated["F"], dtype=np.float64)
|
||||
runtime.full_current_sweep_raw = np.asarray(calibrated["I"], dtype=np.float32)
|
||||
runtime.current_aux_curves = aux_curves
|
||||
runtime.current_info = info
|
||||
recompute_current_processed_sweep(push_to_ring=True)
|
||||
refresh_current_window(push_to_ring=True)
|
||||
if drained > 0:
|
||||
update_physical_axes()
|
||||
return drained
|
||||
@ -727,9 +904,11 @@ def run_pyqtgraph(args) -> None:
|
||||
except Exception:
|
||||
bg_fft_for_line = None
|
||||
|
||||
if redraw_needed and (runtime.current_sweep_raw is not None or runtime.calib_envelope is not None):
|
||||
if redraw_needed:
|
||||
xs = resolve_curve_xs(
|
||||
runtime.current_sweep_raw.size if runtime.current_sweep_raw is not None else runtime.calib_envelope.size
|
||||
runtime.current_sweep_raw.size
|
||||
if runtime.current_sweep_raw is not None
|
||||
else (runtime.calib_envelope.size if runtime.calib_envelope is not None else 0)
|
||||
)
|
||||
displayed_calib = None
|
||||
|
||||
@ -871,8 +1050,16 @@ def run_pyqtgraph(args) -> None:
|
||||
runtime.current_peak_amplitude = None
|
||||
else:
|
||||
curve_fft_ref.setVisible(False)
|
||||
curve_fft.setData([], [])
|
||||
for box in fft_peak_boxes:
|
||||
box.setVisible(False)
|
||||
fft_bg_line.setVisible(False)
|
||||
fft_left_line.setVisible(False)
|
||||
fft_right_line.setVisible(False)
|
||||
spec_left_line.setVisible(False)
|
||||
spec_right_line.setVisible(False)
|
||||
runtime.current_peak_width = None
|
||||
runtime.current_peak_amplitude = None
|
||||
runtime.peak_candidates = []
|
||||
refresh_peak_params_label([])
|
||||
runtime.plot_dirty = False
|
||||
|
||||
Reference in New Issue
Block a user