implemented averagignd and subtracting avg from B-scan. Control byBG_AVG_WINDOW_SEC = 5.0

BG_SUBTRACT_ALPHA = 1.0
BG_SUBTRACT_ENABLED = True
# Метод расчёта фона: 'median' или 'mean'
BG_BASELINE_METHOD = 'median'
This commit is contained in:
2025-11-20 23:00:51 +03:00
parent be059bad2a
commit 696887b4c7

89
main.py
View File

@ -14,6 +14,7 @@ from datetime import datetime, timedelta
import threading
import queue
from sys import argv
from collections import deque
# ================================================================================
# ПАРАМЕТРЫ И КОНСТАНТЫ
@ -45,6 +46,13 @@ FILES_STORED_N_MAX = 100
# Минимально допустимое число точек F0 для принятия данных
MIN_F0_POINTS = 100
# Усреднение B-scan по времени (фон)
BG_AVG_WINDOW_SEC = 5.0
BG_SUBTRACT_ALPHA = 1.0
BG_SUBTRACT_ENABLED = True
# Метод расчёта фона: 'median' или 'mean'
BG_BASELINE_METHOD = 'median'
# ПЕРЕЧИСЛЕНИЕ ТИПОВ ДАННЫХ
DATA_TYPE_RAW = "RAW"
DATA_TYPE_SYNC_DET = "SYNC_DET"
@ -453,6 +461,13 @@ class DataAnalyzerApp:
self.processed_count = 0
self.skipped_count = 0
# Буфер последних колонок для фонового усреднения B-scan
self.bscan_buffer = deque() # элементов: (np.ndarray col, datetime t, str type)
self.bg_avg_window_sec = BG_AVG_WINDOW_SEC
self.bg_subtract_alpha = BG_SUBTRACT_ALPHA
self.bg_subtract_enabled = BG_SUBTRACT_ENABLED
self.bg_method = BG_BASELINE_METHOD
# ============================================================================
# ИНИЦИАЛИЗАЦИЯ ИНТЕРФЕЙСА
# ============================================================================
@ -854,6 +869,13 @@ class DataAnalyzerApp:
# Собираем данные для отображения
display_cols = []
display_types = [] # Тип каждого столбца
# Предрасчёт фонового усреднения по строкам текущего окна
bg_vec = None
if self.bg_subtract_enabled and self.bscan_buffer:
desired_row_start = row_min
desired_row_end = row_max + 1
bg_vec = self.compute_background_slice(desired_row_start, desired_row_end)
alpha = self.bg_subtract_alpha if self.bg_subtract_enabled else 0.0
for i in range(col_min, col_max):
col_data = self.B_scan_data[i]
col_type = self.B_scan_types[i]
@ -862,7 +884,13 @@ class DataAnalyzerApp:
row_end = min(row_max + 1, len(col_data))
row_start = min(row_min, len(col_data) - 1)
if row_end > row_start:
display_cols.append(col_data[row_start:row_end])
seg = col_data[row_start:row_end]
if bg_vec is not None and col_type != "GAP":
take = min(len(seg), len(bg_vec))
if take > 0 and alpha != 0.0:
seg = seg.copy()
seg[:take] = seg[:take] - alpha * bg_vec[:take]
display_cols.append(seg)
display_types.append(col_type)
display_times = self.B_scan_times[col_min:col_max]
@ -1054,6 +1082,10 @@ class DataAnalyzerApp:
self.B_scan_times.append(current_time)
self.B_scan_types.append(data_type)
self.last_file_time = current_time
# Сохраняем в буфер для фонового усреднения (не добавляем GAP)
if data_type != "GAP":
self.bscan_buffer.append((np.asarray(data_col, dtype=float), current_time, data_type))
self.prune_bscan_buffer(current_time)
return
# Используем переменный интервал между файлами
@ -1084,6 +1116,61 @@ class DataAnalyzerApp:
self.B_scan_times.append(current_time)
self.B_scan_types.append(data_type)
self.last_file_time = current_time
# Сохраняем в буфер для фонового усреднения (не добавляем GAP)
if data_type != "GAP":
self.bscan_buffer.append((np.asarray(data_col, dtype=float), current_time, data_type))
self.prune_bscan_buffer(current_time)
def prune_bscan_buffer(self, now_time: datetime):
"""Удаляет столбцы старше окна усреднения."""
try:
threshold = now_time - timedelta(seconds=self.bg_avg_window_sec)
while self.bscan_buffer and self.bscan_buffer[0][1] < threshold:
self.bscan_buffer.popleft()
except Exception:
pass
def compute_background_slice(self, row_start: int, row_end: int) -> np.ndarray:
"""Пер-строчная статистика (median/mean) по окну последних колонок для строк [row_start:row_end)."""
n_rows = max(0, row_end - row_start)
if n_rows <= 0 or not self.bscan_buffer:
return np.zeros((0,), dtype=float)
# Собираем сегменты для указанного диапазона строк
segments = []
for col, t, tname in self.bscan_buffer:
if col is None:
continue
L = len(col)
if L <= row_start:
continue
take = min(n_rows, L - row_start)
if take <= 0:
continue
seg = np.asarray(col[row_start:row_start + take], dtype=float)
segments.append(seg)
if not segments:
return np.zeros((n_rows,), dtype=float)
# Формируем матрицу (num_cols x n_rows) с NaN, заполняя доступные значения
num_cols = len(segments)
M = np.full((num_cols, n_rows), np.nan, dtype=float)
for i, seg in enumerate(segments):
take = min(n_rows, seg.shape[0])
if take > 0:
M[i, :take] = seg[:take]
# Агрегирование по времени (ось 0), игнорируя NaN
with np.errstate(all='ignore'):
if getattr(self, 'bg_method', 'median') == 'mean':
bg = np.nanmean(M, axis=0)
else:
bg = np.nanmedian(M, axis=0)
# Заменим NaN на 0 (строки без данных в окне)
bg = np.nan_to_num(bg, nan=0.0)
return bg
def compute_fft(self):
"""Вычисляем FFT спектр."""