fastsim_core/vehicle/
chassis.rs1pub use super::*;
2
3#[derive(
5 Clone, Debug, Serialize, Deserialize, PartialEq, IsVariant, derive_more::From, TryInto,
6)]
7pub enum DriveTypes {
8 RWD,
10 FWD,
12 AWD,
14 #[serde(alias = "FourWD")]
16 #[serde(rename = "4WD")]
17 FourWD,
18}
19
20impl SerdeAPI for DriveTypes {}
21impl Init for DriveTypes {}
22
23#[serde_api]
24#[non_exhaustive]
25#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
26#[serde(deny_unknown_fields)]
27pub struct Chassis {
29 pub drag_coef: si::Ratio,
31 pub frontal_area: si::Area,
33 pub wheel_rr_coef: si::Ratio,
35 pub wheel_inertia: si::MomentOfInertia,
37 pub num_wheels: u8,
39 #[serde(default)]
41 pub wheel_radius: Option<si::Length>,
42 #[serde(default)]
44 pub tire_code: Option<String>,
45 pub cg_height: si::Length,
47 pub wheel_fric_coef: si::Ratio,
49
50 pub drive_type: DriveTypes,
52 pub drive_axle_weight_frac: si::Ratio,
54 pub wheel_base: si::Length,
56
57 pub(crate) mass: Option<si::Mass>,
58 pub(crate) glider_mass: Option<si::Mass>,
60 #[serde(default)]
62 pub cargo_mass: Option<si::Mass>,
63}
64
65impl SerdeAPI for Chassis {}
66impl Init for Chassis {}
67
68impl Mass for Chassis {
69 fn mass(&self) -> anyhow::Result<Option<si::Mass>> {
70 let derived_mass = self
71 .derived_mass()
72 .with_context(|| anyhow!(format_dbg!()))?;
73 if let (Some(derived_mass), Some(set_mass)) = (derived_mass, self.mass) {
74 ensure!(
75 utils::almost_eq_uom(&set_mass, &derived_mass, None),
76 format!(
77 "{}",
78 format_dbg!(utils::almost_eq_uom(&set_mass, &derived_mass, None)),
79 )
80 );
81 }
82 Ok(self.mass)
83 }
84
85 fn set_mass(
86 &mut self,
87 new_mass: Option<si::Mass>,
88 _side_effect: MassSideEffect,
89 ) -> anyhow::Result<()> {
90 let derived_mass = self
91 .derived_mass()
92 .with_context(|| anyhow!(format_dbg!()))?;
93 self.mass = match (new_mass, derived_mass) {
94 (Some(new_mass), Some(dm)) => {
96 if dm != new_mass {
97 self.expunge_mass_fields();
98 }
99 Some(new_mass)
100 }
101 (Some(new_mass), None) => Some(new_mass),
102 (None, Some(dm)) => Some(dm),
103 (None, None) => bail!(
104 "Not all mass fields in `{}` are set and no mass was provided.",
105 stringify!(Chassis)
106 ),
107 };
108 ensure!(
109 self.mass > Some(0.0 * uc::KG),
110 "{} mass must be positive",
111 stringify!(Chassis)
112 );
113 Ok(())
114 }
115
116 fn derived_mass(&self) -> anyhow::Result<Option<si::Mass>> {
117 let mass =
118 if let (Some(glider_mass), Some(cargo_mass)) = (self.glider_mass, self.cargo_mass) {
119 Some(glider_mass + cargo_mass)
120 } else if let (None, None) = (self.glider_mass, self.cargo_mass) {
121 None
122 } else {
123 bail!(
124 "`{}` field masses are not consistently set to `Some` or `None`",
125 stringify!(Chassis)
126 )
127 };
128 Ok(mass)
129 }
130
131 fn expunge_mass_fields(&mut self) {
132 self.mass = None;
133 self.glider_mass = None;
134 self.cargo_mass = None;
135 }
136}