use crate::VerticalDistance;
use crate::measurements::{Length, Temperature};
mod altering_factors;
mod builder;
mod influences;
pub use altering_factors::*;
pub use builder::*;
pub use influences::*;
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Debug, Default)]
pub struct TakeoffLandingDistance {
ground_roll: Length,
clear_obstacle: Length,
}
impl TakeoffLandingDistance {
pub fn ground_roll(&self) -> &Length {
&self.ground_roll
}
pub fn clear_obstacle(&self) -> &Length {
&self.clear_obstacle
}
}
#[derive(Clone, PartialEq, Debug, Default)]
pub struct TakeoffLandingPerformance {
table: Vec<(VerticalDistance, Temperature, Length, Length)>,
factors: Option<AlteringFactors>,
notes: Option<String>,
}
impl TakeoffLandingPerformance {
pub fn new(
table: Vec<(VerticalDistance, Temperature, Length, Length)>,
factors: Option<AlteringFactors>,
notes: Option<String>,
) -> Self {
Self {
table,
factors,
notes,
}
}
pub fn builder<I>(table: I) -> TakeoffLandingPerformanceBuilder
where
I: IntoIterator<Item = (VerticalDistance, Temperature, Length, Length)>,
{
TakeoffLandingPerformanceBuilder::new(table)
}
pub fn min_distance(&self, influences: &Influences) -> TakeoffLandingDistance {
let distance = self.distance(influences.temperature(), influences.level());
TakeoffLandingDistance {
ground_roll: distance.ground_roll
* self
.factors
.as_ref()
.map_or(1.0, |f| f.ground_roll_factor(influences)),
clear_obstacle: distance.clear_obstacle
* self
.factors
.as_ref()
.map_or(1.0, |f| f.clear_obstacle_factor(influences)),
}
}
pub fn distance(
&self,
temperature: &Temperature,
pa: &VerticalDistance,
) -> TakeoffLandingDistance {
let closest = self
.table
.iter()
.reduce(|closest, row| {
let closest_level = if row.0 >= *pa && closest.0 >= *pa {
if row.0 < closest.0 { row } else { closest }
} else if row.0 >= *pa {
row
} else {
closest
};
if row.1 >= *temperature && closest_level.1 >= *temperature {
if row.1 < closest_level.1 {
row
} else {
closest_level
}
} else if row.1 >= *temperature {
row
} else {
closest_level
}
})
.expect("table should not be empty");
TakeoffLandingDistance {
ground_roll: closest.2,
clear_obstacle: closest.3,
}
}
pub fn notes(&self) -> Option<&String> {
self.notes.as_ref()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_closest_distance_from_table() {
let perf = TakeoffLandingPerformance::new(
vec![
(
VerticalDistance::PressureAltitude(0),
Temperature::c(0.0),
Length::ft(800.0),
Length::ft(1500.0),
),
(
VerticalDistance::PressureAltitude(0),
Temperature::c(40.0),
Length::ft(1000.0),
Length::ft(2000.0),
),
(
VerticalDistance::PressureAltitude(8000),
Temperature::c(0.0),
Length::ft(1800.0),
Length::ft(3600.0),
),
(
VerticalDistance::PressureAltitude(8000),
Temperature::c(30.0),
Length::ft(2300.0),
Length::ft(4800.0),
),
],
None,
None,
);
assert_eq!(
perf.distance(&Temperature::c(-10.0), &VerticalDistance::Gnd)
.ground_roll,
Length::ft(800.0)
);
assert_eq!(
perf.distance(
&Temperature::c(20.0),
&VerticalDistance::PressureAltitude(1000)
)
.ground_roll,
Length::ft(2300.0)
);
}
}