добавил подсчет по dbfs

fft
Sergey Revyakin 3 weeks ago
parent 3a936fac9c
commit 6911d8ab25

@ -10,11 +10,10 @@ import os
from common.runtime import load_root_env, resolve_hackrf_index from common.runtime import load_root_env, resolve_hackrf_index
load_root_env(__file__) load_root_env(__file__)
def get_hack_id(): def get_hack_id():
return resolve_hackrf_index('HACKID_1200', 'orange_scripts/main_1200.py') return resolve_hackrf_index('HACKID_1200', 'orange_scripts/main_1200.py')
serial_number = os.getenv('HACKID_1200') serial_number = os.getenv('HACKID_1200')
pos = None pos = None
output = [] output = []

@ -10,11 +10,10 @@ import os
from common.runtime import load_root_env, resolve_hackrf_index from common.runtime import load_root_env, resolve_hackrf_index
load_root_env(__file__) load_root_env(__file__)
def get_hack_id(): def get_hack_id():
return resolve_hackrf_index('HACKID_2400', 'orange_scripts/main_2400.py') return resolve_hackrf_index('HACKID_2400', 'orange_scripts/main_2400.py')
serial_number = os.getenv('HACKID_2400') serial_number = os.getenv('HACKID_2400')
pos = None pos = None
output = [] output = []

@ -10,11 +10,10 @@ import os
from common.runtime import load_root_env, resolve_hackrf_index from common.runtime import load_root_env, resolve_hackrf_index
load_root_env(__file__) load_root_env(__file__)
def get_hack_id(): def get_hack_id():
return resolve_hackrf_index('HACKID_915', 'orange_scripts/main_915.py') return resolve_hackrf_index('HACKID_915', 'orange_scripts/main_915.py')
serial_number = os.getenv('HACKID_915') serial_number = os.getenv('HACKID_915')
pos = None pos = None
output = [] output = []

@ -1,170 +1,182 @@
import statistics import os
import math
# Более лучшая версия кода есть в FRScanner import statistics
class DataBuffer: # Более лучшая версия кода есть в FRScanner
"""
Класс с реализацией циклического буффера.
class DataBuffer:
Атрибуты: """
current_column: Указатель на текущий столбец буфера, который обновляем. Класс с реализацией циклического буффера.
thinning_counter: Прореживающий множитель на текующей итерации.
current_counter: Указатель на количество чтений между последним обновлением столбца и предыдущим атрибутом. Атрибуты:
num_of_thinning_iter: Прореживающий множитель. Раз в это количечество раз будет обнволяться столбец буфера. current_column: Указатель на текущий столбец буфера, который обновляем.
line_size: Количество строк буфера = количеству каналов. thinning_counter: Прореживающий множитель на текующей итерации.
columns_size: Количество столбцов = фиксированное число. current_counter: Указатель на количество чтений между последним обновлением столбца и предыдущим атрибутом.
multiply_factor: Процентный показатель превышения сигналом уровня шума. ex m_p = 1.1 => триггер, если num_of_thinning_iter: Прореживающий множитель. Раз в это количечество раз будет обнволяться столбец буфера.
сигнал превышает шум на 10%. line_size: Количество строк буфера = количеству каналов.
num_for_alarm: Количество раз, превышающих шум, при которых триггеримся = фиксированное число. columns_size: Количество столбцов = фиксированное число.
is_init: Флаг инициализации буфера. = True, если инициализирован. multiply_factor: Процентный показатель превышения сигналом уровня шума.
buffer: Массив для буфера. num_for_alarm: Количество раз, превышающих шум, при которых триггеримся.
buffer_medians: Массив для медиан столбцов букера. is_init: Флаг инициализации буфера. = True, если инициализирован.
buffer_alarms: Массив для количества тревог по столбца буфера. buffer: Массив для буфера.
""" buffer_medians: Массив для медиан столбцов букера.
buffer_alarms: Массив для количества тревог по столбца буфера.
def __init__(self, columns_size, num_of_thinning_iter, num_of_channels, multiply_factor, num_for_alarm): """
"""
Инициализируем класс. 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 columns_size:
:param multiply_factor: :param num_of_thinning_iter:
:param num_for_alarm: :param num_of_channels:
""" :param multiply_factor:
self.current_column = 0 :param num_for_alarm:
self.thinning_counter = 1 """
self.current_counter = 1 self.current_column = 0
self.num_of_thinning_iter = num_of_thinning_iter self.thinning_counter = 1
self.line_size = num_of_channels self.current_counter = 1
self.columns_size = columns_size self.num_of_thinning_iter = num_of_thinning_iter
self.multiply_factor = multiply_factor self.line_size = num_of_channels
self.num_for_alarm = num_for_alarm self.columns_size = columns_size
self.is_init = False self.multiply_factor = multiply_factor
self.buffer = [[0 for _ in range(self.columns_size)] for _ in range(self.line_size)] self.num_for_alarm = num_for_alarm
self.buffer_medians = [0] * self.line_size self.is_init = False
self.buffer_alarms = [0] * self.line_size self.buffer = [[0 for _ in range(self.columns_size)] for _ in range(self.line_size)]
self.buffer_medians = [0] * self.line_size
def get_buffer(self): self.buffer_alarms = [0] * self.line_size
return self.buffer
self.prev_values = [None] * self.line_size
def get_medians(self): self.trend_streak = [0] * self.line_size
return self.buffer_medians
# Рост в 15% по линейной мощности относительно фоновой медианы в dBFS.
def get_alarms(self): self.dbfs_delta_ratio = float(os.getenv('dbfs_delta_percent', 15)) / 100.0
return self.buffer_alarms # Допускаем небольшой обратный ход, чтобы не сбрасываться от микрошума.
self.dbfs_max_backstep_db = float(os.getenv('dbfs_max_backstep_db', 0.25))
def check_init(self): # Минимум подряд "плавных" шагов перед учетом как устойчивого роста.
return self.is_init self.dbfs_min_trend_steps = int(os.getenv('dbfs_min_trend_steps', max(1, self.num_for_alarm)))
def print(self): def get_buffer(self):
print('buffer is: ') return self.buffer
for i in range(self.line_size):
print(self.buffer[i], end=' ') def get_medians(self):
print() return self.buffer_medians
def medians(self): def get_alarms(self):
""" return self.buffer_alarms
Вычислить медиану по строке буфера.
:return: None def check_init(self):
""" return self.is_init
if self.check_init():
for i in range(self.line_size): def print(self):
self.buffer_medians[i] = statistics.median(self.buffer[i]) print('buffer is: ')
# print('medians is: ', self.buffer_medians) for i in range(self.line_size):
# return self.buffer_medians print(self.buffer[i], end=' ')
print()
def alarms_fill_zeros(self):
self.buffer_alarms = [0] * self.line_size def medians(self):
def update(self, data): """
""" Вычислить медиану по строке буфера.
Обновление буфера. :return: None
Если номер текущего чтения совпадает с количеством прореживающего множителя на текущем обновлении буфера, то """
1. Обновляем буфер. if self.check_init():
2. Двигаем курсор на след столбец. Если был последний столбец, то двигаем курсор в начало. for i in range(self.line_size):
3. Берем медианы по буферу, если он уже проиницализирован. self.buffer_medians[i] = statistics.median(self.buffer[i])
4. Сбрасываем счетчик текущих чтений.
5. Если был последний столбец (и мы уже переключились на первый), то 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
:param data: Массив с метриками сигнала по каналам.
:return: None @staticmethod
""" def _dbfs_growth_ratio(current_db, baseline_db):
return math.pow(10.0, (current_db - baseline_db) / 10.0) - 1.0
# TODO: Добавить время релаксации - если система затриггерилась, то перестать обновлять буфер на N чтений,
# где N задается в .env-template. Сейчас есть бага, что буфер перестает обновляться только когда система def update(self, data):
# триггерится. Между тем, когда приходит аларм и num_for_alarm, когда сигнал алармовский, но система еще не """
# триггерится, буфер продолжает обновляться. В таких условиях буфер может набрать в себя алармовских сигналов Обновление буфера.
# и повысить пороги. Пример такой ситуации: дрон висит на 1км, система его видит, но сигнал превышает порог раз Если номер текущего чтения совпадает с количеством прореживающего множителя на текущем обновлении буфера, то
# через раз и аларм срабатывает не всегда. В таких условиях наберется высокий сигнал, повысятся пороги и когда 1. Обновляем буфер.
# дрон начнет движение вперед, он будет заметен на более низкой дистанции, чем обычно, так как пороги повышены. 2. Двигаем курсор на след столбец. Если был последний столбец, то двигаем курсор в начало.
3. Берем медианы по буферу, если он уже проиницализирован.
if self.current_counter == self.thinning_counter: 4. Сбрасываем счетчик текущих чтений.
for i in range(self.line_size): 5. Если был последний столбец (и мы уже переключились на первый), то
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() :param data: Массив с метриками сигнала по каналам.
self.current_counter = 1 :return: None
if self.current_column == 0: """
if self.thinning_counter == 1:
self.is_init = True if self.current_counter == self.thinning_counter:
self.medians() for i in range(self.line_size):
print('Начальная калибровка завершена.') self.buffer[i][self.current_column] = data[i]
if self.thinning_counter < self.num_of_thinning_iter: self.current_column = (self.current_column + 1) % self.columns_size
self.thinning_counter *= 2 self.medians()
# print('thinning counter обновлен: ', self.thinning_counter) self.current_counter = 1
if self.current_column == 0:
else: if self.thinning_counter == 1:
self.current_counter += 1 self.is_init = True
# print('curr counter обновлен: ', self.current_counter) self.medians()
print('Начальная калибровка завершена.')
def check_alarm(self, data): if self.thinning_counter < self.num_of_thinning_iter:
""" self.thinning_counter *= 2
Проверка триггера системы. else:
Если значение по каналу превышает медиану (порог) шума на какой-то процент, то инкремент буфер аларма по каналу. self.current_counter += 1
Превышение num_for_alarm подряд - триггер. Если после n превышений, где n<num_for_alarm приходит сигнал не
первышающий порог, то сбрасываем буфер алармов. def check_alarm(self, data):
:param data: """
:return: Да/нет. Проверка триггера системы по dBFS во времени.
""" Триггер: устойчивый рост относительно фоновой медианы не меньше dbfs_delta_percent,
if self.check_init(): подтвержденный несколькими последовательными чтениями.
ratios=[] """
print("="*50) if self.check_init():
for i in range(len(data)): for i in range(len(data)):
exceeding = data[i] > self.multiply_factor * self.buffer_medians[i] baseline = self.buffer_medians[i]
ratios.append(data[i]/self.buffer_medians[i]) current = data[i]
if exceeding:
self.buffer_alarms[i] += 1 growth_ratio = self._dbfs_growth_ratio(current, baseline)
# print('Инкремент буффер алармов по каналу {0}, текущее число по этому каналу: {1}'.format(i,self.buffer_alarms[i]))
else: prev = self.prev_values[i]
self.buffer_alarms[i] = 0 delta_db = 0.0 if prev is None else current - prev
# print('Обнулили буффер алармов по каналу {0}, текущее число по этому каналу: {1}'.format(i,self.buffer_alarms[i])) monotonic_or_stable = (prev is None) or (delta_db >= -self.dbfs_max_backstep_db)
if self.buffer_alarms[i] >= self.num_for_alarm: if monotonic_or_stable:
# print('Сработала тревога по каналу {0}, текущее число по этому каналу: {1}'.format(i,self.buffer_alarms[i])) self.trend_streak[i] += 1
self.buffer_alarms = [0] * self.line_size else:
print("Отношения:", [f"{r:.3f}" for r in ratios]) self.trend_streak[i] = 0
print("!"*50)
return True exceeding = (
print("Отношения:", [f"{r:.3f}" for r in ratios]) growth_ratio >= self.dbfs_delta_ratio
print("="*50) and self.trend_streak[i] >= self.dbfs_min_trend_steps
)
return False if exceeding:
self.buffer_alarms[i] += 1
def check_single_alarm(self, median, cur_channel): else:
""" self.buffer_alarms[i] = 0
Проверка, является ли текущая медиана по каналу превышающей порог.
:param median: меди (хар-ка) по каналу. self.prev_values[i] = current
:param cur_channel: индекс канала внутри частоты.
:return: Да/нет. if self.buffer_alarms[i] >= self.num_for_alarm:
""" self.buffer_alarms = [0] * self.line_size
if self.check_init(): self.trend_streak = [0] * self.line_size
exceeding = median > self.multiply_factor * self.buffer_medians[cur_channel] return True
print(median/self.buffer_medians[cur_channel])
if exceeding: return False
return True
else: def check_single_alarm(self, median, cur_channel):
return False """
Проверка, является ли текущая метрика по каналу превышающей порог роста.
:param median: текущая метрика в dBFS.
:param cur_channel: индекс канала внутри частоты.
:return: Да/нет.
"""
if self.check_init():
baseline = self.buffer_medians[cur_channel]
exceeding = self._dbfs_growth_ratio(median, baseline) >= self.dbfs_delta_ratio
if exceeding:
return True
else:
return False

@ -23,7 +23,6 @@ from common.runtime import load_root_env, resolve_hackrf_index
load_root_env(__file__) load_root_env(__file__)
def get_hack_id(): def get_hack_id():
return resolve_hackrf_index('hack_3300', 'src/main_3300.py') return resolve_hackrf_index('hack_3300', 'src/main_3300.py')
serial_number = os.getenv('hack_3300') serial_number = os.getenv('hack_3300')

@ -21,11 +21,10 @@ import os
from common.runtime import load_root_env, resolve_hackrf_index from common.runtime import load_root_env, resolve_hackrf_index
load_root_env(__file__) load_root_env(__file__)
def get_hack_id(): def get_hack_id():
return resolve_hackrf_index('hack_433', 'src/main_433.py') return resolve_hackrf_index('hack_433', 'src/main_433.py')
serial_number = os.getenv('hack_433') serial_number = os.getenv('hack_433')
pos = None pos = None
output = [] output = []

@ -23,7 +23,6 @@ from common.runtime import load_root_env, resolve_hackrf_index
load_root_env(__file__) load_root_env(__file__)
def get_hack_id(): def get_hack_id():
return resolve_hackrf_index('hack_4500', 'src/main_4500.py') return resolve_hackrf_index('hack_4500', 'src/main_4500.py')
serial_number = os.getenv('hack_4500') serial_number = os.getenv('hack_4500')

@ -18,14 +18,12 @@ import time
import threading import threading
import subprocess import subprocess
import os import os
from common.runtime import load_root_env, resolve_hackrf_index from common.runtime import load_root_env, resolve_hackrf_index
load_root_env(__file__) load_root_env(__file__)
def get_hack_id(): def get_hack_id():
return resolve_hackrf_index('hack_5200', 'src/main_5200.py') return resolve_hackrf_index('hack_5200', 'src/main_5200.py')
serial_number = os.getenv('hack_5200') serial_number = os.getenv('hack_5200')
pos = None pos = None
output = [] output = []

@ -21,11 +21,10 @@ import os
from common.runtime import load_root_env, resolve_hackrf_index from common.runtime import load_root_env, resolve_hackrf_index
load_root_env(__file__) load_root_env(__file__)
def get_hack_id(): def get_hack_id():
return resolve_hackrf_index('hack_5800', 'src/main_5800.py') return resolve_hackrf_index('hack_5800', 'src/main_5800.py')
serial_number = os.getenv('hack_5800') serial_number = os.getenv('hack_5800')
pos = None pos = None
output = [] output = []

@ -21,11 +21,10 @@ import os
from common.runtime import load_root_env, resolve_hackrf_index from common.runtime import load_root_env, resolve_hackrf_index
load_root_env(__file__) load_root_env(__file__)
def get_hack_id(): def get_hack_id():
return resolve_hackrf_index('hack_750', 'src/main_750.py') return resolve_hackrf_index('hack_750', 'src/main_750.py')
serial_number = os.getenv('hack_750') serial_number = os.getenv('hack_750')
pos = None pos = None
output = [] output = []

@ -21,11 +21,10 @@ import os
from common.runtime import load_root_env, resolve_hackrf_index from common.runtime import load_root_env, resolve_hackrf_index
load_root_env(__file__) load_root_env(__file__)
def get_hack_id(): def get_hack_id():
return resolve_hackrf_index('hack_868', 'src/main_868.py') return resolve_hackrf_index('hack_868', 'src/main_868.py')
serial_number = os.getenv('hack_868') serial_number = os.getenv('hack_868')
pos = None pos = None
output = [] output = []

Loading…
Cancel
Save