fix: correct Forza FH5/FH6 UDP packet parser offsets per official spec
- Speed: offset 256 (m/s * 3.6 = km/h), was incorrectly at 244 (PositionX) - Gear: offset 319 (U8), was at 264/298 - Throttle/Brake: offsets 315/316 (U8, 0-255) - Steering: offset 320 (S8, -127~127) - RPM: offset 16 ✓, MaxRPM: offset 8 ✓ - Lap times: BestLap=296, LastLap=300, CurrentLap=304 - LapNumber: offset 312 (U16) - Boost: 284, Fuel: 288, Position: 244/248/252 - Updated all Forza parsers (FH4, FH5) and fallback parser - Ref: Forza Data Out official format (324 bytes)
This commit is contained in:
@@ -15,37 +15,36 @@ class ForzaHorizon4Parser:
|
|||||||
td = TelemetryData(game_id="forza_horizon_4", timestamp=time.time())
|
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]}"}
|
td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"}
|
||||||
|
|
||||||
if len(data) < 323:
|
if len(data) < 312:
|
||||||
return td
|
return td
|
||||||
|
|
||||||
try:
|
try:
|
||||||
offset = 12
|
td.raw["is_race_on"] = struct.unpack_from("<i", data, 0)[0]
|
||||||
td.rpm = struct.unpack_from("<f", data, 8)[0]
|
td.max_rpm = struct.unpack_from("<f", data, 8)[0]
|
||||||
td.max_rpm = struct.unpack_from("<f", data, 16)[0]
|
td.rpm = struct.unpack_from("<f", data, 16)[0]
|
||||||
td.horsepower = struct.unpack_from("<f", data, 12)[0]
|
td.acceleration_x = struct.unpack_from("<f", data, 20)[0]
|
||||||
td.torque = struct.unpack_from("<f", data, 20)[0]
|
td.acceleration_y = struct.unpack_from("<f", data, 24)[0]
|
||||||
td.boost = struct.unpack_from("<f", data, 308)[0]
|
td.acceleration_z = struct.unpack_from("<f", data, 28)[0]
|
||||||
td.fuel = struct.unpack_from("<f", data, 312)[0]
|
td.position_x = struct.unpack_from("<f", data, 244)[0]
|
||||||
td.oil_temp = struct.unpack_from("<f", data, 316)[0]
|
td.position_y = struct.unpack_from("<f", data, 248)[0]
|
||||||
td.engine_temp = struct.unpack_from("<f", data, 320)[0]
|
td.position_z = struct.unpack_from("<f", data, 252)[0]
|
||||||
td.speed_mph = struct.unpack_from("<f", data, 244)[0]
|
td.speed_kmh = struct.unpack_from("<f", data, 256)[0] * 3.6
|
||||||
td.speed_kmh = td.speed_mph * 1.60934
|
td.speed_mph = td.speed_kmh * 0.621371
|
||||||
td.gear = struct.unpack_from("<B", data, 264)[0]
|
td.horsepower = struct.unpack_from("<f", data, 260)[0] / 745.7
|
||||||
td.best_lap = struct.unpack_from("<f", data, 268)[0]
|
td.torque = struct.unpack_from("<f", data, 264)[0]
|
||||||
td.last_lap = struct.unpack_from("<f", data, 276)[0]
|
td.boost = struct.unpack_from("<f", data, 284)[0]
|
||||||
td.lap_time = struct.unpack_from("<f", data, 284)[0]
|
td.fuel = struct.unpack_from("<f", data, 288)[0]
|
||||||
td.lap_number = struct.unpack_from("<H", data, 292)[0]
|
td.best_lap = struct.unpack_from("<f", data, 296)[0]
|
||||||
td.position_x = struct.unpack_from("<f", data, 0)[0]
|
td.last_lap = struct.unpack_from("<f", data, 300)[0]
|
||||||
td.position_y = struct.unpack_from("<f", data, 4)[0]
|
td.lap_time = struct.unpack_from("<f", data, 304)[0]
|
||||||
td.position_z = struct.unpack_from("<f", data, 552)[0]
|
td.lap_number = struct.unpack_from("<H", data, 312)[0]
|
||||||
td.acceleration_x = struct.unpack_from("<f", data, 300)[0]
|
td.raw["race_position"] = struct.unpack_from("<B", data, 314)[0]
|
||||||
td.acceleration_y = struct.unpack_from("<f", data, 304)[0]
|
td.throttle = struct.unpack_from("<B", data, 315)[0] / 255.0
|
||||||
td.acceleration_z = struct.unpack_from("<f", data, 196)[0]
|
td.brake = struct.unpack_from("<B", data, 316)[0] / 255.0
|
||||||
td.throttle = struct.unpack_from("<f", data, 228)[0]
|
td.clutch = struct.unpack_from("<B", data, 317)[0] / 255.0
|
||||||
td.brake = struct.unpack_from("<f", data, 232)[0]
|
td.handbrake = struct.unpack_from("<B", data, 318)[0] / 255.0
|
||||||
td.steering = struct.unpack_from("<f", data, 204)[0]
|
td.gear = struct.unpack_from("<B", data, 319)[0]
|
||||||
td.clutch = struct.unpack_from("<f", data, 252)[0]
|
td.steering = max(-1.0, min(1.0, struct.unpack_from("<b", data, 320)[0] / 127.0))
|
||||||
td.handbrake = struct.unpack_from("<f", data, 256)[0]
|
|
||||||
except struct.error:
|
except struct.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -13,38 +13,66 @@ class ForzaHorizon5Parser:
|
|||||||
|
|
||||||
def parse(self, data: bytes, addr: tuple) -> TelemetryData:
|
def parse(self, data: bytes, addr: tuple) -> TelemetryData:
|
||||||
td = TelemetryData(game_id="forza_horizon_5", timestamp=time.time())
|
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]}"}
|
td.raw = {
|
||||||
|
"raw_hex": data.hex(),
|
||||||
|
"length": len(data),
|
||||||
|
"addr": f"{addr[0]}:{addr[1]}",
|
||||||
|
}
|
||||||
|
|
||||||
if len(data) < 323:
|
if len(data) < 312:
|
||||||
return td
|
return td
|
||||||
|
|
||||||
try:
|
try:
|
||||||
td.rpm = struct.unpack_from("<f", data, 8)[0]
|
td.raw["is_race_on"] = struct.unpack_from("<i", data, 0)[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.max_rpm = struct.unpack_from("<f", data, 8)[0]
|
||||||
td.boost = struct.unpack_from("<f", data, 308)[0]
|
td.rpm = struct.unpack_from("<f", data, 16)[0]
|
||||||
td.fuel = struct.unpack_from("<f", data, 312)[0]
|
|
||||||
td.oil_temp = struct.unpack_from("<f", data, 316)[0]
|
# 加速度 (m/s²)
|
||||||
td.engine_temp = struct.unpack_from("<f", data, 320)[0]
|
td.acceleration_x = struct.unpack_from("<f", data, 20)[0]
|
||||||
td.speed_mph = struct.unpack_from("<f", data, 244)[0]
|
td.acceleration_y = struct.unpack_from("<f", data, 24)[0]
|
||||||
td.speed_kmh = td.speed_mph * 1.60934
|
td.acceleration_z = struct.unpack_from("<f", data, 28)[0]
|
||||||
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.position_x = struct.unpack_from("<f", data, 244)[0]
|
||||||
td.lap_time = struct.unpack_from("<f", data, 284)[0]
|
td.position_y = struct.unpack_from("<f", data, 248)[0]
|
||||||
td.lap_number = struct.unpack_from("<H", data, 292)[0]
|
td.position_z = struct.unpack_from("<f", data, 252)[0]
|
||||||
td.position_x = struct.unpack_from("<f", data, 0)[0]
|
|
||||||
td.position_y = struct.unpack_from("<f", data, 4)[0]
|
# 速度 (m/s)
|
||||||
td.position_z = struct.unpack_from("<f", data, 552)[0]
|
td.speed_kmh = struct.unpack_from("<f", data, 256)[0] * 3.6
|
||||||
td.acceleration_x = struct.unpack_from("<f", data, 300)[0]
|
td.speed_mph = td.speed_kmh * 0.621371
|
||||||
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.horsepower = struct.unpack_from("<f", data, 260)[0] / 745.7
|
||||||
td.brake = struct.unpack_from("<f", data, 232)[0]
|
td.torque = struct.unpack_from("<f", data, 264)[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]
|
td.boost = struct.unpack_from("<f", data, 284)[0]
|
||||||
|
td.fuel = struct.unpack_from("<f", data, 288)[0]
|
||||||
|
|
||||||
|
# 圈速
|
||||||
|
td.best_lap = struct.unpack_from("<f", data, 296)[0]
|
||||||
|
td.last_lap = struct.unpack_from("<f", data, 300)[0]
|
||||||
|
td.lap_time = struct.unpack_from("<f", data, 304)[0]
|
||||||
|
td.lap_number = struct.unpack_from("<H", data, 312)[0]
|
||||||
|
|
||||||
|
# 排名
|
||||||
|
td.raw["race_position"] = struct.unpack_from("<B", data, 314)[0]
|
||||||
|
|
||||||
|
# 玩家输入 (U8, 0-255)
|
||||||
|
td.throttle = struct.unpack_from("<B", data, 315)[0] / 255.0
|
||||||
|
td.brake = struct.unpack_from("<B", data, 316)[0] / 255.0
|
||||||
|
td.clutch = struct.unpack_from("<B", data, 317)[0] / 255.0
|
||||||
|
td.handbrake = struct.unpack_from("<B", data, 318)[0] / 255.0
|
||||||
|
|
||||||
|
# 档位
|
||||||
|
td.gear = struct.unpack_from("<B", data, 319)[0]
|
||||||
|
|
||||||
|
# 转向 (-127 ~ 127)
|
||||||
|
td.steering = max(-1.0, min(1.0,
|
||||||
|
struct.unpack_from("<b", data, 320)[0] / 127.0))
|
||||||
|
|
||||||
except struct.error:
|
except struct.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -27,47 +27,36 @@ class ForzaParser(BaseParser):
|
|||||||
td = TelemetryData(game_id=self._format, timestamp=time.time())
|
td = TelemetryData(game_id=self._format, timestamp=time.time())
|
||||||
td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"}
|
td.raw = {"raw_hex": data.hex(), "length": len(data), "addr": f"{addr[0]}:{addr[1]}"}
|
||||||
|
|
||||||
if len(data) < 323:
|
if len(data) < 312:
|
||||||
logger.warning("Forza data too short: %d bytes", len(data))
|
logger.warning("Forza data too short: %d bytes", len(data))
|
||||||
return td
|
return td
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self._format == "fh4":
|
td.raw["is_race_on"] = struct.unpack_from("<i", data, 0)[0]
|
||||||
offset = 0
|
td.rpm = struct.unpack_from("<f", data, 16)[0]
|
||||||
else:
|
td.max_rpm = struct.unpack_from("<f", data, 8)[0]
|
||||||
offset = 0
|
td.horsepower = struct.unpack_from("<f", data, 260)[0] / 745.7
|
||||||
|
td.torque = struct.unpack_from("<f", data, 264)[0]
|
||||||
td.rpm = struct.unpack_from("<f", data, 8)[0]
|
td.boost = struct.unpack_from("<f", data, 284)[0]
|
||||||
td.max_rpm = struct.unpack_from("<f", data, 16)[0]
|
td.fuel = struct.unpack_from("<f", data, 288)[0]
|
||||||
td.horsepower = struct.unpack_from("<f", data, 12)[0]
|
td.speed_kmh = struct.unpack_from("<f", data, 256)[0] * 3.6
|
||||||
td.torque = struct.unpack_from("<f", data, 20)[0]
|
td.speed_mph = td.speed_kmh * 0.621371
|
||||||
|
td.gear = struct.unpack_from("<B", data, 319)[0]
|
||||||
td.boost = struct.unpack_from("<f", data, 308)[0]
|
td.best_lap = struct.unpack_from("<f", data, 296)[0]
|
||||||
td.fuel = struct.unpack_from("<f", data, 312)[0]
|
td.last_lap = struct.unpack_from("<f", data, 300)[0]
|
||||||
td.oil_temp = struct.unpack_from("<f", data, 316)[0]
|
td.lap_time = struct.unpack_from("<f", data, 304)[0]
|
||||||
td.engine_temp = struct.unpack_from("<f", data, 320)[0]
|
td.lap_number = struct.unpack_from("<H", data, 312)[0]
|
||||||
|
td.position_x = struct.unpack_from("<f", data, 244)[0]
|
||||||
td.speed_mph = struct.unpack_from("<f", data, 244)
|
td.position_y = struct.unpack_from("<f", data, 248)[0]
|
||||||
td.speed_kmh = td.speed_mph * 1.60934
|
td.position_z = struct.unpack_from("<f", data, 252)[0]
|
||||||
td.gear = struct.unpack_from("<B", data, 264)[0]
|
td.acceleration_x = struct.unpack_from("<f", data, 20)[0]
|
||||||
td.best_lap = struct.unpack_from("<f", data, 268)[0]
|
td.acceleration_y = struct.unpack_from("<f", data, 24)[0]
|
||||||
td.last_lap = struct.unpack_from("<f", data, 276)[0]
|
td.acceleration_z = struct.unpack_from("<f", data, 28)[0]
|
||||||
td.lap_time = struct.unpack_from("<f", data, 284)[0]
|
td.throttle = struct.unpack_from("<B", data, 315)[0] / 255.0
|
||||||
td.lap_number = struct.unpack_from("<H", data, 292)[0]
|
td.brake = struct.unpack_from("<B", data, 316)[0] / 255.0
|
||||||
|
td.steering = max(-1.0, min(1.0, struct.unpack_from("<b", data, 320)[0] / 127.0))
|
||||||
td.position_x = struct.unpack_from("<f", data, 0)[0]
|
td.clutch = struct.unpack_from("<B", data, 317)[0] / 255.0
|
||||||
td.position_y = struct.unpack_from("<f", data, 4)[0]
|
td.handbrake = struct.unpack_from("<B", data, 318)[0] / 255.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]
|
|
||||||
|
|
||||||
td.raw.update({
|
td.raw.update({
|
||||||
"speed_mph": td.speed_mph,
|
"speed_mph": td.speed_mph,
|
||||||
|
|||||||
Reference in New Issue
Block a user