feat: add 2022 spec car status packet parser

This commit is contained in:
Maciej Pędzich 2025-02-18 21:34:16 +01:00
parent 8ea06e5a53
commit 839e1ab30b
Signed by: maciejpedzich
GPG Key ID: CE4A303D84882F0D
5 changed files with 272 additions and 28 deletions

View File

@ -1,14 +1,11 @@
pub mod wheel_index;
pub mod driver_id;
pub mod wheel_index;
use binrw::BinRead;
use bitflags::bitflags;
use serde::{Deserialize, Serialize};
pub(crate) const MAX_NUM_CARS: usize = 22;
pub(crate) const MAX_NUM_MARSHAL_ZONES: usize = 21;
pub(crate) const MAX_NUM_WEATHER_FORECAST_SAMPLES: usize = 56;
pub(crate) const MAX_AI_DIFFICULTY: u8 = 110;
#[non_exhaustive]
#[derive(
@ -983,3 +980,170 @@ pub enum MfdPanelIndex {
Temperatures = 4,
Closed = 255,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum TractionControl {
Off = 0,
Medium = 1,
Full = 2,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum FuelMix {
Lean = 0,
Standard = 1,
Rich = 2,
Max = 3,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum ErsDeployMode {
None = 0,
Medium = 1,
Overtake = 2,
Hotlap = 3,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(i8))]
pub enum VehicleFiaFlag {
Unknown = -1,
None = 0,
Green = 1,
Blue = 2,
Yellow = 3,
Red = 4,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(i8))]
pub enum DrsAllowed {
Unknown = -1,
NotAllowed = 0,
Allowed = 1,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum ActualTyreCompound {
Unknown = 0,
F1C5 = 16,
F1C4 = 17,
F1C3 = 18,
F1C2 = 19,
F1C1 = 20,
F1Inter = 7,
F1Wet = 8,
ClassicDry = 9,
ClassicWet = 10,
F2SuperSoft = 11,
F2Soft = 12,
F2Medium = 13,
F2Hard = 14,
F2Wet = 15,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum VisualTyreCompound {
Unknown = 0,
F1Soft = 16,
F1Medium = 17,
F1Hard = 18,
F1Inter = 7,
F1Wet = 8,
ClassicDry = 9,
ClassicWet = 10,
F2SuperSoft = 19,
F2Soft = 20,
F2Medium = 21,
F2Hard = 22,
F2Wet = 15,
}

View File

@ -3,9 +3,9 @@ pub mod packets;
use crate::constants::PacketId;
use crate::packets::{
F1PacketCarSetupsData, F1PacketCarTelemetryData, F1PacketEventData,
F1PacketLapData, F1PacketMotionData, F1PacketParticipantsData,
F1PacketSessionData,
F1PacketCarSetupsData, F1PacketCarStatusData, F1PacketCarTelemetryData,
F1PacketEventData, F1PacketLapData, F1PacketMotionData,
F1PacketParticipantsData, F1PacketSessionData,
};
use binrw::io::Cursor;
@ -93,4 +93,6 @@ pub struct F1PacketBody {
pub car_setups: Option<F1PacketCarSetupsData>,
#[br(if(packet_id == PacketId::CarTelemetry), args(packet_format))]
pub car_telemetry: Option<F1PacketCarTelemetryData>,
#[br(if(packet_id == PacketId::CarStatus), args(packet_format))]
pub car_status: Option<F1PacketCarStatusData>,
}

72
src/packets/car_status.rs Normal file
View File

@ -0,0 +1,72 @@
use super::u8_to_bool;
use crate::constants::{
ActualTyreCompound, DrsAllowed, ErsDeployMode, FuelMix, TractionControl,
VehicleFiaFlag, VisualTyreCompound,
};
use binrw::BinRead;
use serde::{Deserialize, Serialize};
#[derive(
BinRead, PartialEq, PartialOrd, Copy, Clone, Debug, Serialize, Deserialize,
)]
#[br(
little,
import(_packet_format: u16),
assert(
max_gears <= 8,
"Car status entry has an invalid max number of gears: {}",
max_gears
)
)]
pub struct CarStatusData {
/// How much traction control is enabled.
pub traction_control: TractionControl,
/// Whether ABS is enabled.
#[br(map(u8_to_bool))]
pub anti_lock_brakes: bool,
/// Fuel mix currently in use.
pub fuel_mix: FuelMix,
/// Front brake bias (percentage).
pub front_brake_bias: u8,
/// Whether the pit limiter is enabled.
#[br(map(u8_to_bool))]
pub pit_limiter_enabled: bool,
/// Current fuel mass.
pub fuel_in_tank: f32,
/// Fuel capacity.
pub fuel_capacity: f32,
/// Fuel remaining in terms of laps.
pub fuel_remaining_laps: f32,
/// Car's max RPM, point of rev limiter.
pub max_rpm: u16,
/// Car's idle RPM.
pub idle_rpm: u16,
/// Maximum number of gears.
pub max_gears: u8,
/// Whether DRS can be used (might be unknown).
pub drs_allowed: DrsAllowed,
/// 0 = DRS is unavailable, Non-zero = DRS will be available in X metres.
pub drs_activation_distance: u16,
/// Actual tyre compound currently in use.
pub actual_tyre_compound: ActualTyreCompound,
/// Visible tyre compound currently in use.
pub visual_tyre_compound: VisualTyreCompound,
/// Age of the current set of tyres in laps.
pub tyres_age_laps: u8,
/// Flag the driver is currently being shown.
pub vehicle_fia_flag: VehicleFiaFlag,
/// ERS energy store in Joules.
pub ers_store_energy: f32,
/// ERS deployment mode.
pub ers_deploy_mode: ErsDeployMode,
/// ERS energy harvested this lap by the MGU-K.
pub ers_harvested_this_lap_mguk: f32,
/// ERS energy harvested this lap by the MGU-H.
pub ers_harvested_this_lap_mguh: f32,
/// ERS energy deployed this lap.
pub ers_deployed_this_lap: f32,
/// Whether the car has paused in a network game.
#[br(map(u8_to_bool))]
pub network_paused: bool,
}

View File

@ -27,7 +27,7 @@ use serde::{Deserialize, Serialize};
brake
),
assert(
(0..=100).contains(&clutch),
clutch <= 100,
"Car telemetry entry has an invalid clutch value: {}",
clutch
),
@ -37,7 +37,7 @@ use serde::{Deserialize, Serialize};
gear
),
assert(
(0..=100).contains(&rev_lights_percent),
rev_lights_percent <= 100,
"Car telemetry entry has an invalid rev lights percentage: {}",
rev_lights_percent
),
@ -51,7 +51,7 @@ pub struct CarTelemetryData {
pub steer: f32,
/// Amount of brake applied. Value in range `(0.0..=1.0)`.
pub brake: f32,
/// Amount of clutch applied. Value in range `(0..=100)`.
/// Amount of clutch applied (percentage).
pub clutch: u8,
/// Selected gear. Neutral = 0, reverse = -1.
pub gear: i8,

View File

@ -1,4 +1,5 @@
pub mod car_setups;
pub mod car_status;
pub mod car_telemetry;
pub mod event;
pub mod lap_data;
@ -9,8 +10,7 @@ pub mod session;
use crate::constants::{
BrakingAssist, DynamicRacingLine, DynamicRacingLineType, ForecastAccuracy,
Formula, GameMode, GearboxAssist, MfdPanelIndex, Ruleset, SafetyCarStatus,
SessionLength, SessionType, TrackId, Weather, MAX_AI_DIFFICULTY,
MAX_NUM_CARS, MAX_NUM_MARSHAL_ZONES, MAX_NUM_WEATHER_FORECAST_SAMPLES,
SessionLength, SessionType, TrackId, Weather, MAX_NUM_CARS,
};
use crate::packets::event::EventDataDetails;
use crate::packets::lap_data::LapData;
@ -19,6 +19,7 @@ use crate::packets::participants::ParticipantsData;
use crate::packets::session::{MarshalZone, WeatherForecastSample};
use crate::packets::car_setups::CarSetupData;
use crate::packets::car_status::CarStatusData;
use crate::packets::car_telemetry::CarTelemetryData;
use binrw::BinRead;
use serde::{Deserialize, Serialize};
@ -49,17 +50,17 @@ pub struct F1PacketMotionData {
little,
import(packet_format: u16),
assert(
num_marshal_zones <= MAX_NUM_MARSHAL_ZONES,
num_marshal_zones <= 21,
"Session packet has an invalid number of marshal zones: {}",
num_marshal_zones
),
assert(
num_weather_forecast_samples <= MAX_NUM_WEATHER_FORECAST_SAMPLES,
num_weather_forecast_samples <= 56,
"Session packet has an invalid number of weather forecast samples: {}",
num_weather_forecast_samples
),
assert(
ai_difficulty <= MAX_AI_DIFFICULTY,
ai_difficulty <= 110,
"Session packet has an invalid AI difficulty value: {}",
ai_difficulty
),
@ -104,7 +105,7 @@ pub struct F1PacketSessionData {
pub num_marshal_zones: usize,
/// List of marshal zones.
/// Has a size of 21 regardless of the `num_marshal_zones` value.
#[br(count(MAX_NUM_MARSHAL_ZONES), args{ inner: (packet_format,) })]
#[br(count(21), args{ inner: (packet_format,) })]
pub marshal_zones: Vec<MarshalZone>,
/// Safety car deployment status.
pub safety_car_status: SafetyCarStatus,
@ -116,10 +117,7 @@ pub struct F1PacketSessionData {
pub num_weather_forecast_samples: usize,
/// List of up to weather forecast samples.
/// Has a size of 56 regardless of the `num_weather_forecast_samples` value.
#[br(
count(MAX_NUM_WEATHER_FORECAST_SAMPLES),
args{ inner: (packet_format,) }
)]
#[br(count(56), args{ inner: (packet_format,) })]
pub weather_forecast_samples: Vec<WeatherForecastSample>,
/// Weather forecast accuracy.
pub forecast_accuracy: ForecastAccuracy,
@ -224,8 +222,7 @@ pub struct F1PacketParticipantsData {
#[br(map(u8_to_usize))]
pub num_active_cars: usize,
/// Data for all participants.
/// Should have a size equal to
/// [`num_active_cars`](field@F1PacketParticipantsData::num_active_cars)
/// Should have a size equal to `num_active_cars`.
#[br(count(num_active_cars), args{ inner: (packet_format,) })]
pub participants: Vec<ParticipantsData>,
}
@ -261,20 +258,29 @@ pub struct F1PacketCarSetupsData {
)]
pub struct F1PacketCarTelemetryData {
/// Telemetry data for all cars on track.
/// Should have a size of 22
#[br(
count(MAX_NUM_CARS),
args{ inner: (packet_format,) }
)]
/// Should have a size of 22.
#[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
pub car_telemetry_data: Vec<CarTelemetryData>,
/// Index of currently open MFD panel.
pub mfd_panel_index: MfdPanelIndex,
/// See [`mfd_panel_index`](field@F1PacketCarTelemetryData::mfd_panel_index)
/// See [`mfd_panel_index`](field@F1PacketCarTelemetryData::mfd_panel_index).
pub mfd_panel_index_secondary_player: MfdPanelIndex,
/// Suggested gear (0 if no gear suggested).
pub suggested_gear: i8,
}
/// Car status data for each car in the race.
#[non_exhaustive]
#[derive(
BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize,
)]
#[br(little, import(packet_format: u16))]
pub struct F1PacketCarStatusData {
/// Status data for all cars. Should have a size of 22.
#[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
pub car_status_data: Vec<CarStatusData>,
}
/// Extended motion data for player's car. Available as a:
/// - part of [`F1PacketMotionData`] in the 2022 format
/// - standalone packet from the 2023 format onwards