DataPad is a live flight data display panel that receives real-time aircraft telemetry via UDP from DCS World through the forward_parsed_udp.py script.
Phase 1 (experimental): This implementation represents Phase 1 of DataPad. Future phases will expand telemetry coverage and add visual and security improvements, including live animated aircraft visualizations based on flight attitude and a dedicated DataPad UI redesign. DataPad uses ECDH key exchange with AES-GCM encrypted UDP telemetry for secure communication. See docs/technical/ECDH_USAGE_GUIDE.md for setup instructions.
- FlightData.kt: Kotlin data classes that match the JSON structure from DCS
- DataPadManager.kt: Manages UDP socket reception on port 5010, parses JSON, and provides reactive state via StateFlow
- DataPadPopup.kt: Full-screen popup displaying live flight information
- LocalDataPadManager.kt: CompositionLocal for accessing DataPadManager in Compose UI
- MainActivity.kt: Initializes DataPadManager and provides it via CompositionLocal
- FlightMiniStatusBar.kt: New flight icon button to open DataPad popup
- Live connection indicator (green = receiving data, gray = waiting)
- Time since last update display (e.g., "5s ago", "2m ago")
- Auto-disconnect after 5 seconds of no data
Organized in collapsible sections:
-
Aircraft & Pilot
- Aircraft type
- Pilot name
- Coalition
- Group
-
Flight Parameters
- Altitude (meters)
- Heading (degrees)
- Pitch (degrees)
- Bank (degrees)
- Angle of Attack (AoA) — degrees (if available from draw arguments or aircraft module)
- G-Load — X/Y/Z axes (G)
- Ground Speed / Indicated Airspeed / True Airspeed
- Vertical Speed (VSI)
- Mach number
- Flight Controls & Trim — elevator, aileron, rudder positions and trim values (if exposed)
-
Position
- Latitude/Longitude
- DCS world coordinates (X, Y, Z)
-
Navigation & Waypoints
- Current waypoint name
- Distance to waypoint
- Bearing to waypoint
- ETA (time/seconds)
- Flight plan: current WP index / total waypoints
- Route name
-
Engine & Performance
- Engine RPM / N1, EGT, Throttle position, Afterburner state (where available)
- Fuel flow (if exposed) and derived endurance
-
Aircraft Mass
- Total mass, empty weight and payload mass (if exposed by module)
-
Flight Controls / Trim
- Elevator/Aileron/Rudder positions and trims
-
Gear & Configuration
- Gear positions (nose/left/right), Weight-on-Wheels (WoW), Flaps %, Speedbrake %, Hook state
-
Lights & Canopy
- Landing/Taxi/Navigation/Strobe/Formation lights and canopy status
-
Mission Time
- Mission clock / model time (seconds)
- Nearby Units / Ground Objects
- Nearby unit list (type, name, coalition, position, distance) for situational awareness
-
Fuel
- Total fuel capacity
- Remaining fuel (internal/external)
- Fuel flow rate
- Endurance (minutes remaining)
-
Weapons & Stores
- Master Arm status (SAFE/ARM)
- Selected weapon
- Weapon stations (station #, type, count)
- Total munitions count
-
Threats & EW
- RWR contacts (ID, type, bearing, priority, locked)
- Total threats detected
- Radar mode, range, lock status, track count
- Countermeasures: Chaff/Flare counts, dispenser mode
-
Avionics & Systems
- Autopilot enabled/mode/Flight Director
- Transponder code & mode
- Radio frequencies (COM1/COM2/Guard/Active)
- Master Caution/Warning
- System faults & alerts
-
Environment
- Wind direction & speed
- Temperature
- Pressure
- Visibility
- Cloud layers
-
Systems Status
- Radar Active
- Jamming
- IR Jamming
- AI On
- Human control
-
Additional Info
- Timestamp
- Unit ID
- Streamer version
- Data age (latency)
- Update rate (Hz)
Ensure the DCS export script is running in dcs /scripts/ folder and generating player_aircraft_parsed.jsonl
Run the Python script to forward data to the app:
python forward_parsed_udp.py --host <ANDROID_DEVICE_IP> --port 5010- Tap the flight icon (
✈️ ) button in the top status bar - View live flight data as it streams in
- Connection status shows if data is being received
- Default UDP port: 5010
- Configurable in
DataPadManager.UDP_PORT
Ensure your firewall allows UDP traffic on port 5010:
# Windows
New-NetFirewallRule -DisplayName "DCS DataPad" -Direction Inbound -Protocol UDP -LocalPort 5010 -Action AllowINTERNETpermission is required in AndroidManifest.xml (already added)
- DataPad uses ECDH (Elliptic Curve Diffie-Hellman) key exchange for secure communication
- All telemetry is encrypted with AES-GCM using session keys derived from ECDH
- Perfect Forward Secrecy ensures past sessions remain secure even if keys are compromised
- Device authorization via
authorized_devices.jsonon the server - See
docs/technical/ECDH_USAGE_GUIDE.mdfor complete setup and security configuration - The DataPad UI Settings dialog (⚙️) allows you to configure UDP port, bind IP, device name, and server IP for handshake
Expected JSON format (one object per UDP datagram):
{
"aircraft": "FA-18C_hornet",
"unitName": "[RGS] FATBEE | FTBE",
"coalition": "Enemies",
"alt": 46.83,
"heading": 4.43,
"pitch": -0.007,
"bank": -0.0002,
"lat": 42.17,
"long": 42.46,
"pos": {"x": -285764.44, "y": 46.83, "z": 682926.49},
"radarActive": true,
"isHuman": true,
"timestamp": "2025-12-15T19:52:22Z"
}{
"aircraft": "FA-18C_hornet",
"unitName": "[RGS] FATBEE | FTBE",
"coalition": "Blue",
"country": 2,
"group": "Hornet Flight",
"alt": 5486.3,
"heading": 245.8,
"pitch": 2.1,
"bank": -5.3,
"lat": 42.17,
"long": 42.46,
"pos": {"x": -285764.44, "y": 5486.3, "z": 682926.49},
"groundSpeed": 420.5,
"indicatedAirspeed": 385.2,
"trueAirspeed": 425.8,
"verticalSpeed": 12.3,
"mach": 0.64,
"fuel": {
"total": 4800,
"remaining": 3250,
"internal": 2100,
"external": 1150,
"endurance": 45.2,
"fuelFlow": 1.2
},
"waypoint": {
"current": "WP03-TARGET",
"distance": 38500,
"bearing": 248.5,
"eta": "2025-12-15T20:15:30Z",
"etaSeconds": 342
},
"flightPlan": {
"currentIndex": 2,
"totalWaypoints": 5,
"route": "BASE-IP-TGT-EGRESS-RTB"
},
"weapons": {
"masterArm": true,
"selected": "AIM-120C",
"stations": [
{"station": 2, "type": "AIM-120C", "count": 2},
{"station": 8, "type": "AIM-120C", "count": 2},
{"station": 3, "type": "AIM-9X", "count": 1},
{"station": 7, "type": "AIM-9X", "count": 1}
],
"totalCount": 6
},
"rwr": {
"contacts": [
{"id": "SA-6", "type": "SAM", "bearing": 315.0, "priority": 3, "locked": true},
{"id": "MIG-29", "type": "AI", "bearing": 180.5, "priority": 2, "locked": false}
],
"threatsDetected": 2
},
"angleOfAttack": 3.5,
"gLoad": { "x": 1.0, "y": 0.0, "z": 1.0 },
"engines": {
"rpm": { "left": 98.5, "right": 98.5 },
"egt": { "left": 560.0, "right": 558.0 },
"throttle": 0.75,
"afterburner": false
},
"aircraftMass": { "total": 17200, "empty": 12000, "payload": 4200 },
"flightControls": { "pitch": 0.01, "roll": -0.02, "yaw": 0.0, "trimPitch": 0.0, "trimRoll": 0.0, "trimYaw": 0.0 },
"mechanical": { "gear": { "nose": 1.0, "left": 1.0, "right": 1.0 }, "flaps": 0.25, "speedbrake": 0.0, "canopy": 0.0, "hook": 0.0 },
"lights": { "landing": 0.0, "taxi": 0.0, "navigation": 1.0, "strobe": 0.0, "formation": 0.0 },
"systems": { "electrical": "OK", "hydraulic": "OK", "apuOn": false, "generatorOn": true },
"missionTime": 4523,
"radar": {
"mode": "RWS",
"range": 80.0,
"locked": false,
"trackCount": 3,
"tracks": [ { "id": 1, "range": 12000, "azimuth": 45.0, "elevation": 1.5, "locked": false } ]
},
"nearbyUnits": [ { "id": "U123", "name": "Tanker", "type": "KC-135", "coalition": "Blue", "distance": 3200 } ],
"countermeasures": {
"chaffCount": 60,
"flareCount": 30,
"dispenserMode": "AUTO"
},
"autopilot": {
"enabled": true,
"mode": "ALT HOLD",
"flightDirector": true
},
"transponder": {
"code": "2341",
"mode": "ALT",
"ident": false
},
"radios": {
"com1": 251.5,
"com2": 127.5,
"guard": true,
"activeFreq": 251.5
},
"warnings": {
"masterCaution": false,
"masterWarning": false,
"faults": [],
"alerts": ["LOW FUEL"]
},
"environment": {
"windDirection": 270.0,
"windSpeed": 15.5,
"temperature": -12.3,
"pressure": 1013.25,
"visibility": 10000.0,
"clouds": "SCT040"
},
"radarActive": true,
"jamming": false,
"irJamming": false,
"isHuman": true,
"timestamp": "2025-12-15T19:52:22Z",
"unitID": "16777472",
"lua_version": "1.2",
"streamer_version": "2.0",
"dataAge": 0.05,
"updateRate": 20.0
}A comprehensive list of fields the DataPad will receive from the UDP JSON stream (name · type · notes). Fields mirror the FlightData.kt data model.
aircraft· string — aircraft type identifierunitName· string — pilot / unit display namecoalition· string — coalition name (Blue/Enemies/etc.)country· int — country codegroup· string — group name
alt· number — altitude (meters)heading· number — heading (radians in-stream; converted to degrees in UI)pitch· number — pitch (radians)bank· number — bank/roll (radians)angleOfAttack· number? — AoA (degrees), when available from draw arguments or modulegLoad· object? {x,y,z} — G-force in Gs on X/Y/Z axeslat· number — latitude (decimal degrees)long· number — longitude (decimal degrees)pos· object {x,y,z} — DCS world coordinates
engines· object? — engine telemetryrpm· object? {left,right} — percent or RPMegt· object? {left,right} — exhaust gas temperature (°C)throttle· number? — throttle position (0.0–1.0)afterburner· bool? — afterburner engaged
aircraftMass· object? — {total,empty,payload} (kg)
flightControls· object? — {pitch,roll,yaw,trimPitch,trimRoll,trimYaw}
mechanical· object? — {gear,flaps,speedbrake,canopy,hook,wheelBrake,noseGearSteeringEnabled}gear· object? {nose,left,right} — 0.0=up, 1.0=downweightOnWheels· bool? — true if on ground
lights· object? — {landing,taxi,navigation,strobe,formation}
systems· object? — {electrical,hydraulic,apuOn,generatorOn}
radar.tracks· list? — each track {id,range,azimuth,elevation,locked}
nearbyUnits· list? — nearby objects with{ id, name, type, coalition, distance }
missionTime· number? — mission model time in seconds
groundSpeed· number? — ground speed (units variable)indicatedAirspeed· number? — IAStrueAirspeed· number? — TASverticalSpeed· number? — vertical speed (m/s)mach· number? — Mach number
fuel· object? — fuel detailstotal· number — total capacityremaining· number — remaining fuelinternal· number? — internal fuelexternal· number? — external fuelendurance· number? — minutes remainingfuelFlow· number? — consumption rate
waypoint· object? — current waypoint infocurrent· string? — waypoint namedistance· number? — distance to WPbearing· number? — bearing to WP (degrees)eta· string? — ISO timestamp or textual ETAetaSeconds· number? — ETA in seconds
flightPlan· object? — flight plan infocurrentIndex· int? — current WP indextotalWaypoints· int? — total WPsroute· string? — route name
weapons· object? — weapons summarymasterArm· bool — master arm stateselected· string? — selected weaponstations· list? — station entries {station,type,count}totalCount· int? — total munitions
rwr· object? — RWR/contactscontacts· list? — each contact: {id,type,bearing,priority,locked}threatsDetected· int — total detected threats
radar· object? — radar mode/status {mode,range,locked,trackCount}countermeasures· object? — {chaffCount,flareCount,dispenserMode}
autopilot· object? — {enabled,mode,flightDirector}transponder· object? — {code,mode,ident}radios· object? — {com1,com2,guard,activeFreq}warnings· object? — {masterCaution,masterWarning,faults,alerts}
environment· object? — {windDirection,windSpeed,temperature,pressure,visibility,clouds}
isHuman· bool — player-controlledborn· bool — unit born/active flagaiOn· bool — AI controlradarActive· bool — radar activejamming· bool — EW jammingirJamming· bool — IR jamminginvisible· bool — map/world visibility flag
timestamp· string — ISO timestamp for the sampleunitID· string — unique unit identifierlua_version· string — exporter Lua versionstreamer_version· string — streamer/exporter versiondataAge· number? — latency / age (seconds)updateRate· number? — reported update rate (Hz)
Notes: Optional fields are suffixed with
?above and may be absent depending on the exporter script or aircraft. Units can vary depending on DCS/exporter settings (e.g., wind speed m/s vs knots); the UI applies reasonable formatting/conversions.
- UDP reception runs on IO dispatcher (background thread)
- State updates trigger UI recomposition automatically via StateFlow
- Socket timeout: 1 second (prevents blocking)
- Invalid JSON is logged but doesn't crash the app
- Connection auto-detects drops after 5 seconds of no data
- DataPadManager starts on app launch
- Cleans up UDP socket on app disposal
- Minimal battery impact when not receiving data
- Configurable UDP port in settings
- Data logging/recording
- Custom alert thresholds (altitude, speed, etc.)
- Multiple aircraft tracking
- Map integration with position overlay
- Live animated aircraft visualization showing orientation (pitch/roll/heading) and movement
- Dedicated DataPad UI/UX redesign and controls
- ✅ Implemented: AES-GCM encrypted UDP telemetry (see
docs/technical/AES_GCM_ENCRYPTION.md)