|
|
|
@ -1,5 +1,6 @@
|
|
|
|
import os
|
|
|
|
import os
|
|
|
|
import statistics
|
|
|
|
import statistics
|
|
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
|
|
# Более лучшая версия кода есть в FRScanner
|
|
|
|
# Более лучшая версия кода есть в FRScanner
|
|
|
|
|
|
|
|
|
|
|
|
@ -36,6 +37,7 @@ class DataBuffer:
|
|
|
|
self.is_init = False
|
|
|
|
self.is_init = False
|
|
|
|
|
|
|
|
|
|
|
|
self.buffer = [[0 for _ in range(self.columns_size)] for _ in range(self.line_size)]
|
|
|
|
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_medians = [0.0] * self.line_size
|
|
|
|
self.buffer_mads = [0.0] * self.line_size
|
|
|
|
self.buffer_mads = [0.0] * self.line_size
|
|
|
|
self.buffer_alarms = [0] * self.line_size
|
|
|
|
self.buffer_alarms = [0] * self.line_size
|
|
|
|
@ -60,6 +62,9 @@ class DataBuffer:
|
|
|
|
def get_buffer(self):
|
|
|
|
def get_buffer(self):
|
|
|
|
return self.buffer
|
|
|
|
return self.buffer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_timestamps(self):
|
|
|
|
|
|
|
|
return self.buffer_timestamps
|
|
|
|
|
|
|
|
|
|
|
|
def get_medians(self):
|
|
|
|
def get_medians(self):
|
|
|
|
return self.buffer_medians
|
|
|
|
return self.buffer_medians
|
|
|
|
|
|
|
|
|
|
|
|
@ -86,6 +91,15 @@ class DataBuffer:
|
|
|
|
deviations = [abs(v - median) for v in values]
|
|
|
|
deviations = [abs(v - median) for v in values]
|
|
|
|
return statistics.median(deviations)
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
def medians(self):
|
|
|
|
def medians(self):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Вычислить медиану и MAD по строкам буфера.
|
|
|
|
Вычислить медиану и MAD по строкам буфера.
|
|
|
|
@ -116,13 +130,31 @@ class DataBuffer:
|
|
|
|
return [None] * self.line_size
|
|
|
|
return [None] * self.line_size
|
|
|
|
return [self.get_threshold(i, k) for i in range(self.line_size)]
|
|
|
|
return [self.get_threshold(i, k) 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):
|
|
|
|
|
|
|
|
threshold_on = self.get_threshold(i, self.mad_k_on)
|
|
|
|
|
|
|
|
threshold_off = self.get_threshold(i, self.mad_k_off)
|
|
|
|
|
|
|
|
packet_times = [self._format_ts(ts) for ts in self.buffer_timestamps[i]]
|
|
|
|
|
|
|
|
print(
|
|
|
|
|
|
|
|
f' ch={i} median={self.buffer_medians[i]:.6f} '
|
|
|
|
|
|
|
|
f'mad={self.buffer_mads[i]:.6f} '
|
|
|
|
|
|
|
|
f'threshold_on={threshold_on:.6f} threshold_off={threshold_off:.6f} '
|
|
|
|
|
|
|
|
f'packet_times={packet_times}'
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def alarms_fill_zeros(self):
|
|
|
|
def alarms_fill_zeros(self):
|
|
|
|
self.buffer_alarms = [0] * self.line_size
|
|
|
|
self.buffer_alarms = [0] * self.line_size
|
|
|
|
self.trend_streak = [0] * self.line_size
|
|
|
|
self.trend_streak = [0] * self.line_size
|
|
|
|
self.prev_values = [None] * self.line_size
|
|
|
|
self.prev_values = [None] * self.line_size
|
|
|
|
self.last_alarm_channels = []
|
|
|
|
self.last_alarm_channels = []
|
|
|
|
|
|
|
|
|
|
|
|
def update(self, data):
|
|
|
|
def update(self, data, packet_timestamps=None):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Обновление буфера.
|
|
|
|
Обновление буфера.
|
|
|
|
Если номер текущего чтения совпадает с количеством прореживающего множителя на текущем обновлении буфера, то
|
|
|
|
Если номер текущего чтения совпадает с количеством прореживающего множителя на текущем обновлении буфера, то
|
|
|
|
@ -135,20 +167,30 @@ class DataBuffer:
|
|
|
|
До тех пор, пока множитель на итерации меньше фиксированного, увеличиваем в два раза.
|
|
|
|
До тех пор, пока множитель на итерации меньше фиксированного, увеличиваем в два раза.
|
|
|
|
В противном случае - увеличиваем номер чтения.
|
|
|
|
В противном случае - увеличиваем номер чтения.
|
|
|
|
:param data: Массив с метриками сигнала по каналам.
|
|
|
|
:param data: Массив с метриками сигнала по каналам.
|
|
|
|
|
|
|
|
:param packet_timestamps: Времена пакетов SDR для каждой метрики канала.
|
|
|
|
:return: None
|
|
|
|
: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')
|
|
|
|
|
|
|
|
|
|
|
|
if self.current_counter == self.thinning_counter:
|
|
|
|
if self.current_counter == self.thinning_counter:
|
|
|
|
|
|
|
|
updated_column = self.current_column
|
|
|
|
for i in range(self.line_size):
|
|
|
|
for i in range(self.line_size):
|
|
|
|
self.buffer[i][self.current_column] = data[i]
|
|
|
|
self.buffer[i][self.current_column] = data[i]
|
|
|
|
|
|
|
|
self.buffer_timestamps[i][self.current_column] = packet_timestamps[i]
|
|
|
|
self.current_column = (self.current_column + 1) % self.columns_size
|
|
|
|
self.current_column = (self.current_column + 1) % self.columns_size
|
|
|
|
self.medians()
|
|
|
|
self.medians()
|
|
|
|
|
|
|
|
if self.check_init():
|
|
|
|
|
|
|
|
self.log_threshold_update(updated_column)
|
|
|
|
self.current_counter = 1
|
|
|
|
self.current_counter = 1
|
|
|
|
if self.current_column == 0:
|
|
|
|
if self.current_column == 0:
|
|
|
|
if self.thinning_counter == 1:
|
|
|
|
if self.thinning_counter == 1:
|
|
|
|
self.is_init = True
|
|
|
|
self.is_init = True
|
|
|
|
self.medians()
|
|
|
|
self.medians()
|
|
|
|
print('Начальная калибровка завершена.')
|
|
|
|
print('Начальная калибровка завершена.')
|
|
|
|
|
|
|
|
self.log_threshold_update(updated_column)
|
|
|
|
if self.thinning_counter < self.num_of_thinning_iter:
|
|
|
|
if self.thinning_counter < self.num_of_thinning_iter:
|
|
|
|
self.thinning_counter *= 2
|
|
|
|
self.thinning_counter *= 2
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
|