Telemetry capture¶
GT7 streams encrypted telemetry over UDP at roughly 60 Hz while driving is on screen. This page describes exactly how the datalogger captures and decodes it.
The handshake¶
The console only streams to a client that keeps asking for data:
- The datalogger binds UDP port 33740 and sends a heartbeat — a single
character — to the console's port 33739 every 1.6 s. (GT7 stops sending
after ~100 packets without a heartbeat.) The character selects the packet format:
A,B,~, orC(see packet formats below); the datalogger sendsCby default (GT7_PACKET_FORMAT, changeable live in Settings › Connection). - The heartbeat is addressed to
GT7_PS_IPif configured. If not, it is broadcast (255.255.255.255), and the console's address is learned from the source address of the first telemetry packet that arrives — that's the auto-discovery mechanism. - If no packet arrives for 5 s the connection is considered stale and the status
indicator drops to "waiting for telemetry" while heartbeats continue. Without
GT7_PS_IPthey go back to being broadcast (and sent to the last known address as well), so a console that wakes from rest mode on a new DHCP address is found again on its own. With an IP configured the heartbeat only ever goes to it; only Find console broadcasts.
The console streams to every client that keeps sending heartbeats, not only the latest one. Two dataloggers on different machines can capture the same drive at once, and so can a datalogger and the phone app talking to the console directly (#144).
Packet formats¶
Four packet formats exist, each a strict superset of the previous. Parsing keys off the datagram length, so whatever the console answers is decoded correctly:
| Heartbeat | Size | Adds |
|---|---|---|
A |
296 B | base telemetry (everything in the table below) |
B |
316 B | steering wheel rotation, sway / heave / surge accelerations |
~ |
344 B | filtered throttle & brake (the brake applied after ABS, live driving only), per-wheel torque vectors, energy recovery, and three fields the community decoded in 2026: a powertrain byte, an energy-recovery flag and the delivered engine torque (#150) |
C |
368 B | per-wheel surface type, live lap timer, front-wheel steering angles, wheelbase, car category (needs GT7 v1.68+) |
The extended fields are parsed into the typed packet model (as None when the
console sends a smaller format). Three of the ~ fields were unnamed in the community
layout until late 2026; what they mean comes from community reverse-engineering
(GTPlanet's telemetry thread, Nenkai/PDTools#14), not from Polyphony:
| Offset | Field | Meaning |
|---|---|---|
0x13E |
powertrain_flags (u8) |
powertrain / motor layout: 0 on every combustion car checked, 4 electric, 9 four-motor cars with active torque vectoring, 2 also seen. Possibly a bitmask |
0x13F |
energy_recovery_available (u8 → bool) |
whether energy_recovery carries anything for this car: set on most hybrids and EVs. PDTools calls it NoGasConsumption |
0x154 |
engine_torque_norm (f32) |
the engine torque being delivered, normalized: tracks boost and throttle, negative on the overrun, exactly 0 with the clutch in or in neutral, not bounded to ±1. Recorded per tick as the engine_torque column |
set Packet format to A in Settings › Connection if no data arrives. |
Decryption¶
Each datagram is encrypted with Salsa20:
- Key — the first 32 bytes of the ASCII string
Simulator Interface Packet GT7 ver 0.0(i.e.Simulator Interface Packet GT7 v). - Nonce — a 4-byte little-endian seed
iv1is read unencrypted at offset0x40of the datagram. A second value is derived asiv2 = iv1 XOR <constant>, and the 8-byte Salsa20 nonce isiv2followed byiv1(both little-endian). The XOR constant depends on the packet format:0xDEADBEAFforA(yes,BEAF— that is the real GT7 constant),0xDEADBEEFforBandC,0x55FABB4Ffor~. The decoder picks the constant matching the datagram size and falls back to trying the others. - Validation — after decryption, the first four bytes must equal the magic
0x47375330("G7S0"). Anything else is counted as a decode error and dropped.
Decoded fields¶
Every packet decodes to a typed structure with ~50 fields. The important ones:
| Offset | Field | Units / notes |
|---|---|---|
| 0x04 | position X/Y/Z | m, GT7 world coordinates |
| 0x10 | velocity X/Y/Z | m/s, world axes |
| 0x1C | orientation x/y/z | the car's orientation as a unit quaternion, with 0x28. The community layout names these rotation pitch/yaw/roll; they are not angles (how that was settled) |
| 0x28 | orientation w | named relative orientation to north; not a heading |
| 0x2C | angular velocity X/Y/Z | rad/s (Y = yaw rate) |
| 0x38 | body height | m |
| 0x3C | engine RPM | rpm |
| 0x44 | fuel level / capacity | L (capacity 0 for EVs) |
| 0x4C | speed | m/s |
| 0x50 | boost | raw − 1.0 → bar |
| 0x54–0x5C | oil pressure, water temp, oil temp | bar, °C, °C |
| 0x60 | tire temps FL/FR/RL/RR | °C |
| 0x74 | current lap / total laps | total = 0 in time trial |
| 0x78 | best / last lap time | ms, −1 when unset |
| 0x80 | in-game time of day | ms |
| 0x84 | race position / total | |
| 0x88 | RPM alert min/max | shift-light thresholds |
| 0x8E | flags | bitmask, see below |
| 0x90 | current gear (low nibble) / suggested gear (high nibble) | current: 0 = reverse, 15 = neutral; suggested: 15 = no suggestion, recorded as suggested_gear |
| 0x91 | throttle / brake | 0–255 → % via ÷2.55 |
| 0xA4 | wheel angular speed FL/FR/RL/RR | rad/s, signed |
| 0xB4 | tire radius FL/FR/RL/RR | m |
| 0xC4 | suspension travel FL/FR/RL/RR | m |
| 0xF4 | clutch, clutch engagement, RPM after clutch | |
| 0x104 | gear ratios (8) + transmission top speed | |
| 0x124 | car ID | maps to the car inventory (app/data/cars.json) |
Flags bitmask — CAR_ON_TRACK (bit 0), PAUSED (1), LOADING (2), IN_GEAR (3),
HAS_TURBO (4), REV_LIMITER (5), HANDBRAKE (6), lights (7–9), ASM_ACTIVE (10),
TCS_ACTIVE (11).
For storage, the aid flags are remapped into a stable, compact aids bitmask persisted
with every sample: TCS=1, ASM=2, HANDBRAKE=4, REV_LIMITER=8. This keeps recorded
laps immune to any future changes in GT7's flag layout.
Wheel slip (computed at decode time)¶
GT7 doesn't send slip directly; it sends wheel angular speed and tire radius. Per wheel:
slip = |wheel_rad_per_s| × tire_radius / car_speed_mps
slip < 1under braking → the wheel is turning slower than the car is moving — lockingslip > 1under power → the wheel is spinning faster than the car — wheelspin- Below 1 m/s car speed the ratio is meaningless, so all four are forced to
1.0.
This is a slip-ratio proxy (wheel surface speed ÷ car speed), unsigned — not a signed SAE slip ratio — but it is exactly what's needed to spot lockups and wheelspin.
From packet to dashboard¶
Decoded packets flow through a bounded queue (600 packets ≈ 10 s) into a single ordered consumer:
- if recording is on, the packet feeds lap detection;
- the latest frame is broadcast to all WebSocket clients, throttled to
GT7_WS_RATE(default 30 Hz) — capture stays at 60 Hz regardless.
If the queue ever fills (slow disk), the oldest packet is dropped so the live view
never lags behind reality. Received/dropped/decode-error counters are visible in
GET /api/status and Settings › Health.
Protocol references
The GT7 telemetry format is community-reverse-engineered. Useful companion projects: snipem/gt7dashboard (Python dashboard this project reached parity with) and MacManley/gt7-udp (a C++ parser for ESP32 / ESP8266 boards with well-documented packet offsets).
The simulated source¶
GT7_SOURCE=sim swaps the UDP listener for a synthetic source that emits the same
packet stream at exactly 60 Hz: a 3,200 m circuit, ~223 km/h baseline with two braking
zones, deliberate front lockups, rear wheelspin on slow launches, a kerb strike at ~21 %
of the lap, and TCS/ASM/rev-limiter activity. It uses a fixed random seed, so demo data
is reproducible. It also suggests gears, as a stand-in for GT7's own hint, which has not
been checked against a console: the gear the next corner calls for, shown from up to
150 m before it while the car is in a higher one. On about
half its laps the driver holds fourth through the tight right-hander at ~1,600 m where
third is suggested, so the gear choice figures
differ from lap to lap.