A simple Rust application that captures F1 25 game telemetry data via UDP and outputs it as JSON logs in real-time.
- 🏎️ Captures F1 25 UDP telemetry packets
- 📊 Parses common telemetry header fields
- 📝 Outputs structured JSON logs
- ⚡ Async UDP handling with Tokio
- 🛠️ Configurable interface and port
- 🎨 Pretty or compact JSON output
- 🛑 Graceful shutdown with Ctrl+C
- 🧪 Optional raw hex packet dump (
--raw) - 🎯 Packet filtering by ID (
--filter 6,14) - 🗂️ Per-lap directory logging (
--laps-out telemetry_laps/) - 🔍 Structured body parsing (CarTelemetry + TimeTrial so far; others fallback to unsupported)
- Rust 1.75+ installed
- F1 25 game with telemetry enabled
# Build the project
cd f1logger
cargo build --release
# Run with default settings (listens on 0.0.0.0:20777)
cargo run
# Run with custom settings
cargo run -- --iface 127.0.0.1 --port 20777 --pretty--iface <IP>: Interface to bind to (default: 0.0.0.0)--port <PORT>: Port to listen on (default: 20777)--pretty: Enable pretty-printed JSON output--filter <list>: Comma-separated packet IDs to keep (e.g.--filter 6,14)--raw: Log raw packets (header parsing skipped) withrawHexfield--laps-out <dir>: Enable per-lap logging; writes one folder per lap with per-packet files
- Open F1 25 game
- Go to Settings → Telemetry
- Enable UDP Telemetry Output
- Set:
- IP Address:
127.0.0.1(for local capture) - Port:
20777 - Send Rate:
60Hz(recommended)
- IP Address:
{"ts":"2025-09-02T10:30:45.123456789Z","src":"127.0.0.1:54321","bytes":1464,"parsedHeader":true,"packetFormat":2025,"gameVersion":"1.25","packetVersion":1,"packetId":6,"packetName":"CarTelemetry","sessionUID":12345678901234567890,"sessionTime":123.456,"frame":7890,"playerCarIndex":0}{
"ts": "2025-09-02T10:30:45.123456789Z",
"src": "127.0.0.1:54321",
"bytes": 1464,
"parsedHeader": true,
"packetFormat": 2025,
"gameVersion": "1.25",
"packetVersion": 1,
"packetId": 6,
"packetName": "CarTelemetry",
"sessionUID": 12345678901234567890,
"sessionTime": 123.456,
"frame": 7890,
"playerCarIndex": 0
}The logger recognizes these F1 telemetry packet types:
| ID | Name | Description |
|---|---|---|
| 0 | Motion | Car motion data (position, velocity, acceleration) |
| 1 | Session | Session and track information |
| 2 | LapData | Lap times and sector data |
| 3 | Event | Race events (penalties, retirements, etc.) |
| 4 | Participants | Driver and team information |
| 5 | CarSetups | Car setup configurations |
| 6 | CarTelemetry | Car telemetry (speed, throttle, brake, RPM, etc.) |
| 7 | CarStatus | Car status (fuel, ERS, damage, penalties) |
| 8 | FinalClassification | Final race classification |
| 9 | LobbyInfo | Multiplayer lobby information |
| 10 | CarDamage | Car damage information |
| 11 | SessionHistory | Session history data |
| 12 | TyreSets | Tyre compound and usage data |
| 13 | MotionEx | Extended motion data |
| 14 | TimeTrial | Time Trial ghost / delta data sets |
Currently implemented body decoders: CarTelemetry (6) and TimeTrial (14). Other packet IDs are emitted with packetName but body content is not yet expanded (appears as unsupported in per-lap body logs). Raw mode (--raw) bypasses all decoding.
When --laps-out <dir> is provided the logger creates a hierarchy like:
telemetry_laps/
silverstone-2025-09-02T10-30-45Z-lap-1/
CarTelemetry.log # JSON line records (one per packet) with headers + summary
CarTelemetryBody.log # Detailed parsed multi-car body (if implemented)
TimeTrialBody.log # Time Trial data (if present)
Session.log # Header-only until body implemented
silverstone-2025-09-02T10-32-05Z-lap-2/
...
Lap rotation is driven by the player's current lap number (LapData packet) and track slug derives from the Session packet trackId. If either packet hasn't arrived yet the logger buffers in the current (first) lap folder.
Tips:
- Use
--filter 6to record only CarTelemetry for lighter storage. - Combine
--filter 6,14 --laps-out telemetry_lapsto study pace vs. time trial deltas. - Add
--prettyonly for human inspection; compact mode is smaller & faster.
- Check that F1 25 telemetry is enabled in game settings
- Verify IP address and port match between game and logger
- Check firewall settings (allow UDP traffic on port 20777)
- Try running as administrator/sudo if needed
- The logger will continue running even if header parsing fails
- Parse errors are logged with a note explaining the issue
- Common causes: game version mismatch, corrupted packets
- Default buffer size is 2048 bytes (suitable for F1 25 packets)
- High packet rates (60Hz) may generate significant output
- Consider redirecting output to a file for analysis:
cargo run > telemetry.jsonl
f1logger/
├── Cargo.toml # Dependencies and metadata
├── src/
│ └── main.rs # Main application code
└── README.md # This file
- Packet body parsing for specific packet types
- Database storage (SQLite, PostgreSQL)
- Real-time dashboard with web interface
- Data export to CSV/Excel formats
This project is open source. Feel free to modify and distribute.