Compare commits

..

No commits in common. 'fft' and 'main' have entirely different histories.
fft ... main

3
.gitignore vendored

@ -183,5 +183,4 @@ cython_debug/
#.idea/ #.idea/
*.png *.png
/logs/*.log /logs/*.log
runtime/

@ -148,7 +148,19 @@ docker compose -f deploy/docker/docker-compose.yml down
``` ```
### Все SDR-сервисы (systemd) ### Все SDR-сервисы (systemd)
Запуск всех SDR unitов:
```bash
sudo systemctl start dronedetector-sdr-433.service
sudo systemctl start dronedetector-sdr-750.service
sudo systemctl start dronedetector-sdr-868.service
sudo systemctl start dronedetector-sdr-3300.service
sudo systemctl start dronedetector-sdr-4500.service
sudo systemctl start dronedetector-sdr-5200.service
sudo systemctl start dronedetector-sdr-5800.service
sudo systemctl start dronedetector-sdr-915.service
sudo systemctl start dronedetector-sdr-1200.service
sudo systemctl start dronedetector-sdr-2400.service
```
```bash ```bash
sudo systemctl status dronedetector-sdr-433.service sudo systemctl status dronedetector-sdr-433.service
sudo systemctl status dronedetector-sdr-750.service sudo systemctl status dronedetector-sdr-750.service
@ -176,13 +188,26 @@ sudo systemctl stop dronedetector-sdr-2400.service
``` ```
Альтернатива одной командой:
```bash
for u in dronedetector-sdr-{433,750,868,3300,4500,5200,5800,915,1200,2400}.service; do
sudo systemctl start "$u"
done
```
Проверка статуса всех SDR unitов: Проверка статуса всех SDR unitов:
```bash ```bash
sudo systemctl status dronedetector-sdr-*.service --no-pager sudo systemctl status dronedetector-sdr-*.service --no-pager
``` ```
### Полный ручной старт всего контура
```bash
docker compose -f deploy/docker/docker-compose.yml up -d
for u in dronedetector-sdr-{433,750,868,3300,4500,5200,5800,915,1200,2400}.service; do
sudo systemctl start "$u"
done
```
### Просмотр логов SDR ### Просмотр логов SDR
``` bash ``` bash
# 50 последних # 50 последних

@ -1,18 +0,0 @@
# syntax=docker/dockerfile:1.7
FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONPATH=/app
WORKDIR /app
COPY deploy/requirements/telemetry_server.txt /tmp/requirements.txt
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --upgrade pip && \
pip install -r /tmp/requirements.txt
COPY . /app
EXPOSE 5020
CMD ["python3", "-m", "telemetry.telemetry_server"]

@ -9,7 +9,6 @@ services:
- ../../.env - ../../.env
environment: environment:
- PYTHONPATH=/app - PYTHONPATH=/app
- JAMMER_STATE_FILE=/app/runtime/jammer_active.flag
working_dir: /app working_dir: /app
command: ["python3", "-m", "src.server_to_master"] command: ["python3", "-m", "src.server_to_master"]
restart: unless-stopped restart: unless-stopped
@ -17,7 +16,6 @@ services:
- "5010:5010" - "5010:5010"
volumes: volumes:
- ../../.env:/app/.env:ro - ../../.env:/app/.env:ro
- ../../runtime:/app/runtime
networks: networks:
- dronedetector-net - dronedetector-net
@ -50,29 +48,6 @@ services:
networks: networks:
- dronedetector-net - dronedetector-net
dronedetector-telemetry-server:
container_name: dronedetector-telemetry-server
image: dronedetector-telemetry-server:latest
build:
context: ../..
dockerfile: deploy/docker/Dockerfile.telemetry_server
env_file:
- ../../.env
environment:
- PYTHONPATH=/app
working_dir: /app
command: ["python3", "-m", "telemetry.telemetry_server"]
restart: unless-stopped
ports:
- "5020:5020"
volumes:
- ../../.env:/app/.env:ro
- ../../telemetry:/app/telemetry
- ../../common:/app/common
networks:
- dronedetector-net
networks: networks:
dronedetector-net: dronedetector-net:
name: dronedetector-net name: dronedetector-net

@ -1,3 +0,0 @@
fastapi==0.115.6
uvicorn[standard]==0.32.1
python-dotenv==1.0.1

@ -10,10 +10,11 @@ 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,10 +10,11 @@ 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,10 +10,11 @@ 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 = []

BIN
out.iq

Binary file not shown.

@ -7,14 +7,14 @@ COMPOSE_FILE="${PROJECT_ROOT}/deploy/docker/docker-compose.yml"
SDR_UNITS=( SDR_UNITS=(
dronedetector-sdr-433.service dronedetector-sdr-433.service
dronedetector-sdr-750.service dronedetector-sdr-750.service
#dronedetector-sdr-868.service dronedetector-sdr-868.service
dronedetector-sdr-3300.service dronedetector-sdr-3300.service
#dronedetector-sdr-4500.service dronedetector-sdr-4500.service
dronedetector-sdr-5200.service dronedetector-sdr-5200.service
dronedetector-sdr-5800.service dronedetector-sdr-5800.service
#dronedetector-sdr-915.service dronedetector-sdr-915.service
#dronedetector-sdr-1200.service dronedetector-sdr-1200.service
#dronedetector-sdr-2400.service dronedetector-sdr-2400.service
) )
log() { log() {

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

@ -1,176 +1,168 @@
import os import os
from core.data_buffer import DataBuffer from core.data_buffer import DataBuffer
def get_centre_freq(freq): def get_centre_freq(freq):
""" """
Получить название частоты по ее диапазону. Получить название частоты по ее диапазону.
:param freq: Частота, которую обрабатываем. :param freq: Частота, которую обрабатываем.
:return: Название частоты. :return: Название частоты.
""" """
c_freq = 0 c_freq = 0
if 5.46e9 <= freq <= 6.0e9: if 5.46e9 <= freq <= 6.0e9:
c_freq = 5800 c_freq = 5800
if 5.0e9 <= freq <= 5.4e9: if 5.0e9 <= freq <= 5.4e9:
c_freq = 5200 c_freq = 5200
if 4.5e9 <= freq <= 4.7e9: if 4.5e9 <= freq <= 4.7e9:
c_freq = 4500 c_freq = 4500
if 3.3e9 <= freq <= 3.5e9: if 3.3e9 <= freq <= 3.5e9:
c_freq = 3300 c_freq = 3300
if 2.4e9 <= freq <= 2.5e9: if 2.4e9 <= freq <= 2.5e9:
c_freq = 2400 c_freq = 2400
if 1e9 <= freq <= 1.36e9: if 1e9 <= freq <= 1.36e9:
c_freq = 1200 c_freq = 1200
if 0.9e9 <= freq <= 0.960e9: if 0.9e9 <= freq <= 0.960e9:
c_freq = 915 c_freq = 915
if 0.830e9 <= freq <= 0.890e9: if 0.830e9 <= freq <= 0.890e9:
c_freq = 868 c_freq = 868
if 0.700e9 <= freq <= 0.780e9: if 0.700e9 <= freq <= 0.780e9:
c_freq = 750 c_freq = 750
if 0.380e9 <= freq <= 0.500e9: if 0.380e9 <= freq <= 0.500e9:
c_freq = 433 c_freq = 433
return str(c_freq) return str(c_freq)
class MultiChannel: class MultiChannel:
""" """
Класс с реализацией переключателя каналов. Присутствует поддержка нескольких частот, а поэтому Класс с реализацией переключателя каналов. Присутствует поддержка нескольких частот, а поэтому
Атрибуты: Атрибуты:
steps: Массив шагов для разных частот. Ex. steps = [-20e6, -5e6, -3e6], i-ый элемент соответствует i-ой steps: Массив шагов для разных частот. Ex. steps = [-20e6, -5e6, -3e6], i-ый элемент соответствует i-ой
частоте для обработке, типа 1.2, 915 и 868. частоте для обработке, типа 1.2, 915 и 868.
bases: Массив верхних границ диапазонов рассматриваемых частот. Ex bases = [1.36e9, 0.93e9, 0.87e9] для bases: Массив верхних границ диапазонов рассматриваемых частот. Ex bases = [1.36e9, 0.93e9, 0.87e9] для
1.2, 915 и 868. 1.2, 915 и 868.
roofs: То же самое, только нижних границ. Ex roofs = [1e9, 0.9e9, 0.85e9] roofs: То же самое, только нижних границ. Ex roofs = [1e9, 0.9e9, 0.85e9]
cur_channel: Указатель на текущий канал, который обрабатываем. cur_channel: Указатель на текущий канал, который обрабатываем.
cur_roof: Указатель на нижнюю границу текущей обрабатываемой частоты. cur_roof: Указатель на нижнюю границу текущей обрабатываемой частоты.
cur_step: Указатель на шаг текущей обрабатываемой частоты. cur_step: Указатель на шаг текущей обрабатываемой частоты.
num_chs: Массив из каналов по обрабатываемым частотам. Вычисляется автоматически исходя из границ и шага. num_chs: Массив из каналов по обрабатываемым частотам. Вычисляется автоматически исходя из границ и шага.
init_freq: Чекер на инициализацию частоты перед началом работы скрипта. Нужен из-за особенности init_freq: Чекер на инициализацию частоты перед началом работы скрипта. Нужен из-за особенности
работы графов GNURadio и функции work в embedded Python блоке. работы графов GNURadio и функции work в embedded Python блоке.
DB: Список из циклических буферов для соответствующих чатсот. DB: Список из циклических буферов для соответствующих чатсот.
""" """
def __init__(self, steps, bases, roofs): def __init__(self, steps, bases, roofs):
""" """
Инициализация класса. Инициализация класса.
:param steps: Список с шагами для соответствующих частот. :param steps: Список с шагами для соответствующих частот.
:param bases: Список верхних границ диапазонов частот, с которыми работаем. :param bases: Список верхних границ диапазонов частот, с которыми работаем.
:param roofs: Список нижних границ --//--. :param roofs: Список нижних границ --//--.
""" """
self.steps = steps self.steps = steps
self.bases = bases self.bases = bases
self.roofs = roofs self.roofs = roofs
self.cur_channel = self.bases[0] self.cur_channel = self.bases[0]
self.cur_roof = self.roofs[0] self.cur_roof = self.roofs[0]
self.cur_step = self.steps[0] self.cur_step = self.steps[0]
self.num_chs = [] self.num_chs = []
self.init_freq = False self.init_freq = False
self.DB = [] self.DB = []
def init_f(self): def init_f(self):
""" """
Инициализация начальной частоты, с которой начинаем обработку. Инициализация начальной частоты, с которой начинаем обработку.
:return: Верхняя граница первой частоты из набора частот. :return: Верхняя граница первой частоты из набора частот.
""" """
self.init_freq = True self.init_freq = True
return self.bases[0] return self.bases[0]
def get_cur_channel(self): def get_cur_channel(self):
""" """
Получить текущий обрабатываемый канал. Получить текущий обрабатываемый канал.
:return: Канал обработки. :return: Канал обработки.
""" """
return self.cur_channel return self.cur_channel
def change_channel(self): def change_channel(self):
""" """
Функция смены канала. Идет от верхней границы диапазона частоты к нижней с шагом step. Если дошли до нижней Функция смены канала. Идет от верхней границы диапазона частоты к нижней с шагом step. Если дошли до нижней
границы, то переключаемся на следующую частоту посредством переноса курсора текущего канала на верхнюю границу границы, то переключаемся на следующую частоту посредством переноса курсора текущего канала на верхнюю границу
новой частоты и указатель нижней границы также двигаем на следующую позицию. Если частота для обработки одна, то новой частоты и указатель нижней границы также двигаем на следующую позицию. Если частота для обработки одна, то
указатель текущего канала возвращается в начало - верхней границы этой же частоты. Указатель нижней границы не указатель текущего канала возвращается в начало - верхней границы этой же частоты. Указатель нижней границы не
изменяется. изменяется.
:return: Канал после смены. :return: Канал после смены.
""" """
if not self.init_freq: if not self.init_freq:
return self.init_f() return self.init_f()
if self.cur_channel <= self.cur_roof: if self.cur_channel <= self.cur_roof:
if self.cur_roof == self.roofs[-1]: if self.cur_roof == self.roofs[-1]:
self.cur_channel = self.bases[0] self.cur_channel = self.bases[0]
self.cur_roof = self.roofs[0] self.cur_roof = self.roofs[0]
self.cur_step = self.steps[0] self.cur_step = self.steps[0]
else: else:
next_roofs = self.roofs.index(self.cur_roof) + 1 next_roofs = self.roofs.index(self.cur_roof) + 1
self.cur_channel = self.bases[next_roofs] self.cur_channel = self.bases[next_roofs]
self.cur_roof = self.roofs[next_roofs] self.cur_roof = self.roofs[next_roofs]
self.cur_step = self.steps[next_roofs] self.cur_step = self.steps[next_roofs]
else: else:
self.cur_channel += self.cur_step self.cur_channel += self.cur_step
# print('Канал частоты изменен на ', self.cur_channel / 1000000) # print('Канал частоты изменен на ', self.cur_channel / 1000000)
return self.get_cur_channel() return self.get_cur_channel()
def get_num_chs(self, idx_freq): def get_num_chs(self, idx_freq):
""" """
Вычисляет количество каналов на частоте исходя из верхнего, нижнего диапазонов и шага. Вычисляет количество каналов на частоте исходя из верхнего, нижнего диапазонов и шага.
:param idx_freq: id частоты внутри класса. Т.е. в данный момент обрабатывается несколько частот, то id = :param idx_freq: id частоты внутри класса. Т.е. в данный момент обрабатывается несколько частот, то id =
индексу верхней границы в bases для данной частоты, или нижней границы в roofs или шагу в steps. индексу верхней границы в bases для данной частоты, или нижней границы в roofs или шагу в steps.
В примерах из описания атрибутов индекс частоты 915 будет равен единице (т.к. идет вторым элементом в списках). В примерах из описания атрибутов индекс частоты 915 будет равен единице (т.к. идет вторым элементом в списках).
:return: Количество каналов. :return: Количество каналов.
""" """
if (idx_freq + 1) > len(self.num_chs): if (idx_freq + 1) > len(self.num_chs):
tmp = self.bases[idx_freq] tmp = self.bases[idx_freq]
counter = 0 counter = 0
while tmp >= self.roofs[idx_freq]: while tmp >= self.roofs[idx_freq]:
counter += 1 counter += 1
tmp += self.steps[idx_freq] tmp += self.steps[idx_freq]
self.num_chs.append(counter) self.num_chs.append(counter)
return counter return counter
else: else:
return self.num_chs[idx_freq] return self.num_chs[idx_freq]
def check_f(self, freq): def check_f(self, freq):
""" """
Проверить наличие частоты в классе. Если да, то вернуть количество каналов и циклический буфер этой частоты. Проверить наличие частоты в классе. Если да, то вернуть количество каналов и циклический буфер этой частоты.
:param freq: Частота. :param freq: Частота.
:return: Количество каналов, циклический буфер выбранной частоты ИЛИ none. :return: Количество каналов, циклический буфер выбранной частоты ИЛИ none.
""" """
for i in range(len(self.bases)): for i in range(len(self.bases)):
if self.roofs[i] <= freq <= self.bases[i]: if self.roofs[i] <= freq <= self.bases[i]:
return self.get_num_chs(i), self.DB[i] return self.get_num_chs(i), self.DB[i]
else: else:
return None, None return None, None
def fill_DB(self): def fill_DB(self):
""" """
Инициализировать циклические буферы для всех частот в отдельный список. Инициализировать циклические буферы для всех частот в отдельный список.
:return: N0nE. :return: N0nE.
""" """
for i in range(len(self.bases)): for i in range(len(self.bases)):
freq = get_centre_freq(self.bases[i]) freq = get_centre_freq(self.bases[i])
buffer_columns_size = int(os.getenv('buffer_columns_size_' + str(freq))) buffer_columns_size = int(os.getenv('buffer_columns_size_' + str(freq)))
num_of_thinning_iter = int(os.getenv('num_of_thinning_iter_' + str(freq))) num_of_thinning_iter = int(os.getenv('num_of_thinning_iter_' + str(freq)))
multiply_factor = float(os.getenv('multiply_factor_' + str(freq))) multiply_factor = float(os.getenv('multiply_factor_' + str(freq)))
num_for_alarm = int(os.getenv('num_for_alarm_' + str(freq))) num_for_alarm = int(os.getenv('num_for_alarm_' + str(freq)))
num_chs = self.get_num_chs(i) num_chs = self.get_num_chs(i)
self.DB.append( self.DB.append(
DataBuffer( DataBuffer(buffer_columns_size, num_of_thinning_iter, num_chs, multiply_factor, num_for_alarm))
buffer_columns_size,
num_of_thinning_iter, def db_alarms_zeros(self, circle_buffer):
num_chs, """
multiply_factor, При отработке системы зануляет алармы во всех буферах, кроме текущего, т.к. в текущем уже занулилось.
num_for_alarm, :param circle_buffer: Циклический буфер текущей обрабатываемой частоты.
freq_tag=str(freq), :return: None.
) """
) for i in range(len(self.DB)):
if self.DB[i] != circle_buffer:
def db_alarms_zeros(self, circle_buffer): self.DB[i].alarms_fill_zeros()
"""
При отработке системы зануляет алармы во всех буферах, кроме текущего, т.к. в текущем уже занулилось.
:param circle_buffer: Циклический буфер текущей обрабатываемой частоты.
:return: None.
"""
for i in range(len(self.DB)):
if self.DB[i] != circle_buffer:
self.DB[i].alarms_fill_zeros()

@ -1,165 +1,107 @@
import os import os
import math import numpy as np
import time from typing import Union
import numpy as np from common.runtime import load_root_env
from typing import Union
from common.runtime import load_root_env
load_root_env(__file__) load_root_env(__file__)
def get_signal_length(freq): def get_signal_length(freq):
length = int(os.getenv('signal_length_' + str(freq))) length = int(os.getenv('signal_length_' + str(freq)))
return length return length
class Signal: class Signal:
""" """
Класс сбора и предобработки сигнала. Класс сбора и предобработки сигнала.
Атрибуты: Атрибуты:
length: Длина сигнала. length: Длина сигнала.
signal: Массив, в который собираем сигнал. signal: Массив, в который собираем сигнал.
""" """
def __init__(self, conv_method='average'): def __init__(self, conv_method='average'):
self.conv_method = conv_method self.conv_method = conv_method
self.signal = [] self.signal = []
self.signal_abs = [] self.signal_abs = []
self.last_packet_ts = None
self.metric_mode = os.getenv('signal_metric_mode', 'fft_top_bins').strip().lower() def get_signal(self):
self.fft_top_bins = max(1, int(os.getenv('signal_fft_top_bins', '2048'))) """
self.fft_window = os.getenv('signal_fft_window', 'hann').strip().lower() Возвращает собранный сигнал.
:return: Массив с сигналом.
def get_signal(self): """
""" return self.signal, self.signal_abs
Возвращает собранный сигнал.
:return: Массив с сигналом. def clear(self) -> None:
""" """
return self.signal, self.signal_abs Очистить массив с сигналом после предобработки?
:return: None
def get_last_packet_ts(self): """
return self.last_packet_ts self.signal = []
self.signal_abs = []
def clear(self) -> None:
""" def signal_preprocessing(self, length) -> float:
Очистить массив с сигналом после предобработки? """
:return: None Предобработка сигнала.
"""
self.signal = [] :return: Число типа float - "характеристика сигнала".
self.signal_abs = [] """
self.last_packet_ts = None signal = np.array([self.signal.real[0:length], self.signal.imag[0:length]], dtype=np.float32)
signal_abs = np.linalg.norm(signal, axis=0) # Поэлементный модуль комплексного числа. shape.result
def _build_window(self, size: int) -> np.ndarray: # (1, self.length)
if self.fft_window in {'', 'none', 'rect', 'rectangular'}: if self.conv_method == 'max':
return np.ones(size, dtype=np.float32) result = np.max(signal_abs)
if self.fft_window == 'hann': else:
return np.hanning(size).astype(np.float32, copy=False) result = np.median(signal_abs)
raise ValueError(f'unsupported fft window: {self.fft_window}') self.signal = signal
self.signal_abs = signal_abs
def _compute_iq_power(self, samples: np.ndarray, signal_abs: np.ndarray) -> float: return result
if self.conv_method == 'max':
return float(np.max(signal_abs * signal_abs)) def fill_signal(self, lvl, length) -> Union[int, float]:
"""
if self.metric_mode in {'fft', 'fft_top_bins', 'top_bins'}: Сбор сигнала в соответствующий массив. Если уже собран, то предобработка.
window = self._build_window(samples.size) :param lvl: Массив, без ограничения общности, с неизвестной длиной, содержащий сигнал.
windowed = samples.astype(np.complex64, copy=False) * window :param length:
spectrum = np.fft.fft(windowed) :return: 0 - если еще нет нужного количества сигнала, "характеристика" иначе.
power_bins = (np.abs(spectrum) ** 2).astype(np.float32, copy=False) """
power_bins /= max(float(np.sum(window * window)), 1.0) if len(self.signal) <= length:
y = np.array(lvl).ravel()
bins_to_keep = min(self.fft_top_bins, power_bins.size) self.signal = np.concatenate((self.signal, y), axis=None)
top_bins = np.partition(power_bins, power_bins.size - bins_to_keep)[-bins_to_keep:] return 0
return float(np.mean(top_bins)) else:
preproc_signal = self.signal_preprocessing(length)
return float(np.mean(signal_abs * signal_abs)) #self.clear()
return preproc_signal
def signal_preprocessing(self, length) -> float:
"""
Предобработка сигнала. class SignalsArray:
"""
:return: Число типа float - "характеристика сигнала". Класс для сохранения медиан сигналов на частотах.
""" Атрибуты:
samples = np.asarray(self.signal).ravel()[0:length] sig_array: Список для сохранения медиан.
if samples.size == 0: counter: Индикатор наполненности массива.
return 0.0 """
def __init__(self):
# Основной режим: считаем dBFS из IQ-вектора. self.sig_array = []
if np.iscomplexobj(samples): self.counter = 0
i = samples.real.astype(np.float32, copy=False)
q = samples.imag.astype(np.float32, copy=False) def fill_sig_arr(self, metrica, num_chs=3):
signal = np.array([i, q], dtype=np.float32) """
signal_abs = np.sqrt(i * i + q * q).astype(np.float32, copy=False) Аппендим характеристику сигнала (метрику) в массив длиной num_chs.
:param metrica: Характеристика сигнала (метрика).
power = self._compute_iq_power(samples, signal_abs) :param num_chs: Количество каналов на частоте.
:return: Индекс канала внутри частоты и массив с характеристиками, если заполнен, иначе - пустой.
result = 10.0 * math.log10(max(power, 1e-20)) """
self.signal = signal if num_chs:
self.signal_abs = signal_abs if self.counter < num_chs:
return result self.sig_array.append(metrica)
self.counter += 1
# Fallback: если на вход уже подали скалярную метрику, агрегируем как есть. if self.counter == num_chs:
scalar_samples = samples.astype(np.float32, copy=False) arr = self.sig_array
if self.conv_method == 'max': self.sig_array = []
result = float(np.max(scalar_samples)) self.counter = 0
else: return num_chs - 1, arr
result = float(np.median(scalar_samples)) else:
return self.counter - 1, []
self.signal = scalar_samples else:
self.signal_abs = np.abs(scalar_samples) return 0, []
return result
def fill_signal(self, lvl, length) -> Union[int, float]:
"""
Сбор сигнала в соответствующий массив. Если уже собран, то предобработка.
:param lvl: Массив, без ограничения общности, с неизвестной длиной, содержащий сигнал.
:param length:
:return: 0 - если еще нет нужного количества сигнала, "характеристика" иначе.
"""
if len(self.signal) <= length:
y = np.array(lvl).ravel()
self.signal = np.concatenate((self.signal, y), axis=None)
return 0
else:
self.last_packet_ts = time.time()
preproc_signal = self.signal_preprocessing(length)
return preproc_signal
class SignalsArray:
"""
Класс для сохранения медиан сигналов на частотах.
Атрибуты:
sig_array: Список для сохранения медиан.
counter: Индикатор наполненности массива.
"""
def __init__(self):
self.sig_array = []
self.sig_ts_array = []
self.counter = 0
def fill_sig_arr(self, metrica, packet_ts=None, num_chs=3):
"""
Аппендим характеристику сигнала (метрику) в массив длиной num_chs.
:param metrica: Характеристика сигнала (метрика).
:param packet_ts: Время завершения набора пакета с SDR для текущей метрики.
:param num_chs: Количество каналов на частоте.
:return: Индекс канала внутри частоты и массив с характеристиками, если заполнен, иначе - пустой.
"""
if num_chs:
if self.counter < num_chs:
self.sig_array.append(metrica)
self.sig_ts_array.append(packet_ts)
self.counter += 1
if self.counter == num_chs:
arr = self.sig_array
arr_ts = self.sig_ts_array
self.sig_array = []
self.sig_ts_array = []
self.counter = 0
return num_chs - 1, arr, arr_ts
else:
return self.counter - 1, [], []
else:
return 0, [], []

@ -1,10 +1,8 @@
import os import os
import datetime import datetime
import time
from common.runtime import load_root_env, validate_env, as_bool, as_str from common.runtime import load_root_env, validate_env, as_bool, as_str
from smb.SMBConnection import SMBConnection from smb.SMBConnection import SMBConnection
from utils.datas_processing import pack_elems, agregator, send_data, send_telemetry, save_data, remote_save_data from utils.datas_processing import pack_elems, agregator, send_data, save_data, remote_save_data
from utils.jammer_state_flag import is_jammer_active
from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length
from core.multichannelswitcher import MultiChannel, get_centre_freq from core.multichannelswitcher import MultiChannel, get_centre_freq
@ -48,11 +46,6 @@ the_pc_name = os.getenv('the_pc_name')
remote_pc_name = os.getenv('remote_pc_name') remote_pc_name = os.getenv('remote_pc_name')
smb_domain = os.getenv('smb_domain') smb_domain = os.getenv('smb_domain')
freq_endpoint = os.getenv('freq_endpoint') freq_endpoint = os.getenv('freq_endpoint')
telemetry_enabled = as_bool(os.getenv('telemetry_enabled', '1'))
telemetry_host = os.getenv('telemetry_host', '127.0.0.1')
telemetry_port = os.getenv('telemetry_port', '5020')
telemetry_endpoint = os.getenv('telemetry_endpoint', 'telemetry')
telemetry_timeout_sec = float(os.getenv('telemetry_timeout_sec', '0.30'))
elems_to_save = elems_to_save.split(',') elems_to_save = elems_to_save.split(',')
file_types_to_save = file_types_to_save.split(',') file_types_to_save = file_types_to_save.split(',')
@ -76,12 +69,11 @@ def work(lvl):
freq = get_centre_freq(f) freq = get_centre_freq(f)
signal_length = get_signal_length(freq) signal_length = get_signal_length(freq)
median = tmp_signal.fill_signal(lvl, signal_length) median = tmp_signal.fill_signal(lvl, signal_length)
packet_ts = tmp_signal.get_last_packet_ts()
if median: if median:
try: try:
num_chs, circle_buffer = multi_channel.check_f(f) num_chs, circle_buffer = multi_channel.check_f(f)
cur_channel, sigs_array, sigs_ts_array = tmp_sigs_array.fill_sig_arr(median, packet_ts=packet_ts, num_chs=num_chs) cur_channel, sigs_array = tmp_sigs_array.fill_sig_arr(median, num_chs)
if sigs_array: if sigs_array:
print('Значения на {0}: {1}'.format(freq, sigs_array)) print('Значения на {0}: {1}'.format(freq, sigs_array))
@ -91,40 +83,8 @@ def work(lvl):
if alarm: if alarm:
print('----ALARM---- ', freq) print('----ALARM---- ', freq)
multi_channel.db_alarms_zeros(circle_buffer) multi_channel.db_alarms_zeros(circle_buffer)
elif not is_jammer_active(): else:
circle_buffer.update(sigs_array, packet_timestamps=sigs_ts_array) circle_buffer.update(sigs_array)
if telemetry_enabled:
try:
max_idx = max(range(len(sigs_array)), key=lambda idx: sigs_array[idx])
dbfs_current = float(sigs_array[max_idx])
dbfs_threshold = circle_buffer.get_threshold(max_idx)
channel_thresholds = circle_buffer.get_thresholds()
alarm_channels = circle_buffer.get_last_alarm_channels() if alarm else []
send_telemetry(
data={
"freq": str(freq),
"ts": time.time(),
"dbfs_current": dbfs_current,
"dbfs_threshold": dbfs_threshold,
"alarm": bool(alarm),
"channel_idx": int(max_idx),
"channels_total": int(len(sigs_array)),
"channel_values": [float(v) for v in sigs_array],
"channel_thresholds": channel_thresholds,
"alarm_channels": alarm_channels,
},
host=telemetry_host,
port=telemetry_port,
endpoint=telemetry_endpoint,
timeout_sec=telemetry_timeout_sec,
)
except Exception as exc:
if debug_flag:
print(f"telemetry send failed: {exc}")
if send_to_module_flag: if send_to_module_flag:
send_data(agregator(freq, alarm), localhost, localport, freq_endpoint) send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)

@ -1,13 +1,11 @@
import os import os
import datetime import datetime
import time from common.runtime import load_root_env, validate_env, as_bool, as_str
from common.runtime import load_root_env, validate_env, as_bool, as_str
from smb.SMBConnection import SMBConnection from smb.SMBConnection import SMBConnection
from utils.datas_processing import pack_elems, agregator, send_data, send_telemetry, save_data, remote_save_data from utils.datas_processing import pack_elems, agregator, send_data, save_data, remote_save_data
from utils.jammer_state_flag import is_jammer_active from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length
from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length from core.multichannelswitcher import MultiChannel, get_centre_freq
from core.multichannelswitcher import MultiChannel, get_centre_freq
load_root_env(__file__) load_root_env(__file__)
freq_suffix = os.path.splitext(os.path.basename(__file__))[0].split("_")[-1] freq_suffix = os.path.splitext(os.path.basename(__file__))[0].split("_")[-1]
validate_env(__file__, { validate_env(__file__, {
@ -25,136 +23,98 @@ validate_env(__file__, {
f"f_bases_{freq_suffix}": as_str, f"f_bases_{freq_suffix}": as_str,
f"f_roofs_{freq_suffix}": as_str, f"f_roofs_{freq_suffix}": as_str,
}) })
debug_flag = as_bool(os.getenv('debug_flag', '0')) debug_flag = as_bool(os.getenv('debug_flag', '0'))
send_to_module_flag = as_bool(os.getenv('send_to_module_flag', '0')) send_to_module_flag = as_bool(os.getenv('send_to_module_flag', '0'))
save_data_flag = as_bool(os.getenv('save_data_flag', '0')) save_data_flag = as_bool(os.getenv('save_data_flag', '0'))
module_name = os.getenv('module_name') module_name = os.getenv('module_name')
elems_to_save = os.getenv('elems_to_save') elems_to_save = os.getenv('elems_to_save')
file_types_to_save = os.getenv('file_types_to_save') file_types_to_save = os.getenv('file_types_to_save')
localhost = os.getenv('lochost') localhost = os.getenv('lochost')
localport = os.getenv('locport') localport = os.getenv('locport')
f_step = [*map(float, os.getenv('f_step_433').split())] f_step = [*map(float, os.getenv('f_step_433').split())]
f_bases = [*map(float, os.getenv('f_bases_433').split())] f_bases = [*map(float, os.getenv('f_bases_433').split())]
f_roofs = [*map(float, os.getenv('f_roofs_433').split())] f_roofs = [*map(float, os.getenv('f_roofs_433').split())]
path_to_save_medians = os.getenv('path_to_save_medians') path_to_save_medians = os.getenv('path_to_save_medians')
path_to_save_alarms = os.getenv('path_to_save_alarms') path_to_save_alarms = os.getenv('path_to_save_alarms')
smb_host = os.getenv('smb_host') smb_host = os.getenv('smb_host')
smb_port = os.getenv('smb_port') smb_port = os.getenv('smb_port')
smb_user = os.getenv('smb_user') smb_user = os.getenv('smb_user')
smb_pass = os.getenv('smb_pass') smb_pass = os.getenv('smb_pass')
shared_folder = os.getenv('shared_folder') shared_folder = os.getenv('shared_folder')
the_pc_name = os.getenv('the_pc_name') the_pc_name = os.getenv('the_pc_name')
remote_pc_name = os.getenv('remote_pc_name') remote_pc_name = os.getenv('remote_pc_name')
smb_domain = os.getenv('smb_domain') smb_domain = os.getenv('smb_domain')
freq_endpoint = os.getenv('freq_endpoint') freq_endpoint = os.getenv('freq_endpoint')
telemetry_enabled = as_bool(os.getenv('telemetry_enabled', '1'))
telemetry_host = os.getenv('telemetry_host', '127.0.0.1') elems_to_save = elems_to_save.split(',')
telemetry_port = os.getenv('telemetry_port', '5020') file_types_to_save = file_types_to_save.split(',')
telemetry_endpoint = os.getenv('telemetry_endpoint', 'telemetry')
telemetry_timeout_sec = float(os.getenv('telemetry_timeout_sec', '0.30')) tmp_signal = Signal()
tmp_sigs_array = SignalsArray()
elems_to_save = elems_to_save.split(',') multi_channel = MultiChannel(f_step, f_bases, f_roofs)
file_types_to_save = file_types_to_save.split(',') f = multi_channel.init_f()
multi_channel.fill_DB()
tmp_signal = Signal()
tmp_sigs_array = SignalsArray() if debug_flag:
multi_channel = MultiChannel(f_step, f_bases, f_roofs) conn = SMBConnection(smb_user, smb_pass, the_pc_name, remote_pc_name, use_ntlm_v2=True)
f = multi_channel.init_f() conn.connect(smb_host, 139)
multi_channel.fill_DB() filelist = conn.listPath(shared_folder, '/')
print(filelist)
if debug_flag:
conn = SMBConnection(smb_user, smb_pass, the_pc_name, remote_pc_name, use_ntlm_v2=True)
conn.connect(smb_host, 139) def work(lvl):
filelist = conn.listPath(shared_folder, '/')
print(filelist) f = multi_channel.get_cur_channel()
freq = get_centre_freq(f)
signal_length = get_signal_length(freq)
def work(lvl): median = tmp_signal.fill_signal(lvl, signal_length)
f = multi_channel.get_cur_channel() if median:
freq = get_centre_freq(f) try:
signal_length = get_signal_length(freq) num_chs, circle_buffer = multi_channel.check_f(f)
median = tmp_signal.fill_signal(lvl, signal_length) cur_channel, sigs_array = tmp_sigs_array.fill_sig_arr(median, num_chs)
packet_ts = tmp_signal.get_last_packet_ts()
if sigs_array:
if median: print('Значения на {0}: {1}'.format(freq, sigs_array))
try: print('Пороги: ', circle_buffer.get_medians())
num_chs, circle_buffer = multi_channel.check_f(f) alarm = circle_buffer.check_alarm(sigs_array)
cur_channel, sigs_array, sigs_ts_array = tmp_sigs_array.fill_sig_arr(median, packet_ts=packet_ts, num_chs=num_chs)
if alarm:
if sigs_array: print('----ALARM---- ', freq)
print('Значения на {0}: {1}'.format(freq, sigs_array)) multi_channel.db_alarms_zeros(circle_buffer)
print('Пороги: ', circle_buffer.get_medians()) else:
alarm = circle_buffer.check_alarm(sigs_array) circle_buffer.update(sigs_array)
if alarm: if send_to_module_flag:
print('----ALARM---- ', freq) send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)
multi_channel.db_alarms_zeros(circle_buffer)
elif not is_jammer_active(): if save_data_flag:
circle_buffer.update(sigs_array, packet_timestamps=sigs_ts_array) if not circle_buffer.check_init() and circle_buffer.current_column - 1 == 0:
save_data(path_to_save_medians, freq, 'DateTime', 'ALARM', 'max signal', list(range(num_chs)),
list(range(num_chs)))
if telemetry_enabled: if circle_buffer.check_init():
try: save_data(path_to_save_medians, freq, datetime.datetime.now(), alarm, max(sigs_array), sigs_array,
max_idx = max(range(len(sigs_array)), key=lambda idx: sigs_array[idx]) circle_buffer.get_medians())
dbfs_current = float(sigs_array[max_idx])
if debug_flag:
dbfs_threshold = circle_buffer.get_threshold(max_idx) single_alarm = circle_buffer.check_single_alarm(median, cur_channel)
channel_thresholds = circle_buffer.get_thresholds() print(cur_channel, single_alarm)
alarm_channels = circle_buffer.get_last_alarm_channels() if alarm else [] if single_alarm:
data = pack_elems(elems_to_save, file_types_to_save, tmp_signal.get_signal())
send_telemetry( print('SAVE CURRENT SIGNAL SROCHNO TI MENYA SLISHISH?!?!?!?')
data={ try:
"freq": str(freq), remote_save_data(conn, data, module_name, freq, shared_folder, path_to_save_alarms)
"ts": time.time(), except Exception as e:
"dbfs_current": dbfs_current, print(f"Ошибка: {e}")
"dbfs_threshold": dbfs_threshold, else:
"alarm": bool(alarm), print('VSE OKI DOKI SIGNAL SOKHRANYAT NE NUZHNO!!!')
"channel_idx": int(max_idx),
"channels_total": int(len(sigs_array)), f = multi_channel.change_channel()
"channel_values": [float(v) for v in sigs_array], except Exception as e:
"channel_thresholds": channel_thresholds, print(str(e))
"alarm_channels": alarm_channels, print(".", end='')
},
host=telemetry_host, tmp_signal.clear()
port=telemetry_port,
endpoint=telemetry_endpoint, return f
timeout_sec=telemetry_timeout_sec,
)
except Exception as exc:
if debug_flag:
print(f"telemetry send failed: {exc}")
if send_to_module_flag:
send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)
if save_data_flag:
if not circle_buffer.check_init() and circle_buffer.current_column - 1 == 0:
save_data(path_to_save_medians, freq, 'DateTime', 'ALARM', 'max signal', list(range(num_chs)),
list(range(num_chs)))
if circle_buffer.check_init():
save_data(path_to_save_medians, freq, datetime.datetime.now(), alarm, max(sigs_array), sigs_array,
circle_buffer.get_medians())
if debug_flag:
single_alarm = circle_buffer.check_single_alarm(median, cur_channel)
print(cur_channel, single_alarm)
if single_alarm:
data = pack_elems(elems_to_save, file_types_to_save, tmp_signal.get_signal())
print('SAVE CURRENT SIGNAL SROCHNO TI MENYA SLISHISH?!?!?!?')
try:
remote_save_data(conn, data, module_name, freq, shared_folder, path_to_save_alarms)
except Exception as e:
print(f"Ошибка: {e}")
else:
print('VSE OKI DOKI SIGNAL SOKHRANYAT NE NUZHNO!!!')
f = multi_channel.change_channel()
except Exception as e:
print(str(e))
print(".", end='')
tmp_signal.clear()
return f

@ -1,10 +1,8 @@
import os import os
import datetime import datetime
import time
from common.runtime import load_root_env, validate_env, as_bool, as_str from common.runtime import load_root_env, validate_env, as_bool, as_str
from smb.SMBConnection import SMBConnection from smb.SMBConnection import SMBConnection
from utils.datas_processing import pack_elems, agregator, send_data, send_telemetry, save_data, remote_save_data from utils.datas_processing import pack_elems, agregator, send_data, save_data, remote_save_data
from utils.jammer_state_flag import is_jammer_active
from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length
from core.multichannelswitcher import MultiChannel, get_centre_freq from core.multichannelswitcher import MultiChannel, get_centre_freq
@ -48,11 +46,6 @@ the_pc_name = os.getenv('the_pc_name')
remote_pc_name = os.getenv('remote_pc_name') remote_pc_name = os.getenv('remote_pc_name')
smb_domain = os.getenv('smb_domain') smb_domain = os.getenv('smb_domain')
freq_endpoint = os.getenv('freq_endpoint') freq_endpoint = os.getenv('freq_endpoint')
telemetry_enabled = as_bool(os.getenv('telemetry_enabled', '1'))
telemetry_host = os.getenv('telemetry_host', '127.0.0.1')
telemetry_port = os.getenv('telemetry_port', '5020')
telemetry_endpoint = os.getenv('telemetry_endpoint', 'telemetry')
telemetry_timeout_sec = float(os.getenv('telemetry_timeout_sec', '0.30'))
elems_to_save = elems_to_save.split(',') elems_to_save = elems_to_save.split(',')
file_types_to_save = file_types_to_save.split(',') file_types_to_save = file_types_to_save.split(',')
@ -76,12 +69,11 @@ def work(lvl):
freq = get_centre_freq(f) freq = get_centre_freq(f)
signal_length = get_signal_length(freq) signal_length = get_signal_length(freq)
median = tmp_signal.fill_signal(lvl, signal_length) median = tmp_signal.fill_signal(lvl, signal_length)
packet_ts = tmp_signal.get_last_packet_ts()
if median: if median:
try: try:
num_chs, circle_buffer = multi_channel.check_f(f) num_chs, circle_buffer = multi_channel.check_f(f)
cur_channel, sigs_array, sigs_ts_array = tmp_sigs_array.fill_sig_arr(median, packet_ts=packet_ts, num_chs=num_chs) cur_channel, sigs_array = tmp_sigs_array.fill_sig_arr(median, num_chs)
if sigs_array: if sigs_array:
print('Значения на {0}: {1}'.format(freq, sigs_array)) print('Значения на {0}: {1}'.format(freq, sigs_array))
@ -91,40 +83,8 @@ def work(lvl):
if alarm: if alarm:
print('----ALARM---- ', freq) print('----ALARM---- ', freq)
multi_channel.db_alarms_zeros(circle_buffer) multi_channel.db_alarms_zeros(circle_buffer)
elif not is_jammer_active(): else:
circle_buffer.update(sigs_array, packet_timestamps=sigs_ts_array) circle_buffer.update(sigs_array)
if telemetry_enabled:
try:
max_idx = max(range(len(sigs_array)), key=lambda idx: sigs_array[idx])
dbfs_current = float(sigs_array[max_idx])
dbfs_threshold = circle_buffer.get_threshold(max_idx)
channel_thresholds = circle_buffer.get_thresholds()
alarm_channels = circle_buffer.get_last_alarm_channels() if alarm else []
send_telemetry(
data={
"freq": str(freq),
"ts": time.time(),
"dbfs_current": dbfs_current,
"dbfs_threshold": dbfs_threshold,
"alarm": bool(alarm),
"channel_idx": int(max_idx),
"channels_total": int(len(sigs_array)),
"channel_values": [float(v) for v in sigs_array],
"channel_thresholds": channel_thresholds,
"alarm_channels": alarm_channels,
},
host=telemetry_host,
port=telemetry_port,
endpoint=telemetry_endpoint,
timeout_sec=telemetry_timeout_sec,
)
except Exception as exc:
if debug_flag:
print(f"telemetry send failed: {exc}")
if send_to_module_flag: if send_to_module_flag:
send_data(agregator(freq, alarm), localhost, localport, freq_endpoint) send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)

@ -1,13 +1,11 @@
import os import os
import datetime import datetime
import time from common.runtime import load_root_env, validate_env, as_bool, as_str
from common.runtime import load_root_env, validate_env, as_bool, as_str
from smb.SMBConnection import SMBConnection from smb.SMBConnection import SMBConnection
from utils.datas_processing import pack_elems, agregator, send_data, send_telemetry, save_data, remote_save_data from utils.datas_processing import pack_elems, agregator, send_data, save_data, remote_save_data
from utils.jammer_state_flag import is_jammer_active from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length
from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length from core.multichannelswitcher import MultiChannel, get_centre_freq
from core.multichannelswitcher import MultiChannel, get_centre_freq
load_root_env(__file__) load_root_env(__file__)
freq_suffix = os.path.splitext(os.path.basename(__file__))[0].split("_")[-1] freq_suffix = os.path.splitext(os.path.basename(__file__))[0].split("_")[-1]
validate_env(__file__, { validate_env(__file__, {
@ -25,136 +23,98 @@ validate_env(__file__, {
f"f_bases_{freq_suffix}": as_str, f"f_bases_{freq_suffix}": as_str,
f"f_roofs_{freq_suffix}": as_str, f"f_roofs_{freq_suffix}": as_str,
}) })
debug_flag = as_bool(os.getenv('debug_flag', '0')) debug_flag = as_bool(os.getenv('debug_flag', '0'))
send_to_module_flag = as_bool(os.getenv('send_to_module_flag', '0')) send_to_module_flag = as_bool(os.getenv('send_to_module_flag', '0'))
save_data_flag = as_bool(os.getenv('save_data_flag', '0')) save_data_flag = as_bool(os.getenv('save_data_flag', '0'))
module_name = os.getenv('module_name') module_name = os.getenv('module_name')
elems_to_save = os.getenv('elems_to_save') elems_to_save = os.getenv('elems_to_save')
file_types_to_save = os.getenv('file_types_to_save') file_types_to_save = os.getenv('file_types_to_save')
localhost = os.getenv('lochost') localhost = os.getenv('lochost')
localport = os.getenv('locport') localport = os.getenv('locport')
f_step = [*map(float, os.getenv('f_step_5200').split())] f_step = [*map(float, os.getenv('f_step_5200').split())]
f_bases = [*map(float, os.getenv('f_bases_5200').split())] f_bases = [*map(float, os.getenv('f_bases_5200').split())]
f_roofs = [*map(float, os.getenv('f_roofs_5200').split())] f_roofs = [*map(float, os.getenv('f_roofs_5200').split())]
path_to_save_medians = os.getenv('path_to_save_medians') path_to_save_medians = os.getenv('path_to_save_medians')
path_to_save_alarms = os.getenv('path_to_save_alarms') path_to_save_alarms = os.getenv('path_to_save_alarms')
smb_host = os.getenv('smb_host') smb_host = os.getenv('smb_host')
smb_port = os.getenv('smb_port') smb_port = os.getenv('smb_port')
smb_user = os.getenv('smb_user') smb_user = os.getenv('smb_user')
smb_pass = os.getenv('smb_pass') smb_pass = os.getenv('smb_pass')
shared_folder = os.getenv('shared_folder') shared_folder = os.getenv('shared_folder')
the_pc_name = os.getenv('the_pc_name') the_pc_name = os.getenv('the_pc_name')
remote_pc_name = os.getenv('remote_pc_name') remote_pc_name = os.getenv('remote_pc_name')
smb_domain = os.getenv('smb_domain') smb_domain = os.getenv('smb_domain')
freq_endpoint = os.getenv('freq_endpoint') freq_endpoint = os.getenv('freq_endpoint')
telemetry_enabled = as_bool(os.getenv('telemetry_enabled', '1'))
telemetry_host = os.getenv('telemetry_host', '127.0.0.1') elems_to_save = elems_to_save.split(',')
telemetry_port = os.getenv('telemetry_port', '5020') file_types_to_save = file_types_to_save.split(',')
telemetry_endpoint = os.getenv('telemetry_endpoint', 'telemetry')
telemetry_timeout_sec = float(os.getenv('telemetry_timeout_sec', '0.30')) tmp_signal = Signal()
tmp_sigs_array = SignalsArray()
elems_to_save = elems_to_save.split(',') multi_channel = MultiChannel(f_step, f_bases, f_roofs)
file_types_to_save = file_types_to_save.split(',') f = multi_channel.init_f()
multi_channel.fill_DB()
tmp_signal = Signal()
tmp_sigs_array = SignalsArray() if debug_flag:
multi_channel = MultiChannel(f_step, f_bases, f_roofs) conn = SMBConnection(smb_user, smb_pass, the_pc_name, remote_pc_name, use_ntlm_v2=True)
f = multi_channel.init_f() conn.connect(smb_host, 139)
multi_channel.fill_DB() filelist = conn.listPath(shared_folder, '/')
print(filelist)
if debug_flag:
conn = SMBConnection(smb_user, smb_pass, the_pc_name, remote_pc_name, use_ntlm_v2=True)
conn.connect(smb_host, 139) def work(lvl):
filelist = conn.listPath(shared_folder, '/')
print(filelist) f = multi_channel.get_cur_channel()
freq = get_centre_freq(f)
signal_length = get_signal_length(freq)
def work(lvl): median = tmp_signal.fill_signal(lvl, signal_length)
f = multi_channel.get_cur_channel() if median:
freq = get_centre_freq(f) try:
signal_length = get_signal_length(freq) num_chs, circle_buffer = multi_channel.check_f(f)
median = tmp_signal.fill_signal(lvl, signal_length) cur_channel, sigs_array = tmp_sigs_array.fill_sig_arr(median, num_chs)
packet_ts = tmp_signal.get_last_packet_ts()
if sigs_array:
if median: print('Значения на {0}: {1}'.format(freq, sigs_array))
try: print('Пороги: ', circle_buffer.get_medians())
num_chs, circle_buffer = multi_channel.check_f(f) alarm = circle_buffer.check_alarm(sigs_array)
cur_channel, sigs_array, sigs_ts_array = tmp_sigs_array.fill_sig_arr(median, packet_ts=packet_ts, num_chs=num_chs)
if alarm:
if sigs_array: print('----ALARM---- ', freq)
print('Значения на {0}: {1}'.format(freq, sigs_array)) multi_channel.db_alarms_zeros(circle_buffer)
print('Пороги: ', circle_buffer.get_medians()) else:
alarm = circle_buffer.check_alarm(sigs_array) circle_buffer.update(sigs_array)
if alarm: if send_to_module_flag:
print('----ALARM---- ', freq) send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)
multi_channel.db_alarms_zeros(circle_buffer)
elif not is_jammer_active(): if save_data_flag:
circle_buffer.update(sigs_array, packet_timestamps=sigs_ts_array) if not circle_buffer.check_init() and circle_buffer.current_column - 1 == 0:
save_data(path_to_save_medians, freq, 'DateTime', 'ALARM', 'max signal', list(range(num_chs)),
list(range(num_chs)))
if telemetry_enabled: if circle_buffer.check_init():
try: save_data(path_to_save_medians, freq, datetime.datetime.now(), alarm, max(sigs_array), sigs_array,
max_idx = max(range(len(sigs_array)), key=lambda idx: sigs_array[idx]) circle_buffer.get_medians())
dbfs_current = float(sigs_array[max_idx])
if debug_flag:
dbfs_threshold = circle_buffer.get_threshold(max_idx) single_alarm = circle_buffer.check_single_alarm(median, cur_channel)
channel_thresholds = circle_buffer.get_thresholds() print(cur_channel, single_alarm)
alarm_channels = circle_buffer.get_last_alarm_channels() if alarm else [] if single_alarm:
data = pack_elems(elems_to_save, file_types_to_save, tmp_signal.get_signal())
send_telemetry( print('SAVE CURRENT SIGNAL SROCHNO TI MENYA SLISHISH?!?!?!?')
data={ try:
"freq": str(freq), remote_save_data(conn, data, module_name, freq, shared_folder, path_to_save_alarms)
"ts": time.time(), except Exception as e:
"dbfs_current": dbfs_current, print(f"Ошибка: {e}")
"dbfs_threshold": dbfs_threshold, else:
"alarm": bool(alarm), print('VSE OKI DOKI SIGNAL SOKHRANYAT NE NUZHNO!!!')
"channel_idx": int(max_idx),
"channels_total": int(len(sigs_array)), f = multi_channel.change_channel()
"channel_values": [float(v) for v in sigs_array], except Exception as e:
"channel_thresholds": channel_thresholds, print(str(e))
"alarm_channels": alarm_channels, print(".", end='')
},
host=telemetry_host, tmp_signal.clear()
port=telemetry_port,
endpoint=telemetry_endpoint, return f
timeout_sec=telemetry_timeout_sec,
)
except Exception as exc:
if debug_flag:
print(f"telemetry send failed: {exc}")
if send_to_module_flag:
send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)
if save_data_flag:
if not circle_buffer.check_init() and circle_buffer.current_column - 1 == 0:
save_data(path_to_save_medians, freq, 'DateTime', 'ALARM', 'max signal', list(range(num_chs)),
list(range(num_chs)))
if circle_buffer.check_init():
save_data(path_to_save_medians, freq, datetime.datetime.now(), alarm, max(sigs_array), sigs_array,
circle_buffer.get_medians())
if debug_flag:
single_alarm = circle_buffer.check_single_alarm(median, cur_channel)
print(cur_channel, single_alarm)
if single_alarm:
data = pack_elems(elems_to_save, file_types_to_save, tmp_signal.get_signal())
print('SAVE CURRENT SIGNAL SROCHNO TI MENYA SLISHISH?!?!?!?')
try:
remote_save_data(conn, data, module_name, freq, shared_folder, path_to_save_alarms)
except Exception as e:
print(f"Ошибка: {e}")
else:
print('VSE OKI DOKI SIGNAL SOKHRANYAT NE NUZHNO!!!')
f = multi_channel.change_channel()
except Exception as e:
print(str(e))
print(".", end='')
tmp_signal.clear()
return f

@ -1,13 +1,11 @@
import os import os
import datetime import datetime
import time from common.runtime import load_root_env, validate_env, as_bool, as_str
from common.runtime import load_root_env, validate_env, as_bool, as_str
from smb.SMBConnection import SMBConnection from smb.SMBConnection import SMBConnection
from utils.datas_processing import pack_elems, agregator, send_data, send_telemetry, save_data, remote_save_data from utils.datas_processing import pack_elems, agregator, send_data, save_data, remote_save_data
from utils.jammer_state_flag import is_jammer_active from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length
from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length from core.multichannelswitcher import MultiChannel, get_centre_freq
from core.multichannelswitcher import MultiChannel, get_centre_freq
load_root_env(__file__) load_root_env(__file__)
freq_suffix = os.path.splitext(os.path.basename(__file__))[0].split("_")[-1] freq_suffix = os.path.splitext(os.path.basename(__file__))[0].split("_")[-1]
validate_env(__file__, { validate_env(__file__, {
@ -25,136 +23,98 @@ validate_env(__file__, {
f"f_bases_{freq_suffix}": as_str, f"f_bases_{freq_suffix}": as_str,
f"f_roofs_{freq_suffix}": as_str, f"f_roofs_{freq_suffix}": as_str,
}) })
debug_flag = as_bool(os.getenv('debug_flag', '0')) debug_flag = as_bool(os.getenv('debug_flag', '0'))
send_to_module_flag = as_bool(os.getenv('send_to_module_flag', '0')) send_to_module_flag = as_bool(os.getenv('send_to_module_flag', '0'))
save_data_flag = as_bool(os.getenv('save_data_flag', '0')) save_data_flag = as_bool(os.getenv('save_data_flag', '0'))
module_name = os.getenv('module_name') module_name = os.getenv('module_name')
elems_to_save = os.getenv('elems_to_save') elems_to_save = os.getenv('elems_to_save')
file_types_to_save = os.getenv('file_types_to_save') file_types_to_save = os.getenv('file_types_to_save')
localhost = os.getenv('lochost') localhost = os.getenv('lochost')
localport = os.getenv('locport') localport = os.getenv('locport')
f_step = [*map(float, os.getenv('f_step_5800').split())] f_step = [*map(float, os.getenv('f_step_5800').split())]
f_bases = [*map(float, os.getenv('f_bases_5800').split())] f_bases = [*map(float, os.getenv('f_bases_5800').split())]
f_roofs = [*map(float, os.getenv('f_roofs_5800').split())] f_roofs = [*map(float, os.getenv('f_roofs_5800').split())]
path_to_save_medians = os.getenv('path_to_save_medians') path_to_save_medians = os.getenv('path_to_save_medians')
path_to_save_alarms = os.getenv('path_to_save_alarms') path_to_save_alarms = os.getenv('path_to_save_alarms')
smb_host = os.getenv('smb_host') smb_host = os.getenv('smb_host')
smb_port = os.getenv('smb_port') smb_port = os.getenv('smb_port')
smb_user = os.getenv('smb_user') smb_user = os.getenv('smb_user')
smb_pass = os.getenv('smb_pass') smb_pass = os.getenv('smb_pass')
shared_folder = os.getenv('shared_folder') shared_folder = os.getenv('shared_folder')
the_pc_name = os.getenv('the_pc_name') the_pc_name = os.getenv('the_pc_name')
remote_pc_name = os.getenv('remote_pc_name') remote_pc_name = os.getenv('remote_pc_name')
smb_domain = os.getenv('smb_domain') smb_domain = os.getenv('smb_domain')
freq_endpoint = os.getenv('freq_endpoint') freq_endpoint = os.getenv('freq_endpoint')
telemetry_enabled = as_bool(os.getenv('telemetry_enabled', '1'))
telemetry_host = os.getenv('telemetry_host', '127.0.0.1') elems_to_save = elems_to_save.split(',')
telemetry_port = os.getenv('telemetry_port', '5020') file_types_to_save = file_types_to_save.split(',')
telemetry_endpoint = os.getenv('telemetry_endpoint', 'telemetry')
telemetry_timeout_sec = float(os.getenv('telemetry_timeout_sec', '0.30')) tmp_signal = Signal()
tmp_sigs_array = SignalsArray()
elems_to_save = elems_to_save.split(',') multi_channel = MultiChannel(f_step, f_bases, f_roofs)
file_types_to_save = file_types_to_save.split(',') f = multi_channel.init_f()
multi_channel.fill_DB()
tmp_signal = Signal()
tmp_sigs_array = SignalsArray() if debug_flag:
multi_channel = MultiChannel(f_step, f_bases, f_roofs) conn = SMBConnection(smb_user, smb_pass, the_pc_name, remote_pc_name, use_ntlm_v2=True)
f = multi_channel.init_f() conn.connect(smb_host, 139)
multi_channel.fill_DB() filelist = conn.listPath(shared_folder, '/')
print(filelist)
if debug_flag:
conn = SMBConnection(smb_user, smb_pass, the_pc_name, remote_pc_name, use_ntlm_v2=True)
conn.connect(smb_host, 139) def work(lvl):
filelist = conn.listPath(shared_folder, '/')
print(filelist) f = multi_channel.get_cur_channel()
freq = get_centre_freq(f)
signal_length = get_signal_length(freq)
def work(lvl): median = tmp_signal.fill_signal(lvl, signal_length)
f = multi_channel.get_cur_channel() if median:
freq = get_centre_freq(f) try:
signal_length = get_signal_length(freq) num_chs, circle_buffer = multi_channel.check_f(f)
median = tmp_signal.fill_signal(lvl, signal_length) cur_channel, sigs_array = tmp_sigs_array.fill_sig_arr(median, num_chs)
packet_ts = tmp_signal.get_last_packet_ts()
if sigs_array:
if median: print('Значения на {0}: {1}'.format(freq, sigs_array))
try: print('Пороги: ', circle_buffer.get_medians())
num_chs, circle_buffer = multi_channel.check_f(f) alarm = circle_buffer.check_alarm(sigs_array)
cur_channel, sigs_array, sigs_ts_array = tmp_sigs_array.fill_sig_arr(median, packet_ts=packet_ts, num_chs=num_chs)
if alarm:
if sigs_array: print('----ALARM---- ', freq)
print('Значения на {0}: {1}'.format(freq, sigs_array)) multi_channel.db_alarms_zeros(circle_buffer)
print('Пороги: ', circle_buffer.get_medians()) else:
alarm = circle_buffer.check_alarm(sigs_array) circle_buffer.update(sigs_array)
if alarm: if send_to_module_flag:
print('----ALARM---- ', freq) send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)
multi_channel.db_alarms_zeros(circle_buffer)
elif not is_jammer_active(): if save_data_flag:
circle_buffer.update(sigs_array, packet_timestamps=sigs_ts_array) if not circle_buffer.check_init() and circle_buffer.current_column - 1 == 0:
save_data(path_to_save_medians, freq, 'DateTime', 'ALARM', 'max signal', list(range(num_chs)),
list(range(num_chs)))
if telemetry_enabled: if circle_buffer.check_init():
try: save_data(path_to_save_medians, freq, datetime.datetime.now(), alarm, max(sigs_array), sigs_array,
max_idx = max(range(len(sigs_array)), key=lambda idx: sigs_array[idx]) circle_buffer.get_medians())
dbfs_current = float(sigs_array[max_idx])
if debug_flag:
dbfs_threshold = circle_buffer.get_threshold(max_idx) single_alarm = circle_buffer.check_single_alarm(median, cur_channel)
channel_thresholds = circle_buffer.get_thresholds() print(cur_channel, single_alarm)
alarm_channels = circle_buffer.get_last_alarm_channels() if alarm else [] if single_alarm:
data = pack_elems(elems_to_save, file_types_to_save, tmp_signal.get_signal())
send_telemetry( print('SAVE CURRENT SIGNAL SROCHNO TI MENYA SLISHISH?!?!?!?')
data={ try:
"freq": str(freq), remote_save_data(conn, data, module_name, freq, shared_folder, path_to_save_alarms)
"ts": time.time(), except Exception as e:
"dbfs_current": dbfs_current, print(f"Ошибка: {e}")
"dbfs_threshold": dbfs_threshold, else:
"alarm": bool(alarm), print('VSE OKI DOKI SIGNAL SOKHRANYAT NE NUZHNO!!!')
"channel_idx": int(max_idx),
"channels_total": int(len(sigs_array)), f = multi_channel.change_channel()
"channel_values": [float(v) for v in sigs_array], except Exception as e:
"channel_thresholds": channel_thresholds, print(str(e))
"alarm_channels": alarm_channels, print(".", end='')
},
host=telemetry_host, tmp_signal.clear()
port=telemetry_port,
endpoint=telemetry_endpoint, return f
timeout_sec=telemetry_timeout_sec,
)
except Exception as exc:
if debug_flag:
print(f"telemetry send failed: {exc}")
if send_to_module_flag:
send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)
if save_data_flag:
if not circle_buffer.check_init() and circle_buffer.current_column - 1 == 0:
save_data(path_to_save_medians, freq, 'DateTime', 'ALARM', 'max signal', list(range(num_chs)),
list(range(num_chs)))
if circle_buffer.check_init():
save_data(path_to_save_medians, freq, datetime.datetime.now(), alarm, max(sigs_array), sigs_array,
circle_buffer.get_medians())
if debug_flag:
single_alarm = circle_buffer.check_single_alarm(median, cur_channel)
print(cur_channel, single_alarm)
if single_alarm:
data = pack_elems(elems_to_save, file_types_to_save, tmp_signal.get_signal())
print('SAVE CURRENT SIGNAL SROCHNO TI MENYA SLISHISH?!?!?!?')
try:
remote_save_data(conn, data, module_name, freq, shared_folder, path_to_save_alarms)
except Exception as e:
print(f"Ошибка: {e}")
else:
print('VSE OKI DOKI SIGNAL SOKHRANYAT NE NUZHNO!!!')
f = multi_channel.change_channel()
except Exception as e:
print(str(e))
print(".", end='')
tmp_signal.clear()
return f

@ -1,13 +1,11 @@
import os import os
import datetime import datetime
import time from common.runtime import load_root_env, validate_env, as_bool, as_str
from common.runtime import load_root_env, validate_env, as_bool, as_str
from smb.SMBConnection import SMBConnection from smb.SMBConnection import SMBConnection
from utils.datas_processing import pack_elems, agregator, send_data, send_telemetry, save_data, remote_save_data from utils.datas_processing import pack_elems, agregator, send_data, save_data, remote_save_data
from utils.jammer_state_flag import is_jammer_active from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length
from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length from core.multichannelswitcher import MultiChannel, get_centre_freq
from core.multichannelswitcher import MultiChannel, get_centre_freq
load_root_env(__file__) load_root_env(__file__)
freq_suffix = os.path.splitext(os.path.basename(__file__))[0].split("_")[-1] freq_suffix = os.path.splitext(os.path.basename(__file__))[0].split("_")[-1]
validate_env(__file__, { validate_env(__file__, {
@ -25,139 +23,101 @@ validate_env(__file__, {
f"f_bases_{freq_suffix}": as_str, f"f_bases_{freq_suffix}": as_str,
f"f_roofs_{freq_suffix}": as_str, f"f_roofs_{freq_suffix}": as_str,
}) })
debug_flag = as_bool(os.getenv('debug_flag', '0')) debug_flag = as_bool(os.getenv('debug_flag', '0'))
send_to_module_flag = as_bool(os.getenv('send_to_module_flag', '0')) send_to_module_flag = as_bool(os.getenv('send_to_module_flag', '0'))
save_data_flag = as_bool(os.getenv('save_data_flag', '0')) save_data_flag = as_bool(os.getenv('save_data_flag', '0'))
module_name = os.getenv('module_name') module_name = os.getenv('module_name')
elems_to_save = os.getenv('elems_to_save') elems_to_save = os.getenv('elems_to_save')
file_types_to_save = os.getenv('file_types_to_save') file_types_to_save = os.getenv('file_types_to_save')
localhost = os.getenv('lochost') localhost = os.getenv('lochost')
localport = os.getenv('locport') localport = os.getenv('locport')
f_step = [*map(float, os.getenv('f_step_750').split())] f_step = [*map(float, os.getenv('f_step_750').split())]
f_bases = [*map(float, os.getenv('f_bases_750').split())] f_bases = [*map(float, os.getenv('f_bases_750').split())]
f_roofs = [*map(float, os.getenv('f_roofs_750').split())] f_roofs = [*map(float, os.getenv('f_roofs_750').split())]
path_to_save_medians = os.getenv('path_to_save_medians') path_to_save_medians = os.getenv('path_to_save_medians')
path_to_save_alarms = os.getenv('path_to_save_alarms') path_to_save_alarms = os.getenv('path_to_save_alarms')
smb_host = os.getenv('smb_host') smb_host = os.getenv('smb_host')
smb_port = os.getenv('smb_port') smb_port = os.getenv('smb_port')
smb_user = os.getenv('smb_user') smb_user = os.getenv('smb_user')
smb_pass = os.getenv('smb_pass') smb_pass = os.getenv('smb_pass')
shared_folder = os.getenv('shared_folder') shared_folder = os.getenv('shared_folder')
the_pc_name = os.getenv('the_pc_name') the_pc_name = os.getenv('the_pc_name')
remote_pc_name = os.getenv('remote_pc_name') remote_pc_name = os.getenv('remote_pc_name')
smb_domain = os.getenv('smb_domain') smb_domain = os.getenv('smb_domain')
freq_endpoint = os.getenv('freq_endpoint') freq_endpoint = os.getenv('freq_endpoint')
telemetry_enabled = as_bool(os.getenv('telemetry_enabled', '1'))
telemetry_host = os.getenv('telemetry_host', '127.0.0.1') elems_to_save = elems_to_save.split(',')
telemetry_port = os.getenv('telemetry_port', '5020') file_types_to_save = file_types_to_save.split(',')
telemetry_endpoint = os.getenv('telemetry_endpoint', 'telemetry')
telemetry_timeout_sec = float(os.getenv('telemetry_timeout_sec', '0.30')) tmp_signal = Signal()
tmp_sigs_array = SignalsArray()
elems_to_save = elems_to_save.split(',') multi_channel = MultiChannel(f_step, f_bases, f_roofs)
file_types_to_save = file_types_to_save.split(',') f = multi_channel.init_f()
multi_channel.fill_DB()
tmp_signal = Signal()
tmp_sigs_array = SignalsArray() if debug_flag:
multi_channel = MultiChannel(f_step, f_bases, f_roofs) conn = SMBConnection(smb_user, smb_pass, the_pc_name, remote_pc_name, use_ntlm_v2=True)
f = multi_channel.init_f() conn.connect(smb_host, 139)
multi_channel.fill_DB() filelist = conn.listPath(shared_folder, '/')
print(filelist)
if debug_flag:
conn = SMBConnection(smb_user, smb_pass, the_pc_name, remote_pc_name, use_ntlm_v2=True)
conn.connect(smb_host, 139) def work(lvl):
filelist = conn.listPath(shared_folder, '/')
print(filelist) f = multi_channel.get_cur_channel()
freq = get_centre_freq(f)
signal_length = get_signal_length(freq)
def work(lvl): median = tmp_signal.fill_signal(lvl, signal_length)
f = multi_channel.get_cur_channel() if median:
freq = get_centre_freq(f) print(1)
signal_length = get_signal_length(freq) try:
median = tmp_signal.fill_signal(lvl, signal_length) num_chs, circle_buffer = multi_channel.check_f(f)
packet_ts = tmp_signal.get_last_packet_ts() print(num_chs, circle_buffer)
cur_channel, sigs_array = tmp_sigs_array.fill_sig_arr(median, num_chs)
if median: print(3)
print(1)
try: if sigs_array:
num_chs, circle_buffer = multi_channel.check_f(f) print('Значения на {0}: {1}'.format(freq, sigs_array))
print(num_chs, circle_buffer) print('Пороги: ', circle_buffer.get_medians())
cur_channel, sigs_array, sigs_ts_array = tmp_sigs_array.fill_sig_arr(median, packet_ts=packet_ts, num_chs=num_chs) alarm = circle_buffer.check_alarm(sigs_array)
print(3)
if alarm:
if sigs_array: print('----ALARM---- ', freq)
print('Значения на {0}: {1}'.format(freq, sigs_array)) multi_channel.db_alarms_zeros(circle_buffer)
print('Пороги: ', circle_buffer.get_medians()) else:
alarm = circle_buffer.check_alarm(sigs_array) circle_buffer.update(sigs_array)
if alarm: if send_to_module_flag:
print('----ALARM---- ', freq) send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)
multi_channel.db_alarms_zeros(circle_buffer)
elif not is_jammer_active(): if save_data_flag:
circle_buffer.update(sigs_array, packet_timestamps=sigs_ts_array) if not circle_buffer.check_init() and circle_buffer.current_column - 1 == 0:
save_data(path_to_save_medians, freq, 'DateTime', 'ALARM', 'max signal', list(range(num_chs)),
list(range(num_chs)))
if telemetry_enabled: if circle_buffer.check_init():
try: save_data(path_to_save_medians, freq, datetime.datetime.now(), alarm, max(sigs_array), sigs_array,
max_idx = max(range(len(sigs_array)), key=lambda idx: sigs_array[idx]) circle_buffer.get_medians())
dbfs_current = float(sigs_array[max_idx])
if debug_flag:
dbfs_threshold = circle_buffer.get_threshold(max_idx) single_alarm = circle_buffer.check_single_alarm(median, cur_channel)
channel_thresholds = circle_buffer.get_thresholds() print(cur_channel, single_alarm)
alarm_channels = circle_buffer.get_last_alarm_channels() if alarm else [] if single_alarm:
data = pack_elems(elems_to_save, file_types_to_save, tmp_signal.get_signal())
send_telemetry( print('SAVE CURRENT SIGNAL SROCHNO TI MENYA SLISHISH?!?!?!?')
data={ try:
"freq": str(freq), remote_save_data(conn, data, module_name, freq, shared_folder, path_to_save_alarms)
"ts": time.time(), except Exception as e:
"dbfs_current": dbfs_current, print(f"Ошибка: {e}")
"dbfs_threshold": dbfs_threshold, else:
"alarm": bool(alarm), print('VSE OKI DOKI SIGNAL SOKHRANYAT NE NUZHNO!!!')
"channel_idx": int(max_idx),
"channels_total": int(len(sigs_array)), f = multi_channel.change_channel()
"channel_values": [float(v) for v in sigs_array], except Exception as e:
"channel_thresholds": channel_thresholds, print(str(e))
"alarm_channels": alarm_channels, print(".", end='')
},
host=telemetry_host, tmp_signal.clear()
port=telemetry_port,
endpoint=telemetry_endpoint, return f
timeout_sec=telemetry_timeout_sec,
)
except Exception as exc:
if debug_flag:
print(f"telemetry send failed: {exc}")
if send_to_module_flag:
send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)
if save_data_flag:
if not circle_buffer.check_init() and circle_buffer.current_column - 1 == 0:
save_data(path_to_save_medians, freq, 'DateTime', 'ALARM', 'max signal', list(range(num_chs)),
list(range(num_chs)))
if circle_buffer.check_init():
save_data(path_to_save_medians, freq, datetime.datetime.now(), alarm, max(sigs_array), sigs_array,
circle_buffer.get_medians())
if debug_flag:
single_alarm = circle_buffer.check_single_alarm(median, cur_channel)
print(cur_channel, single_alarm)
if single_alarm:
data = pack_elems(elems_to_save, file_types_to_save, tmp_signal.get_signal())
print('SAVE CURRENT SIGNAL SROCHNO TI MENYA SLISHISH?!?!?!?')
try:
remote_save_data(conn, data, module_name, freq, shared_folder, path_to_save_alarms)
except Exception as e:
print(f"Ошибка: {e}")
else:
print('VSE OKI DOKI SIGNAL SOKHRANYAT NE NUZHNO!!!')
f = multi_channel.change_channel()
except Exception as e:
print(str(e))
print(".", end='')
tmp_signal.clear()
return f

@ -1,13 +1,11 @@
import os import os
import datetime import datetime
import time from common.runtime import load_root_env, validate_env, as_bool, as_str
from common.runtime import load_root_env, validate_env, as_bool, as_str
from smb.SMBConnection import SMBConnection from smb.SMBConnection import SMBConnection
from utils.datas_processing import pack_elems, agregator, send_data, send_telemetry, save_data, remote_save_data from utils.datas_processing import pack_elems, agregator, send_data, save_data, remote_save_data
from utils.jammer_state_flag import is_jammer_active from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length
from core.sig_n_medi_collect import Signal, SignalsArray, get_signal_length from core.multichannelswitcher import MultiChannel, get_centre_freq
from core.multichannelswitcher import MultiChannel, get_centre_freq
load_root_env(__file__) load_root_env(__file__)
freq_suffix = os.path.splitext(os.path.basename(__file__))[0].split("_")[-1] freq_suffix = os.path.splitext(os.path.basename(__file__))[0].split("_")[-1]
validate_env(__file__, { validate_env(__file__, {
@ -25,136 +23,98 @@ validate_env(__file__, {
f"f_bases_{freq_suffix}": as_str, f"f_bases_{freq_suffix}": as_str,
f"f_roofs_{freq_suffix}": as_str, f"f_roofs_{freq_suffix}": as_str,
}) })
debug_flag = as_bool(os.getenv('debug_flag', '0')) debug_flag = as_bool(os.getenv('debug_flag', '0'))
send_to_module_flag = as_bool(os.getenv('send_to_module_flag', '0')) send_to_module_flag = as_bool(os.getenv('send_to_module_flag', '0'))
save_data_flag = as_bool(os.getenv('save_data_flag', '0')) save_data_flag = as_bool(os.getenv('save_data_flag', '0'))
module_name = os.getenv('module_name') module_name = os.getenv('module_name')
elems_to_save = os.getenv('elems_to_save') elems_to_save = os.getenv('elems_to_save')
file_types_to_save = os.getenv('file_types_to_save') file_types_to_save = os.getenv('file_types_to_save')
localhost = os.getenv('lochost') localhost = os.getenv('lochost')
localport = os.getenv('locport') localport = os.getenv('locport')
f_step = [*map(float, os.getenv('f_step_868').split())] f_step = [*map(float, os.getenv('f_step_868').split())]
f_bases = [*map(float, os.getenv('f_bases_868').split())] f_bases = [*map(float, os.getenv('f_bases_868').split())]
f_roofs = [*map(float, os.getenv('f_roofs_868').split())] f_roofs = [*map(float, os.getenv('f_roofs_868').split())]
path_to_save_medians = os.getenv('path_to_save_medians') path_to_save_medians = os.getenv('path_to_save_medians')
path_to_save_alarms = os.getenv('path_to_save_alarms') path_to_save_alarms = os.getenv('path_to_save_alarms')
smb_host = os.getenv('smb_host') smb_host = os.getenv('smb_host')
smb_port = os.getenv('smb_port') smb_port = os.getenv('smb_port')
smb_user = os.getenv('smb_user') smb_user = os.getenv('smb_user')
smb_pass = os.getenv('smb_pass') smb_pass = os.getenv('smb_pass')
shared_folder = os.getenv('shared_folder') shared_folder = os.getenv('shared_folder')
the_pc_name = os.getenv('the_pc_name') the_pc_name = os.getenv('the_pc_name')
remote_pc_name = os.getenv('remote_pc_name') remote_pc_name = os.getenv('remote_pc_name')
smb_domain = os.getenv('smb_domain') smb_domain = os.getenv('smb_domain')
freq_endpoint = os.getenv('freq_endpoint') freq_endpoint = os.getenv('freq_endpoint')
telemetry_enabled = as_bool(os.getenv('telemetry_enabled', '1'))
telemetry_host = os.getenv('telemetry_host', '127.0.0.1') elems_to_save = elems_to_save.split(',')
telemetry_port = os.getenv('telemetry_port', '5020') file_types_to_save = file_types_to_save.split(',')
telemetry_endpoint = os.getenv('telemetry_endpoint', 'telemetry')
telemetry_timeout_sec = float(os.getenv('telemetry_timeout_sec', '0.30')) tmp_signal = Signal()
tmp_sigs_array = SignalsArray()
elems_to_save = elems_to_save.split(',') multi_channel = MultiChannel(f_step, f_bases, f_roofs)
file_types_to_save = file_types_to_save.split(',') f = multi_channel.init_f()
multi_channel.fill_DB()
tmp_signal = Signal()
tmp_sigs_array = SignalsArray() if debug_flag:
multi_channel = MultiChannel(f_step, f_bases, f_roofs) conn = SMBConnection(smb_user, smb_pass, the_pc_name, remote_pc_name, use_ntlm_v2=True)
f = multi_channel.init_f() conn.connect(smb_host, 139)
multi_channel.fill_DB() filelist = conn.listPath(shared_folder, '/')
print(filelist)
if debug_flag:
conn = SMBConnection(smb_user, smb_pass, the_pc_name, remote_pc_name, use_ntlm_v2=True)
conn.connect(smb_host, 139) def work(lvl):
filelist = conn.listPath(shared_folder, '/')
print(filelist) f = multi_channel.get_cur_channel()
freq = get_centre_freq(f)
signal_length = get_signal_length(freq)
def work(lvl): median = tmp_signal.fill_signal(lvl, signal_length)
f = multi_channel.get_cur_channel() if median:
freq = get_centre_freq(f) try:
signal_length = get_signal_length(freq) num_chs, circle_buffer = multi_channel.check_f(f)
median = tmp_signal.fill_signal(lvl, signal_length) cur_channel, sigs_array = tmp_sigs_array.fill_sig_arr(median, num_chs)
packet_ts = tmp_signal.get_last_packet_ts()
if sigs_array:
if median: print('Значения на {0}: {1}'.format(freq, sigs_array))
try: print('Пороги: ', circle_buffer.get_medians())
num_chs, circle_buffer = multi_channel.check_f(f) alarm = circle_buffer.check_alarm(sigs_array)
cur_channel, sigs_array, sigs_ts_array = tmp_sigs_array.fill_sig_arr(median, packet_ts=packet_ts, num_chs=num_chs)
if alarm:
if sigs_array: print('----ALARM---- ', freq)
print('Значения на {0}: {1}'.format(freq, sigs_array)) multi_channel.db_alarms_zeros(circle_buffer)
print('Пороги: ', circle_buffer.get_medians()) else:
alarm = circle_buffer.check_alarm(sigs_array) circle_buffer.update(sigs_array)
if alarm: if send_to_module_flag:
print('----ALARM---- ', freq) send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)
multi_channel.db_alarms_zeros(circle_buffer)
elif not is_jammer_active(): if save_data_flag:
circle_buffer.update(sigs_array, packet_timestamps=sigs_ts_array) if not circle_buffer.check_init() and circle_buffer.current_column - 1 == 0:
save_data(path_to_save_medians, freq, 'DateTime', 'ALARM', 'max signal', list(range(num_chs)),
list(range(num_chs)))
if telemetry_enabled: if circle_buffer.check_init():
try: save_data(path_to_save_medians, freq, datetime.datetime.now(), alarm, max(sigs_array), sigs_array,
max_idx = max(range(len(sigs_array)), key=lambda idx: sigs_array[idx]) circle_buffer.get_medians())
dbfs_current = float(sigs_array[max_idx])
if debug_flag:
dbfs_threshold = circle_buffer.get_threshold(max_idx) single_alarm = circle_buffer.check_single_alarm(median, cur_channel)
channel_thresholds = circle_buffer.get_thresholds() print(cur_channel, single_alarm)
alarm_channels = circle_buffer.get_last_alarm_channels() if alarm else [] if single_alarm:
data = pack_elems(elems_to_save, file_types_to_save, tmp_signal.get_signal())
send_telemetry( print('SAVE CURRENT SIGNAL SROCHNO TI MENYA SLISHISH?!?!?!?')
data={ try:
"freq": str(freq), remote_save_data(conn, data, module_name, freq, shared_folder, path_to_save_alarms)
"ts": time.time(), except Exception as e:
"dbfs_current": dbfs_current, print(f"Ошибка: {e}")
"dbfs_threshold": dbfs_threshold, else:
"alarm": bool(alarm), print('VSE OKI DOKI SIGNAL SOKHRANYAT NE NUZHNO!!!')
"channel_idx": int(max_idx),
"channels_total": int(len(sigs_array)), f = multi_channel.change_channel()
"channel_values": [float(v) for v in sigs_array], except Exception as e:
"channel_thresholds": channel_thresholds, print(str(e))
"alarm_channels": alarm_channels, print(".", end='')
},
host=telemetry_host, tmp_signal.clear()
port=telemetry_port,
endpoint=telemetry_endpoint, return f
timeout_sec=telemetry_timeout_sec,
)
except Exception as exc:
if debug_flag:
print(f"telemetry send failed: {exc}")
if send_to_module_flag:
send_data(agregator(freq, alarm), localhost, localport, freq_endpoint)
if save_data_flag:
if not circle_buffer.check_init() and circle_buffer.current_column - 1 == 0:
save_data(path_to_save_medians, freq, 'DateTime', 'ALARM', 'max signal', list(range(num_chs)),
list(range(num_chs)))
if circle_buffer.check_init():
save_data(path_to_save_medians, freq, datetime.datetime.now(), alarm, max(sigs_array), sigs_array,
circle_buffer.get_medians())
if debug_flag:
single_alarm = circle_buffer.check_single_alarm(median, cur_channel)
print(cur_channel, single_alarm)
if single_alarm:
data = pack_elems(elems_to_save, file_types_to_save, tmp_signal.get_signal())
print('SAVE CURRENT SIGNAL SROCHNO TI MENYA SLISHISH?!?!?!?')
try:
remote_save_data(conn, data, module_name, freq, shared_folder, path_to_save_alarms)
except Exception as e:
print(f"Ошибка: {e}")
else:
print('VSE OKI DOKI SIGNAL SOKHRANYAT NE NUZHNO!!!')
f = multi_channel.change_channel()
except Exception as e:
print(str(e))
print(".", end='')
tmp_signal.clear()
return f

@ -23,6 +23,7 @@ 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,10 +21,11 @@ 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,6 +23,7 @@ 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,12 +18,14 @@ 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,10 +21,11 @@ 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,10 +21,11 @@ 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,10 +21,11 @@ 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 = []

@ -14,7 +14,6 @@ from fastapi import FastAPI
from common.runtime import load_root_env, validate_env, as_bool, as_float, as_int, as_str from common.runtime import load_root_env, validate_env, as_bool, as_float, as_int, as_str
from datetime import datetime, timedelta from datetime import datetime, timedelta
import logging import logging
from src.utils.jammer_state_flag import set_jammer_active
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
@ -417,7 +416,6 @@ async def jammer_active():
freqs_alarm = {freq: 0 for freq in freqs} freqs_alarm = {freq: 0 for freq in freqs}
jammer_event = True jammer_event = True
set_jammer_active(True)
print('АКТИВИРУЕМ ПОДАВИТЕЛЬ ААААААААААААААААААААААААААААААААААААААААААААААА!!!!') print('АКТИВИРУЕМ ПОДАВИТЕЛЬ ААААААААААААААААААААААААААААААААААААААААААААААА!!!!')
print('-' * 20) print('-' * 20)
@ -440,7 +438,6 @@ async def jammer_deactive():
global sending_data_task global sending_data_task
alarm = False alarm = False
jammer_event = False jammer_event = False
set_jammer_active(False)
sending_data_task = asyncio.create_task(sending_data()) sending_data_task = asyncio.create_task(sending_data())
print('ОТКЛЮАЕМ ПОДАВИТЕЛЬ ААААААААААААААААААААААААААААААААААААААААААААААААА!!!!') print('ОТКЛЮАЕМ ПОДАВИТЕЛЬ ААААААААААААААААААААААААААААААААААААААААААААААААА!!!!')
@ -499,7 +496,6 @@ async def jam_server():
await jammer_deactive() await jammer_deactive()
except Exception as e: except Exception as e:
jam_server_connect = None jam_server_connect = None
set_jammer_active(False)
if jammer_event: if jammer_event:
await jammer_deactive() await jammer_deactive()
@ -512,7 +508,6 @@ async def startup_event():
""" """
global sending_data_task global sending_data_task
set_jammer_active(False)
asyncio.create_task(jam_server()) asyncio.create_task(jam_server())
sending_data_task = asyncio.create_task(sending_data()) sending_data_task = asyncio.create_task(sending_data())

@ -1,183 +1,149 @@
import os import os
import io import io
import csv import csv
import time import itertools
import itertools import requests
import requests import numpy as np
import numpy as np from datetime import datetime
from datetime import datetime
def pack_elems(names, file_types, *elems):
_telemetry_error_last_ts = 0.0 if len(names) != len(file_types) or len(names) != len(elems):
raise ValueError('Длин массивов имен и типов файлов и не совпадает с количество элементов для сохранения')
return {name: {'file_type': file_type, 'elem': elem} for name, file_type, elem in zip(names, file_types, elems)}
def pack_elems(names, file_types, *elems):
if len(names) != len(file_types) or len(names) != len(elems):
raise ValueError('Длин массивов имен и типов файлов и не совпадает с количество элементов для сохранения') def agregator(freq, alarm):
return {name: {'file_type': file_type, 'elem': elem} for name, file_type, elem in zip(names, file_types, elems)} if alarm:
amplitude = 9
else:
def agregator(freq, alarm): amplitude = 0
if alarm:
amplitude = 9 data = {"freq": freq,
else: "amplitude": amplitude
amplitude = 0 }
return data
data = {"freq": freq,
"amplitude": amplitude
} def send_data(data, localhost, localport, endpoint):
return data """
Отправка данных по POST на модуль сервер.
:param data: Данные для отправки.
def send_data(data, localhost, localport, endpoint): :param localhost: Хост модуль сервера.
""" :param localport: Порт модуль сервера.
Отправка данных по POST на модуль сервер. """
:param data: Данные для отправки.
:param localhost: Хост модуль сервера. def _post(port):
:param localport: Порт модуль сервера. url = "http://{0}:{1}/{2}".format(localhost, port, endpoint)
""" return requests.post(url, json=data), url
def _post(port): try:
url = "http://{0}:{1}/{2}".format(localhost, port, endpoint) response, url = _post(localport)
return requests.post(url, json=data), url if response.status_code == 200:
print("Данные успешно отправлены и приняты!", url)
try: return
response, url = _post(localport)
if response.status_code == 200: # Частый кейс: порт 5000 занят локальным registry (DroneDetectPCSoft).
print("Данные успешно отправлены и приняты!", url) # Пробуем порт модуля сервера из env (например, 5010).
return fallback_port = os.getenv('GENERAL_SERVER_PORT')
if response.status_code == 404 and fallback_port and str(localport) != str(fallback_port):
# Частый кейс: порт 5000 занят локальным registry (DroneDetectPCSoft). response_fb, url_fb = _post(fallback_port)
# Пробуем порт модуля сервера из env (например, 5010). if response_fb.status_code == 200:
fallback_port = os.getenv('GENERAL_SERVER_PORT') #print("Данные успешно отправлены и приняты!", url_fb)
if response.status_code == 404 and fallback_port and str(localport) != str(fallback_port): return
response_fb, url_fb = _post(fallback_port) print("Ошибка при отправке данных:", response_fb.status_code, url_fb)
if response_fb.status_code == 200: return
return
print("Ошибка при отправке данных:", response_fb.status_code, url_fb) print("Ошибка при отправке данных:", response.status_code, url)
return except Exception as e:
print(str(e))
print("Ошибка при отправке данных:", response.status_code, url)
except Exception as e:
print(str(e)) def save_data(path_to_save, freq, *args):
"""
Сохранение данных в csv файл. Используется для сохранения метрик и медиан сигнала на каналах с датой и временем
def send_telemetry(data, host, port, endpoint='telemetry', timeout_sec=0.30): - для анализа.
""" :param path_to_save: Путь для сохранения.
Best-effort отправка телеметрии на отдельный telemetry-server. :param freq: Обрабатываемая частота.
Ошибки намеренно не пробрасываются, чтобы не влиять на основной детект/аларм поток. :param args: Что сохраняем в файл.
""" """
global _telemetry_error_last_ts
try:
host = '' if host is None else str(host).strip() if not os.path.exists(path_to_save):
port = '' if port is None else str(port).strip() print('Folder was created.')
endpoint = str(endpoint or 'telemetry').strip().lstrip('/') os.makedirs(path_to_save)
if not host or not port: with open(path_to_save + 'data_' + str(freq) + '.csv', 'a', newline='') as f:
return writer = csv.writer(f)
args2 = itertools.chain(*(arg if isinstance(arg, list) else [arg] for arg in args))
try: writer.writerow(args2)
url = f"http://{host}:{port}/{endpoint}" print('Write csv.')
response = requests.post(url, json=data, timeout=float(timeout_sec))
if response.status_code == 200: except Exception as e:
return print(str(e))
now = time.time()
if now - _telemetry_error_last_ts >= 10.0: def prepare_folders_paths(path):
print(f"telemetry http error: {response.status_code} {url}") folders = path.split('/')
_telemetry_error_last_ts = now folders.pop()
except Exception as exc: folders = [elem + '/' for elem in folders]
now = time.time() print(folders)
if now - _telemetry_error_last_ts >= 10.0: cur_path = ''
print(f"telemetry send failed: {exc}") print(cur_path)
_telemetry_error_last_ts = now return folders, cur_path
def save_data(path_to_save, freq, *args): def remote_save_data(conn, data, module_name, freq, share_folder, path_to_save):
""" """
Сохранение данных в csv файл. Используется для сохранения метрик и медиан сигнала на каналах с датой и временем Сохранение данных (сигнала) в файл на удаленный диск.
- для анализа. :param conn:
:param path_to_save: Путь для сохранения. :param data:
:param freq: Обрабатываемая частота. :param module_name:
:param args: Что сохраняем в файл. :param freq:
""" :param share_folder:
:param path_to_save:
try: :return:
if not os.path.exists(path_to_save): """
print('Folder was created.') # cur_datetime = datetime.now().strftime('%d_%m_%Y_%H_%M_%S')
os.makedirs(path_to_save) # file_name = f'alarm_{module_name}_{freq}_{cur_datetime}.npy'
# path = f"{path_to_save_medians}{module_name}/{str(freq)}/"
with open(path_to_save + 'data_' + str(freq) + '.csv', 'a', newline='') as f: # path_to_file = f"{path}{file_name}"
writer = csv.writer(f) # print(path_to_file)
args2 = itertools.chain(*(arg if isinstance(arg, list) else [arg] for arg in args)) #
writer.writerow(args2) # folders, cur_path = prepare_folders_paths(path)
print('Write csv.') #
# buffer = io.BytesIO()
except Exception as e: # np.save(buffer, data)
print(str(e)) # buffer.seek(0)
#
# for i in range(len(folders)):
def prepare_folders_paths(path): # cur_path = cur_path + folders[i]
folders = path.split('/') # try:
folders.pop() # conn.listPath(share_folder, cur_path)
folders = [elem + '/' for elem in folders] # except Exception:
print(folders) # conn.createDirectory(share_folder, cur_path)
cur_path = '' #
print(cur_path) # conn.storeFile(share_folder, path_to_file, buffer)
return folders, cur_path for name, values in data.items():
elem_name = name
file_type = values['file_type']
def remote_save_data(conn, data, module_name, freq, share_folder, path_to_save): elem_data = values['elem']
""" print(elem_data.shape)
Сохранение данных (сигнала) в файл на удаленный диск. buffer = io.BytesIO()
:param conn: np.save(buffer, elem_data)
:param data: buffer.seek(0)
:param module_name:
:param freq: cur_datetime = datetime.now().strftime('%d_%m_%Y_%H_%M_%S')
:param share_folder: file_name = f'alarm_{elem_name}_{module_name}_{freq}_{cur_datetime}.{file_type}'
:param path_to_save: path = f"{path_to_save}{module_name}/{str(freq)}/{elem_name}/"
:return: path_to_file = f"{path}{file_name}"
""" folders, cur_path = prepare_folders_paths(path)
# cur_datetime = datetime.now().strftime('%d_%m_%Y_%H_%M_%S')
# file_name = f'alarm_{module_name}_{freq}_{cur_datetime}.npy' for i in range(len(folders)):
# path = f"{path_to_save_medians}{module_name}/{str(freq)}/" cur_path = cur_path + folders[i]
# path_to_file = f"{path}{file_name}" try:
# print(path_to_file) conn.listPath(share_folder, cur_path)
# except Exception:
# folders, cur_path = prepare_folders_paths(path) conn.createDirectory(share_folder, cur_path)
#
# buffer = io.BytesIO() conn.storeFile(share_folder, path_to_file, buffer)
# np.save(buffer, data)
# buffer.seek(0)
#
# for i in range(len(folders)):
# cur_path = cur_path + folders[i]
# try:
# conn.listPath(share_folder, cur_path)
# except Exception:
# conn.createDirectory(share_folder, cur_path)
#
# conn.storeFile(share_folder, path_to_file, buffer)
for name, values in data.items():
elem_name = name
file_type = values['file_type']
elem_data = values['elem']
print(elem_data.shape)
buffer = io.BytesIO()
np.save(buffer, elem_data)
buffer.seek(0)
cur_datetime = datetime.now().strftime('%d_%m_%Y_%H_%M_%S')
file_name = f'alarm_{elem_name}_{module_name}_{freq}_{cur_datetime}.{file_type}'
path = f"{path_to_save}{module_name}/{str(freq)}/{elem_name}/"
path_to_file = f"{path}{file_name}"
folders, cur_path = prepare_folders_paths(path)
for i in range(len(folders)):
cur_path = cur_path + folders[i]
try:
conn.listPath(share_folder, cur_path)
except Exception:
conn.createDirectory(share_folder, cur_path)
conn.storeFile(share_folder, path_to_file, buffer)

@ -1,47 +0,0 @@
import os
import time
from pathlib import Path
_DEFAULT_FLAG_PATH = Path(__file__).resolve().parents[2] / "runtime" / "jammer_active.flag"
_FLAG_PATH = Path(os.getenv("JAMMER_STATE_FILE", str(_DEFAULT_FLAG_PATH)))
_CACHE_TTL_SEC = float(os.getenv("JAMMER_STATE_CACHE_TTL_SEC", "0.25"))
_STALE_SEC = float(os.getenv("JAMMER_STATE_STALE_SEC", "5.0"))
_cached_value = False
_cached_checked_monotonic = 0.0
def _read_uncached() -> bool:
try:
stat = _FLAG_PATH.stat()
if time.time() - stat.st_mtime > _STALE_SEC:
return False
return _FLAG_PATH.read_text(encoding="ascii").strip() == "1"
except OSError:
return False
def is_jammer_active() -> bool:
global _cached_value
global _cached_checked_monotonic
now = time.monotonic()
if now - _cached_checked_monotonic < _CACHE_TTL_SEC:
return _cached_value
_cached_value = _read_uncached()
_cached_checked_monotonic = now
return _cached_value
def set_jammer_active(active: bool) -> None:
global _cached_value
global _cached_checked_monotonic
_FLAG_PATH.parent.mkdir(parents=True, exist_ok=True)
tmp_path = _FLAG_PATH.with_name(f"{_FLAG_PATH.name}.{os.getpid()}.tmp")
tmp_path.write_text("1" if active else "0", encoding="ascii")
os.replace(tmp_path, _FLAG_PATH)
_cached_value = bool(active)
_cached_checked_monotonic = time.monotonic()

@ -1 +0,0 @@
# telemetry package

@ -1,465 +0,0 @@
import asyncio
import os
import time
from collections import defaultdict, deque
from typing import Any, Deque, Dict, List, Optional
from fastapi import FastAPI, Query, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
from pydantic import BaseModel, Field
from common.runtime import load_root_env
load_root_env(__file__)
TELEMETRY_BIND_HOST = os.getenv('telemetry_bind_host', os.getenv('lochost', '0.0.0.0'))
TELEMETRY_BIND_PORT = int(os.getenv('telemetry_bind_port', os.getenv('telemetry_port', '5020')))
TELEMETRY_HISTORY_SEC = int(float(os.getenv('telemetry_history_sec', '900')))
TELEMETRY_MAX_POINTS_PER_FREQ = int(os.getenv('telemetry_max_points_per_freq', '5000'))
def _new_buffer() -> Deque[Dict[str, Any]]:
return deque(maxlen=TELEMETRY_MAX_POINTS_PER_FREQ)
app = FastAPI(title='DroneDetector Telemetry Server')
_buffers: Dict[str, Deque[Dict[str, Any]]] = defaultdict(_new_buffer)
_ws_clients: List[WebSocket] = []
_state_lock = asyncio.Lock()
class TelemetryPoint(BaseModel):
freq: str
ts: float = Field(default_factory=lambda: time.time())
dbfs_current: float
dbfs_threshold: Optional[float] = None
alarm: bool = False
channel_idx: int = 0
channels_total: int = 1
channel_values: Optional[List[float]] = None
channel_thresholds: Optional[List[Optional[float]]] = None
alarm_channels: Optional[List[int]] = None
def _prune_freq_locked(freq: str, now_ts: float) -> None:
cutoff = now_ts - TELEMETRY_HISTORY_SEC
buf = _buffers[freq]
while buf and float(buf[0].get('ts', 0.0)) < cutoff:
buf.popleft()
def _copy_series_locked(seconds: int, freq: Optional[str] = None) -> Dict[str, List[Dict[str, Any]]]:
now_ts = time.time()
cutoff = now_ts - seconds
if freq is not None:
data = [point for point in _buffers.get(freq, []) if float(point.get('ts', 0.0)) >= cutoff]
return {freq: data}
series: Dict[str, List[Dict[str, Any]]] = {}
for key, buf in _buffers.items():
series[key] = [point for point in buf if float(point.get('ts', 0.0)) >= cutoff]
return series
async def _broadcast(message: Dict[str, Any]) -> None:
dead: List[WebSocket] = []
for ws in list(_ws_clients):
try:
await ws.send_json(message)
except Exception:
dead.append(ws)
if dead:
async with _state_lock:
for ws in dead:
if ws in _ws_clients:
_ws_clients.remove(ws)
@app.post('/telemetry')
async def ingest_telemetry(point: TelemetryPoint):
payload = point.model_dump()
freq = str(payload['freq'])
now_ts = time.time()
async with _state_lock:
_buffers[freq].append(payload)
_prune_freq_locked(freq, now_ts)
await _broadcast({'type': 'point', 'data': payload})
return {'ok': True}
@app.get('/telemetry/history')
async def telemetry_history(
freq: Optional[str] = Query(default=None),
seconds: int = Query(default=300, ge=10, le=86400),
):
seconds = min(seconds, TELEMETRY_HISTORY_SEC)
async with _state_lock:
series = _copy_series_locked(seconds=seconds, freq=freq)
return {'seconds': seconds, 'series': series}
@app.websocket('/telemetry/ws')
async def telemetry_ws(websocket: WebSocket):
await websocket.accept()
async with _state_lock:
_ws_clients.append(websocket)
snapshot = _copy_series_locked(seconds=min(300, TELEMETRY_HISTORY_SEC), freq=None)
await websocket.send_json({'type': 'snapshot', 'data': snapshot})
try:
while True:
# Keepalive channel from browser; content is ignored.
await websocket.receive_text()
except WebSocketDisconnect:
pass
finally:
async with _state_lock:
if websocket in _ws_clients:
_ws_clients.remove(websocket)
MONITOR_HTML = """
<!doctype html>
<html>
<head>
<meta charset=\"utf-8\" />
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />
<title>DroneDetector Telemetry</title>
<script src=\"https://cdn.plot.ly/plotly-2.35.2.min.js\"></script>
<style>
:root {
--bg: #f6f8fb;
--card: #ffffff;
--line: #d9dde5;
--text: #1c232e;
--green: #12b76a;
--red: #ef4444;
--muted: #5b6574;
}
body { margin: 0; background: var(--bg); color: var(--text); font-family: system-ui, -apple-system, Segoe UI, sans-serif; }
.wrap { max-width: 1800px; margin: 0 auto; padding: 14px; }
.head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.meta { font-size: 13px; color: var(--muted); }
.grid { display: flex; flex-direction: column; gap: 10px; }
.card { width: 100%; background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 8px 8px 8px; }
.title-row { display: flex; justify-content: space-between; align-items: center; margin: 4px 8px; }
.title { font-size: 20px; font-weight: 700; }
.ctrl { display: flex; align-items: center; gap: 6px; }
.ctrl label { font-size: 12px; color: var(--muted); }
.ctrl select { border: 1px solid var(--line); border-radius: 6px; padding: 2px 6px; }
.plot { height: 260px; width: 100%; }
.events-title { font-size: 12px; color: var(--muted); margin: 2px 8px 4px; }
.events { max-height: 110px; overflow-y: auto; border-top: 1px dashed var(--line); margin: 0 8px; padding-top: 4px; }
.ev { display: flex; justify-content: space-between; font-size: 12px; line-height: 1.4; color: var(--text); }
.ev-t { color: var(--muted); }
.ev-empty { color: var(--muted); font-size: 12px; }
</style>
</head>
<body>
<div class=\"wrap\">
<div class=\"head\">
<div>
<h2 style=\"margin:0;\">DroneDetector Telemetry Monitor</h2>
<div class=\"meta\">Green: dBFS current, Red: channel threshold, Red dots: alarm points</div>
</div>
<div class=\"meta\" id=\"status\">connecting...</div>
</div>
<div class=\"grid\" id=\"plots\"></div>
</div>
<script>
const windowSec = 300;
const state = {}; // freq -> points[]
const selectedChannel = {}; // freq -> 'max' | channel index as string
function numericSortFreq(a, b) {
return Number(a) - Number(b);
}
function formatTime(ts) {
return new Date(Number(ts) * 1000).toLocaleTimeString('ru-RU', {hour12: false});
}
function getChannelCount(freq) {
const pts = state[freq] || [];
let maxCount = 1;
for (const p of pts) {
if (Number.isFinite(Number(p.channels_total))) {
maxCount = Math.max(maxCount, Number(p.channels_total));
}
if (Array.isArray(p.channel_values)) {
maxCount = Math.max(maxCount, p.channel_values.length);
}
}
return maxCount;
}
function ensurePlot(freq) {
if (document.getElementById(`plot-${freq}`)) return;
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = `
<div class=\"title-row\">
<div class=\"title\">${freq} MHz</div>
<div class=\"ctrl\">
<label for=\"chan-${freq}\">channel</label>
<select id=\"chan-${freq}\"></select>
</div>
</div>
<div class=\"plot\" id=\"plot-${freq}\"></div>
<div class=\"events-title\">Alarms (time -> channel)</div>
<div class=\"events\" id=\"events-${freq}\"></div>
`;
document.getElementById('plots').appendChild(card);
selectedChannel[freq] = 'max';
const sel = document.getElementById(`chan-${freq}`);
sel.addEventListener('change', () => {
selectedChannel[freq] = sel.value;
render(freq);
});
}
function updateChannelSelector(freq) {
const sel = document.getElementById(`chan-${freq}`);
if (!sel) return;
const prev = selectedChannel[freq] ?? 'max';
const count = getChannelCount(freq);
const opts = ['max'];
for (let i = 0; i < count; i += 1) opts.push(String(i));
sel.innerHTML = '';
for (const v of opts) {
const option = document.createElement('option');
option.value = v;
option.textContent = v === 'max' ? 'max' : `ch ${v}`;
sel.appendChild(option);
}
selectedChannel[freq] = opts.includes(prev) ? prev : 'max';
sel.value = selectedChannel[freq];
}
function trimPoints(freq) {
const arr = state[freq] || [];
const cutoff = Date.now() / 1000 - windowSec;
state[freq] = arr.filter(p => Number(p.ts) >= cutoff);
}
function getPointValueForSelection(point, selection) {
if (selection === 'max') {
return {
y: point.dbfs_current ?? null,
threshold: point.dbfs_threshold ?? null,
};
}
const idx = Number(selection);
if (!Number.isInteger(idx)) {
return {y: null, threshold: null};
}
const y = Array.isArray(point.channel_values) && idx < point.channel_values.length
? point.channel_values[idx]
: null;
const threshold = Array.isArray(point.channel_thresholds) && idx < point.channel_thresholds.length
? point.channel_thresholds[idx]
: null;
return {y, threshold};
}
function isAlarmForSelection(point, selection) {
if (point.alarm !== true) return false;
if (selection === 'max') return true;
const idx = Number(selection);
if (!Number.isInteger(idx)) return false;
if (Array.isArray(point.alarm_channels) && point.alarm_channels.length > 0) {
return point.alarm_channels.includes(idx);
}
return Number(point.channel_idx) === idx;
}
function renderAlarmEvents(freq, pts) {
const el = document.getElementById(`events-${freq}`);
if (!el) return;
const alarmPts = pts.filter(p => p.alarm === true);
if (alarmPts.length === 0) {
el.innerHTML = '<div class=\"ev-empty\">no alarms</div>';
return;
}
const rows = alarmPts.slice(-20).reverse().map((p) => {
const channels = Array.isArray(p.alarm_channels) && p.alarm_channels.length > 0
? p.alarm_channels.join(',')
: String(p.channel_idx ?? '-');
return `<div class=\"ev\"><span class=\"ev-t\">${formatTime(p.ts)}</span><span>ch ${channels}</span></div>`;
});
el.innerHTML = rows.join('');
}
function render(freq) {
ensurePlot(freq);
trimPoints(freq);
updateChannelSelector(freq);
const pts = state[freq] || [];
const sel = selectedChannel[freq] ?? 'max';
const x = [];
const y = [];
const thr = [];
const alarmX = [];
const alarmY = [];
for (const p of pts) {
const metric = getPointValueForSelection(p, sel);
if (metric.y === null || metric.y === undefined) {
continue;
}
const ts = new Date(Number(p.ts) * 1000);
x.push(ts);
y.push(metric.y);
thr.push(metric.threshold);
if (isAlarmForSelection(p, sel)) {
alarmX.push(ts);
alarmY.push(metric.y);
}
}
const labelSuffix = sel === 'max' ? 'max' : `ch ${sel}`;
const traces = [
{
x,
y,
mode: 'lines',
name: `dBFS (${labelSuffix})`,
line: {color: '#12b76a', width: 2},
},
{
x,
y: thr,
mode: 'lines',
name: `Threshold (${labelSuffix})`,
line: {color: '#ef4444', width: 2, dash: 'dash'},
},
{
x: alarmX,
y: alarmY,
mode: 'markers',
name: 'Alarm',
marker: {color: '#ef4444', size: 6, symbol: 'circle'},
},
];
Plotly.react(`plot-${freq}`, traces, {
margin: {l: 40, r: 12, t: 12, b: 32},
showlegend: true,
legend: {orientation: 'h', y: 1.16},
xaxis: {
title: 'time',
tickformat: '%H:%M:%S',
hoverformat: '%H:%M:%S',
range: [new Date(Date.now() - windowSec * 1000), new Date()],
},
yaxis: {title: 'dBFS'},
}, {displayModeBar: false, responsive: true});
renderAlarmEvents(freq, pts);
}
function renderAll() {
const freqs = Object.keys(state).sort(numericSortFreq);
freqs.forEach(render);
}
async function loadInitial() {
const res = await fetch(`/telemetry/history?seconds=${windowSec}`);
const payload = await res.json();
const series = payload.series || {};
for (const [freq, points] of Object.entries(series)) {
state[freq] = points;
}
renderAll();
}
function connectWs() {
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
const ws = new WebSocket(`${proto}://${location.host}/telemetry/ws`);
ws.onopen = () => {
document.getElementById('status').textContent = 'ws connected';
setInterval(() => {
if (ws.readyState === 1) ws.send('ping');
}, 20000);
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'snapshot' && msg.data) {
for (const [freq, points] of Object.entries(msg.data)) {
state[freq] = points;
}
renderAll();
return;
}
if (msg.type !== 'point') return;
const p = msg.data;
const freq = String(p.freq);
if (!state[freq]) state[freq] = [];
state[freq].push(p);
render(freq);
};
ws.onclose = () => {
document.getElementById('status').textContent = 'ws disconnected, retrying...';
setTimeout(connectWs, 1500);
};
ws.onerror = () => {
document.getElementById('status').textContent = 'ws error';
};
}
setInterval(() => {
renderAll();
}, 1000);
loadInitial().then(connectWs).catch((e) => {
document.getElementById('status').textContent = `init error: ${e}`;
connectWs();
});
</script>
</body>
</html>
"""
@app.get('/', response_class=HTMLResponse)
@app.get('/monitor', response_class=HTMLResponse)
async def monitor_page():
return HTMLResponse(content=MONITOR_HTML)
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host=TELEMETRY_BIND_HOST, port=TELEMETRY_BIND_PORT)
Loading…
Cancel
Save