From 81be6ea8c175da85636ac3adf928cc04657189b3 Mon Sep 17 00:00:00 2001 From: Stream Date: Sun, 19 Apr 2026 14:20:01 +0300 Subject: [PATCH] auto-sync: 2026-04-19 14:20:01 --- tasks/flightradar24/README.md | 186 +------------- tasks/flightradar24/compose/.env.example | 16 +- tasks/flightradar24/compose/README.md | 54 +++- .../flightradar24/compose/docker-compose.yml | 114 +++++++-- tasks/flightradar24/db/README.md | 23 +- tasks/flightradar24/db/init/002_schema.sql | 124 ++++++++++ tasks/flightradar24/db/init/003_retention.sql | 23 ++ tasks/flightradar24/docs/ARCHITECTURE.md | 171 ++----------- .../flightradar24/docs/INGEST_API_CONTRACT.md | 44 ++++ tasks/flightradar24/docs/RTL-SDR_TZ.md | 233 ++---------------- 10 files changed, 407 insertions(+), 581 deletions(-) create mode 100644 tasks/flightradar24/db/init/002_schema.sql create mode 100644 tasks/flightradar24/db/init/003_retention.sql create mode 100644 tasks/flightradar24/docs/INGEST_API_CONTRACT.md diff --git a/tasks/flightradar24/README.md b/tasks/flightradar24/README.md index 264f315..9f08dca 100644 --- a/tasks/flightradar24/README.md +++ b/tasks/flightradar24/README.md @@ -6,13 +6,18 @@ - **noisemap / FR24-прототип** — визуализация и расчёт шумовой плотности на базе исторических данных Flightradar24 - **RTL-SDR ingest-контур** — локальный приём ADS-B, хранение в PostgreSQL/PostGIS и recovery после сбоев +## Текущий курс +- ingest-контур строится как набор отдельных контейнеров: `capture`, `preprocess`, `api`, `monitoring` +- `raw_packets` хранят base64 payload + метаданные, retention 3 дня +- ingest и API связаны через схему PostgreSQL, а не через файловый контракт + ## Куда смотреть - `tasks/flightradar24/PROJECT.md` — общий статус проекта -- `tasks/flightradar24/docs/INDEX.md` — карта документации - `tasks/flightradar24/docs/ARCHITECTURE.md` — контейнерная архитектура ingest-контура - `tasks/flightradar24/docs/RTL-SDR_TZ.md` — ТЗ на RTL-SDR ingest - `tasks/flightradar24/docs/VM_SETUP.md` — инструкция по созданию VM в PVE - `tasks/flightradar24/docs/DEV_AGENT_HANDOFF.md` — пакет передачи Dev-агенту +- `tasks/flightradar24/compose/README.md` — compose skeleton и контракт API ## Текущий статус - FR24-прототип жив и остаётся в проекте как историческая и визуальная база @@ -29,182 +34,3 @@ - **Траектории полётов** за выбранный период (до 12 месяцев) - **Шумовые границы** вокруг траекторий, рассчитанные на основе высоты полёта - **Интерактивные элементы**: зум, фильтры по времени, всплывающая информация о рейсах - -## 🎯 Цели проекта - -1. **Визуализация**: Наглядно показать распределение шумового загрязнения от авиации -2. **Анализ**: Выявить наиболее загруженные воздушные коридоры -3. **Информирование**: Предоставить данные для исследований и принятия решений -4. **Оптимизация**: Эффективное использование данных Flightradar24 API в рамках тарифа Explorer - -## 🛠 Технологический стек - -### Бэкенд -- **Язык**: Python 3.8+ -- **Фреймворк**: FastAPI (лёгкий, асинхронный) -- **Библиотеки**: - - `requests` - работа с Flightradar24 API - - `sqlalchemy` / `sqlite3` - работа с базой данных - - `pandas` / `numpy` - обработка данных - - `python-dotenv` - управление конфигурацией - -### Фронтенд -- **Карты**: Leaflet.js с OpenStreetMap -- **Интерфейс**: Bootstrap 5 + чистый JavaScript -- **Визуализация**: встроенные возможности Leaflet + D3.js (опционально) - -### Хранение данных -- **База данных**: SQLite (разработка) / PostgreSQL (production) -- **Кэш**: файловая система (JSON/CSV) -- **Конфигурация**: `.env` файл + `config.json` - -## 📊 Модель шумового воздействия (v1.0) - -### Основные допущения -- **Фактор**: только высота полёта -- **Зависимость**: уровень шума обратно пропорционален высоте -- **Формула**: - ``` - noise_radius_km = base_radius * (min_height / actual_height) * factor - ``` - где: - - `base_radius` = 5.0 км (базовый радиус шума) - - `min_height` = 300 м (минимальная высота для расчёта) - - `factor` = 0.01 (коэффициент масштабирования) - -### Параметры (config.json) -```json -"noise_model": { - "base_noise_radius_km": 5.0, - "height_factor": 0.01, - "min_height_m": 300, - "max_height_m": 12000, - "min_radius_km": 0.5, - "max_radius_km": 10.0 -} -``` - -### Бэклог для v2.0 -- Учёт типа воздушного судна -- Учёт времени суток (ночные полёты) -- Учёт направления и скорости ветра -- Привязка к санитарным нормам - -## 🚀 Быстрый старт - -### 1. Установка зависимостей -```bash -pip install -r requirements.txt -``` - -### 2. Настройка API ключа -```bash -export FLIGHTRADAR24_API_KEY="your_api_key_here" -``` -Или создайте файл `.env`: -``` -FLIGHTRADAR24_API_KEY=your_api_key_here -``` - -### 3. Проверка подключения -```bash -python scripts/check_api.py -``` - -### 4. Запуск тестового сбора данных -```bash -python scripts/flightradar24_explorer.py -``` - -## 📁 Структура проекта - -``` -flightradar24/ -├── README.md # Эта документация -├── requirements.txt # Зависимости Python -├── config.json # Конфигурация проекта -├── scripts/ # Вспомогательные скрипты -│ ├── check_api.py # Проверка API ключа -│ └── flightradar24_explorer.py # Базовый клиент API -├── reports/ # Отчёты и документация -│ └── ТЗ_Карта_шумового_загрязнения_Flightradar24.md -├── data/ # Данные (будет создано) -│ ├── cache/ # Кэшированные данные API -│ ├── processed/ # Обработанные данные -│ └── exports/ # Экспортированные файлы -├── backend/ # Бэкенд приложения (будет создано) -├── frontend/ # Фронтенд приложения (будет создано) -└── docs/ # Документация (будет создано) -``` - -## 🔧 Конфигурация - -Основные параметры настройки в `config.json`: - -| Параметр | Описание | Значение по умолчанию | -|----------|----------|----------------------| -| `geography.region` | Регион исследования | Московская область | -| `geography.bounds` | Границы региона | 54.5-56.5°N, 35.5-39.5°E | -| `data_collection.target_period_months` | Целевой период данных | 12 месяцев | -| `data_collection.initial_period_days` | Начальный период для тестирования | 30 дней | -| `visualization.default_center` | Центр карты | [55.7558, 37.6173] (Москва) | -| `visualization.default_zoom` | Увеличение карты | 9 | - -## 💳 Использование кредитов Flightradar24 API - -### Тариф Explorer -- **Базовый лимит**: 60,000 кредитов/месяц -- **Промо-период**: до 120,000 кредитов/месяц (до 31.05.2026) -- **Обновление**: раз в неделю/месяц в зависимости от лимитов - -### Примерная стоимость запросов -| Endpoint | Кредитов/запрос | Примерное использование | -|----------|-----------------|-------------------------| -| `flight/list` (live) | 5 | 100 запросов = 500 кредитов | -| `flight/{id}/history` | 20 | 50 рейсов = 1,000 кредитов | -| `flight/{id}/playback` | 30 | 30 треков = 900 кредитов | - -### Стратегия оптимизации -1. **Кэширование**: Сохранять полученные данные локально -2. **Пакетная обработка**: Собирать данные партиями -3. **Приоритизация**: Сначала ключевые маршруты и периоды -4. **Мониторинг**: Регулярно проверять остаток кредитов - -## 📈 План разработки - -### Этап 1: Подготовка и прототип (23-25 марта 2026) -- [ ] Проверка доступности исторических данных -- [ ] Создание proof-of-concept с 10-50 траекториями -- [ ] Согласование визуализации с Заказчиком - -### Этап 2: Разработка бэкенда (26-28 марта 2026) -- [ ] Архитектура FastAPI приложения -- [ ] Система сбора и обработки данных -- [ ] API для фронтенда - -### Этап 3: Разработка фронтенда (29-31 марта 2026) -- [ ] Интерфейс карты (Leaflet) -- [ ] Панель управления и фильтры -- [ ] Интеграция с бэкендом - -### Этап 4: Тестирование и оптимизация (1-2 апреля 2026) -- [ ] Функциональное тестирование -- [ ] Оптимизация производительности -- [ ] Документация и развёртывание - -## 🔗 Полезные ссылки - -- [Flightradar24 API Documentation](https://fr24api.flightradar24.com/) -- [Leaflet.js Documentation](https://leafletjs.com/) -- [FastAPI Documentation](https://fastapi.tiangolo.com/) -- [Полное ТЗ проекта](reports/ТЗ_Карта_шумового_загрязнения_Flightradar24.md) - -## 📞 Контакты - -- **Заказчик**: Слава -- **Исполнитель**: Стрим (ИИ-ассистент) -- **Канал связи**: Telegram через OpenClaw - ---- - -*Проект находится в активной разработке. Последнее обновление: 22 марта 2026.* \ No newline at end of file diff --git a/tasks/flightradar24/compose/.env.example b/tasks/flightradar24/compose/.env.example index f892d61..11667d4 100644 --- a/tasks/flightradar24/compose/.env.example +++ b/tasks/flightradar24/compose/.env.example @@ -9,8 +9,16 @@ POSTGRES_PUBLISHED_PORT=5432 POSTGRES_HOST=postgres POSTGRES_PORT=5432 -# Ingest scaffold -INGEST_MODE=placeholder -RAW_RETENTION_DAYS=3 -OVERLAP_MINUTES=10 +API_PUBLISHED_PORT=8080 +MONITORING_PUBLISHED_PORT=9090 + +# Capture / preprocess CAPTURE_SOURCE=rtl-sdr +RTLSDR_DEVICE_INDEX=0 +RTLSDR_SAMPLE_RATE=2000000 +RTLSDR_CENTER_FREQUENCY=1090000000 +RTLSDR_GAIN=auto +RAW_RETENTION_DAYS=3 +PARTITION_RETENTION_DAYS=3 +OVERLAP_MINUTES=10 +MONITORING_INTERVAL_SECONDS=30 diff --git a/tasks/flightradar24/compose/README.md b/tasks/flightradar24/compose/README.md index dac1c65..e6fb26a 100644 --- a/tasks/flightradar24/compose/README.md +++ b/tasks/flightradar24/compose/README.md @@ -1,21 +1,51 @@ -# Compose scaffold for FR24 ingest +# FR24 ingest compose skeleton -This directory contains the baseline Docker Compose skeleton for the RTL-SDR ingest contour. +This directory contains the Docker Compose skeleton for the RTL-SDR ingest contour. ## Services -- `postgres` — PostgreSQL 16 + PostGIS -- `ingest` — placeholder for capture/preprocess wiring -- `status` — simple heartbeat/status scaffold +- `postgres` — PostgreSQL 16 + PostGIS, single source of truth for ingest and API +- `capture` — RTL-SDR access and `raw_packets` writer +- `preprocess` — normalization, flights/tracks/track_points builder, recovery +- `api` — reader for noisemap UI and future viewer endpoints +- `monitoring` — separate container for healthchecks, disk/DB/capture status, alerts ## Layout - `../db/postgres` — PostgreSQL data directory -- `../db/init` — init SQL scripts for schema/bootstrap -- `../ingest` — ingest service code placeholder -- `../logs/ingest` — ingest logs -- `../logs/status` — status heartbeat files -- `../backup` — backup artifacts +- `../db/init` — bootstrap SQL for extensions and schema +- `../ingest` — capture and preprocess implementation +- `../frontend` — API/viewer implementation +- `../logs/*` — service logs +- `../backup` — pg_dump / restore artifacts + +## Architecture notes +- `raw_packets` stores binary payloads as base64 plus metadata: timestamp, frequency, RSSI, SNR, samplerate. +- Raw retention is 3 days. +- Raw data is partitioned by date. +- `capture` and `preprocess` are separate containers. +- `api` is included now so the ingest↔API contract is fixed early. +- The contract between ingest and API is the PostgreSQL schema itself: ingest writes tables, API reads the same tables. + +## API / data contract +The API reads: +- `captures` +- `raw_packets` +- `aircraft` +- `flights` +- `track_points` +- `tracks` +- `processing_state` +- `noise_results` + +Planned endpoints: +- `GET /health` — service status +- `GET /captures` — capture sessions +- `GET /aircraft` — aircraft list and search +- `GET /flights` — flights with filters +- `GET /tracks/{track_id}` — a single track with points +- `GET /noise-results` — noise polygons and summaries +- `GET /dashboard/status` — ingest / recovery / backlog status for UI +- `GET /viewer/config` — map/UI bootstrap metadata ## Notes -- No destructive migrations are included. -- The `ingest` service is a placeholder; wire real capture/preprocess logic later. +- No runtime is started from this repository during preparation. - The stack expects a `.env` file copied from `.env.example` in this directory. diff --git a/tasks/flightradar24/compose/docker-compose.yml b/tasks/flightradar24/compose/docker-compose.yml index aeca42e..6c4dd19 100644 --- a/tasks/flightradar24/compose/docker-compose.yml +++ b/tasks/flightradar24/compose/docker-compose.yml @@ -4,12 +4,20 @@ name: fr24-ingest x-common-env: &common-env TZ: ${TZ:-UTC} + APP_ENV: ${APP_ENV:-dev} POSTGRES_HOST: ${POSTGRES_HOST:-postgres} POSTGRES_PORT: ${POSTGRES_PORT:-5432} POSTGRES_DB: ${POSTGRES_DB:-fr24} POSTGRES_USER: ${POSTGRES_USER:-fr24} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-change-me} - APP_ENV: ${APP_ENV:-dev} + RAW_RETENTION_DAYS: ${RAW_RETENTION_DAYS:-3} + PARTITION_RETENTION_DAYS: ${PARTITION_RETENTION_DAYS:-3} + OVERLAP_MINUTES: ${OVERLAP_MINUTES:-10} + CAPTURE_SOURCE: ${CAPTURE_SOURCE:-rtl-sdr} + RTLSDR_DEVICE_INDEX: ${RTLSDR_DEVICE_INDEX:-0} + RTLSDR_SAMPLE_RATE: ${RTLSDR_SAMPLE_RATE:-2000000} + RTLSDR_CENTER_FREQUENCY: ${RTLSDR_CENTER_FREQUENCY:-1090000000} + RTLSDR_GAIN: ${RTLSDR_GAIN:-auto} services: postgres: @@ -35,25 +43,24 @@ services: networks: - fr24-net - ingest: + capture: image: alpine:3.20 - container_name: fr24-ingest - command: ["sh", "-c", "echo 'ingest placeholder: wire capture/preprocess here'; tail -f /dev/null"] + container_name: fr24-capture + command: ["sh", "-c", "echo 'capture placeholder: read RTL-SDR and write raw_packets'; tail -f /dev/null"] environment: <<: *common-env - INGEST_MODE: ${INGEST_MODE:-placeholder} - RAW_RETENTION_DAYS: ${RAW_RETENTION_DAYS:-3} - OVERLAP_MINUTES: ${OVERLAP_MINUTES:-10} - CAPTURE_SOURCE: ${CAPTURE_SOURCE:-rtl-sdr} + SERVICE_ROLE: capture + devices: + - "/dev/bus/usb:/dev/bus/usb" volumes: - ../ingest:/app - - ../logs/ingest:/var/log/fr24 + - ../logs/capture:/var/log/fr24 - ../backup:/backup depends_on: postgres: condition: service_healthy healthcheck: - test: ["CMD-SHELL", "test -f /tmp/ingest-ready && echo ok || exit 1"] + test: ["CMD-SHELL", "test -f /tmp/capture-ready || exit 1"] interval: 30s timeout: 5s retries: 3 @@ -62,17 +69,89 @@ services: networks: - fr24-net - status: + preprocess: image: alpine:3.20 - container_name: fr24-status - command: ["sh", "-c", "while true; do date -u +'%Y-%m-%dT%H:%M:%SZ' > /status/heartbeat; sleep 30; done"] + container_name: fr24-preprocess + command: ["sh", "-c", "echo 'preprocess placeholder: normalize data and build flights/tracks'; tail -f /dev/null"] + environment: + <<: *common-env + SERVICE_ROLE: preprocess volumes: - - ../logs/status:/status + - ../ingest:/app + - ../logs/preprocess:/var/log/fr24 + - ../backup:/backup + depends_on: + postgres: + condition: service_healthy + capture: + condition: service_started healthcheck: - test: ["CMD-SHELL", "test -f /status/heartbeat"] + test: ["CMD-SHELL", "test -f /tmp/preprocess-ready || exit 1"] interval: 30s timeout: 5s retries: 3 + start_period: 10s + restart: unless-stopped + networks: + - fr24-net + + api: + image: alpine:3.20 + container_name: fr24-api + command: ["sh", "-c", "echo 'api placeholder: noisemap reader and UI endpoints'; tail -f /dev/null"] + environment: + <<: *common-env + SERVICE_ROLE: api + API_PORT: ${API_PORT:-8080} + ports: + - "${API_PUBLISHED_PORT:-8080}:8080" + volumes: + - ../frontend:/app + - ../logs/api:/var/log/fr24 + depends_on: + postgres: + condition: service_healthy + preprocess: + condition: service_started + healthcheck: + test: ["CMD-SHELL", "test -f /tmp/api-ready || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s + restart: unless-stopped + networks: + - fr24-net + + monitoring: + image: alpine:3.20 + container_name: fr24-monitoring + command: ["sh", "-c", "echo 'monitoring placeholder: disk, db, capture status, alerts'; tail -f /dev/null"] + environment: + <<: *common-env + SERVICE_ROLE: monitoring + MONITORING_INTERVAL_SECONDS: ${MONITORING_INTERVAL_SECONDS:-30} + ports: + - "${MONITORING_PUBLISHED_PORT:-9090}:9090" + volumes: + - ../logs/monitoring:/var/log/fr24 + - ../backup:/backup + - /var/run/docker.sock:/var/run/docker.sock:ro + depends_on: + postgres: + condition: service_healthy + capture: + condition: service_started + preprocess: + condition: service_started + api: + condition: service_started + healthcheck: + test: ["CMD-SHELL", "test -f /tmp/monitoring-ready || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s restart: unless-stopped networks: - fr24-net @@ -81,8 +160,3 @@ networks: fr24-net: name: fr24-net driver: bridge - -volumes: - postgres-data: - ingest-logs: - backups: diff --git a/tasks/flightradar24/db/README.md b/tasks/flightradar24/db/README.md index 49cfab6..9c398ed 100644 --- a/tasks/flightradar24/db/README.md +++ b/tasks/flightradar24/db/README.md @@ -1,7 +1,24 @@ # Database layout - `postgres/` — PostgreSQL persistent data volume -- `init/` — bootstrap SQL for extensions and initial schema +- `init/` — bootstrap SQL for extensions, schema, and retention helpers -Current bootstrap only enables `postgis` and `pgcrypto`. -No irreversible migrations are included. +Bootstrap now enables: +- `postgis` +- `pgcrypto` +- core FR24 ingest schema under `fr24` + +Schema objects: +- `captures` +- `raw_packets` +- `aircraft` +- `flights` +- `tracks` +- `track_points` +- `processing_state` +- `noise_results` + +Notes: +- `raw_packets` is partitioned by `partition_date` +- raw retention target is 3 days +- no runtime is started from this repository during preparation diff --git a/tasks/flightradar24/db/init/002_schema.sql b/tasks/flightradar24/db/init/002_schema.sql new file mode 100644 index 0000000..82ec877 --- /dev/null +++ b/tasks/flightradar24/db/init/002_schema.sql @@ -0,0 +1,124 @@ +CREATE SCHEMA IF NOT EXISTS fr24; + +CREATE TABLE IF NOT EXISTS fr24.captures ( + capture_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + started_at TIMESTAMPTZ NOT NULL, + ended_at TIMESTAMPTZ, + source TEXT NOT NULL DEFAULT 'rtl-sdr', + device_index INTEGER NOT NULL DEFAULT 0, + center_frequency_hz BIGINT NOT NULL, + sample_rate_hz INTEGER NOT NULL, + gain_db NUMERIC(6,2), + status TEXT NOT NULL DEFAULT 'active', + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + notes TEXT +); + +CREATE TABLE IF NOT EXISTS fr24.raw_packets ( + raw_packet_id BIGSERIAL PRIMARY KEY, + capture_id UUID NOT NULL REFERENCES fr24.captures(capture_id) ON DELETE CASCADE, + observed_at TIMESTAMPTZ NOT NULL, + partition_date DATE NOT NULL DEFAULT (CURRENT_DATE), + frequency_hz BIGINT NOT NULL, + rssi_dbm NUMERIC(8,3), + snr_db NUMERIC(8,3), + samplerate_hz INTEGER NOT NULL, + payload_base64 TEXT NOT NULL, + payload_bytes INTEGER, + decoded_format TEXT, + message_type TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +) PARTITION BY RANGE (partition_date); + +CREATE TABLE IF NOT EXISTS fr24.raw_packets_default + PARTITION OF fr24.raw_packets DEFAULT; + +CREATE TABLE IF NOT EXISTS fr24.aircraft ( + aircraft_id BIGSERIAL PRIMARY KEY, + icao24 TEXT NOT NULL UNIQUE, + callsign TEXT, + registration TEXT, + aircraft_type TEXT, + operator_name TEXT, + first_seen_at TIMESTAMPTZ, + last_seen_at TIMESTAMPTZ, + metadata JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS fr24.flights ( + flight_id BIGSERIAL PRIMARY KEY, + aircraft_id BIGINT NOT NULL REFERENCES fr24.aircraft(aircraft_id) ON DELETE CASCADE, + departure_airport TEXT, + arrival_airport TEXT, + callsign TEXT, + flight_number TEXT, + started_at TIMESTAMPTZ NOT NULL, + ended_at TIMESTAMPTZ, + status TEXT NOT NULL DEFAULT 'active', + source TEXT NOT NULL DEFAULT 'rtl-sdr', + metadata JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS fr24.tracks ( + track_id BIGSERIAL PRIMARY KEY, + flight_id BIGINT NOT NULL REFERENCES fr24.flights(flight_id) ON DELETE CASCADE, + geometry GEOMETRY(LineString, 4326), + point_count INTEGER NOT NULL DEFAULT 0, + min_altitude_m NUMERIC(10,2), + max_altitude_m NUMERIC(10,2), + distance_m NUMERIC(14,2), + duration_seconds INTEGER, + last_point_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS fr24.track_points ( + track_point_id BIGSERIAL PRIMARY KEY, + track_id BIGINT NOT NULL REFERENCES fr24.tracks(track_id) ON DELETE CASCADE, + flight_id BIGINT NOT NULL REFERENCES fr24.flights(flight_id) ON DELETE CASCADE, + observed_at TIMESTAMPTZ NOT NULL, + point_order INTEGER NOT NULL, + geom GEOMETRY(Point, 4326) NOT NULL, + altitude_m NUMERIC(10,2), + ground_speed_kt NUMERIC(10,2), + vertical_rate_fpm NUMERIC(10,2), + heading_deg NUMERIC(10,2), + source_packet_id BIGINT REFERENCES fr24.raw_packets(raw_packet_id) ON DELETE SET NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + UNIQUE (track_id, point_order) +); + +CREATE TABLE IF NOT EXISTS fr24.processing_state ( + state_key TEXT PRIMARY KEY, + state_value JSONB NOT NULL, + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + note TEXT +); + +CREATE TABLE IF NOT EXISTS fr24.noise_results ( + noise_result_id BIGSERIAL PRIMARY KEY, + flight_id BIGINT REFERENCES fr24.flights(flight_id) ON DELETE CASCADE, + track_id BIGINT REFERENCES fr24.tracks(track_id) ON DELETE CASCADE, + computed_at TIMESTAMPTZ NOT NULL DEFAULT now(), + period_start TIMESTAMPTZ, + period_end TIMESTAMPTZ, + noise_model TEXT NOT NULL DEFAULT 'v1', + geometry GEOMETRY(Polygon, 4326), + summary JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_raw_packets_capture_time ON fr24.raw_packets (capture_id, observed_at DESC); +CREATE INDEX IF NOT EXISTS idx_raw_packets_partition_date ON fr24.raw_packets (partition_date); +CREATE INDEX IF NOT EXISTS idx_aircraft_last_seen ON fr24.aircraft (last_seen_at DESC); +CREATE INDEX IF NOT EXISTS idx_flights_aircraft_started ON fr24.flights (aircraft_id, started_at DESC); +CREATE INDEX IF NOT EXISTS idx_track_points_track_time ON fr24.track_points (track_id, observed_at); +CREATE INDEX IF NOT EXISTS idx_track_points_flight_time ON fr24.track_points (flight_id, observed_at); +CREATE INDEX IF NOT EXISTS idx_tracks_flight ON fr24.tracks (flight_id); +CREATE INDEX IF NOT EXISTS idx_noise_results_computed_at ON fr24.noise_results (computed_at DESC); diff --git a/tasks/flightradar24/db/init/003_retention.sql b/tasks/flightradar24/db/init/003_retention.sql new file mode 100644 index 0000000..1dc10ff --- /dev/null +++ b/tasks/flightradar24/db/init/003_retention.sql @@ -0,0 +1,23 @@ +CREATE OR REPLACE FUNCTION fr24.drop_raw_partitions_older_than(retention_days INTEGER) +RETURNS void +LANGUAGE plpgsql +AS $$ +DECLARE + partition_record RECORD; + cutoff_date DATE := CURRENT_DATE - retention_days; +BEGIN + FOR partition_record IN + SELECT inhrelid::regclass::text AS partition_name + FROM pg_inherits + JOIN pg_class parent ON pg_inherits.inhparent = parent.oid + WHERE parent.relname = 'raw_packets' + AND parent.relnamespace = 'fr24'::regnamespace + LOOP + IF partition_record.partition_name <> 'fr24.raw_packets_default' THEN + EXECUTE format('ALTER TABLE %s DETACH PARTITION %s', 'fr24.raw_packets', partition_record.partition_name); + END IF; + END LOOP; +END; +$$; + +COMMENT ON FUNCTION fr24.drop_raw_partitions_older_than(INTEGER) IS 'Retention helper placeholder; actual cleanup is driven by monitoring/preprocess scheduler.'; diff --git a/tasks/flightradar24/docs/ARCHITECTURE.md b/tasks/flightradar24/docs/ARCHITECTURE.md index 7159b6b..62b259c 100644 --- a/tasks/flightradar24/docs/ARCHITECTURE.md +++ b/tasks/flightradar24/docs/ARCHITECTURE.md @@ -10,169 +10,46 @@ - хранить данные в PostgreSQL/PostGIS - отдавать данные в noisemap / карту -## 2. Ограничения среды -- 1 VM в PVE -- USB RTL-SDR проброшен в VM -- ресурсы VM ограничены: - - CPU: до 6 vCPU - - RAM: 10–12 GB - - SSD: 80–100 GB max -- развёртывание через Docker Compose +## 2. Контейнеры -## 3. Логическая схема - -```text -RTL-SDR USB - ↓ -capture - ↓ -raw_packets / captures (PostgreSQL) - ↓ -preprocess - ↓ -flights / tracks / track_points / processing_state - ↓ -noise_results - ↓ -noisemap / UI / API -``` - -## 4. Контейнеры - -### 4.1 `postgres` -**Назначение:** хранение всех данных проекта. - -Содержит: +### 2.1 `postgres` +Хранит: - `captures` - `raw_packets` - `aircraft` - `flights` -- `track_points` - `tracks` +- `track_points` - `processing_state` - `noise_results` -Требования: -- PostgreSQL -- PostGIS enabled -- volume для данных -- регулярные backup/restore процедуры +### 2.2 `capture` +- читает USB RTL-SDR +- пишет `captures` и `raw_packets` +- не занимается нормализацией -### 4.2 `capture` -**Назначение:** приём ADS-B потока с RTL-SDR. +### 2.3 `preprocess` +- читает `raw_packets` +- строит `aircraft`, `flights`, `tracks`, `track_points` +- управляет `processing_state` +- поддерживает recovery -Функции: -- чтение USB-устройства -- получение live-потока -- запись сырья в PostgreSQL -- фиксация `captures` -- маркировка границ сессий приёма +### 2.4 `api` +- читает те же таблицы +- отдаёт данные для noisemap UI +- не пересчитывает геометрию на лету -Требования: -- лёгкий CPU footprint -- устойчивость к временной потере downstream -- healthcheck -- автоматический рестарт +### 2.5 `monitoring` +- отдельный контейнер +- healthchecks, disk/DB/capture status, alerts -### 4.3 `preprocess` -**Назначение:** нормализация и догоняющая обработка. - -Функции: -- обработка live-потока -- разбор `raw_packets` -- построение `aircraft` -- построение `flights` -- построение `tracks` и `track_points` -- обновление `processing_state` -- recovery с overlap при сбоях - -Требования: -- инкрементальная обработка -- дедупликация на границах overlap -- ограничение по памяти -- возможность догонять пропуски автоматически - -### 4.4 `api` / `viewer` -**Назначение:** отдача данных для noisemap. - -Функции: -- чтение обработанных данных из PostgreSQL -- отдача треков, точек и шумовых результатов -- фильтрация по периоду и зоне -- API для карты/визуализации - -Требования: -- лёгкие запросы -- минимальная логика в рантайме -- кэширование только при реальной необходимости - -### 4.5 `monitoring` (optional) -**Назначение:** наблюдение за здоровьем контура. - -Функции: -- healthcheck контейнеров -- мониторинг задержки обработки -- мониторинг заполнения диска -- мониторинг статуса RTL-SDR -- алерты при деградации - -## 5. Docker Compose структура -Минимальный compose должен поднимать: +## 3. Docker Compose порядок +Минимальный состав: - `postgres` - `capture` - `preprocess` - `api` - -Опционально: - `monitoring` -### Сетевое правило -- контейнеры общаются внутри одной docker network -- наружу публикуется только `api` и, если нужно, `monitoring` -- `postgres` не публикуется наружу - -## 6. Данные и потоки - -### 6.1 Live flow -1. RTL-SDR → `capture` -2. `capture` пишет сырьё в `raw_packets` -3. `preprocess` читает сырьё -4. `preprocess` обновляет `flights`, `tracks`, `track_points` -5. `api` отдаёт данные на карту - -### 6.2 Recovery flow -1. `preprocess` обнаруживает отставание -2. ищет недостающий диапазон в `raw_packets` -3. добирает пропущенные записи -4. обрабатывает с небольшим overlap -5. обновляет `processing_state` - -## 7. Автозапуск и устойчивость -- контейнеры стартуют после reboot VM -- каждый контейнер имеет healthcheck -- сбой одного контейнера не должен валить всю VM -- `capture` должен уметь переживать временный разрыв downstream -- `preprocess` должен уметь догонять запись после восстановления - -## 8. Производительность -С учётом ограничений VM: -- минимизировать число контейнеров и лишних сервисов -- не тащить тяжёлые брокеры очередей без необходимости -- не строить много промежуточных копий данных -- хранить сырьё коротко и чистить автоматически -- тяжёлые вычисления держать инкрементальными - -## 9. Что должно быть детализировано дальше -Следующие документы/итерации должны закрыть: -- схему БД и индексы -- backup/restore -- мониторинг и алерты -- контракт между `preprocess` и `api` -- точный формат `raw_packets` -- стратегию партиционирования - -## 10. Связь с проектом -Этот документ — техническая основа для: -- `tasks/flightradar24/PROJECT.md` -- `tasks/flightradar24/docs/RTL-SDR_TZ.md` - +## 4. Контракт ingest↔API +Контрактом является схема PostgreSQL. Детали — в `docs/INGEST_API_CONTRACT.md`. diff --git a/tasks/flightradar24/docs/INGEST_API_CONTRACT.md b/tasks/flightradar24/docs/INGEST_API_CONTRACT.md new file mode 100644 index 0000000..f35c100 --- /dev/null +++ b/tasks/flightradar24/docs/INGEST_API_CONTRACT.md @@ -0,0 +1,44 @@ +# Ingest ↔ API contract + +## Principle +The contract between ingest and API is the PostgreSQL schema. +Ingest writes rows; API reads the same rows. +No file exchange is required for the core data path. + +## API read model +The API reads these tables: +- `fr24.captures` — capture sessions and source metadata +- `fr24.raw_packets` — raw base64 packets with radio metadata +- `fr24.aircraft` — aircraft identity and enrichment +- `fr24.flights` — flight sessions and state +- `fr24.tracks` — track aggregates +- `fr24.track_points` — ordered track geometry points +- `fr24.processing_state` — recovery cursors and pipeline state +- `fr24.noise_results` — computed noise polygons and summaries + +## Ingest write model +- `capture` writes `captures` and `raw_packets` +- `preprocess` writes `aircraft`, `flights`, `tracks`, `track_points`, `processing_state`, `noise_results` +- `monitoring` does not own core data; it reports status and alerts + +## Planned endpoints +- `GET /health` +- `GET /captures` +- `GET /raw-packets` with pagination and time filters +- `GET /aircraft` +- `GET /flights` +- `GET /tracks/{track_id}` +- `GET /track-points?track_id=...` +- `GET /noise-results` +- `GET /dashboard/status` +- `GET /viewer/config` + +## Query expectations +- API should prefer indexed filters by time, flight, aircraft, and partition date. +- API should not reconstruct track geometry from raw packets at request time. +- API should read prebuilt aggregates from `tracks` and `track_points`. + +## Retention expectations +- `raw_packets` keep 3 days by default. +- Cleanup is partition-based by date. +- Aggregated tables may retain longer, depending on UI needs. diff --git a/tasks/flightradar24/docs/RTL-SDR_TZ.md b/tasks/flightradar24/docs/RTL-SDR_TZ.md index bb2a949..445084a 100644 --- a/tasks/flightradar24/docs/RTL-SDR_TZ.md +++ b/tasks/flightradar24/docs/RTL-SDR_TZ.md @@ -1,219 +1,22 @@ -# ТЗ: Приём, хранение и обработка данных с RTL-SDR для FR24 / noisemap +# ТЗ: RTL-SDR ingest-контур -## 1. Цель -Построить локальный контур приёма ADS-B данных с RTL-SDR, их предобработки, хранения в PostgreSQL/PostGIS и дальнейшего использования для: -- восстановления данных после сбоев без потерь, -- формирования сущностей рейсов и треков, -- расчёта производных шумовых данных, -- визуализации на карте в рамках проекта FR24 / noisemap. +## Цель +Построить локальный ADS-B ingest-контур на RTL-SDR с PostgreSQL/PostGIS, recovery и API для noisemap UI. -## 2. Целевой контур -Система разворачивается на одной VM в PVE. +## Обязательные сервисы +- `postgres` +- `capture` +- `preprocess` +- `api` +- `monitoring` -### Ограничения VM -- CPU: до 6 vCPU -- RAM: 10–12 GB -- SSD: 80–100 GB max +## Требования к данным +- raw packets хранятся как base64 payload + radio metadata +- retention raw: 3 дня +- partitioning: по дате +- схема БД — контракт между ingest и API -### Аппаратный вход -- RTL-SDR подключён к физической машине по USB -- PVE-хост пробрасывает USB в VM - -### Развёртывание -- Docker Compose -- каждый компонент в отдельном контейнере - -## 3. Архитектура -### Компоненты внутри VM -- postgres + postgis -- capture -- preprocess -- api/viewer -- optional: monitoring - -### Принцип -- одна VM -- одна PostgreSQL база -- сырьё и обработанные данные живут в одной БД, но логически разделены -- сырьё хранится с небольшим retention и используется для догоняющей обработки - -## 4. Режимы работы -### 4.1 Live mode -- система принимает поток в реальном времени -- записи сохраняются в сырьевой слой -- данные сразу уходят в предобработку - -### 4.2 Recovery mode -- если обработка отстала или упала, -- но сырьё ещё доступно, -- система автоматически догоняет пропущенный диапазон - -### 4.3 Overlap mode -- при догоняющей обработке допускается небольшой перекрывающийся участок -- это нужно, чтобы не терять данные на границах и компенсировать сбои - -## 5. Хранение данных -### 5.1 Retention -- сырьё хранится 3 дня -- после этого raw-слой чистится автоматически -- 3 дня — минимальный гарантированный горизонт для восстановления - -### 5.2 База -- PostgreSQL -- PostGIS обязателен - -### 5.3 Принцип хранения -- raw-данные отделены от core-данных -- core-данные очищены и оптимизированы под запросы карты и расчётов -- схема должна быть компактной, без лишних таблиц и тяжёлой нормализации - -## 6. Объекты хранения -Минимальный набор сущностей: - -### `captures` -Сессии приёма. -Хранят: -- старт и конец сессии -- источник приёма -- статус -- покрытый диапазон времени -- признаки сбоя / восстановления - -### `raw_packets` -Сырой поток. -Хранят: -- timestamp -- payload/packet data -- signal metadata при наличии -- привязку к capture/session - -### `aircraft` -Справочник бортов. -Хранят: -- ICAO24 -- callsign -- тип/категорию при наличии -- оператор/авиакомпанию при наличии - -### `flights` -Сущность рейса. -Хранят: -- flight id / number -- aircraft -- время начала и конца -- маршрут / аэропорты при наличии -- состояние заполненности - -### `track_points` -Точки трека. -Хранят: -- время -- координаты -- высоту -- скорость -- курс -- принадлежность к flight/track - -### `tracks` -Сводка трека. -Хранят: -- bbox -- длительность -- min/max altitude -- количество точек -- краткое описание маршрута - -### `processing_state` -Состояние пайплайна. -Хранят: -- последний успешный checkpoint -- обработанный диапазон -- отставание -- состояние recovery - -### `noise_results` -Производные данные для карты. -Хранят: -- расчётные зоны/радиусы -- интенсивность/плотность -- геометрию для визуализации -- привязку к рейсу/треку/времени - -## 7. Основные требования к обработке -### 7.1 Приём -- принимать ADS-B поток от RTL-SDR -- сохранять сырьё до обработки -- не терять данные при временном сбое downstream - -### 7.2 Предобработка -- нормализовать записи -- выделять рейсы -- строить треки -- обновлять state обработки - -### 7.3 Восстановление -- после сбоя искать недостающий диапазон -- добирать пропущенные записи из raw-слоя -- допускается overlap - -### 7.4 Оптимизация -- обработка должна быть лёгкой по CPU и памяти -- БД и схема должны быть компактными -- лишних промежуточных слоёв не заводить - -## 8. Карта и визуализация -Система должна поддерживать данные, нужные для: -- отображения треков рейсов -- отображения шумовых зон -- анализа плотности пролётов -- фильтрации по времени -- отрисовки результатов на карте - -## 9. Требования к инфраструктуре -- запуск через Docker Compose -- отдельные контейнеры для ключевых частей -- автозапуск после ребута VM -- возможность мониторить состояние приёма и БД -- логирование по каждому компоненту отдельно - -## 10. Критерии приёмки -Система считается готовой, если: -- RTL-SDR отдаёт поток в VM через USB passthrough -- raw-данные сохраняются в PostgreSQL -- raw retention работает на 3 дня -- при сбое система может догнать пропущенный диапазон -- в БД присутствуют рейсы, треки и точки треков -- PostGIS используется для геоданных -- данные доступны для визуализации на карте -- контейнеры поднимаются через Docker Compose - -## 11. Открытые вопросы, которые ещё стоит уточнить позже -- точный формат сырьевых пакетов -- нужен ли отдельный UI мониторинга -- нужен ли архив beyond 3 days -- какие индексы и партиционирование делать на старте -- где именно будет жить API noisemap после переноса - -## 12. Итерация 2 — обязательный список улучшений -Следующая итерация ТЗ должна включать: -- мониторинг отвалившегося RTL-SDR и автоперезапуск capture -- healthcheck контейнеров и БД -- политика ротации логов и объёма диска -- схема индексов и партиционирования raw/track tables -- unique key / dedup стратегия для overlapping recovery -- экспорт/импорт исторических данных при миграции -- резервное копирование PostgreSQL и восстановление -- уведомления о деградации приёма или заполнении диска -- контракт между ingest-слоем и noisemap UI, если они остаются раздельными - -## 13. Анализ: ничего ли не упущено -Дополнительно стоит проверить перед реализацией: -- нужен ли отдельный UI мониторинга -- где живёт API после переноса -- как именно партиционируются `raw_packets` и `track_points` -- нужен ли отдельный архив после 3 дней -- какую политику дедупликации выбрать для overlap на границах -- как восстанавливать БД после полного падения VM - ---- -Подготовлено на основе текущей архитектуры FR24/noisemap и согласованных ограничений VM. +## Нефункциональные требования +- `capture` и `preprocess` должны быть отдельными контейнерами +- `monitoring` должен быть отдельным контейнером +- API нужен в первой compose-итерации