ampl parser

This commit is contained in:
awe
2026-04-15 19:09:11 +03:00
parent 3cb3d1c31a
commit c40df97085
10 changed files with 371 additions and 27 deletions

View File

@ -65,6 +65,7 @@ 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
LOGDET_EXP_INPUT_LIMIT = 80.0
def sanitize_curve_data_for_display(
@ -515,6 +516,30 @@ def convert_tty_i16_to_voltage(codes: np.ndarray, range_v: float) -> np.ndarray:
return np.clip(volt, -range_abs_v, range_abs_v).astype(np.float32, copy=False)
def build_logdet_voltage_fft_input(
codes: np.ndarray,
range_v: float,
*,
exp_input_limit: float = LOGDET_EXP_INPUT_LIMIT,
) -> Tuple[np.ndarray, np.ndarray]:
"""Convert 1a00 log-detector codes to raw volts and a real FFT input ``exp(V)``."""
volts = convert_tty_i16_to_voltage(codes, range_v)
if volts.size <= 0:
empty = np.zeros((0,), dtype=np.float32)
return empty, empty
try:
limit_v = abs(float(exp_input_limit))
except Exception:
limit_v = float(LOGDET_EXP_INPUT_LIMIT)
if (not np.isfinite(limit_v)) or limit_v <= 0.0:
limit_v = float(LOGDET_EXP_INPUT_LIMIT)
clipped_v = np.clip(volts, -limit_v, limit_v).astype(np.float32, copy=False)
fft_input = np.exp(clipped_v).astype(np.float32, copy=False)
return volts, fft_input
def decimate_curve_for_display(
xs: Optional[np.ndarray],
ys: Optional[np.ndarray],
@ -1211,6 +1236,50 @@ def run_pyqtgraph(args) -> None:
path = ""
return path or "fft_background.npy"
def get_signal_kind(info: Optional[SweepInfo] = None) -> Optional[str]:
payload = runtime.current_info if info is None else info
if not isinstance(payload, dict):
return None
signal_kind = payload.get("signal_kind")
if signal_kind in {"bin_iq", "bin_logdet"}:
return str(signal_kind)
return None
def current_packet_is_complex() -> bool:
return bool(complex_sweep_mode) and get_signal_kind() != "bin_logdet"
def refresh_signal_mode_labels() -> None:
signal_kind = get_signal_kind()
active_complex = current_packet_is_complex()
is_logdet = signal_kind == "bin_logdet"
is_bin_iq = signal_kind == "bin_iq"
try:
if is_logdet:
p_line.setTitle("Лог-детектор (В)")
p_line.setLabel("left", "В")
p_fft.setTitle("FFT: exp(V)")
parsed_data_cb.setText("Сырые log-detector (В)")
elif is_bin_iq:
p_line.setTitle("Сырые CH1/CH2 (В) и CH1^2 + CH2^2 (В^2)")
p_line.setLabel("left", "CH1^2 + CH2^2, В^2")
p_fft.setTitle("FFT: CH1 + i*CH2")
parsed_data_cb.setText("Сырые CH1/CH2 (В)")
elif complex_sweep_mode:
p_line.setTitle("Сырые данные до FFT")
p_line.setLabel("left", "Y")
p_fft.setTitle("FFT: Re / Im / Abs")
parsed_data_cb.setText("Сырые Re/Im")
else:
p_line.setTitle("Сырые данные")
p_line.setLabel("left", "Y")
p_fft.setTitle("FFT")
parsed_data_cb.setText("данные после парсинга")
p_fft.setLabel("left", "Амплитуда" if active_complex else "дБ")
p_complex_calib.setVisible(bool(active_complex))
except Exception:
pass
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
@ -1230,8 +1299,28 @@ def run_pyqtgraph(args) -> None:
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)
runtime.full_current_sweep_codes = None
return True
def rebuild_logdet_voltage_curve_from_codes() -> bool:
if (not bin_iq_power_mode) or runtime.full_current_sweep_codes is None:
return False
code_arr = np.asarray(runtime.full_current_sweep_codes, dtype=np.float32).reshape(-1)
if code_arr.size <= 0:
return False
sweep_raw_v, fft_input = build_logdet_voltage_fft_input(code_arr, tty_range_v)
runtime.full_current_aux_curves = None
runtime.full_current_aux_curves_codes = None
runtime.full_current_sweep_raw = sweep_raw_v
runtime.full_current_fft_source = fft_input
return True
def rebuild_bin_scaled_curves_from_codes() -> bool:
signal_kind = get_signal_kind()
if signal_kind == "bin_logdet":
return rebuild_logdet_voltage_curve_from_codes()
return rebuild_tty_voltage_curves_from_codes()
def reset_background_state(*, clear_profile: bool = True) -> None:
runtime.background_buffer.reset()
if clear_profile:
@ -1522,7 +1611,7 @@ def run_pyqtgraph(args) -> None:
tty_range_spin.setValue(tty_range_v)
finally:
tty_range_change_in_progress = False
if rebuild_tty_voltage_curves_from_codes():
if rebuild_bin_scaled_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} В")
@ -1790,6 +1879,7 @@ def run_pyqtgraph(args) -> None:
set_fft_curve_visibility()
set_fft_mode()
set_fft_low_cut_percent()
refresh_signal_mode_labels()
try:
range_min_spin.valueChanged.connect(lambda _v: set_working_range())
@ -2031,10 +2121,23 @@ def run_pyqtgraph(args) -> None:
f"ui short sweep width:{int(sweep.size)} expected:{int(runtime.ring.width)}",
)
base_freqs = np.linspace(SWEEP_FREQ_MIN_GHZ, SWEEP_FREQ_MAX_GHZ, sweep.size, dtype=np.float64)
runtime.current_info = info
runtime.full_current_aux_curves = None
runtime.full_current_aux_curves_codes = None
runtime.full_current_sweep_codes = None
runtime.full_current_fft_source = None
if complex_sweep_mode and aux_curves is not None:
signal_kind = get_signal_kind(info)
if signal_kind == "bin_logdet":
calibrated = calibrate_freqs(
{
"F": base_freqs,
"I": sweep,
}
)
runtime.full_current_freqs = np.asarray(calibrated["F"], dtype=np.float64)
runtime.full_current_sweep_codes = np.asarray(calibrated["I"], dtype=np.float32)
rebuild_logdet_voltage_curve_from_codes()
elif complex_sweep_mode and aux_curves is not None:
try:
aux_1, aux_2 = aux_curves
calibrated_aux_1_payload = calibrate_freqs({"F": base_freqs, "I": aux_1})
@ -2083,7 +2186,8 @@ def run_pyqtgraph(args) -> None:
except Exception:
runtime.full_current_aux_curves = None
runtime.full_current_aux_curves_codes = None
runtime.current_info = info
runtime.full_current_sweep_codes = None
refresh_signal_mode_labels()
refresh_current_window(push_to_ring=True)
processed_frames += 1
last_packet_processed_at = time.time()
@ -2139,6 +2243,9 @@ def run_pyqtgraph(args) -> None:
)
if redraw_needed:
refresh_signal_mode_labels()
active_signal_kind = get_signal_kind()
active_complex_mode = current_packet_is_complex()
xs = resolve_curve_xs(
runtime.current_sweep_raw.size
if runtime.current_sweep_raw is not None
@ -2202,7 +2309,7 @@ def run_pyqtgraph(args) -> None:
curve_norm.setData([], [])
if fixed_ylim is None:
if bin_iq_power_mode:
if active_signal_kind == "bin_iq":
y_series = [
runtime.current_sweep_raw,
displayed_calib,
@ -2237,7 +2344,7 @@ def run_pyqtgraph(args) -> None:
complex_calib_plot_signal: Optional[np.ndarray] = None
if (
complex_sweep_mode
active_complex_mode
and complex_calib_enabled
and runtime.current_fft_input is not None
and np.iscomplexobj(runtime.current_fft_input)
@ -2274,7 +2381,7 @@ def run_pyqtgraph(args) -> None:
or runtime.current_fft_mag.size != distance_axis.size
or runtime.plot_dirty
or (
complex_sweep_mode
active_complex_mode
and (
runtime.current_fft_complex is None
or runtime.current_fft_complex.size != distance_axis.size
@ -2317,7 +2424,7 @@ def run_pyqtgraph(args) -> None:
fft_vals_db = fft_mag_to_db(fft_mag_plot)
ref_curve_for_range = None
if complex_sweep_mode:
if active_complex_mode:
visible_abs, visible_real, visible_imag = resolve_visible_fft_curves(
fft_complex_plot,
fft_mag_plot,