-- ============================================================ -- Phase 2 Step 2: Track tables + fr24_mart schema -- ============================================================ -- ── fr24_ext: FR24 API tracks ──────────────────────────────── CREATE TABLE IF NOT EXISTS 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 INDEX IF NOT EXISTS idx_flight_tracks_fr24_date ON fr24_ext.flight_tracks_fr24 (flight_date); CREATE INDEX IF NOT EXISTS idx_flight_tracks_fr24_flight ON fr24_ext.flight_tracks_fr24 (flight_number, flight_date); CREATE TABLE IF NOT EXISTS fr24_ext.track_points_fr24 ( id BIGSERIAL PRIMARY KEY, track_id BIGINT NOT NULL REFERENCES fr24_ext.flight_tracks_fr24(id) ON DELETE CASCADE, 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 IF NOT EXISTS idx_track_points_fr24_track ON fr24_ext.track_points_fr24 (track_id, observed_at); -- ── fr24_ext: FlightAware tracks ───────────────────────────── CREATE TABLE IF NOT EXISTS 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(20), destination_icao VARCHAR(20), actual_off TIMESTAMPTZ, actual_on TIMESTAMPTZ, departure_delay INTEGER, -- seconds arrival_delay INTEGER, -- seconds actual_distance INTEGER, -- nautical miles flight_date DATE NOT NULL, fetched_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_flight_tracks_fa_date ON fr24_ext.flight_tracks_fa (flight_date); CREATE INDEX IF NOT EXISTS idx_flight_tracks_fa_ident ON fr24_ext.flight_tracks_fa (ident_iata, flight_date); CREATE TABLE IF NOT EXISTS fr24_ext.track_points_fa ( id BIGSERIAL PRIMARY KEY, track_id BIGINT NOT NULL REFERENCES fr24_ext.flight_tracks_fa(id) ON DELETE CASCADE, observed_at TIMESTAMPTZ NOT NULL, lat DOUBLE PRECISION NOT NULL, lon DOUBLE PRECISION NOT NULL, altitude_ft INTEGER, -- already converted: hundreds_of_feet * 100 gspeed_kt INTEGER, heading SMALLINT, update_type VARCHAR(5) -- M=ADS-B, D=dead reckoning ); CREATE INDEX IF NOT EXISTS idx_track_points_fa_track ON fr24_ext.track_points_fa (track_id, observed_at); -- ── fr24_mart schema ───────────────────────────────────────── CREATE SCHEMA IF NOT EXISTS fr24_mart; -- Unified flights table CREATE TABLE IF NOT EXISTS 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, -- source flags 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, -- source keys schedule_id BIGINT, fr24_track_id BIGINT, fa_track_id BIGINT, rtlsdr_flight_id BIGINT, updated_at TIMESTAMPTZ DEFAULT now() ); CREATE UNIQUE INDEX IF NOT EXISTS idx_mart_flights_date_callsign ON fr24_mart.flights (flight_date, callsign); CREATE INDEX IF NOT EXISTS idx_mart_flights_date ON fr24_mart.flights (flight_date); -- Unified track points (best source) CREATE TABLE IF NOT EXISTS fr24_mart.track_points ( id BIGSERIAL PRIMARY KEY, flight_id BIGINT NOT NULL REFERENCES fr24_mart.flights(id) ON DELETE CASCADE, 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 IF NOT EXISTS idx_mart_track_points_flight ON fr24_mart.track_points (flight_id, observed_at); CREATE INDEX IF NOT EXISTS idx_mart_track_points_geo ON fr24_mart.track_points (lat, lon); -- Noise grid (0.01° cells) CREATE TABLE IF NOT EXISTS 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 IF NOT EXISTS idx_noise_grid_date ON fr24_mart.noise_grid (period_date); -- Source coverage metrics CREATE TABLE IF NOT EXISTS 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() );