You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DroneDetector/capture_hourly.sh

360 lines
8.8 KiB
Bash

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env bash
set -Eeuo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_OWNER="${CAPTURE_USER:-$(stat -c %U "$SCRIPT_DIR")}"
SCRIPT_OWNER_HOME="$(getent passwd "$SCRIPT_OWNER" | cut -d: -f6)"
cd "$SCRIPT_DIR"
source "$SCRIPT_DIR/.env"
############################
# НАСТРОЙКИ
############################
BASE_DIR="${CAPTURE_BASE_DIR:-${SCRIPT_OWNER_HOME}/dataset/noise}"
# Путь к python из venv
PYTHON_BIN="${PYTHON_BIN:-$SCRIPT_DIR/.venv-sdr/bin/python}"
# Путь к headless скрипту
SCRIPT_PATH="${SCRIPT_PATH:-$SCRIPT_DIR/scripts_nn/data_saver_headless.py}"
RUN_ONCE="${RUN_ONCE:-0}"
CAPTURE_LOG_FILE="${CAPTURE_LOG_FILE:-$BASE_DIR/capture_hourly.log}"
SYSTEMCTL_BIN=(systemctl)
CURRENT_CAPTURE_PID=""
CURRENT_SERVICE_UNIT=""
# Лимиты
lim_all=10
PER_FREQ_LIMIT_BYTES=$((1 * 1024 * 1024 * 1024)) # GiB на частоту за запуск
TOTAL_LIMIT_BYTES=$((lim_all * 1024 * 1024 * 1024)) # общий лимит GiB
CYCLE_SECONDS=1 # один цикл в час
# Параметры SDR
SAMP_RATE="20e6"
SPLIT_SIZE="400000"
DELAY="0.01"
RF_GAIN="12"
IF_GAIN="30"
BB_GAIN="36"
############################
# ЧАСТОТЫ И SERIAL ИЗ ENV
############################
ORDER=(433)
declare -A SERIAL
declare -A FREQ_HZ
declare -A SERVICE_UNIT
SERIAL[433]="$hack_433"
FREQ_HZ[433]="433000000"
SERIAL[750]="$hack_750"
FREQ_HZ[750]="750000000"
SERIAL[915]="$hack_915"
FREQ_HZ[915]="915000000"
SERIAL[1200]="$hack_1200"
FREQ_HZ[1200]="1200000000"
SERIAL[2400]="$hack_2400"
FREQ_HZ[2400]="2400000000"
SERIAL[3300]="$hack_3300"
FREQ_HZ[3300]="3300000000"
SERIAL[4500]="$hack_4500"
FREQ_HZ[4500]="4500000000"
SERIAL[5200]="$hack_5200"
FREQ_HZ[5200]="5200000000"
SERIAL[5800]="$hack_5800"
FREQ_HZ[5800]="5800000000"
SERVICE_UNIT[433]="dronedetector-sdr-433.service"
SERVICE_UNIT[750]="dronedetector-sdr-750.service"
SERVICE_UNIT[915]="dronedetector-sdr-915.service"
SERVICE_UNIT[1200]="dronedetector-sdr-1200.service"
SERVICE_UNIT[2400]="dronedetector-sdr-2400.service"
SERVICE_UNIT[3300]="dronedetector-sdr-3300.service"
SERVICE_UNIT[4500]="dronedetector-sdr-4500.service"
SERVICE_UNIT[5200]="dronedetector-sdr-5200.service"
SERVICE_UNIT[5800]="dronedetector-sdr-5800.service"
############################
# ВСПОМОГАТЕЛЬНОЕ
############################
log() {
printf '[%s] %s\n' "$(date '+%F %T')" "$*"
}
systemctl_run() {
"${SYSTEMCTL_BIN[@]}" "$@"
}
service_exists() {
local unit="$1"
systemctl_run show "$unit" >/dev/null 2>&1
}
stop_band_service() {
local band="$1"
local unit="${SERVICE_UNIT[$band]:-}"
if [[ -z "$unit" ]]; then
log "Для band=$band не найден service unit"
return 0
fi
if ! service_exists "$unit"; then
log "Service unit $unit не установлен, пропускаю stop/start"
return 0
fi
log "Останавливаю service $unit перед записью band=$band"
systemctl_run stop "$unit"
CURRENT_SERVICE_UNIT="$unit"
}
start_current_service() {
if [[ -z "$CURRENT_SERVICE_UNIT" ]]; then
return 0
fi
log "Запускаю service $CURRENT_SERVICE_UNIT после записи"
systemctl_run start "$CURRENT_SERVICE_UNIT"
CURRENT_SERVICE_UNIT=""
}
cleanup_capture() {
if [[ -n "$CURRENT_CAPTURE_PID" ]] && kill -0 "$CURRENT_CAPTURE_PID" 2>/dev/null; then
log "Останавливаю текущий PID=$CURRENT_CAPTURE_PID при завершении скрипта"
kill -TERM "$CURRENT_CAPTURE_PID" 2>/dev/null || true
wait "$CURRENT_CAPTURE_PID" 2>/dev/null || true
fi
CURRENT_CAPTURE_PID=""
if [[ -n "$CURRENT_SERVICE_UNIT" ]]; then
log "Восстанавливаю service $CURRENT_SERVICE_UNIT при завершении скрипта"
systemctl_run start "$CURRENT_SERVICE_UNIT" || true
CURRENT_SERVICE_UNIT=""
fi
}
on_signal() {
local sig="$1"
log "Получен сигнал $sig, завершаю работу"
exit 130
}
dir_size_bytes() {
local path="$1"
if [[ -e "$path" ]]; then
du -sb "$path" 2>/dev/null | awk '{print $1}'
else
echo 0
fi
}
total_size_bytes() {
dir_size_bytes "$BASE_DIR"
}
ensure_requirements() {
if [[ ! -x "$PYTHON_BIN" ]]; then
echo "Не найден python: $PYTHON_BIN" >&2
exit 1
fi
if [[ ! -f "$SCRIPT_PATH" ]]; then
echo "Не найден script: $SCRIPT_PATH" >&2
exit 1
fi
if ! command -v systemctl >/dev/null 2>&1; then
echo "Не найден systemctl" >&2
exit 1
fi
if [[ "${EUID}" -ne 0 ]]; then
if command -v sudo >/dev/null 2>&1; then
SYSTEMCTL_BIN=(sudo systemctl)
else
echo "Для управления service нужен root или sudo" >&2
exit 1
fi
fi
mkdir -p "$BASE_DIR"
mkdir -p "$(dirname "$CAPTURE_LOG_FILE")"
if [[ -n "${CAPTURE_ORDER:-}" ]]; then
ORDER=()
local band
for band in ${CAPTURE_ORDER//,/ }; do
ORDER+=("$band")
done
fi
if [[ "${#ORDER[@]}" -eq 0 ]]; then
echo "ORDER пустой. Укажите частоты в ORDER или задайте CAPTURE_ORDER." >&2
exit 1
fi
log "Рабочая директория: $SCRIPT_DIR"
log "BASE_DIR: $BASE_DIR"
log "ORDER: ${ORDER[*]}"
log "RUN_ONCE: $RUN_ONCE"
}
run_one_freq() {
local band="$1"
local serial="${SERIAL[$band]}"
local freq="${FREQ_HZ[$band]}"
if [[ -z "$serial" ]]; then
log "Для band=$band пустой serial, пропускаю"
return 0
fi
local ts out_dir log_file
ts="$(date '+%F_%H-%M-%S')"
out_dir="$BASE_DIR/$band/$ts"
log_file="$out_dir/run.log"
log "Старт band=$band serial=$serial freq=$freq dir=$out_dir"
if ! mkdir -p "$out_dir"; then
log "Не удалось создать каталог $out_dir"
return 1
fi
stop_band_service "$band"
"$PYTHON_BIN" "$SCRIPT_PATH" \
--serial "$serial" \
--freq "$freq" \
--save-dir "$out_dir" \
--file-tag "${band}_" \
--samp-rate "$SAMP_RATE" \
--split-size "$SPLIT_SIZE" \
--delay "$DELAY" \
--rf-gain "$RF_GAIN" \
--if-gain "$IF_GAIN" \
--bb-gain "$BB_GAIN" \
>"$log_file" 2>&1 &
local pid=$!
CURRENT_CAPTURE_PID="$pid"
log "PID=$pid"
while kill -0 "$pid" 2>/dev/null; do
local cur_dir_size cur_total_size
cur_dir_size="$(dir_size_bytes "$out_dir")"
cur_total_size="$(total_size_bytes)"
if (( cur_total_size >= TOTAL_LIMIT_BYTES )); then
log "Достигнут общий лимит $lim_all GiB. Останавливаю PID=$pid"
kill -TERM "$pid" 2>/dev/null || true
wait "$pid" 2>/dev/null || true
CURRENT_CAPTURE_PID=""
start_current_service
return 2
fi
if (( cur_dir_size >= PER_FREQ_LIMIT_BYTES )); then
log "Для band=$band достигнут лимит 3 GiB. Останавливаю PID=$pid"
kill -TERM "$pid" 2>/dev/null || true
for _ in {1..10}; do
if ! kill -0 "$pid" 2>/dev/null; then
break
fi
sleep 1
done
if kill -0 "$pid" 2>/dev/null; then
log "PID=$pid не завершился по TERM, отправляю KILL"
kill -KILL "$pid" 2>/dev/null || true
fi
wait "$pid" 2>/dev/null || true
break
fi
sleep 1
done
CURRENT_CAPTURE_PID=""
start_current_service
log "Завершен band=$band, размер=$(du -sh "$out_dir" | awk '{print $1}')"
return 0
}
main_loop() {
while true; do
local total_before cycle_start elapsed sleep_left
total_before="$(total_size_bytes)"
if (( total_before >= TOTAL_LIMIT_BYTES )); then
log "Общий размер уже >= GiB, выхожу"
break
fi
cycle_start="$(date +%s)"
log "Новый цикл"
for band in "${ORDER[@]}"; do
if (( $(total_size_bytes) >= TOTAL_LIMIT_BYTES )); then
log "Общий лимит достигнут внутри цикла, выхожу"
return 0
fi
run_one_freq "$band" || {
rc=$?
if [[ $rc -eq 2 ]]; then
log "Остановка по общему лимиту"
return 0
else
log "Ошибка записи band=$band rc=$rc"
return "$rc"
fi
}
done
if [[ "$RUN_ONCE" == "1" ]]; then
log "RUN_ONCE=1, завершаю работу после одного цикла"
break
fi
elapsed=$(( $(date +%s) - cycle_start ))
sleep_left=$(( CYCLE_SECONDS - elapsed ))
if (( sleep_left > 0 )); then
log "Цикл занял ${elapsed} сек, жду ${sleep_left} сек до следующего часа"
sleep "$sleep_left"
else
log "Цикл занял ${elapsed} сек, паузы нет"
fi
done
}
trap cleanup_capture EXIT
trap 'on_signal INT' INT
trap 'on_signal TERM' TERM
ensure_requirements
main_loop