fix: F1 24 parser matches official EA SPORTS UDP spec with packet types

This commit is contained in:
2026-07-27 18:56:46 +08:00
parent 5630d0e332
commit 36755cc78a
+60 -12
View File
@@ -8,6 +8,12 @@ def get_parser():
class F124Parser:
"""F1 24 UDP telemetry parser based on official EA SPORTS specification."""
# Packet header (29 bytes)
HEADER_SIZE = 29
CAR_TELEMETRY_SIZE = 43 # sizeof(CarTelemetryData) with padding
def game_id(self) -> str:
return "f1_24"
@@ -15,22 +21,64 @@ class F124Parser:
td = TelemetryData(game_id="f1_24", timestamp=time.time())
td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"}
if len(data) < 1289:
if len(data) < 50:
return td
try:
td.speed_kmh = struct.unpack_from("<f", data, 37)[0]
# Parse header
packet_id = struct.unpack_from("<B", data, 6)[0]
player_idx = struct.unpack_from("<B", data, 27)[0]
td.raw["packet_id"] = packet_id
td.raw["player_index"] = player_idx
if packet_id == 6: # Car Telemetry Packet (ID=6)
offset = self.HEADER_SIZE + player_idx * self.CAR_TELEMETRY_SIZE
td.speed_kmh = struct.unpack_from("<H", data, offset)[0]
td.speed_mph = td.speed_kmh * 0.621371
td.rpm = struct.unpack_from("<H", data, 41)[0]
td.max_rpm = struct.unpack_from("<H", data, 43)[0]
td.gear = struct.unpack_from("<B", data, 46)[0] & 0x0F
td.throttle = struct.unpack_from("<f", data, 47)[0]
td.brake = struct.unpack_from("<f", data, 55)[0]
td.steering = struct.unpack_from("<B", data, 45)[0] / 127.0
td.lap_number = struct.unpack_from("<B", data, 262)[0]
td.lap_time = struct.unpack_from("<f", data, 63)[0]
td.best_lap = struct.unpack_from("<f", data, 268)[0]
td.fuel = struct.unpack_from("<f", data, 51)[0]
td.throttle = struct.unpack_from("<f", data, offset + 4)[0]
td.steering = struct.unpack_from("<f", data, offset + 8)[0]
td.brake = struct.unpack_from("<f", data, offset + 12)[0]
td.clutch = struct.unpack_from("<B", data, offset + 16)[0] / 100.0
td.gear = struct.unpack_from("<b", data, offset + 17)[0]
td.rpm = struct.unpack_from("<H", data, offset + 18)[0]
td.raw["drs"] = struct.unpack_from("<B", data, offset + 20)[0]
td.raw["rev_lights_percent"] = struct.unpack_from("<B", data, offset + 21)[0]
td.raw["rev_lights_bit"] = struct.unpack_from("<H", data, offset + 22)[0]
for i in range(4):
td.raw[f"tyre_surface_temp_{i}"] = struct.unpack_from("<B", data, offset + 30 + i)[0]
td.raw[f"tyre_inner_temp_{i}"] = struct.unpack_from("<B", data, offset + 34 + i)[0]
td.raw["engine_temp"] = struct.unpack_from("<H", data, offset + 38)[0]
elif packet_id == 0: # Motion Packet (ID=0)
offset = self.HEADER_SIZE + player_idx * 64 # sizeof(CarMotionData)
td.acceleration_x = struct.unpack_from("<f", data, offset + 28)[0]
td.acceleration_y = struct.unpack_from("<f", data, offset + 32)[0]
td.acceleration_z = struct.unpack_from("<f", data, offset + 36)[0]
elif packet_id == 2: # Lap Data Packet (ID=2)
offset = self.HEADER_SIZE + player_idx * 24 # sizeof(LapData)
td.last_lap = struct.unpack_from("<f", data, offset)[0]
td.lap_time = struct.unpack_from("<f", data, offset + 4)[0]
td.best_lap = struct.unpack_from("<f", data, offset + 8)[0]
elif packet_id == 7: # Car Status Packet (ID=7)
offset = self.HEADER_SIZE + player_idx * 41 # sizeof(CarStatusData)
td.raw["traction_control"] = struct.unpack_from("<B", data, offset)[0]
td.raw["anti_lock_brakes"] = struct.unpack_from("<B", data, offset + 1)[0]
td.fuel = struct.unpack_from("<f", data, offset + 2)[0] / 110.0 # normalized to 0-1
if td.fuel > 1.0:
td.fuel = min(1.0, td.fuel / 100.0)
td.max_rpm = struct.unpack_from("<H", data, offset + 26)[0]
td.raw["ers_deploy_mode"] = struct.unpack_from("<B", data, offset + 34)[0]
td.raw["ers_store_energy"] = struct.unpack_from("<f", data, offset + 36)[0]
except struct.error:
pass