This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
AirJedi Desktop is a real-time ADS-B aircraft tracking application built with Rust and egui. It connects to a BaseStation protocol TCP feed (localhost:30003), displays aircraft on an interactive map with Carto tiles, and visualizes flight paths with altitude-based color coding.
# Build the project
cargo build
# Build optimized release version
cargo build --release
# Run the application
cargo run
# Run with release optimizations
cargo run --releaseThe application will automatically:
- Attempt GPS location via CoreLocation (macOS) or fall back to IP geolocation
- Connect to
localhost:30003for BaseStation ADS-B data - Display aircraft within 400 miles on the map
src/main.rs - UI layer and application entry point
- egui-based GUI with map rendering
- GPS location detection (CoreLocation on macOS, IP fallback)
- Receiver location marker display
- Aircraft visualization with altitude-colored trails
- Map interaction (pan/pinch-zoom)
- Aviation overlay rendering (airports, runways, navaids)
- Airport filtering UI with 3 modes (FrequentlyUsed, All, MajorOnly)
- Background aviation data loading with progress indicator
- Spatial bounding box calculation for viewport-based filtering
- Performance: Cached aviation data filtering - Only recalculates when bounds/filter changes
- Performance: Cached spatial bounds - Reduces per-frame calculations
- Generic hover popup system with
MapItemPopuptrait (airports, navaids, aircraft) - Constants:
TRAIL_MAX_AGE_SECONDS(300s),TRAIL_SOLID_DURATION_SECONDS(225s),TRAIL_FADE_DURATION_SECONDS(75s)
src/basestation.rs - Core aircraft tracking logic
Aircraftstruct: Stores position, velocity, altitude, track, callsign, and position historyAircraftTracker: HashMap-based aircraft registry with spatial filtering- BaseStation MSG protocol parser (MSG,1/3/4/5/6/7/8 types)
- Position validation: 400-mile radius filter, 10-mile jump detection
- Haversine distance calculations for accuracy
- Position history management (5 minutes stored)
src/tcp_client.rs - Async TCP connection handler
- Connects to BaseStation feed at
localhost:30003 - Automatic reconnection on disconnect (5-second retry)
- Message parsing loop with periodic cleanup (every 100 messages)
- Aircraft timeout: 3 minutes of inactivity
src/tiles.rs - Map tile management
- Web Mercator projection utilities
- Carto basemap tile fetching (dark_all theme)
- Disk cache at
~/.cache/airjedi_egui/tiles/(7-day TTL) - SHA256-based cache filenames
- Subdomain load balancing (a-d.basemaps.cartocdn.com)
- Async tile loading with texture management
src/aviation_data.rs - Aviation overlay data
- Airport, Runway, and Navaid struct definitions
- CSV parser for OurAirports data format (serde-based)
- Async download functionality (
load_or_download()) - Automatic download from davidmegginson.github.io on first run
- Spatial filtering with bounding box queries
- Airport filtering methods:
is_frequently_used(),is_public_airplane_airport(),has_scheduled_service() - Methods for zoom-based visibility control
- Color-coding by airport size and navaid type
- TCP Client receives BaseStation messages → parses → updates
AircraftTracker - AircraftTracker validates positions (distance/jump filters) → stores in
Aircraft.position_history - Main UI reads tracker state (via Arc) → renders map + aircraft + trails
- Tile Manager fetches tiles on-demand → caches → provides textures to UI
- Main thread: UI rendering (egui), map interaction, aircraft display
- Background tokio runtime: TCP connection, message parsing (spawned in
AdsbApp::new()) - Shared state:
Arc<Mutex<AircraftTracker>>synchronized between threads
Aircraft positions are rejected if they:
- Exceed 400 miles from the receiver location
- "Jump" more than 10 miles from the last known position
- Are stored in history only if they change by >100 meters
This prevents displaying erroneous data while maintaining smooth trails.
- Trails use
position_historyVec with timestamps - Color coding based on altitude at each point (0-10k ft = cyan, 40k+ ft = purple)
- Opacity fades linearly over the last 7.5 minutes (solid → transparent)
- Trail segments connect consecutive points with altitude-colored lines
Uses Web Mercator (EPSG:3857) for compatibility with standard web map tiles:
WebMercator::lat_to_y()/lon_to_x()convert lat/lon to tile coordinates- Supports zoom levels 6-12 (configured via
map_zoom_levelfloat) - Center point determines which tiles are visible
- On macOS: Uses CoreLocation via objc/cocoa bindings
- Requests location authorization and waits 2 seconds for GPS fix
- Falls back to IP geolocation (ipapi.co, then ip-api.com)
- Receiver position stored separately from map center to allow panning
Generic, extensible popup system for map items:
MapItemPopuptrait: Any type can implementrender_popup(&self, ui: &mut egui::Ui)HoveredMapItemenum: Holds currently hovered item (Airport, Navaid, Aircraft)- Hover detection: Proximity-based detection with 8-10px margin around each item
- Popup rendering: Uses
egui::AreawithOrder::Tooltipfor proper layering - Aircraft hover behavior: Hovering over an aircraft also selects it in the contact list and auto-scrolls the list to center the selection
- Colorful displays:
- Airport popups: ICAO (color by size), name, type badge, elevation, scheduled service indicator, coordinates
- Navaid popups: Ident (color by type), name, type badge, frequency, coordinates
- Aircraft popups: Callsign/ICAO, altitude (altitude-gradient color), speed, heading (with cardinal direction), vertical rate (climb/descend indicators), position, last update timestamp
To add popups for new item types:
- Add variant to
HoveredMapItemenum - Implement
MapItemPopuptrait for the type - Add hover detection in rendering code
- Add match arm in popup rendering logic
Aviation data is automatically downloaded from OurAirports on first startup to ./data/:
airports.csv- Airport locations and metadatarunways.csv- Runway endpoints and surface typesnavaids.csv- VOR, NDB, DME navigation aids
Files are cached and reused on subsequent runs. To force re-download, delete the ./data/ directory.
Airport filter modes:
AirportFilter::FrequentlyUsed- Default, shows airports with scheduled serviceAirportFilter::All- All public airplane airports (excludes heliports)AirportFilter::MajorOnly- Only large international airports
Edit src/tcp_client.rs:9:
let address = "localhost:30003"; // Change to your BaseStation feedEdit src/basestation.rs in AircraftTracker::new():
max_distance_miles: 400.0, // Radius filterEdit src/basestation.rs in Aircraft::update_position():
if distance_from_last > 10.0 { // Jump detection thresholdEdit src/main.rs constants:
const TRAIL_MAX_AGE_SECONDS: f32 = 300.0; // Total history stored (5 minutes)
const TRAIL_SOLID_DURATION_SECONDS: f32 = 225.0; // Solid trail duration (75% - 3.75 minutes)
const TRAIL_FADE_DURATION_SECONDS: f32 = 75.0; // Fade-out period (25% - 1.25 minutes)Edit src/tcp_client.rs:
const CLEANUP_INTERVAL_MESSAGES: u32 = 100; // Cleanup frequency
const AIRCRAFT_TIMEOUT_SECONDS: i64 = 180; // 3 minutesmacOS GPS support requires core-foundation, objc, and cocoa dependencies (see Cargo.toml target-specific deps). The implementation uses unsafe Objective-C bindings to access CLLocationManager.
On other platforms, GPS location detection is skipped and IP geolocation is used directly.