feat: TurboSu initial release - racing telemetry dashboard

This commit is contained in:
Yei.J. (AskaEth)
2026-07-25 15:23:40 +08:00
commit d61bb61a83
61 changed files with 5270 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
{
"id": "assetto_corsa",
"name": "Assetto Corsa",
"parser_type": "ac",
"telemetry_format": "ac",
"description": "神力科莎 UDP 遥测数据输出",
"author": "Yei.J. (AskaEth)",
"version": "1.0.0",
"default_port": 9996
}
+33
View File
@@ -0,0 +1,33 @@
import time
from server.telemetry.data import TelemetryData
def get_parser():
return AssettoCorsaParser()
class AssettoCorsaParser:
def game_id(self) -> str:
return "assetto_corsa"
def parse(self, data: bytes, addr: tuple) -> TelemetryData:
td = TelemetryData(game_id="assetto_corsa", timestamp=time.time())
td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"}
try:
text = data.decode("utf-8", errors="replace").rstrip("\r\n")
parts = text.split("\t")
if len(parts) < 10:
return td
td.speed_kmh = float(parts[0])
td.speed_mph = td.speed_kmh * 0.621371
td.rpm = float(parts[1])
td.gear = int(float(parts[2]))
td.throttle = float(parts[3])
td.brake = float(parts[4])
td.steering = float(parts[5])
td.fuel = float(parts[6])
except Exception:
pass
return td
@@ -0,0 +1,10 @@
{
"id": "assetto_corsa_competizione",
"name": "Assetto Corsa Competizione",
"parser_type": "acc",
"telemetry_format": "acc",
"description": "神力科莎:竞速 UDP 遥测数据输出",
"author": "Yei.J. (AskaEth)",
"version": "1.0.0",
"default_port": 20777
}
@@ -0,0 +1,34 @@
import struct
import time
from server.telemetry.data import TelemetryData
def get_parser():
return ACCParser()
class ACCParser:
def game_id(self) -> str:
return "assetto_corsa_competizione"
def parse(self, data: bytes, addr: tuple) -> TelemetryData:
td = TelemetryData(game_id="assetto_corsa_competizione", timestamp=time.time())
td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"}
if len(data) < 200:
return td
try:
td.speed_kmh = struct.unpack_from("<f", data, 0)[0]
td.speed_mph = td.speed_kmh * 0.621371
td.rpm = struct.unpack_from("<f", data, 4)[0]
td.max_rpm = struct.unpack_from("<f", data, 8)[0]
td.gear = struct.unpack_from("<B", data, 12)[0]
td.throttle = struct.unpack_from("<f", data, 16)[0]
td.brake = struct.unpack_from("<f", data, 20)[0]
td.steering = struct.unpack_from("<f", data, 24)[0]
td.fuel = struct.unpack_from("<f", data, 28)[0]
except struct.error:
pass
return td
+10
View File
@@ -0,0 +1,10 @@
{
"id": "f1_24",
"name": "F1 24",
"parser_type": "f1",
"telemetry_format": "f124",
"description": "F1 24 UDP 遥测数据输出",
"author": "Yei.J. (AskaEth)",
"version": "1.0.0",
"default_port": 20777
}
+37
View File
@@ -0,0 +1,37 @@
import struct
import time
from server.telemetry.data import TelemetryData
def get_parser():
return F124Parser()
class F124Parser:
def game_id(self) -> str:
return "f1_24"
def parse(self, data: bytes, addr: tuple) -> TelemetryData:
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:
return td
try:
td.speed_kmh = struct.unpack_from("<f", data, 37)[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]
except struct.error:
pass
return td
@@ -0,0 +1,10 @@
{
"id": "forza_horizon_4",
"name": "Forza Horizon 4",
"parser_type": "forza",
"telemetry_format": "fh4",
"description": "极限竞速:地平线4 遥测数据输出",
"author": "Yei.J. (AskaEth)",
"version": "1.0.0",
"default_port": 20777
}
+52
View File
@@ -0,0 +1,52 @@
import struct
import time
from server.telemetry.data import TelemetryData
def get_parser():
return ForzaHorizon4Parser()
class ForzaHorizon4Parser:
def game_id(self) -> str:
return "forza_horizon_4"
def parse(self, data: bytes, addr: tuple) -> TelemetryData:
td = TelemetryData(game_id="forza_horizon_4", timestamp=time.time())
td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"}
if len(data) < 323:
return td
try:
offset = 12
td.rpm = struct.unpack_from("<f", data, 8)[0]
td.max_rpm = struct.unpack_from("<f", data, 16)[0]
td.horsepower = struct.unpack_from("<f", data, 12)[0]
td.torque = struct.unpack_from("<f", data, 20)[0]
td.boost = struct.unpack_from("<f", data, 308)[0]
td.fuel = struct.unpack_from("<f", data, 312)[0]
td.oil_temp = struct.unpack_from("<f", data, 316)[0]
td.engine_temp = struct.unpack_from("<f", data, 320)[0]
td.speed_mph = struct.unpack_from("<f", data, 244)[0]
td.speed_kmh = td.speed_mph * 1.60934
td.gear = struct.unpack_from("<B", data, 264)[0]
td.best_lap = struct.unpack_from("<f", data, 268)[0]
td.last_lap = struct.unpack_from("<f", data, 276)[0]
td.lap_time = struct.unpack_from("<f", data, 284)[0]
td.lap_number = struct.unpack_from("<H", data, 292)[0]
td.position_x = struct.unpack_from("<f", data, 0)[0]
td.position_y = struct.unpack_from("<f", data, 4)[0]
td.position_z = struct.unpack_from("<f", data, 552)[0]
td.acceleration_x = struct.unpack_from("<f", data, 300)[0]
td.acceleration_y = struct.unpack_from("<f", data, 304)[0]
td.acceleration_z = struct.unpack_from("<f", data, 196)[0]
td.throttle = struct.unpack_from("<f", data, 228)[0]
td.brake = struct.unpack_from("<f", data, 232)[0]
td.steering = struct.unpack_from("<f", data, 204)[0]
td.clutch = struct.unpack_from("<f", data, 252)[0]
td.handbrake = struct.unpack_from("<f", data, 256)[0]
except struct.error:
pass
return td
@@ -0,0 +1,10 @@
{
"id": "forza_horizon_5",
"name": "Forza Horizon 5",
"parser_type": "forza",
"telemetry_format": "fh5",
"description": "极限竞速:地平线5 遥测数据输出",
"author": "Yei.J. (AskaEth)",
"version": "1.0.0",
"default_port": 20777
}
+51
View File
@@ -0,0 +1,51 @@
import struct
import time
from server.telemetry.data import TelemetryData
def get_parser():
return ForzaHorizon5Parser()
class ForzaHorizon5Parser:
def game_id(self) -> str:
return "forza_horizon_5"
def parse(self, data: bytes, addr: tuple) -> TelemetryData:
td = TelemetryData(game_id="forza_horizon_5", timestamp=time.time())
td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"}
if len(data) < 323:
return td
try:
td.rpm = struct.unpack_from("<f", data, 8)[0]
td.max_rpm = struct.unpack_from("<f", data, 16)[0]
td.horsepower = struct.unpack_from("<f", data, 12)[0]
td.torque = struct.unpack_from("<f", data, 20)[0]
td.boost = struct.unpack_from("<f", data, 308)[0]
td.fuel = struct.unpack_from("<f", data, 312)[0]
td.oil_temp = struct.unpack_from("<f", data, 316)[0]
td.engine_temp = struct.unpack_from("<f", data, 320)[0]
td.speed_mph = struct.unpack_from("<f", data, 244)[0]
td.speed_kmh = td.speed_mph * 1.60934
td.gear = struct.unpack_from("<B", data, 264)[0]
td.best_lap = struct.unpack_from("<f", data, 268)[0]
td.last_lap = struct.unpack_from("<f", data, 276)[0]
td.lap_time = struct.unpack_from("<f", data, 284)[0]
td.lap_number = struct.unpack_from("<H", data, 292)[0]
td.position_x = struct.unpack_from("<f", data, 0)[0]
td.position_y = struct.unpack_from("<f", data, 4)[0]
td.position_z = struct.unpack_from("<f", data, 552)[0]
td.acceleration_x = struct.unpack_from("<f", data, 300)[0]
td.acceleration_y = struct.unpack_from("<f", data, 304)[0]
td.acceleration_z = struct.unpack_from("<f", data, 196)[0]
td.throttle = struct.unpack_from("<f", data, 228)[0]
td.brake = struct.unpack_from("<f", data, 232)[0]
td.steering = struct.unpack_from("<f", data, 204)[0]
td.clutch = struct.unpack_from("<f", data, 252)[0]
td.handbrake = struct.unpack_from("<f", data, 256)[0]
except struct.error:
pass
return td
@@ -0,0 +1,10 @@
{
"id": "forza_motorsport",
"name": "Forza Motorsport (2023)",
"parser_type": "forza",
"telemetry_format": "fm8",
"description": "极限竞速 Motorsport 遥测数据输出",
"author": "Yei.J. (AskaEth)",
"version": "1.0.0",
"default_port": 20777
}
+48
View File
@@ -0,0 +1,48 @@
import struct
import time
from server.telemetry.data import TelemetryData
def get_parser():
return ForzaMotorsportParser()
class ForzaMotorsportParser:
def game_id(self) -> str:
return "forza_motorsport"
def parse(self, data: bytes, addr: tuple) -> TelemetryData:
td = TelemetryData(game_id="forza_motorsport", timestamp=time.time())
td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"}
if len(data) < 323:
return td
try:
td.rpm = struct.unpack_from("<f", data, 8)[0]
td.max_rpm = struct.unpack_from("<f", data, 16)[0]
td.horsepower = struct.unpack_from("<f", data, 12)[0]
td.torque = struct.unpack_from("<f", data, 20)[0]
td.boost = struct.unpack_from("<f", data, 308)[0]
td.fuel = struct.unpack_from("<f", data, 312)[0]
td.oil_temp = struct.unpack_from("<f", data, 316)[0]
td.engine_temp = struct.unpack_from("<f", data, 320)[0]
td.speed_mph = struct.unpack_from("<f", data, 244)[0]
td.speed_kmh = td.speed_mph * 1.60934
td.gear = struct.unpack_from("<B", data, 264)[0]
td.best_lap = struct.unpack_from("<f", data, 268)[0]
td.last_lap = struct.unpack_from("<f", data, 276)[0]
td.lap_time = struct.unpack_from("<f", data, 284)[0]
td.lap_number = struct.unpack_from("<H", data, 292)[0]
td.position_x = struct.unpack_from("<f", data, 0)[0]
td.position_y = struct.unpack_from("<f", data, 4)[0]
td.position_z = struct.unpack_from("<f", data, 552)[0]
td.throttle = struct.unpack_from("<f", data, 228)[0]
td.brake = struct.unpack_from("<f", data, 232)[0]
td.steering = struct.unpack_from("<f", data, 204)[0]
td.clutch = struct.unpack_from("<f", data, 252)[0]
td.handbrake = struct.unpack_from("<f", data, 256)[0]
except struct.error:
pass
return td
+10
View File
@@ -0,0 +1,10 @@
{
"id": "iracing",
"name": "iRacing",
"parser_type": "iracing",
"telemetry_format": "ir",
"description": "iRacing 遥测数据输出",
"author": "Yei.J. (AskaEth)",
"version": "1.0.0",
"default_port": 20777
}
+32
View File
@@ -0,0 +1,32 @@
import struct
import time
from server.telemetry.data import TelemetryData
def get_parser():
return IRacingParser()
class IRacingParser:
def game_id(self) -> str:
return "iracing"
def parse(self, data: bytes, addr: tuple) -> TelemetryData:
td = TelemetryData(game_id="iracing", timestamp=time.time())
td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"}
if len(data) < 100:
return td
try:
td.speed_mph = struct.unpack_from("<f", data, 36)[0]
td.speed_kmh = td.speed_mph * 1.60934
td.rpm = struct.unpack_from("<f", data, 48)[0]
td.gear = struct.unpack_from("<i", data, 56)[0]
td.throttle = struct.unpack_from("<f", data, 4)[0]
td.brake = struct.unpack_from("<f", data, 8)[0]
td.steering = struct.unpack_from("<f", data, 0)[0]
except struct.error:
pass
return td