Files
wiki/tasks/flightradar24/docs/PHASE2_STEP2_DATA_MART.md
2026-04-20 23:00:01 +03:00

350 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Фаза 2, Шаг 2: Загрузка треков + Витрина данных
## Статус
🔲 БТ готово, реализация не начата
## Цель
Загрузить треки рейсов из двух внешних источников (FR24 API и FlightAware AeroAPI) в отдельные таблицы, затем объединить всё в витрину `fr24_mart` для шумовой карты.
---
## Архитектура источников
```
fr24_ext.schedule ← Яндекс.Расписания (готово ✅)
fr24_ext.flight_tracks_fr24 ← FR24 API (Flight tracks)
fr24_ext.flight_tracks_fa ← FlightAware AeroAPI (track endpoint)
fr24.flights + fr24.positions ← RTL-SDR (готово ✅)
fr24_mart.flights_unified
fr24_mart.track_points
fr24_mart.noise_grid
```
---
## Источник 1: FR24 API — Flight Tracks
### Ключевые решения
- **Endpoint:** `GET /api/flight-tracks?flight_id={fr24_id}` — полный трек рейса одним запросом
- **Стоимость:** 40 кредитов/рейс ($0.012)
- **Ключ:** `MVM0hi4S7RRh7Dm4EOl1ShpDPc8CrmITXT2LY5y4dd84a62a` (KEY2, подтверждён рабочим)
- **Тариф:** Explorer (30K кредитов/мес, история 30 дней)
- **Загрузка:** ТОЛЬКО по явной команде Славы (не автоматически)
- **Период:** T-1 (вчерашний день)
- **Scope:** рейсы аэропортов SVO/DME/VKO/ZIA (через `flight-summary/light` за день)
- **Rate limit:** 10 запросов/мин (Explorer)
### Стратегия загрузки
1. `flight-summary/light` → список fr24_id за вчерашний день по 4 аэропортам (~1500 рейсов)
2. Для каждого fr24_id → `flight-tracks` → треки с точками
3. Сохранить в `fr24_ext.flight_tracks_fr24` + `fr24_ext.track_points_fr24`
4. Отметить загрузку в `fr24_ext.load_state`
### Что возвращает `flight-summary/light`
```json
{
"fr24_id": "3f509576",
"flight": "SU208",
"callsign": "AFL208",
"type": "B77W",
"reg": "RA-73140",
"orig_icao": "UUEE",
"dest_icao": "ZSPD",
"datetime_takeoff": "2026-04-20T17:16:13Z",
"datetime_landed": null,
"flight_ended": true
}
```
### Что возвращает `flight-tracks`
```json
[{
"fr24_id": "3f4f0101",
"tracks": [{
"timestamp": "2026-04-20T10:07:14Z",
"lat": 49.72, "lon": 43.57,
"alt": 25975, "gspeed": 432,
"vspeed": 1280, "track": 336,
"squawk": "0054", "source": "MLAT"
}, ...]
}]
```
~782 точки на рейс (интервал ~5-6 сек)
### Расчёт расхода кредитов
- Summary: 1500 × 3 кредита = 4500 кредитов
- Tracks: 1500 × 40 кредитов = 60 000 кредитов
- **Итого за 1 день: ~64 500 кредитов**
- Explorer (30K/мес): хватит на ~0.5 дня → нужно пополнять кредиты перед каждым запуском
- При пополнении до Essential ($90/мес, 333K): ~5 дней истории/мес
### Схема БД `fr24_ext`
```sql
CREATE TABLE fr24_ext.flight_tracks_fr24 (
id BIGSERIAL PRIMARY KEY,
fr24_id VARCHAR(20) NOT NULL UNIQUE,
flight_number VARCHAR(20),
callsign VARCHAR(20),
aircraft_type VARCHAR(10),
registration VARCHAR(15),
origin_icao VARCHAR(5),
destination_icao VARCHAR(5),
actual_takeoff TIMESTAMPTZ,
actual_landed TIMESTAMPTZ,
flight_date DATE NOT NULL,
fetched_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE fr24_ext.track_points_fr24 (
id BIGSERIAL PRIMARY KEY,
track_id BIGINT REFERENCES fr24_ext.flight_tracks_fr24(id),
observed_at TIMESTAMPTZ NOT NULL,
lat DOUBLE PRECISION NOT NULL,
lon DOUBLE PRECISION NOT NULL,
altitude_ft INTEGER,
gspeed_kt INTEGER,
vspeed_fpm INTEGER,
heading SMALLINT,
squawk VARCHAR(5),
source VARCHAR(10)
);
CREATE INDEX ON fr24_ext.track_points_fr24 (track_id, observed_at);
```
---
## Источник 2: FlightAware AeroAPI — Треки
### Ключевые решения
- **Endpoint:** `GET /aeroapi/flights/{fa_flight_id}/track`
- **Ключ:** `7qMijd3b3gVudezng3eVhKtup8iKFr75` (подтверждён рабочим)
- **Тариф:** Personal (500 запросов/мес бесплатно)
- **Использование:** треки рейсов, для которых нет трека в FR24 или нужна верификация
- **История:** до января 2011
- **Загрузка:** только по команде (как и FR24)
### Стратегия загрузки
1. `GET /aeroapi/flights/{ident}` → получить fa_flight_id для рейса
2. `GET /aeroapi/flights/{fa_flight_id}/track` → полный трек
3. Сохранить в `fr24_ext.flight_tracks_fa` + `fr24_ext.track_points_fa`
### Что возвращает track endpoint
```json
{
"actual_distance": 948,
"positions": [{
"altitude": 222, // сотни футов
"groundspeed": 382,
"heading": 315,
"latitude": 54.75,
"longitude": 37.55,
"timestamp": "2026-04-20T11:00:14Z",
"update_type": "M" // M=ADS-B, D=dead reckoning
}, ...]
}
```
~400 точек на рейс (интервал ~30-60 сек)
### Бонус — фактические времена из `/flights/{ident}`
FlightAware возвращает полный набор фактических времён:
- `actual_off` — фактический взлёт (wheels off)
- `actual_on` — фактическая посадка (wheels on)
- `departure_delay` — задержка вылета (сек)
- `arrival_delay` — задержка прилёта (сек)
Эти данные можно сохранять в `fr24_ext.schedule` (UPDATE) для обогащения табло.
### Расчёт лимитов
- Personal: 500 запросов/мес
- На каждый рейс нужно 2 запроса (flights + track) = 250 рейсов/мес бесплатно
- При T-1 загрузке: 250 рейсов/мес ÷ 22 рабочих дня ≈ **11 рейсов/день** на free tier
- Для полного покрытия нужен Standard тариф (уточнить цену в portal)
### Схема БД `fr24_ext`
```sql
CREATE TABLE fr24_ext.flight_tracks_fa (
id BIGSERIAL PRIMARY KEY,
fa_flight_id VARCHAR(50) NOT NULL UNIQUE,
ident_iata VARCHAR(10),
ident_icao VARCHAR(10),
registration VARCHAR(15),
aircraft_type VARCHAR(10),
origin_icao VARCHAR(5),
destination_icao VARCHAR(5),
actual_off TIMESTAMPTZ,
actual_on TIMESTAMPTZ,
departure_delay INTEGER, -- секунды
arrival_delay INTEGER, -- секунды
actual_distance INTEGER, -- nautical miles
flight_date DATE NOT NULL,
fetched_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE fr24_ext.track_points_fa (
id BIGSERIAL PRIMARY KEY,
track_id BIGINT REFERENCES fr24_ext.flight_tracks_fa(id),
observed_at TIMESTAMPTZ NOT NULL,
lat DOUBLE PRECISION NOT NULL,
lon DOUBLE PRECISION NOT NULL,
altitude_ft INTEGER, -- сотни футов × 100
gspeed_kt INTEGER,
heading SMALLINT,
update_type VARCHAR(5) -- M=ADS-B, D=dead reckoning
);
CREATE INDEX ON fr24_ext.track_points_fa (track_id, observed_at);
```
---
## Витрина `fr24_mart`
### Цель
Единая модель для шумовой карты — объединяет все источники, рассчитывает шум по сетке.
### Приоритет источников для трека
`RTL-SDR` > `FR24 API` > `FlightAware` > нет данных
RTL-SDR точнее (5-сек интервал, локальный приём), FR24 детальнее FlightAware (5 vs 30 сек).
### Схема `fr24_mart`
```sql
-- Единая таблица рейсов
CREATE TABLE fr24_mart.flights (
id BIGSERIAL PRIMARY KEY,
flight_number VARCHAR(20),
callsign VARCHAR(20),
icao24 CHAR(6),
airline_iata VARCHAR(5),
origin_iata VARCHAR(5),
destination_iata VARCHAR(5),
aircraft_type VARCHAR(50),
flight_date DATE NOT NULL,
scheduled_dep TIMESTAMPTZ,
actual_dep TIMESTAMPTZ,
actual_arr TIMESTAMPTZ,
duration_min INTEGER,
-- источники
has_schedule BOOLEAN DEFAULT false,
has_rtlsdr BOOLEAN DEFAULT false,
has_fr24 BOOLEAN DEFAULT false,
has_fa BOOLEAN DEFAULT false,
track_source VARCHAR(10), -- 'rtlsdr'|'fr24'|'fa'|null
track_points INTEGER,
-- ключи источников
schedule_id BIGINT,
fr24_track_id BIGINT,
fa_track_id BIGINT,
rtlsdr_flight_id BIGINT,
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE UNIQUE INDEX ON fr24_mart.flights (flight_date, callsign);
-- Единые точки трека (лучший источник)
CREATE TABLE fr24_mart.track_points (
id BIGSERIAL PRIMARY KEY,
flight_id BIGINT REFERENCES fr24_mart.flights(id),
observed_at TIMESTAMPTZ NOT NULL,
lat DOUBLE PRECISION NOT NULL,
lon DOUBLE PRECISION NOT NULL,
altitude_m INTEGER,
speed_kt INTEGER,
heading SMALLINT,
source VARCHAR(10)
);
CREATE INDEX ON fr24_mart.track_points (flight_id, observed_at);
CREATE INDEX ON fr24_mart.track_points (lat, lon);
-- Шумовая сетка (агрегат по 0.01°)
CREATE TABLE fr24_mart.noise_grid (
id BIGSERIAL PRIMARY KEY,
grid_lat NUMERIC(7,4) NOT NULL,
grid_lon NUMERIC(7,4) NOT NULL,
period_date DATE NOT NULL,
flight_count INTEGER DEFAULT 0,
noise_score FLOAT DEFAULT 0,
avg_altitude_m FLOAT,
updated_at TIMESTAMPTZ DEFAULT now(),
UNIQUE (grid_lat, grid_lon, period_date)
);
CREATE INDEX ON fr24_mart.noise_grid (period_date);
-- Метрики покрытия источников
CREATE TABLE fr24_mart.source_coverage (
coverage_date DATE PRIMARY KEY,
total_schedule INTEGER DEFAULT 0,
with_rtlsdr INTEGER DEFAULT 0,
with_fr24 INTEGER DEFAULT 0,
with_fa INTEGER DEFAULT 0,
schedule_only INTEGER DEFAULT 0,
rtlsdr_pct FLOAT,
fr24_pct FLOAT,
fa_pct FLOAT,
updated_at TIMESTAMPTZ DEFAULT now()
);
```
### Обновление витрины
- Контейнер `fr24-mart` (отдельный)
- Cron: ежечасно (инкрементальное обновление)
- Полный rebuild по флагу
---
## UI статистики — страница `/data-sources`
### Блоки страницы
**1. Покрытие источников** (за выбранный период, по дням)
- Яндекс.Расписания: N рейсов
- FR24 треки: N рейсов, % от табло
- FlightAware: N рейсов, % от табло
- RTL-SDR: N рейсов, % от табло
- График: stacked bar по дням
**2. Качество данных**
- % рейсов с маршрутом (origin/destination)
- % рейсов с треком
- % рейсов с фактическими временами
- % рейсов с типом самолёта
- Медиана точек в треке по источнику
**3. Расход кредитов FR24**
- Потрачено кредитов на загрузку (из load_state)
- Среднее кредитов/день
**4. Топ авиакомпаний** (по количеству рейсов)
**5. Топ маршрутов** (откуда/куда)
**6. Загрузка аэропортов** (SVO/DME/VKO/ZIA по часам)
---
## Контейнеры
| Контейнер | Назначение | Порт |
|---|---|---|
| `fr24-schedule` | Яндекс.Расписания (готов ✅) | 8000 |
| `fr24-tracks-fr24` | Загрузчик FR24 треков | 8001 |
| `fr24-tracks-fa` | Загрузчик FlightAware треков | 8002 |
| `fr24-mart` | Обновление витрины | — |
---
## Зависимости и порядок реализации
1. **DDL** — схемы `fr24_ext` (доп. таблицы) + `fr24_mart`
2. **fr24-tracks-fr24** — воркер FR24 API (ручной запуск)
3. **fr24-tracks-fa** — воркер FlightAware API (ручной запуск)
4. **fr24-mart** — воркер витрины (автоматический, каждый час)
5. **UI `/data-sources`** — страница статистики
---
## Открытые вопросы
- [ ] Тариф FlightAware Standard — уточнить цену за track запрос
- [ ] Глубина исторической загрузки FR24 — сколько дней заливать при первом запуске
- [ ] Шумовая модель — использовать прототип из `tasks/flightradar24/prototype/` или переписать