A high-performance, low-latency telemetry capture and analysis suite for sim racing games (Assetto Corsa, Assetto Corsa Competizione, iRacing, Le Mans Ultimate, etc.).
This project is built using a hybrid C/Rust architecture:
telemetry-fetcher(C11): A lightweight, low-level capture agent that maps game shared memory regions (Windows MMIO) at high frequencies (up to 360Hz) and writes them to disk in a highly packed, proprietary binary stream format (.rts).telemetry-analyzer(Rust): A robust GUI-driven analysis application built usingeguiandeframeto parse.rtsfiles in microseconds and plot driver inputs, speeds, GPS track maps, and suspension/tire behaviors for lap optimization.
telemetry-lab/
├── telemetry-fetcher/ # C capture utility
│ ├── include/
│ │ ├── adapters/ # Windows MMIO mappings for AC & iRacing
│ │ └── rts/ # Custom file writer & binary structures
│ ├── src/
│ │ ├── adapters/ # Game shared memory readers
│ │ ├── storage/ # File storage logic (.rts format)
│ │ └── main.c # Capture lifecycle and mock generation
│ ├── CMakeLists.txt
│ └── mingw-toolchain.cmake # Cross-compilation setup for Windows target
│
├── telemetry-analyzer/ # Rust GUI analysis application
│ ├── src/
│ │ ├── parser.rs # Zero-allocation .rts file parser
│ │ └── main.rs # egui desktop GUI & plotting pipeline
│ └── Cargo.toml
│
└── README.md # Main documentation
The .rts file extension represents a high-speed, zero-boilerplate telemetry stream. To achieve maximum throughput and minimal write latency, it writes packed binary representation directly from the CPU register states to disk, conforming to little-endian alignment.
Both the C writer (rts_writer.c) and Rust parser (parser.rs) align structures strictly to 1-byte boundary alignment:
- C:
#pragma pack(push, 1)/#pragma pack(pop) - Rust:
#[repr(C, packed)]withbytemucksafe casts
+------------------------------------------------------------+
| RTS HEADER |
| - Magic Signature (4 bytes) "RTS\0" |
| - Version (1 byte) |
| - Game ID (1 byte) |
| - Metadata Length N (2 bytes, uint16_t) |
+------------------------------------------------------------+
| METADATA PAYLOAD |
| - UTF-8 JSON String (N bytes) |
+------------------------------------------------------------+
| TELEMETRY FRAMES STREAM |
| - Frame 1 (77 bytes, packed telemetry struct) |
| - Frame 2 (77 bytes, packed telemetry struct) |
| - ... |
| - Frame M (77 bytes, packed telemetry struct) |
+------------------------------------------------------------+
The header starts with an 8-byte fixed block, followed immediately by a variable-length metadata string payload.
| Offset (Bytes) | Type | Field Name | Description |
|---|---|---|---|
0x00 - 0x03 |
char[4] |
magic |
Magic signature bytes: 'R', 'T', 'S', '\0' |
0x04 |
uint8_t |
version |
File structure version (currently 1) |
0x05 |
uint8_t |
game_id |
Game enum code (0=Unknown, 1=Assetto Corsa, 2=ACC, 3=iRacing, 4=LMU) |
0x06 - 0x07 |
uint16_t |
metadata_length |
Length N of the following metadata payload string (Max 65,535 bytes) |
0x08 - (8+N-1) |
char[N] |
metadata |
UTF-8 encoded JSON metadata string (e.g., track name, car, driver) |
Every telemetry sample is recorded as a single contiguous, packed block of exactly 77 bytes. To avoid alignment paddings, 1-byte packing is strictly enforced.
| Offset (Bytes) | Field Offset | Type | Field Name | Units / Scale | Description |
|---|---|---|---|---|---|
0 |
0x00 |
uint32_t |
session_time_ms |
ms |
Time elapsed since session start |
4 |
0x04 |
float |
throttle |
[0.0, 1.0] |
Driver throttle position (0% to 100%) |
8 |
0x08 |
float |
brake |
[0.0, 1.0] |
Driver brake pressure/position (0% to 100%) |
12 |
0x0C |
float |
clutch |
[0.0, 1.0] |
Driver clutch position (0% to 100%) |
16 |
0x10 |
float |
steering |
[-1.0, 1.0] |
Steering angle: negative=left, positive=right |
20 |
0x14 |
float |
speed_ms |
m/s |
Absolute vehicle speed in meters per second |
24 |
0x18 |
float |
engine_rpm |
rpm |
Current engine RPM |
28 |
0x1C |
int8_t |
gear |
integer |
-1=Reverse, 0=Neutral, 1+=Forward Gears |
29 |
0x1D |
float |
pos_x |
meters |
3D World X coordinates (GPS translation) |
33 |
0x21 |
float |
pos_y |
meters |
3D World Y coordinates (Altitude) |
37 |
0x25 |
float |
pos_z |
meters |
3D World Z coordinates (GPS translation) |
41 |
0x29 |
float |
lap_distance |
meters |
Distance traveled from the starting line |
45 |
0x2D |
float[4] |
suspension_travel |
meters |
Susp. travel array: [FL, FR, RL, RR] |
61 |
0x3D |
float[4] |
tire_temp_c |
°C |
Core tire temperatures: [FL, FR, RL, RR] |
77 |
0x4D |
float[4] |
tire_pressure_kpa |
kPa |
Tires inflation pressures: [FL, FR, RL, RR] |
Since sim racing games are predominantly Windows-only, the telemetry-fetcher uses Windows shared memory APIs. However, it can be built/cross-compiled on Linux using the MinGW compiler toolchain.
To Cross-Compile for Windows on Linux:
Ensure you have mingw-w64 packages installed on your system.
cd telemetry-fetcher
mkdir build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../mingw-toolchain.cmake ..
makeThis produces fetcher.exe in the build directory, ready to run on any sim racing rig.
The analyzer can run natively on both Linux and Windows systems.
Prerequisites: You will need Rust installed on your computer.
cd telemetry-analyzer
cargo run --release- Click "Open File..." in the GUI to load any
.rtsbinary file (e.g. the standarddummy_telemetry.rtsgenerated by the fetcher). - Interact with the speed graphs, throttle/brake maps, tires thermal state plots, and top-down visual GPS track maps.