diff --git a/rfg_adc_plotter/visualization/matplotlib_backend.py b/rfg_adc_plotter/visualization/matplotlib_backend.py index d87180b..160e3e0 100644 --- a/rfg_adc_plotter/visualization/matplotlib_backend.py +++ b/rfg_adc_plotter/visualization/matplotlib_backend.py @@ -66,8 +66,11 @@ def run_matplotlib(args): ref_out_file = getattr(args, 'ref_out', None) ref_in_file = getattr(args, 'ref_in', None) ref_out_saved = False # Флаг, что медиана уже сохранена + # Отдельный буфер для накопления 1000 сырых свипов (не зависит от max_sweeps) + ref_ring: Optional[np.ndarray] = None + ref_ring_head = 0 + ref_ring_count = 0 - # DEBUG: Проверяем что параметр установлен if ref_out_file: print(f"[ref-out] Автосохранение включено, файл: {ref_out_file}") @@ -238,6 +241,7 @@ def run_matplotlib(args): def ensure_buffer(_w: int): nonlocal ring, width, head, x_shared, ring_fft, freq_shared, ring_time nonlocal ring_phase, prev_phase_per_bin, phase_offset_per_bin + nonlocal ref_ring if ring is not None: return width = WF_WIDTH @@ -265,6 +269,9 @@ def run_matplotlib(args): img_phase_obj.set_extent((0, max_sweeps - 1, FREQ_MIN_GHZ, FREQ_MAX_GHZ)) ax_phase_wf.set_xlim(0, max_sweeps - 1) ax_phase_wf.set_ylim(FREQ_MIN_GHZ, FREQ_MAX_GHZ) + # Буфер для медианы (отдельный от ring, размер всегда 1000) + if ref_out_file and ref_ring is None: + ref_ring = np.full((1000, width), np.nan, dtype=np.float32) def _visible_levels_matplotlib(data: np.ndarray, axis) -> Optional[Tuple[float, float]]: """(vmin, vmax) по текущей видимой области imshow (без накопления по времени).""" @@ -301,9 +308,18 @@ def run_matplotlib(args): def push_sweep(s: np.ndarray): nonlocal ring, head, ring_fft, y_min_fft, y_max_fft, ring_time nonlocal ring_phase, prev_phase_per_bin, phase_offset_per_bin, y_min_phase, y_max_phase + nonlocal ref_ring_head, ref_ring_count if s is None or s.size == 0 or ring is None: return + # Сохраняем сырой свип в буфер медианы (до вычитания) + if ref_out_file and not ref_out_saved and ref_ring is not None: + w_ref = ref_ring.shape[1] + take_ref = min(w_ref, s.size) + ref_ring[ref_ring_head, :take_ref] = s[:take_ref] + ref_ring_head = (ref_ring_head + 1) % 1000 + ref_ring_count = min(ref_ring_count + 1, 1000) + # Применяем вычитание медианы если включено if median_subtract_enabled and median_data is not None: take_median = min(s.size, median_data.size) @@ -594,26 +610,13 @@ def run_matplotlib(args): if changed and current_info: status_text.set_text(format_status_kv(current_info)) - # Автоматическое сохранение медианы при накоплении 1000+ свипов - # DEBUG: Проверяем условия каждые 100 кадров - if _frame is not None and _frame % 100 == 0: - print(f"[ref-out DEBUG] _frame={_frame}, ref_out_file={bool(ref_out_file)}, ref_out_saved={ref_out_saved}, ring={'OK' if ring is not None else 'None'}") - - if ref_out_file and not ref_out_saved and ring is not None: - filled_count = np.count_nonzero(~np.isnan(ring[:, 0])) - # DEBUG: Выводим прогресс каждые 100 свипов - if filled_count % 100 == 0 and filled_count > 0: - print(f"[ref-out] Прогресс: {filled_count}/1000 свипов накоплено") - if filled_count >= 1000: - print(f"[ref-out] Достигнуто 1000 свипов, начинаем сохранение...") + # Автоматическое сохранение медианы при накоплении 1000 сырых свипов + if ref_out_file and not ref_out_saved and ref_ring is not None: + if ref_ring_count >= 1000: try: - # Получаем последние 1000 свипов - ordered = ring if head == 0 else np.roll(ring, -head, axis=0) - recent_sweeps = ordered[-1000:, :] - median_sweep = np.nanmedian(recent_sweeps, axis=0) - print(f"[ref-out] Медиана вычислена, размер: {len(median_sweep)}") + ordered = ref_ring if ref_ring_head == 0 else np.roll(ref_ring, -ref_ring_head, axis=0) + median_sweep = np.nanmedian(ordered, axis=0) - # Сохраняем в файл with open(ref_out_file, 'w', newline='') as f: writer = csv.writer(f) writer.writerow(['Index', 'Median_Value']) diff --git a/rfg_adc_plotter/visualization/pyqtgraph_backend.py b/rfg_adc_plotter/visualization/pyqtgraph_backend.py index 1595d75..807a2a8 100644 --- a/rfg_adc_plotter/visualization/pyqtgraph_backend.py +++ b/rfg_adc_plotter/visualization/pyqtgraph_backend.py @@ -281,6 +281,10 @@ def run_pyqtgraph(args): ref_out_file = getattr(args, 'ref_out', None) ref_in_file = getattr(args, 'ref_in', None) ref_out_saved = False # Флаг, что медиана уже сохранена + # Отдельный буфер для накопления 1000 сырых свипов (не зависит от max_sweeps) + ref_ring: Optional[np.ndarray] = None + ref_ring_head = 0 + ref_ring_count = 0 # Автоматическая загрузка медианы при старте if ref_in_file: @@ -331,6 +335,7 @@ def run_pyqtgraph(args): def ensure_buffer(_w: int): nonlocal ring, head, width, x_shared, ring_fft, freq_shared nonlocal ring_phase, prev_phase_per_bin, phase_offset_per_bin + nonlocal ref_ring if ring is not None: return width = WF_WIDTH @@ -354,6 +359,9 @@ def run_pyqtgraph(args): img_phase.setImage(ring_phase.T, autoLevels=False) p_phase_wf.setRange(xRange=(0, max_sweeps - 1), yRange=(0, max(1, fft_bins - 1)), padding=0) p_phase.setXRange(0, max(1, fft_bins - 1), padding=0) + # Буфер для медианы (отдельный от ring, размер всегда 1000) + if ref_out_file and ref_ring is None: + ref_ring = np.full((1000, width), np.nan, dtype=np.float32) def _visible_levels_pyqtgraph(data: np.ndarray) -> Optional[Tuple[float, float]]: """(vmin, vmax) по текущей видимой области ImageItem (без накопления по времени).""" @@ -389,9 +397,18 @@ def run_pyqtgraph(args): def push_sweep(s: np.ndarray): nonlocal ring, head, ring_fft, y_min_fft, y_max_fft nonlocal ring_phase, prev_phase_per_bin, phase_offset_per_bin, y_min_phase, y_max_phase + nonlocal ref_ring_head, ref_ring_count if s is None or s.size == 0 or ring is None: return + # Сохраняем сырой свип в буфер медианы (до вычитания) + if ref_out_file and not ref_out_saved and ref_ring is not None: + w_ref = ref_ring.shape[1] + take_ref = min(w_ref, s.size) + ref_ring[ref_ring_head, :take_ref] = s[:take_ref] + ref_ring_head = (ref_ring_head + 1) % 1000 + ref_ring_count = min(ref_ring_count + 1, 1000) + # Применяем вычитание медианы если включено if median_subtract_enabled and median_data is not None: # Вычитаем медиану из сигнала @@ -593,17 +610,13 @@ def run_pyqtgraph(args): except Exception: pass - # Автоматическое сохранение медианы при накоплении 1000+ свипов - if ref_out_file and not ref_out_saved and ring is not None: - filled_count = np.count_nonzero(~np.isnan(ring[:, 0])) - if filled_count >= 1000: + # Автоматическое сохранение медианы при накоплении 1000 сырых свипов + if ref_out_file and not ref_out_saved and ref_ring is not None: + if ref_ring_count >= 1000: try: - # Получаем последние 1000 свипов - ordered = ring if head == 0 else np.roll(ring, -head, axis=0) - recent_sweeps = ordered[-1000:, :] - median_sweep = np.nanmedian(recent_sweeps, axis=0) + ordered = ref_ring if ref_ring_head == 0 else np.roll(ref_ring, -ref_ring_head, axis=0) + median_sweep = np.nanmedian(ordered, axis=0) - # Сохраняем в файл with open(ref_out_file, 'w', newline='') as f: writer = csv.writer(f) writer.writerow(['Index', 'Median_Value'])