voltage range

This commit is contained in:
awe
2026-04-14 20:39:44 +03:00
parent d170fc11e5
commit 3cb3d1c31a
6 changed files with 148 additions and 22 deletions

View File

@ -61,6 +61,10 @@ DEFAULT_MAIN_WINDOW_WIDTH = 1200
DEFAULT_MAIN_WINDOW_HEIGHT = 680
MIN_MAIN_WINDOW_WIDTH = 640
MIN_MAIN_WINDOW_HEIGHT = 420
TTY_CODE_SCALE_DENOM = 32767.0
TTY_RANGE_DEFAULT_V = 5.0
TTY_RANGE_MIN_V = 1e-6
TTY_RANGE_MAX_V = 1_000_000.0
def sanitize_curve_data_for_display(
@ -489,6 +493,28 @@ def compute_aux_phase_curve(aux_curves: SweepAuxCurves) -> Optional[np.ndarray]:
return phase
def sanitize_tty_voltage_range(range_v: float, default: float = TTY_RANGE_DEFAULT_V) -> float:
"""Return a finite positive full-scale voltage range for tty int16 conversion."""
try:
value = float(range_v)
except Exception:
value = float(default)
if not np.isfinite(value):
value = float(default)
return float(np.clip(abs(value), TTY_RANGE_MIN_V, TTY_RANGE_MAX_V))
def convert_tty_i16_to_voltage(codes: np.ndarray, range_v: float) -> np.ndarray:
"""Convert signed tty int16 code array to clipped voltage values in ``[-range_v, +range_v]``."""
code_arr = np.asarray(codes, dtype=np.float32).reshape(-1)
if code_arr.size <= 0:
return np.zeros((0,), dtype=np.float32)
range_abs_v = sanitize_tty_voltage_range(range_v)
scale_v = range_abs_v / float(TTY_CODE_SCALE_DENOM)
volt = code_arr * np.float32(scale_v)
return np.clip(volt, -range_abs_v, range_abs_v).astype(np.float32, copy=False)
def decimate_curve_for_display(
xs: Optional[np.ndarray],
ys: Optional[np.ndarray],
@ -606,6 +632,7 @@ def run_pyqtgraph(args) -> None:
peak_calibrate_mode = bool(getattr(args, "calibrate", False))
peak_search_enabled = bool(getattr(args, "peak_search", False))
bin_mode = bool(getattr(args, "bin_mode", False))
tty_range_v = sanitize_tty_voltage_range(getattr(args, "tty_range_v", TTY_RANGE_DEFAULT_V))
complex_ascii_mode = bool(getattr(args, "parser_complex_ascii", False))
complex_sweep_mode = bool(
bin_mode
@ -695,7 +722,7 @@ def run_pyqtgraph(args) -> None:
p_line_aux_vb = pg.ViewBox()
try:
p_line.showAxis("right")
p_line.getAxis("right").setLabel("CH1/CH2")
p_line.getAxis("right").setLabel("CH1/CH2, В")
p_line.scene().addItem(p_line_aux_vb)
p_line.getAxis("right").linkToView(p_line_aux_vb)
p_line_aux_vb.setXLink(p_line)
@ -718,7 +745,7 @@ def run_pyqtgraph(args) -> None:
p_line.setLabel("left", "Y")
if bin_iq_power_mode:
try:
p_line.setLabel("left", "CH1^2 + CH2^2")
p_line.setLabel("left", "CH1^2 + CH2^2, В^2")
except Exception:
pass
ch_text = pg.TextItem("", anchor=(1, 1))
@ -790,7 +817,7 @@ def run_pyqtgraph(args) -> None:
try:
if bin_iq_power_mode:
p_fft.setTitle("FFT: CH1 + i*CH2")
p_line.setTitle("Сырые CH1/CH2 и CH1^2 + CH2^2")
p_line.setTitle("Сырые CH1/CH2 (В) и CH1^2 + CH2^2 (В^2)")
else:
p_fft.setTitle("FFT: Re / Im / Abs")
p_line.setTitle("Сырые данные до FFT")
@ -820,7 +847,7 @@ def run_pyqtgraph(args) -> None:
curve_complex_calib_real = p_complex_calib.plot(pen=pg.mkPen((80, 120, 255), width=1))
curve_complex_calib_imag = p_complex_calib.plot(pen=pg.mkPen((120, 200, 120), width=1))
p_complex_calib.setLabel("bottom", "ГГц")
p_complex_calib.setLabel("left", "Амплитуда")
p_complex_calib.setLabel("left", "В" if bin_iq_power_mode else "Амплитуда")
try:
p_complex_calib.setXLink(p_line)
p_complex_calib.setVisible(bool(complex_sweep_mode))
@ -945,10 +972,28 @@ def run_pyqtgraph(args) -> None:
background_buttons_row.addWidget(background_save_btn)
background_buttons_row.addWidget(background_load_btn)
background_group_layout.addLayout(background_buttons_row)
tty_range_group = QtWidgets.QGroupBox("TTY диапазон")
tty_range_layout = QtWidgets.QFormLayout(tty_range_group)
tty_range_layout.setContentsMargins(6, 6, 6, 6)
tty_range_layout.setSpacing(6)
tty_range_spin = QtWidgets.QDoubleSpinBox()
tty_range_spin.setDecimals(6)
tty_range_spin.setRange(TTY_RANGE_MIN_V, TTY_RANGE_MAX_V)
tty_range_spin.setSingleStep(0.1)
try:
tty_range_spin.setSuffix(" V")
except Exception:
pass
tty_range_spin.setValue(tty_range_v)
tty_range_layout.addRow("±V", tty_range_spin)
try:
tty_range_group.setEnabled(bool(bin_iq_power_mode))
except Exception:
pass
parsed_data_cb = QtWidgets.QCheckBox("данные после парсинга")
if complex_sweep_mode:
try:
parsed_data_cb.setText("Сырые CH1/CH2" if bin_iq_power_mode else "Сырые Re/Im")
parsed_data_cb.setText("Сырые CH1/CH2 (В)" if bin_iq_power_mode else "Сырые Re/Im")
parsed_data_cb.setChecked(True)
except Exception:
pass
@ -978,6 +1023,7 @@ def run_pyqtgraph(args) -> None:
settings_layout.addWidget(range_group)
settings_layout.addWidget(calib_group)
settings_layout.addWidget(complex_calib_group)
settings_layout.addWidget(tty_range_group)
settings_layout.addWidget(parsed_data_cb)
settings_layout.addWidget(background_group)
settings_layout.addWidget(fft_mode_label)
@ -1005,6 +1051,7 @@ def run_pyqtgraph(args) -> None:
status_dirty = True
calibration_toggle_in_progress = False
range_change_in_progress = False
tty_range_change_in_progress = False
debug_event_counts: Dict[str, int] = {}
last_queue_backlog = 0
last_backlog_skipped = 0
@ -1164,6 +1211,27 @@ def run_pyqtgraph(args) -> None:
path = ""
return path or "fft_background.npy"
def rebuild_tty_voltage_curves_from_codes() -> bool:
if (not bin_iq_power_mode) or runtime.full_current_aux_curves_codes is None:
return False
try:
code_1, code_2 = runtime.full_current_aux_curves_codes
except Exception:
return False
code_1_arr = np.asarray(code_1, dtype=np.float32).reshape(-1)
code_2_arr = np.asarray(code_2, dtype=np.float32).reshape(-1)
width = min(code_1_arr.size, code_2_arr.size)
if width <= 0:
return False
ch_1_v = convert_tty_i16_to_voltage(code_1_arr[:width], tty_range_v)
ch_2_v = convert_tty_i16_to_voltage(code_2_arr[:width], tty_range_v)
runtime.full_current_aux_curves = (ch_1_v, ch_2_v)
runtime.full_current_fft_source = ch_1_v.astype(np.complex64) + (1j * ch_2_v.astype(np.complex64))
ch_1_v_f64 = ch_1_v.astype(np.float64, copy=False)
ch_2_v_f64 = ch_2_v.astype(np.float64, copy=False)
runtime.full_current_sweep_raw = np.asarray((ch_1_v_f64 * ch_1_v_f64) + (ch_2_v_f64 * ch_2_v_f64), dtype=np.float32)
return True
def reset_background_state(*, clear_profile: bool = True) -> None:
runtime.background_buffer.reset()
if clear_profile:
@ -1435,6 +1503,33 @@ def run_pyqtgraph(args) -> None:
set_status_note(f"диапазон: {new_min:.6g}..{new_max:.6g} GHz")
runtime.mark_dirty()
def set_tty_range() -> None:
nonlocal tty_range_v, tty_range_change_in_progress
if tty_range_change_in_progress:
return
if not bin_iq_power_mode:
return
try:
requested_v = float(tty_range_spin.value())
except Exception:
requested_v = tty_range_v
new_range_v = sanitize_tty_voltage_range(requested_v, default=tty_range_v)
if np.isclose(new_range_v, tty_range_v, rtol=0.0, atol=1e-12):
return
tty_range_v = new_range_v
tty_range_change_in_progress = True
try:
tty_range_spin.setValue(tty_range_v)
finally:
tty_range_change_in_progress = False
if rebuild_tty_voltage_curves_from_codes():
reset_background_state(clear_profile=True)
refresh_current_window(push_to_ring=True, reset_ring=True)
set_status_note(f"tty диапазон: ±{tty_range_v:.6g} В")
else:
set_status_note(f"tty диапазон: ±{tty_range_v:.6g} В (ожидание данных)")
runtime.mark_dirty()
def pick_calib_file() -> None:
start_path = get_calib_file_path()
try:
@ -1687,6 +1782,7 @@ def run_pyqtgraph(args) -> None:
except Exception:
pass
restore_range_controls()
set_tty_range()
set_calib_enabled()
set_complex_calib_enabled()
set_parsed_data_enabled()
@ -1698,6 +1794,7 @@ def run_pyqtgraph(args) -> None:
try:
range_min_spin.valueChanged.connect(lambda _v: set_working_range())
range_max_spin.valueChanged.connect(lambda _v: set_working_range())
tty_range_spin.valueChanged.connect(lambda _v: set_tty_range())
calib_cb.stateChanged.connect(lambda _v: set_calib_enabled())
complex_calib_cb.stateChanged.connect(lambda _v: set_complex_calib_enabled())
parsed_data_cb.stateChanged.connect(lambda _v: set_parsed_data_enabled())
@ -1935,6 +2032,7 @@ def run_pyqtgraph(args) -> None:
)
base_freqs = np.linspace(SWEEP_FREQ_MIN_GHZ, SWEEP_FREQ_MAX_GHZ, sweep.size, dtype=np.float64)
runtime.full_current_aux_curves = None
runtime.full_current_aux_curves_codes = None
runtime.full_current_fft_source = None
if complex_sweep_mode and aux_curves is not None:
try:
@ -1944,21 +2042,20 @@ def run_pyqtgraph(args) -> None:
runtime.full_current_freqs = np.asarray(calibrated_aux_1_payload["F"], dtype=np.float64)
calibrated_aux_1 = np.asarray(calibrated_aux_1_payload["I"], dtype=np.float32)
calibrated_aux_2 = np.asarray(calibrated_aux_2, dtype=np.float32)
runtime.full_current_aux_curves = (calibrated_aux_1, calibrated_aux_2)
runtime.full_current_fft_source = (
calibrated_aux_1.astype(np.complex64) + (1j * calibrated_aux_2.astype(np.complex64))
)
if bin_iq_power_mode:
aux_1_f64 = calibrated_aux_1.astype(np.float64, copy=False)
aux_2_f64 = calibrated_aux_2.astype(np.float64, copy=False)
runtime.full_current_sweep_raw = np.asarray(
(aux_1_f64 * aux_1_f64) + (aux_2_f64 * aux_2_f64),
dtype=np.float32,
)
runtime.full_current_aux_curves_codes = (calibrated_aux_1, calibrated_aux_2)
if not rebuild_tty_voltage_curves_from_codes():
runtime.full_current_aux_curves = None
runtime.full_current_fft_source = None
else:
runtime.full_current_aux_curves = (calibrated_aux_1, calibrated_aux_2)
runtime.full_current_fft_source = (
calibrated_aux_1.astype(np.complex64) + (1j * calibrated_aux_2.astype(np.complex64))
)
runtime.full_current_sweep_raw = np.abs(runtime.full_current_fft_source).astype(np.float32)
except Exception:
runtime.full_current_aux_curves = None
runtime.full_current_aux_curves_codes = None
runtime.full_current_fft_source = None
if runtime.full_current_fft_source is None:
@ -1976,12 +2073,16 @@ def run_pyqtgraph(args) -> None:
aux_1, aux_2 = aux_curves
calibrated_aux_1 = calibrate_freqs({"F": base_freqs, "I": aux_1})["I"]
calibrated_aux_2 = calibrate_freqs({"F": base_freqs, "I": aux_2})["I"]
runtime.full_current_aux_curves = (
np.asarray(calibrated_aux_1, dtype=np.float32),
np.asarray(calibrated_aux_2, dtype=np.float32),
)
calibrated_aux_1_arr = np.asarray(calibrated_aux_1, dtype=np.float32)
calibrated_aux_2_arr = np.asarray(calibrated_aux_2, dtype=np.float32)
if bin_iq_power_mode:
runtime.full_current_aux_curves_codes = (calibrated_aux_1_arr, calibrated_aux_2_arr)
rebuild_tty_voltage_curves_from_codes()
else:
runtime.full_current_aux_curves = (calibrated_aux_1_arr, calibrated_aux_2_arr)
except Exception:
runtime.full_current_aux_curves = None
runtime.full_current_aux_curves_codes = None
runtime.current_info = info
refresh_current_window(push_to_ring=True)
processed_frames += 1