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 BIGINT GENERATED ALWAYS AS IDENTITY, 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(), PRIMARY KEY (raw_packet_id, partition_date) ) 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, source_partition_date DATE, 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);