|
|
|
|
@ -1,170 +1,258 @@
|
|
|
|
|
import statistics
|
|
|
|
|
|
|
|
|
|
# Более лучшая версия кода есть в FRScanner
|
|
|
|
|
|
|
|
|
|
class DataBuffer:
|
|
|
|
|
"""
|
|
|
|
|
Класс с реализацией циклического буффера.
|
|
|
|
|
|
|
|
|
|
Атрибуты:
|
|
|
|
|
current_column: Указатель на текущий столбец буфера, который обновляем.
|
|
|
|
|
thinning_counter: Прореживающий множитель на текующей итерации.
|
|
|
|
|
current_counter: Указатель на количество чтений между последним обновлением столбца и предыдущим атрибутом.
|
|
|
|
|
num_of_thinning_iter: Прореживающий множитель. Раз в это количечество раз будет обнволяться столбец буфера.
|
|
|
|
|
line_size: Количество строк буфера = количеству каналов.
|
|
|
|
|
columns_size: Количество столбцов = фиксированное число.
|
|
|
|
|
multiply_factor: Процентный показатель превышения сигналом уровня шума. ex m_p = 1.1 => триггер, если
|
|
|
|
|
сигнал превышает шум на 10%.
|
|
|
|
|
num_for_alarm: Количество раз, превышающих шум, при которых триггеримся = фиксированное число.
|
|
|
|
|
is_init: Флаг инициализации буфера. = True, если инициализирован.
|
|
|
|
|
buffer: Массив для буфера.
|
|
|
|
|
buffer_medians: Массив для медиан столбцов букера.
|
|
|
|
|
buffer_alarms: Массив для количества тревог по столбца буфера.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, columns_size, num_of_thinning_iter, num_of_channels, multiply_factor, num_for_alarm):
|
|
|
|
|
"""
|
|
|
|
|
Инициализируем класс.
|
|
|
|
|
|
|
|
|
|
:param columns_size:
|
|
|
|
|
:param num_of_thinning_iter:
|
|
|
|
|
:param num_of_channels:
|
|
|
|
|
:param multiply_factor:
|
|
|
|
|
:param num_for_alarm:
|
|
|
|
|
"""
|
|
|
|
|
self.current_column = 0
|
|
|
|
|
self.thinning_counter = 1
|
|
|
|
|
self.current_counter = 1
|
|
|
|
|
self.num_of_thinning_iter = num_of_thinning_iter
|
|
|
|
|
self.line_size = num_of_channels
|
|
|
|
|
self.columns_size = columns_size
|
|
|
|
|
self.multiply_factor = multiply_factor
|
|
|
|
|
self.num_for_alarm = num_for_alarm
|
|
|
|
|
self.is_init = False
|
|
|
|
|
self.buffer = [[0 for _ in range(self.columns_size)] for _ in range(self.line_size)]
|
|
|
|
|
self.buffer_medians = [0] * self.line_size
|
|
|
|
|
self.buffer_alarms = [0] * self.line_size
|
|
|
|
|
|
|
|
|
|
def get_buffer(self):
|
|
|
|
|
return self.buffer
|
|
|
|
|
|
|
|
|
|
def get_medians(self):
|
|
|
|
|
return self.buffer_medians
|
|
|
|
|
|
|
|
|
|
def get_alarms(self):
|
|
|
|
|
return self.buffer_alarms
|
|
|
|
|
|
|
|
|
|
def check_init(self):
|
|
|
|
|
return self.is_init
|
|
|
|
|
|
|
|
|
|
def print(self):
|
|
|
|
|
print('buffer is: ')
|
|
|
|
|
for i in range(self.line_size):
|
|
|
|
|
print(self.buffer[i], end=' ')
|
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
def medians(self):
|
|
|
|
|
"""
|
|
|
|
|
Вычислить медиану по строке буфера.
|
|
|
|
|
:return: None
|
|
|
|
|
"""
|
|
|
|
|
if self.check_init():
|
|
|
|
|
for i in range(self.line_size):
|
|
|
|
|
self.buffer_medians[i] = statistics.median(self.buffer[i])
|
|
|
|
|
# print('medians is: ', self.buffer_medians)
|
|
|
|
|
# return self.buffer_medians
|
|
|
|
|
|
|
|
|
|
def alarms_fill_zeros(self):
|
|
|
|
|
self.buffer_alarms = [0] * self.line_size
|
|
|
|
|
def update(self, data):
|
|
|
|
|
"""
|
|
|
|
|
Обновление буфера.
|
|
|
|
|
Если номер текущего чтения совпадает с количеством прореживающего множителя на текущем обновлении буфера, то
|
|
|
|
|
1. Обновляем буфер.
|
|
|
|
|
2. Двигаем курсор на след столбец. Если был последний столбец, то двигаем курсор в начало.
|
|
|
|
|
3. Берем медианы по буферу, если он уже проиницализирован.
|
|
|
|
|
4. Сбрасываем счетчик текущих чтений.
|
|
|
|
|
5. Если был последний столбец (и мы уже переключились на первый), то
|
|
|
|
|
Если прореживающий множитель на текующей итерации был единица, то мы иницилизировались
|
|
|
|
|
До тех пор, пока множитель на итерации меньше фиксированного, увеличиваем в два раза.
|
|
|
|
|
В противном случае - увеличиваем номер чтения.
|
|
|
|
|
:param data: Массив с метриками сигнала по каналам.
|
|
|
|
|
:return: None
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# TODO: Добавить время релаксации - если система затриггерилась, то перестать обновлять буфер на N чтений,
|
|
|
|
|
# где N задается в .env-template. Сейчас есть бага, что буфер перестает обновляться только когда система
|
|
|
|
|
# триггерится. Между тем, когда приходит аларм и num_for_alarm, когда сигнал алармовский, но система еще не
|
|
|
|
|
# триггерится, буфер продолжает обновляться. В таких условиях буфер может набрать в себя алармовских сигналов
|
|
|
|
|
# и повысить пороги. Пример такой ситуации: дрон висит на 1км, система его видит, но сигнал превышает порог раз
|
|
|
|
|
# через раз и аларм срабатывает не всегда. В таких условиях наберется высокий сигнал, повысятся пороги и когда
|
|
|
|
|
# дрон начнет движение вперед, он будет заметен на более низкой дистанции, чем обычно, так как пороги повышены.
|
|
|
|
|
|
|
|
|
|
if self.current_counter == self.thinning_counter:
|
|
|
|
|
for i in range(self.line_size):
|
|
|
|
|
self.buffer[i][self.current_column] = data[i]
|
|
|
|
|
self.current_column = (self.current_column + 1) % self.columns_size
|
|
|
|
|
#print('Столбец {0} обновлен. Перешли к столбцу {1}: '.format(self.current_column - 1, self.current_column))
|
|
|
|
|
self.medians()
|
|
|
|
|
self.current_counter = 1
|
|
|
|
|
if self.current_column == 0:
|
|
|
|
|
if self.thinning_counter == 1:
|
|
|
|
|
self.is_init = True
|
|
|
|
|
self.medians()
|
|
|
|
|
print('Начальная калибровка завершена.')
|
|
|
|
|
if self.thinning_counter < self.num_of_thinning_iter:
|
|
|
|
|
self.thinning_counter *= 2
|
|
|
|
|
# print('thinning counter обновлен: ', self.thinning_counter)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
self.current_counter += 1
|
|
|
|
|
# print('curr counter обновлен: ', self.current_counter)
|
|
|
|
|
|
|
|
|
|
def check_alarm(self, data):
|
|
|
|
|
"""
|
|
|
|
|
Проверка триггера системы.
|
|
|
|
|
Если значение по каналу превышает медиану (порог) шума на какой-то процент, то инкремент буфер аларма по каналу.
|
|
|
|
|
Превышение num_for_alarm подряд - триггер. Если после n превышений, где n<num_for_alarm приходит сигнал не
|
|
|
|
|
первышающий порог, то сбрасываем буфер алармов.
|
|
|
|
|
:param data:
|
|
|
|
|
:return: Да/нет.
|
|
|
|
|
"""
|
|
|
|
|
if self.check_init():
|
|
|
|
|
ratios=[]
|
|
|
|
|
print("="*50)
|
|
|
|
|
for i in range(len(data)):
|
|
|
|
|
exceeding = data[i] > self.multiply_factor * self.buffer_medians[i]
|
|
|
|
|
ratios.append(data[i]/self.buffer_medians[i])
|
|
|
|
|
if exceeding:
|
|
|
|
|
self.buffer_alarms[i] += 1
|
|
|
|
|
# print('Инкремент буффер алармов по каналу {0}, текущее число по этому каналу: {1}'.format(i,self.buffer_alarms[i]))
|
|
|
|
|
else:
|
|
|
|
|
self.buffer_alarms[i] = 0
|
|
|
|
|
# print('Обнулили буффер алармов по каналу {0}, текущее число по этому каналу: {1}'.format(i,self.buffer_alarms[i]))
|
|
|
|
|
|
|
|
|
|
if self.buffer_alarms[i] >= self.num_for_alarm:
|
|
|
|
|
# print('Сработала тревога по каналу {0}, текущее число по этому каналу: {1}'.format(i,self.buffer_alarms[i]))
|
|
|
|
|
self.buffer_alarms = [0] * self.line_size
|
|
|
|
|
print("Отношения:", [f"{r:.3f}" for r in ratios])
|
|
|
|
|
print("!"*50)
|
|
|
|
|
return True
|
|
|
|
|
print("Отношения:", [f"{r:.3f}" for r in ratios])
|
|
|
|
|
print("="*50)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def check_single_alarm(self, median, cur_channel):
|
|
|
|
|
"""
|
|
|
|
|
Проверка, является ли текущая медиана по каналу превышающей порог.
|
|
|
|
|
:param median: меди (хар-ка) по каналу.
|
|
|
|
|
:param cur_channel: индекс канала внутри частоты.
|
|
|
|
|
:return: Да/нет.
|
|
|
|
|
"""
|
|
|
|
|
if self.check_init():
|
|
|
|
|
exceeding = median > self.multiply_factor * self.buffer_medians[cur_channel]
|
|
|
|
|
print(median/self.buffer_medians[cur_channel])
|
|
|
|
|
if exceeding:
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
return False
|
|
|
|
|
import os
|
|
|
|
|
import statistics
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
# Более лучшая версия кода есть в FRScanner
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DataBuffer:
|
|
|
|
|
"""
|
|
|
|
|
Класс с реализацией циклического буффера.
|
|
|
|
|
|
|
|
|
|
Атрибуты:
|
|
|
|
|
current_column: Указатель на текущий столбец буфера, который обновляем.
|
|
|
|
|
thinning_counter: Прореживающий множитель на текующей итерации.
|
|
|
|
|
current_counter: Указатель на количество чтений между последним обновлением столбца и предыдущим атрибутом.
|
|
|
|
|
num_of_thinning_iter: Прореживающий множитель. Раз в это количество раз будет обновляться столбец буфера.
|
|
|
|
|
line_size: Количество строк буфера = количеству каналов.
|
|
|
|
|
columns_size: Количество столбцов = фиксированное число.
|
|
|
|
|
multiply_factor: Процентный показатель превышения сигналом уровня шума (legacy).
|
|
|
|
|
num_for_alarm: Количество раз, превышающих шум, при которых триггеримся.
|
|
|
|
|
is_init: Флаг инициализации буфера.
|
|
|
|
|
buffer: Массив для буфера.
|
|
|
|
|
buffer_medians: Массив медиан по каналам.
|
|
|
|
|
buffer_mads: Массив MAD по каналам.
|
|
|
|
|
buffer_alarms: Массив для количества тревог по каналам.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, columns_size, num_of_thinning_iter, num_of_channels, multiply_factor, num_for_alarm, freq_tag=None):
|
|
|
|
|
self.current_column = 0
|
|
|
|
|
self.thinning_counter = 1
|
|
|
|
|
self.current_counter = 1
|
|
|
|
|
self.num_of_thinning_iter = num_of_thinning_iter
|
|
|
|
|
self.line_size = num_of_channels
|
|
|
|
|
self.columns_size = columns_size
|
|
|
|
|
self.multiply_factor = multiply_factor
|
|
|
|
|
self.num_for_alarm = num_for_alarm
|
|
|
|
|
self.is_init = False
|
|
|
|
|
|
|
|
|
|
self.buffer = [[0 for _ in range(self.columns_size)] for _ in range(self.line_size)]
|
|
|
|
|
self.buffer_timestamps = [[None for _ in range(self.columns_size)] for _ in range(self.line_size)]
|
|
|
|
|
self.buffer_medians = [0.0] * self.line_size
|
|
|
|
|
self.buffer_mads = [0.0] * self.line_size
|
|
|
|
|
self.buffer_alarms = [0] * self.line_size
|
|
|
|
|
self.last_alarm_channels = []
|
|
|
|
|
self.episode_history = [[0.0 for _ in range(self.num_of_thinning_iter)] for _ in range(self.line_size)]
|
|
|
|
|
self.episode_history_timestamps = [[None for _ in range(self.num_of_thinning_iter)] for _ in range(self.line_size)]
|
|
|
|
|
|
|
|
|
|
self.freq_tag = '' if freq_tag is None else str(freq_tag)
|
|
|
|
|
suffix = f'_{self.freq_tag}' if self.freq_tag else ''
|
|
|
|
|
|
|
|
|
|
self.mad_k_on = float(os.getenv('mad_k_on' + suffix, os.getenv('mad_k_on', 5.0)))
|
|
|
|
|
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))
|
|
|
|
|
)
|
|
|
|
|
self.dbfs_linear_abs_median_scale = float(
|
|
|
|
|
os.getenv('dbfs_linear_abs_median_scale' + suffix, os.getenv('dbfs_linear_abs_median_scale', 0.0))
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def get_buffer(self):
|
|
|
|
|
return self.buffer
|
|
|
|
|
|
|
|
|
|
def get_timestamps(self):
|
|
|
|
|
return self.buffer_timestamps
|
|
|
|
|
|
|
|
|
|
def get_medians(self):
|
|
|
|
|
return self.buffer_medians
|
|
|
|
|
|
|
|
|
|
def get_mads(self):
|
|
|
|
|
return self.buffer_mads
|
|
|
|
|
|
|
|
|
|
def get_alarms(self):
|
|
|
|
|
return self.buffer_alarms
|
|
|
|
|
|
|
|
|
|
def get_last_alarm_channels(self):
|
|
|
|
|
return list(self.last_alarm_channels)
|
|
|
|
|
|
|
|
|
|
def check_init(self):
|
|
|
|
|
return self.is_init
|
|
|
|
|
|
|
|
|
|
def print(self):
|
|
|
|
|
print('buffer is: ')
|
|
|
|
|
for i in range(self.line_size):
|
|
|
|
|
print(self.buffer[i], end=' ')
|
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _calc_mad(values, median):
|
|
|
|
|
deviations = [abs(v - median) for v in values]
|
|
|
|
|
return statistics.median(deviations)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _format_ts(timestamp):
|
|
|
|
|
if timestamp is None:
|
|
|
|
|
return 'None'
|
|
|
|
|
try:
|
|
|
|
|
return datetime.fromtimestamp(float(timestamp)).isoformat(sep=' ', timespec='milliseconds')
|
|
|
|
|
except Exception:
|
|
|
|
|
return str(timestamp)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _mean_timestamp(timestamps):
|
|
|
|
|
filtered = [float(ts) for ts in timestamps if ts is not None]
|
|
|
|
|
if not filtered:
|
|
|
|
|
return None
|
|
|
|
|
return sum(filtered) / len(filtered)
|
|
|
|
|
|
|
|
|
|
def medians(self):
|
|
|
|
|
"""
|
|
|
|
|
Вычислить медиану и MAD по строкам буфера.
|
|
|
|
|
:return: None
|
|
|
|
|
"""
|
|
|
|
|
if self.check_init():
|
|
|
|
|
for i in range(self.line_size):
|
|
|
|
|
med = float(statistics.median(self.buffer[i]))
|
|
|
|
|
self.buffer_medians[i] = med
|
|
|
|
|
self.buffer_mads[i] = float(self._calc_mad(self.buffer[i], med))
|
|
|
|
|
|
|
|
|
|
def get_linear_term(self, median_value):
|
|
|
|
|
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):
|
|
|
|
|
"""
|
|
|
|
|
Получить динамический порог в dB для канала:
|
|
|
|
|
threshold = median + linear_term(median) + mad_k_on * MAD.
|
|
|
|
|
До завершения инициализации возвращает None.
|
|
|
|
|
"""
|
|
|
|
|
if not self.check_init():
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
baseline = float(self.buffer_medians[channel_idx])
|
|
|
|
|
mad_eff = max(float(self.buffer_mads[channel_idx]), self.mad_eps)
|
|
|
|
|
linear_term = self.get_linear_term(baseline)
|
|
|
|
|
return baseline + linear_term + self.mad_k_on * mad_eff
|
|
|
|
|
|
|
|
|
|
def get_thresholds(self):
|
|
|
|
|
if not self.check_init():
|
|
|
|
|
return [None] * 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():
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
now_str = datetime.now().isoformat(sep=' ', timespec='milliseconds')
|
|
|
|
|
freq_tag = self.freq_tag or 'unknown'
|
|
|
|
|
print(f'[threshold-update][{freq_tag}] now={now_str} updated_column={updated_column}')
|
|
|
|
|
for i in range(self.line_size):
|
|
|
|
|
baseline = float(self.buffer_medians[i])
|
|
|
|
|
mad = float(self.buffer_mads[i])
|
|
|
|
|
mad_eff = max(mad, self.mad_eps)
|
|
|
|
|
linear_term = self.get_linear_term(baseline)
|
|
|
|
|
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={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.last_alarm_channels = []
|
|
|
|
|
|
|
|
|
|
def update(self, data, packet_timestamps=None):
|
|
|
|
|
"""
|
|
|
|
|
Обновление буфера.
|
|
|
|
|
Если номер текущего чтения совпадает с количеством прореживающего множителя на текущем обновлении буфера, то
|
|
|
|
|
1. Обновляем буфер.
|
|
|
|
|
2. Двигаем курсор на след столбец. Если был последний столбец, то двигаем курсор в начало.
|
|
|
|
|
3. Берем медианы по буферу, если он уже проиницализирован.
|
|
|
|
|
4. Сбрасываем счетчик текущих чтений.
|
|
|
|
|
5. Если был последний столбец (и мы уже переключились на первый), то
|
|
|
|
|
Если прореживающий множитель на текующей итерации был единица, то мы иницилизировались.
|
|
|
|
|
До тех пор, пока множитель на итерации меньше фиксированного, увеличиваем в два раза.
|
|
|
|
|
В противном случае - увеличиваем номер чтения.
|
|
|
|
|
:param data: Массив с метриками сигнала по каналам.
|
|
|
|
|
:param packet_timestamps: Времена пакетов SDR для каждой метрики канала.
|
|
|
|
|
:return: None
|
|
|
|
|
"""
|
|
|
|
|
if packet_timestamps is None:
|
|
|
|
|
packet_timestamps = [None] * self.line_size
|
|
|
|
|
if len(packet_timestamps) != self.line_size:
|
|
|
|
|
raise ValueError('packet_timestamps length must match number of channels')
|
|
|
|
|
|
|
|
|
|
sample_idx = self.current_counter - 1
|
|
|
|
|
for i in range(self.line_size):
|
|
|
|
|
self.episode_history[i][sample_idx] = float(data[i])
|
|
|
|
|
self.episode_history_timestamps[i][sample_idx] = packet_timestamps[i]
|
|
|
|
|
|
|
|
|
|
if self.current_counter == self.thinning_counter:
|
|
|
|
|
updated_column = self.current_column
|
|
|
|
|
for i in range(self.line_size):
|
|
|
|
|
samples = self.episode_history[i][:self.thinning_counter]
|
|
|
|
|
timestamps = self.episode_history_timestamps[i][:self.thinning_counter]
|
|
|
|
|
self.buffer[i][self.current_column] = float(sum(samples) / len(samples))
|
|
|
|
|
self.buffer_timestamps[i][self.current_column] = self._mean_timestamp(timestamps)
|
|
|
|
|
self.current_column = (self.current_column + 1) % self.columns_size
|
|
|
|
|
self.medians()
|
|
|
|
|
if self.check_init():
|
|
|
|
|
self.log_threshold_update(updated_column)
|
|
|
|
|
for i in range(self.line_size):
|
|
|
|
|
for j in range(self.thinning_counter):
|
|
|
|
|
self.episode_history[i][j] = 0.0
|
|
|
|
|
self.episode_history_timestamps[i][j] = None
|
|
|
|
|
self.current_counter = 1
|
|
|
|
|
if self.current_column == 0:
|
|
|
|
|
if self.thinning_counter == 1:
|
|
|
|
|
self.is_init = True
|
|
|
|
|
self.medians()
|
|
|
|
|
print('Начальная калибровка завершена.')
|
|
|
|
|
self.log_threshold_update(updated_column)
|
|
|
|
|
if self.thinning_counter < self.num_of_thinning_iter:
|
|
|
|
|
self.thinning_counter *= 2
|
|
|
|
|
else:
|
|
|
|
|
self.current_counter += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_alarm(self, data):
|
|
|
|
|
"""
|
|
|
|
|
Проверка триггера системы по dBFS во времени.
|
|
|
|
|
Один порог на канал, набор тревоги и сброс счетчиков как в main.
|
|
|
|
|
"""
|
|
|
|
|
if self.check_init():
|
|
|
|
|
self.last_alarm_channels = []
|
|
|
|
|
for i in range(len(data)):
|
|
|
|
|
current = data[i]
|
|
|
|
|
threshold = self.get_threshold(i)
|
|
|
|
|
exceeding = current >= threshold
|
|
|
|
|
|
|
|
|
|
if exceeding:
|
|
|
|
|
self.buffer_alarms[i] += 1
|
|
|
|
|
else:
|
|
|
|
|
self.buffer_alarms[i] = 0
|
|
|
|
|
|
|
|
|
|
if self.buffer_alarms[i] >= self.num_for_alarm:
|
|
|
|
|
self.last_alarm_channels = [i]
|
|
|
|
|
self.buffer_alarms = [0] * self.line_size
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def check_single_alarm(self, median, cur_channel):
|
|
|
|
|
"""
|
|
|
|
|
Проверка, является ли текущая метрика по каналу превышающей порог.
|
|
|
|
|
:param median: текущая метрика в dBFS.
|
|
|
|
|
:param cur_channel: индекс канала внутри частоты.
|
|
|
|
|
:return: Да/нет.
|
|
|
|
|
"""
|
|
|
|
|
if self.check_init():
|
|
|
|
|
threshold = self.get_threshold(cur_channel)
|
|
|
|
|
return median >= threshold
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|