@ -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 ) + ( 1 j * 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 )
if bin_iq_power_mode :
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 ) + ( 1 j * 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 ,
)
else :
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