diff --git a/Cargo.lock b/Cargo.lock index b1e278f..c93c535 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -555,7 +555,7 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-telemetry" -version = "0.3.0" +version = "0.4.0" dependencies = [ "chrono", "crossterm", diff --git a/Cargo.toml b/Cargo.toml index 0105245..b34e82c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rusty-telemetry" -version = "0.3.0" +version = "0.4.0" edition = "2021" description = "Sim racing telemetry receiver and analyzer" diff --git a/README.md b/README.md index 8d93ec0..298086b 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,170 @@ # rusty-telemetry -Sim racing telemetry receiver and analyzer. Phase 1 provides a simple UDP listener that receives JSON telemetry frames and prints them to stdout. +Multi-port UDP telemetry listener for sim racing games, with a terminal UI. + +Receives real-time telemetry from **Assetto Corsa** and **Project CARS 1 & 2** via UDP, parses binary packet formats, and displays live data in a terminal dashboard built with ratatui. + +## Supported Games + +| Game | Feed | Port | Protocol | Data | +|------|------|------|----------|------| +| Assetto Corsa | KSUDP (built-in) | 9996 | 3-phase handshake, binary RTCarInfo | Speed, RPM, gear, inputs, G-forces, lap times, per-wheel slip, suspension | +| Assetto Corsa | Telemetry Tool Plugin | 10101 | Binary per-driver structs | All of KSUDP + tyre core temps, driver names, ERS, DRS, all cars | +| Project CARS 1 | UDP Telemetry | 5606 | Binary V1 format (1367 bytes) | Speed, RPM, gear, inputs, engine temps, damage, weather, tyre wear | +| Project CARS 2 | UDP Telemetry | 5606 | Binary V2 format (559 bytes) | All of PCARS1 + full 3D position, turbo boost, tyre compounds | + +All feeds are optional. If a feed fails to bind (e.g. the game isn't running), it shows as "not available" in the TUI and the other feeds continue normally. ## Build and Run ```bash -cargo run +cargo run --release ``` -The listener binds to `0.0.0.0:5005` and expects UDP packets containing JSON with the following structure: +Press **q** or **Ctrl+C** to quit. -```json -{ - "timestamp": 1716123456.789, - "speed_kmh": 245.3, - "rpm": 8750, - "gear": 6, - "throttle": 0.85, - "brake": 0.0, - "steering": 0.12, - "g_forces": { "x": 0.5, "y": -0.2, "z": 1.0 }, - "wheel_slip": [0.1, 0.1, 0.0, 0.0], - "suspension_travel": [0.15, 0.14, 0.16, 0.15], - "lap_time": 85.234, - "lap_count": 5 -} +## Configuration + +### Environment Variables + +| Variable | Purpose | Default | +|----------|---------|---------| +| `RUSTY_TELEMETRY_GAME_HOST` | IP of the machine running the sim game (used for KSUDP handshake target) | `127.0.0.1` | +| `RUSTY_TELEMETRY_LISTEN` | Local address to bind for incoming UDP feeds (Telemetry Tool, PCARS) | `0.0.0.0` | +| `RUSTY_TELEMETRY_BIND` | Fallback for both of the above (backward compatibility) | — | + +### Same Machine + +No configuration needed — defaults work out of the box: + +```bash +cargo run --release ``` -Press Ctrl+C to stop. +### Remote Setup (game on a different machine) + +Run rusty-telemetry on a separate machine (e.g. `192.168.1.100`) while the game runs on another (e.g. `192.168.1.2`): + +```bash +# On the analysis machine: +RUSTY_TELEMETRY_GAME_HOST=192.168.1.2 cargo run --release +``` + +Then point the games at the analysis machine: +- **KSUDP**: Works automatically — AC sends telemetry back to the handshake's source address +- **Telemetry Tool**: Set `to_ip = 192.168.1.100` in `config.ini` +- **PCARS**: Set target IP to `192.168.1.100` in the in-game UDP settings + +## Feed Setup + +### KSUDP (AC built-in, port 9996) + +No additional setup. AC's built-in Remote Telemetry UDP server listens on port 9996 at startup. rusty-telemetry performs a 3-phase handshake automatically: + +1. Sends `HANDSHAKE` to AC's port 9996 +2. Receives 408-byte response (car name, driver name, track name in UTF-16LE) +3. Sends `SUBSCRIBE_UPDATE` to start continuous streaming at ~20 Hz + +**Important**: Start AC before rusty-telemetry so AC can bind port 9996 first. + +### Telemetry Tool Plugin (AC, port 10101) + +Optional third-party Python plugin by IkoRein. Provides richer data than KSUDP: tyre core temperatures, driver names, ERS, DRS, and data for all cars in the session. + +1. Download from [RaceDepartment](https://www.racedepartment.com/downloads/telemetry-tool-for-ac.37231/) +2. Extract to `/apps/python/Telemetry_Tool_plugin/` +3. Edit `config.ini`: + ```ini + [telemetry_tool_plugin] + to_ip = 127.0.0.1 + to_port = 10101 + ``` +4. Enable in `python.ini`: `[TELEMETRY_TOOL_PLUGIN] ACTIVE=1` +5. Restart AC + +### Project CARS 1 & 2 (port 5606) + +Configure in-game to send telemetry to your machine's IP on port 5606. + +**PCARS 2**: Options → System → UDP Protocol → set target IP/port, format **2** recommended. + +**PCARS 1**: Options → Visual → UDP Protocol → set target IP/port. + +The parser auto-detects the format: PCARS 1 (1367-byte packets, 3-byte header) or PCARS 2 (559-byte car physics packets, 12-byte header). + +## TUI + +The terminal UI shows one panel per active feed: + +``` +┌──────────────────────────────────────────────────────────┐ +│ rusty-telemetry v0.4.0 -- Telemetry Listener │ +├──────────────────────────────────────────────────────────┤ +│ ╔ * Receiving ╗ │ +│ ║ 20.0 Hz 1200 packets 15.2 KB/s last: 0.05s ║ +│ ║ 245 km/h Gear:5 8750 RPM Lap:1:23.456 ║ +│ ║ THR: 85% BRK: 0% Pos:0.453 ║ +│ ╚→ :9996 recv :42132 [subscribed]╝ │ +│ │ +│ ╔ * Receiving ╗ │ +│ ║ 60.0 Hz 3600 packets 45.8 KB/s last: 0.01s ║ +│ ║ 180 km/h Gear:4 7200 RPM Lap:1:18.123 ║ +│ ║ THR: 72% BRK: 0% Pos:0.500 ║ +│ ╚ Telemetry Tool Port :10101 ╝ │ +│ │ +│ ╔ . Bound, waiting for data ╗ │ +│ ║ 0.0 Hz 0 packets 0.0 KB/s last: never ║ +│ ╚ PCARS Port :5606 ╝ │ +├──────────────────────────────────────────────────────────┤ +│ Session: ./data/20260604_103000/ Ctrl+C Quit │ +└──────────────────────────────────────────────────────────┘ +``` + +Each panel shows: +- Health indicator and status (`*` green/alive, `~` yellow/stale, `.` gray/waiting, `!` red/error) +- Packet frequency, total count, data rate, time since last packet +- Live telemetry: speed, gear, RPM, lap time, throttle/brake percentage, track position +- Connection mode (handshake with target/recv ports, or bind-listen) + +## Data Capture + +All raw UDP packets are written to per-feed binary dump files: + +``` +./data// +├── feed_9996_ksudp.bin +├── feed_10101_telemetry_tool.bin +└── feed_5606_pcars.bin +``` + +Each packet is prefixed with a 4-byte big-endian length header, allowing sequential replay of captured sessions. ## Testing -Send a test packet from another terminal: - ```bash -echo '{"timestamp":1.0,"speed_kmh":100.0,"rpm":5000,"gear":3,"throttle":0.5,"brake":0.0,"steering":0.0,"g_forces":{"x":0.0,"y":0.0,"z":1.0},"wheel_slip":[0.0,0.0,0.0,0.0],"suspension_travel":[0.1,0.1,0.1,0.1],"lap_time":60.0,"lap_count":1}' | nc -u -w0 127.0.0.1 5005 +cargo test ``` -## Future Plans +32 unit tests covering: +- Binary parsers for all three feed formats (KSUDP, Telemetry Tool, PCARS V1/V2) +- Ring buffer frequency calculation and edge cases +- Feed health state machine transitions (Waiting → Alive → Stale → Alive) +- Packet validation, error handling, boundary conditions -- Telemetry parsers for multiple sim formats -- Persistent storage (SQLite or similar) -- Real-time dashboard / web API -- Lap analysis and comparison tools +## Architecture + +``` +src/ +├── main.rs Multi-port UDP listener, TUI event loop, KSUDP handshake +├── binary_utils.rs Shared safe binary read helpers (bounds-checked, zero fallback) +├── telemetry_frame.rs Unified telemetry data model (TelemetryFrame) +├── feed_state.rs Per-feed health/frequency state machine with ring buffer +├── parser_ksudp.rs AC KSUDP binary parser (RTCarInfo, 6 tests) +├── parser_telemetry_tool.rs AC Telemetry Tool binary parser (357-byte driver struct, 7 tests) +├── parser_pcars.rs PCARS 1 & 2 binary parser with auto-detection (5 tests) +└── ui.rs Ratatui TUI rendering +``` + +## License + +Private project — not yet licensed for distribution.