use crate::types::*;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TelemetrySnapshot {
pub session_id: SessionId,
pub car_id: CarId,
pub timestamp: DateTime<Utc>,
pub lap: LapNumber,
pub position: Position,
pub motion: MotionData,
pub tires: TireData,
pub power_unit: PowerUnitData,
pub aero: AeroData,
pub brakes: BrakeData,
pub inputs: DriverInputs,
pub fuel: FuelData,
pub drs: DrsStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MotionData {
pub speed: f32,
pub acceleration: f32,
pub lateral_g: f32,
pub longitudinal_g: f32,
pub vertical_g: f32,
pub yaw_rate: f32,
pub pitch: f32,
pub roll: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TireData {
pub front_left: TireSensor,
pub front_right: TireSensor,
pub rear_left: TireSensor,
pub rear_right: TireSensor,
pub compound: TireCompound,
pub age_laps: u16,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TireSensor {
pub surface_temp: f32,
pub inner_temp: f32,
pub brake_temp: f32,
pub pressure: f32,
pub wear: f32,
pub damage: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum TireCompound {
C0, C1,
C2,
C3,
C4,
C5, Intermediate,
Wet,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PowerUnitData {
pub rpm: u16,
pub throttle: f32,
pub ers_mode: ErsMode,
pub ers_battery: f32,
pub mgu_k_deployment: f32,
pub mgu_h_recovery: f32,
pub engine_temp: f32,
pub oil_temp: f32,
pub oil_pressure: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ErsMode {
None,
Low,
Medium,
High,
Hotlap,
Overtake,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AeroData {
pub front_wing_angle: f32,
pub rear_wing_angle: f32,
pub downforce: f32,
pub drag_coefficient: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrakeData {
pub bias: f32,
pub pressure: f32,
pub front_temp: f32,
pub rear_temp: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DriverInputs {
pub steering: f32,
pub throttle: f32,
pub brake: f32,
pub clutch: f32,
pub gear: i8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FuelData {
pub remaining: f32,
pub consumption_rate: f32,
pub temperature: f32,
pub pressure: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DrsStatus {
Unavailable,
Available,
Activated,
}
impl TelemetrySnapshot {
pub fn average_tire_wear(&self) -> f32 {
(self.tires.front_left.wear
+ self.tires.front_right.wear
+ self.tires.rear_left.wear
+ self.tires.rear_right.wear)
/ 4.0
}
pub fn has_critical_tire_temp(&self) -> bool {
let temps = [
self.tires.front_left.surface_temp,
self.tires.front_right.surface_temp,
self.tires.rear_left.surface_temp,
self.tires.rear_right.surface_temp,
];
temps.iter().any(|&t| t > 120.0)
}
pub fn estimated_fuel_laps(&self) -> f32 {
if self.fuel.consumption_rate > 0.0 {
self.fuel.remaining / self.fuel.consumption_rate
} else {
f32::INFINITY
}
}
pub fn is_braking(&self) -> bool {
self.inputs.brake > 0.1
}
pub fn is_full_throttle(&self) -> bool {
self.inputs.throttle > 0.95
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_tire_sensor() -> TireSensor {
TireSensor {
surface_temp: 95.0,
inner_temp: 100.0,
brake_temp: 350.0,
pressure: 21.5,
wear: 0.3,
damage: 0.0,
}
}
#[test]
fn test_average_tire_wear() {
let snapshot = TelemetrySnapshot {
session_id: SessionId::new(),
car_id: CarId::new(1).unwrap(),
timestamp: Utc::now(),
lap: LapNumber(10),
position: Position(5),
motion: MotionData {
speed: 250.0,
acceleration: 2.0,
lateral_g: 4.0,
longitudinal_g: 2.0,
vertical_g: 1.0,
yaw_rate: 0.1,
pitch: 0.0,
roll: 0.0,
},
tires: TireData {
front_left: TireSensor { wear: 0.2, ..create_test_tire_sensor() },
front_right: TireSensor { wear: 0.25, ..create_test_tire_sensor() },
rear_left: TireSensor { wear: 0.3, ..create_test_tire_sensor() },
rear_right: TireSensor { wear: 0.35, ..create_test_tire_sensor() },
compound: TireCompound::C3,
age_laps: 15,
},
power_unit: PowerUnitData {
rpm: 11000,
throttle: 1.0,
ers_mode: ErsMode::Medium,
ers_battery: 0.8,
mgu_k_deployment: 120.0,
mgu_h_recovery: 0.0,
engine_temp: 105.0,
oil_temp: 140.0,
oil_pressure: 5.5,
},
aero: AeroData {
front_wing_angle: 15.0,
rear_wing_angle: 12.0,
downforce: 15000.0,
drag_coefficient: 0.78,
},
brakes: BrakeData {
bias: 0.58,
pressure: 0.0,
front_temp: 350.0,
rear_temp: 320.0,
},
inputs: DriverInputs {
steering: 0.0,
throttle: 1.0,
brake: 0.0,
clutch: 0.0,
gear: 7,
},
fuel: FuelData {
remaining: 60.0,
consumption_rate: 1.2,
temperature: 45.0,
pressure: 6.0,
},
drs: DrsStatus::Available,
};
assert_eq!(snapshot.average_tire_wear(), 0.275);
assert!(!snapshot.has_critical_tire_temp());
assert!((snapshot.estimated_fuel_laps() - 50.0).abs() < 0.01);
assert!(snapshot.is_full_throttle());
assert!(!snapshot.is_braking());
}
}