diff --git a/src/core/data_buffer.py b/src/core/data_buffer.py index bd61960..e7fc6aa 100644 --- a/src/core/data_buffer.py +++ b/src/core/data_buffer.py @@ -43,19 +43,10 @@ class DataBuffer: self.buffer_alarms = [0] * self.line_size self.last_alarm_channels = [] - self.prev_values = [None] * self.line_size - self.trend_streak = [0] * self.line_size - - # Допускаем небольшой обратный ход, чтобы не сбрасываться от микрошума. - self.dbfs_max_backstep_db = float(os.getenv('dbfs_max_backstep_db', 0.25)) - - self.freq_tag = '' if freq_tag is None else str(freq_tag) suffix = f'_{self.freq_tag}' if self.freq_tag else '' - # Параметры MAD-порогов (per-frequency с fallback на общие). self.mad_k_on = float(os.getenv('mad_k_on' + suffix, os.getenv('mad_k_on', 5.0))) - self.mad_k_off = float(os.getenv('mad_k_off' + suffix, os.getenv('mad_k_off', 2.5))) self.mad_eps = float(os.getenv('mad_eps' + suffix, os.getenv('mad_eps', 0.05))) self.dbfs_linear_offset_db = float( os.getenv('dbfs_linear_offset_db' + suffix, os.getenv('dbfs_linear_offset_db', 0.0)) @@ -120,25 +111,24 @@ class DataBuffer: median_value = float(median_value) return self.dbfs_linear_offset_db + self.dbfs_linear_abs_median_scale * abs(median_value) - def get_threshold(self, channel_idx, k=None): + def get_threshold(self, channel_idx): """ Получить динамический порог в dB для канала: - threshold = median + linear_term(median) + k * MAD. + threshold = median + linear_term(median) + mad_k_on * MAD. До завершения инициализации возвращает None. """ if not self.check_init(): return None - coef = self.mad_k_on if k is None else float(k) baseline = float(self.buffer_medians[channel_idx]) - mad = max(float(self.buffer_mads[channel_idx]), self.mad_eps) + mad_eff = max(float(self.buffer_mads[channel_idx]), self.mad_eps) linear_term = self.get_linear_term(baseline) - return baseline + linear_term + coef * mad + return baseline + linear_term + self.mad_k_on * mad_eff - def get_thresholds(self, k=None): + def get_thresholds(self): if not self.check_init(): return [None] * self.line_size - return [self.get_threshold(i, k) for i in range(self.line_size)] + return [self.get_threshold(i) for i in range(self.line_size)] def log_threshold_update(self, updated_column): if not self.check_init(): @@ -152,22 +142,19 @@ class DataBuffer: mad = float(self.buffer_mads[i]) mad_eff = max(mad, self.mad_eps) linear_term = self.get_linear_term(baseline) - threshold_on = self.get_threshold(i, self.mad_k_on) - threshold_off = self.get_threshold(i, self.mad_k_off) + threshold = self.get_threshold(i) packet_times = [self._format_ts(ts) for ts in self.buffer_timestamps[i]] print( f' ch={i} median={baseline:.6f} ' f'linear_term={linear_term:.6f} ' f'mad={mad:.6f} mad_eff={mad_eff:.6f} ' - f'mad_term_on={self.mad_k_on * mad_eff:.6f} mad_term_off={self.mad_k_off * mad_eff:.6f} ' - f'threshold_on={threshold_on:.6f} threshold_off={threshold_off:.6f} ' + f'mad_term={self.mad_k_on * mad_eff:.6f} ' + f'threshold={threshold:.6f} ' f'packet_times={packet_times}' ) def alarms_fill_zeros(self): self.buffer_alarms = [0] * self.line_size - self.trend_streak = [0] * self.line_size - self.prev_values = [None] * self.line_size self.last_alarm_channels = [] def update(self, data, packet_timestamps=None): @@ -215,55 +202,36 @@ class DataBuffer: def check_alarm(self, data): """ Проверка триггера системы по dBFS во времени. - Триггер: превышение динамического MAD-порога - с подтверждением тренда и несколькими последовательными чтениями. + Один порог на канал, набор тревоги и сброс счетчиков как в main. """ if self.check_init(): self.last_alarm_channels = [] for i in range(len(data)): current = data[i] - threshold_on = self.get_threshold(i, self.mad_k_on) - threshold_off = self.get_threshold(i, self.mad_k_off) - - prev = self.prev_values[i] - delta_db = 0.0 if prev is None else current - prev - monotonic_or_stable = (prev is None) or (delta_db >= -self.dbfs_max_backstep_db) - - if monotonic_or_stable: - self.trend_streak[i] += 1 - else: - self.trend_streak[i] = 0 - - # Hysteresis: после начала серии используем более мягкий порог отпускания. - active_threshold = threshold_off if self.buffer_alarms[i] > 0 else threshold_on - exceeding = ( - current >= active_threshold - ) + threshold = self.get_threshold(i) + exceeding = current >= threshold if exceeding: self.buffer_alarms[i] += 1 else: self.buffer_alarms[i] = 0 - self.prev_values[i] = current - if self.buffer_alarms[i] >= self.num_for_alarm: self.last_alarm_channels = [i] self.buffer_alarms = [0] * self.line_size - self.trend_streak = [0] * self.line_size return True return False def check_single_alarm(self, median, cur_channel): """ - Проверка, является ли текущая метрика по каналу превышающей MAD-порог. + Проверка, является ли текущая метрика по каналу превышающей порог. :param median: текущая метрика в dBFS. :param cur_channel: индекс канала внутри частоты. :return: Да/нет. """ if self.check_init(): - threshold_on = self.get_threshold(cur_channel, self.mad_k_on) - return median >= threshold_on + threshold = self.get_threshold(cur_channel) + return median >= threshold return False