updated parsers to be more robust. No changes in functionality
This commit is contained in:
@ -169,22 +169,12 @@ def _compute_auto_ylim(*series_list: Optional[np.ndarray]) -> Optional[Tuple[flo
|
||||
|
||||
|
||||
def calibrate_freqs(sweep: Mapping[str, Any]) -> SweepData:
|
||||
"""Вернуть копию свипа для будущей калибровки частотной оси."""
|
||||
|
||||
"""Вернуть копию свипа с применённой калибровкой частотной оси."""
|
||||
F = np.asarray(sweep["F"], dtype=np.float64).copy()
|
||||
I = np.asarray(sweep["I"], dtype=np.float64).copy()
|
||||
tmp = []
|
||||
C = np.asarray(CALIBRATION_C, dtype=np.float64)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for f in F:
|
||||
val = C[0] + (f**1) * C[1] + (f**2) * C[2]
|
||||
tmp.append(val)
|
||||
F = np.asanyarray(tmp, dtype=np.float64)
|
||||
|
||||
if F.size > 0:
|
||||
F = C[0] + C[1] * F + C[2] * (F * F)
|
||||
|
||||
if F.size >= 2:
|
||||
F_cal = np.linspace(float(F[0]), float(F[-1]), F.size, dtype=np.float64)
|
||||
@ -198,7 +188,6 @@ def calibrate_freqs(sweep: Mapping[str, Any]) -> SweepData:
|
||||
"I": I_cal,
|
||||
}
|
||||
|
||||
|
||||
def _prepare_fft_segment(
|
||||
sweep: np.ndarray,
|
||||
freqs: Optional[np.ndarray],
|
||||
@ -906,6 +895,8 @@ class SweepReader(threading.Thread):
|
||||
ys: list[int] = []
|
||||
cur_channel: Optional[int] = None
|
||||
cur_channels: set[int] = set()
|
||||
expected_step: Optional[int] = None
|
||||
syncing = False
|
||||
words = deque()
|
||||
|
||||
buf = bytearray()
|
||||
@ -927,9 +918,12 @@ class SweepReader(threading.Thread):
|
||||
words.append(w)
|
||||
i += 2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Бинарный протокол:
|
||||
# старт свипа (актуальный): 0xFFFF, 0xFFFF, 0xFFFF, (ch<<8)|0x0A
|
||||
# старт свипа (legacy): 0xFFFF, 0xFFFF, channel, 0x0A0A
|
||||
# старт свипа: 0xFFFF, 0xFFFF, 0xFFFF, (ch<<8)|0x0A
|
||||
# точка: step, value_hi, value_lo, 0x000A
|
||||
while len(words) >= 4:
|
||||
w0 = int(words[0])
|
||||
@ -938,33 +932,48 @@ class SweepReader(threading.Thread):
|
||||
w3 = int(words[3])
|
||||
|
||||
if w0 == 0xFFFF and w1 == 0xFFFF and w2 == 0xFFFF and (w3 & 0x00FF) == 0x000A:
|
||||
if len(words) < 5:
|
||||
break
|
||||
if int(words[4]) != 1:
|
||||
words.popleft()
|
||||
continue
|
||||
self._finalize_current(xs, ys, cur_channels)
|
||||
xs.clear()
|
||||
ys.clear()
|
||||
cur_channels.clear()
|
||||
cur_channel = (w3 >> 8) & 0x00FF
|
||||
cur_channels.add(cur_channel)
|
||||
expected_step = 1
|
||||
syncing = False
|
||||
for _ in range(4):
|
||||
words.popleft()
|
||||
continue
|
||||
|
||||
if w0 == 0xFFFF and w1 == 0xFFFF and w3 == 0x0A0A:
|
||||
self._finalize_current(xs, ys, cur_channels)
|
||||
xs.clear()
|
||||
ys.clear()
|
||||
cur_channels.clear()
|
||||
cur_channel = w2
|
||||
cur_channels.add(cur_channel)
|
||||
for _ in range(4):
|
||||
if syncing:
|
||||
if w0 == 0x000A:
|
||||
syncing = False
|
||||
expected_step = None
|
||||
words.popleft()
|
||||
continue
|
||||
|
||||
if w3 == 0x000A:
|
||||
if w3 != 0x000A:
|
||||
syncing = True
|
||||
continue
|
||||
|
||||
if w0 <= 0:
|
||||
syncing = True
|
||||
continue
|
||||
|
||||
if expected_step is not None and w0 < expected_step:
|
||||
syncing = True
|
||||
continue
|
||||
|
||||
if cur_channel is not None:
|
||||
cur_channels.add(cur_channel)
|
||||
xs.append(w0)
|
||||
value_u32 = (w1 << 16) | w2
|
||||
ys.append(self._u32_to_i32(value_u32))
|
||||
expected_step = w0 + 1
|
||||
for _ in range(4):
|
||||
words.popleft()
|
||||
continue
|
||||
@ -985,6 +994,8 @@ class SweepReader(threading.Thread):
|
||||
avg_2_vals: list[int] = []
|
||||
cur_channel: Optional[int] = None
|
||||
cur_channels: set[int] = set()
|
||||
expected_step: Optional[int] = None
|
||||
syncing = False
|
||||
words = deque()
|
||||
|
||||
buf = bytearray()
|
||||
@ -1025,6 +1036,11 @@ class SweepReader(threading.Thread):
|
||||
and w4 == 0xFFFF
|
||||
and (w5 & 0x00FF) == 0x000A
|
||||
):
|
||||
if len(words) < 7:
|
||||
break
|
||||
if int(words[6]) != 1:
|
||||
words.popleft()
|
||||
continue
|
||||
self._finalize_current(
|
||||
xs,
|
||||
ys,
|
||||
@ -1039,11 +1055,31 @@ class SweepReader(threading.Thread):
|
||||
cur_channels.clear()
|
||||
cur_channel = (w5 >> 8) & 0x00FF
|
||||
cur_channels.add(cur_channel)
|
||||
expected_step = 1
|
||||
syncing = False
|
||||
for _ in range(6):
|
||||
words.popleft()
|
||||
continue
|
||||
|
||||
if w5 == 0x000A:
|
||||
if syncing:
|
||||
if w0 == 0x000A:
|
||||
syncing = False
|
||||
expected_step = None
|
||||
words.popleft()
|
||||
continue
|
||||
|
||||
if w5 != 0x000A:
|
||||
syncing = True
|
||||
continue
|
||||
|
||||
if w0 <= 0:
|
||||
syncing = True
|
||||
continue
|
||||
|
||||
if expected_step is not None and w0 < expected_step:
|
||||
syncing = True
|
||||
continue
|
||||
|
||||
if cur_channel is not None:
|
||||
cur_channels.add(cur_channel)
|
||||
avg_1 = self._u32_to_i32((w1 << 16) | w2)
|
||||
@ -1052,6 +1088,7 @@ class SweepReader(threading.Thread):
|
||||
avg_1_vals.append(avg_1)
|
||||
avg_2_vals.append(avg_2)
|
||||
ys.append(_log_pair_to_sweep(avg_1, avg_2))
|
||||
expected_step = w0 + 1
|
||||
#ys.append(LOG_BASE**(avg_1/LOG_SCALER) - LOG_BASE**(avg_2/LOG_SCALER))
|
||||
for _ in range(6):
|
||||
words.popleft()
|
||||
@ -1078,6 +1115,8 @@ class SweepReader(threading.Thread):
|
||||
avg_2_vals: list[int] = []
|
||||
cur_channel: Optional[int] = None
|
||||
cur_channels: set[int] = set()
|
||||
expected_step: Optional[int] = None
|
||||
syncing = False
|
||||
words = deque()
|
||||
|
||||
buf = bytearray()
|
||||
@ -1098,41 +1137,23 @@ class SweepReader(threading.Thread):
|
||||
w = int(buf[i]) | (int(buf[i + 1]) << 8)
|
||||
words.append(w)
|
||||
i += 2
|
||||
|
||||
#print(i)
|
||||
#print(words)
|
||||
# Бинарный logscale-протокол (16-bit x2):
|
||||
# старт свипа (новый): 0xFFFF, 0xFFFF, (ch<<8)|0x0A
|
||||
# старт свипа (fallback): 0xFFFF, 0xFFFF, 0xFFFF, (ch<<8)|0x0A
|
||||
# старт свипа: 0xFFFF, 0xFFFF, 0xFFFF, (ch<<8)|0x0A
|
||||
# точка: step, avg1_lo16, avg2_lo16, 0x000A
|
||||
while len(words) >= 3:
|
||||
while len(words) >= 4:
|
||||
w0 = int(words[0])
|
||||
w1 = int(words[1])
|
||||
w2 = int(words[2])
|
||||
|
||||
if w0 == 0xFFFF and w1 == 0xFFFF and (w2 & 0x00FF) == 0x000A:
|
||||
self._finalize_current(
|
||||
xs,
|
||||
ys,
|
||||
cur_channels,
|
||||
raw_curves=(avg_1_vals, avg_2_vals),
|
||||
apply_inversion=False,
|
||||
)
|
||||
xs.clear()
|
||||
ys.clear()
|
||||
avg_1_vals.clear()
|
||||
avg_2_vals.clear()
|
||||
cur_channels.clear()
|
||||
cur_channel = (w2 >> 8) & 0x00FF
|
||||
cur_channels.add(cur_channel)
|
||||
for _ in range(3):
|
||||
words.popleft()
|
||||
continue
|
||||
|
||||
if len(words) < 4:
|
||||
break
|
||||
|
||||
w3 = int(words[3])
|
||||
|
||||
if w0 == 0xFFFF and w1 == 0xFFFF and w2 == 0xFFFF and (w3 & 0x00FF) == 0x000A:
|
||||
if len(words) < 5:
|
||||
break
|
||||
if int(words[4]) != 1:
|
||||
words.popleft()
|
||||
continue
|
||||
self._finalize_current(
|
||||
xs,
|
||||
ys,
|
||||
@ -1147,11 +1168,31 @@ class SweepReader(threading.Thread):
|
||||
cur_channels.clear()
|
||||
cur_channel = (w3 >> 8) & 0x00FF
|
||||
cur_channels.add(cur_channel)
|
||||
expected_step = 1
|
||||
syncing = False
|
||||
for _ in range(4):
|
||||
words.popleft()
|
||||
continue
|
||||
|
||||
if w3 == 0x000A:
|
||||
if syncing:
|
||||
if w0 == 0x000A:
|
||||
syncing = False
|
||||
expected_step = None
|
||||
words.popleft()
|
||||
continue
|
||||
|
||||
if w3 != 0x000A:
|
||||
syncing = True
|
||||
continue
|
||||
|
||||
if w0 <= 0:
|
||||
syncing = True
|
||||
continue
|
||||
|
||||
if expected_step is not None and w0 < expected_step:
|
||||
syncing = True
|
||||
continue
|
||||
|
||||
if cur_channel is not None:
|
||||
cur_channels.add(cur_channel)
|
||||
avg_1 = self._u16_to_i16(w1)
|
||||
@ -1160,6 +1201,7 @@ class SweepReader(threading.Thread):
|
||||
avg_1_vals.append(avg_1)
|
||||
avg_2_vals.append(avg_2)
|
||||
ys.append(_log_pair_to_sweep(avg_1, avg_2))
|
||||
expected_step = w0 + 1
|
||||
for _ in range(4):
|
||||
words.popleft()
|
||||
continue
|
||||
@ -1283,7 +1325,7 @@ def main():
|
||||
action="store_true",
|
||||
help=(
|
||||
"Бинарный logscale-протокол c парой int16 (avg_1, avg_2): "
|
||||
"старт 0xFFFF,0xFFFF,(CH<<8)|0x0A; точка step,avg1_lo16,avg2_lo16,0x000A"
|
||||
"старт 0xFFFF,0xFFFF,0xFFFF,(CH<<8)|0x0A; точка step,avg1_lo16,avg2_lo16,0x000A"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
@ -1348,6 +1390,7 @@ def main():
|
||||
current_sweep_raw: Optional[np.ndarray] = None
|
||||
current_aux_curves: SweepAuxCurves = None
|
||||
current_sweep_norm: Optional[np.ndarray] = None
|
||||
current_fft_db: Optional[np.ndarray] = None
|
||||
last_calib_sweep: Optional[np.ndarray] = None
|
||||
current_info: Optional[SweepInfo] = None
|
||||
x_shared: Optional[np.ndarray] = None
|
||||
@ -2195,6 +2238,7 @@ def run_pyqtgraph(args):
|
||||
current_sweep_raw: Optional[np.ndarray] = None
|
||||
current_aux_curves: SweepAuxCurves = None
|
||||
current_sweep_norm: Optional[np.ndarray] = None
|
||||
current_fft_db: Optional[np.ndarray] = None
|
||||
last_calib_sweep: Optional[np.ndarray] = None
|
||||
current_info: Optional[SweepInfo] = None
|
||||
# Авто-уровни цветовой шкалы водопада сырых данных пересчитываются по видимой области.
|
||||
@ -2212,6 +2256,7 @@ def run_pyqtgraph(args):
|
||||
norm_type = str(getattr(args, "norm_type", "projector")).strip().lower()
|
||||
c_edits = []
|
||||
c_value_labels = []
|
||||
plot_dirty = False
|
||||
# Диапазон по Y: авто по умолчанию (поддерживает отрицательные значения)
|
||||
fixed_ylim: Optional[Tuple[float, float]] = None
|
||||
if args.ylim:
|
||||
@ -2227,7 +2272,7 @@ def run_pyqtgraph(args):
|
||||
return _normalize_by_calib(raw, calib, norm_type=norm_type)
|
||||
|
||||
def _set_calib_enabled():
|
||||
nonlocal calib_enabled, current_sweep_norm
|
||||
nonlocal calib_enabled, current_sweep_norm, plot_dirty
|
||||
try:
|
||||
calib_enabled = bool(calib_cb.isChecked())
|
||||
except Exception:
|
||||
@ -2236,13 +2281,15 @@ def run_pyqtgraph(args):
|
||||
current_sweep_norm = _normalize_sweep(current_sweep_raw, last_calib_sweep)
|
||||
else:
|
||||
current_sweep_norm = None
|
||||
plot_dirty = True
|
||||
|
||||
def _set_bg_subtract_enabled():
|
||||
nonlocal bg_subtract_enabled
|
||||
nonlocal bg_subtract_enabled, plot_dirty
|
||||
try:
|
||||
bg_subtract_enabled = bool(bg_subtract_cb.isChecked())
|
||||
except Exception:
|
||||
bg_subtract_enabled = False
|
||||
plot_dirty = True
|
||||
|
||||
try:
|
||||
calib_cb.stateChanged.connect(lambda _v: _set_calib_enabled())
|
||||
@ -2426,7 +2473,7 @@ def run_pyqtgraph(args):
|
||||
return np.nan_to_num(bg_spec, nan=0.0).astype(np.float32, copy=False)
|
||||
|
||||
def push_sweep(s: np.ndarray, freqs: Optional[np.ndarray] = None):
|
||||
nonlocal ring, ring_time, head, ring_fft, y_min_fft, y_max_fft
|
||||
nonlocal ring, ring_time, head, ring_fft, y_min_fft, y_max_fft, current_fft_db
|
||||
if s is None or s.size == 0 or ring is None:
|
||||
return
|
||||
w = ring.shape[1]
|
||||
@ -2443,6 +2490,7 @@ def run_pyqtgraph(args):
|
||||
fft_mag = _compute_fft_mag_row(s, freqs, bins)
|
||||
ring_fft[(head - 1) % ring_fft.shape[0], :] = fft_mag
|
||||
fft_row = _fft_mag_to_db(fft_mag)
|
||||
current_fft_db = fft_row
|
||||
fr_min = np.nanmin(fft_row)
|
||||
fr_max = np.nanmax(fft_row)
|
||||
if y_min_fft is None or (not np.isnan(fr_min) and fr_min < y_min_fft):
|
||||
@ -2488,8 +2536,9 @@ def run_pyqtgraph(args):
|
||||
current_sweep_norm = None
|
||||
sweep_for_proc = s
|
||||
ensure_buffer(s.size)
|
||||
_update_physical_axes()
|
||||
push_sweep(sweep_for_proc, current_freqs)
|
||||
if drained > 0:
|
||||
_update_physical_axes()
|
||||
return drained
|
||||
|
||||
# Попытка применить LUT из колормэпа (если доступен)
|
||||
@ -2504,11 +2553,12 @@ def run_pyqtgraph(args):
|
||||
pass
|
||||
|
||||
def update():
|
||||
nonlocal current_peak_width
|
||||
nonlocal current_peak_width, plot_dirty, current_fft_db
|
||||
if peak_calibrate_mode and any(edit.hasFocus() for edit in c_edits):
|
||||
return
|
||||
changed = drain_queue() > 0
|
||||
if current_sweep_raw is not None and x_shared is not None:
|
||||
redraw_needed = changed or plot_dirty
|
||||
if redraw_needed and current_sweep_raw is not None and x_shared is not None:
|
||||
if current_freqs is not None and current_freqs.size == current_sweep_raw.size:
|
||||
xs = current_freqs
|
||||
elif current_sweep_raw.size <= x_shared.size:
|
||||
@ -2546,7 +2596,13 @@ def run_pyqtgraph(args):
|
||||
# Обновим спектр
|
||||
sweep_for_fft = current_sweep_norm if current_sweep_norm is not None else current_sweep_raw
|
||||
if sweep_for_fft.size > 0 and distance_shared is not None:
|
||||
fft_vals = _compute_fft_row(sweep_for_fft, current_freqs, distance_shared.size)
|
||||
if (
|
||||
current_fft_db is None
|
||||
or current_fft_db.size != distance_shared.size
|
||||
or plot_dirty
|
||||
):
|
||||
current_fft_db = _compute_fft_row(sweep_for_fft, current_freqs, distance_shared.size)
|
||||
fft_vals = current_fft_db
|
||||
xs_fft = current_distances if current_distances is not None else distance_shared
|
||||
if fft_vals.size > xs_fft.size:
|
||||
fft_vals = fft_vals[: xs_fft.size]
|
||||
@ -2583,6 +2639,7 @@ def run_pyqtgraph(args):
|
||||
spec_left_line.setVisible(False)
|
||||
spec_right_line.setVisible(False)
|
||||
current_peak_width = None
|
||||
plot_dirty = False
|
||||
|
||||
if changed and ring is not None:
|
||||
disp = ring if head == 0 else np.roll(ring, -head, axis=0)
|
||||
|
||||
Reference in New Issue
Block a user