Skip to main content

Aircraft

Struct Aircraft 

Source
pub struct Aircraft { /* private fields */ }
Expand description

The aircraft we’re planning to fly with.

This aircraft provides all information necessary to execute the fuel and mass & balance planning. The aircraft is created in it’s empty configuration. All additional mass are loaded at a Station that places the mass at a fixed distance from a reference datum (the arm). The aircraft is created only with the station arms and mass is mapped to the stations when doing the mass & balance calculation. Stations that account for the mass of fuel are derived from the tanks and fuel consumption.

The aircraft’s mass & balance is calculated by mb for mass and fuel at ramp and after landing. There are further methods to calculate the mass & balance based on simplifications like constant mass during flight or equal fuel distribution across all tanks.

§Examples

This is how a C172 of our flying club with a Diesel engine would look like:

let ac = Aircraft::builder()
    .registration("N12345".to_string())
    .icao_type("C172".to_string())
    .stations(vec![
        Station::new(Length::m(0.94), Some("front seats".to_string())),
        Station::new(Length::m(1.85), Some("back seats".to_string())),
        Station::new(Length::m(2.41), Some("first cargo compartment".to_string())),
        Station::new(Length::m(3.12), Some("second cargo compartment".to_string())),
    ])
    .empty_mass(Mass::kg(807.0))
    .empty_balance(Length::m(1.0))
    .fuel_type(FuelType::Diesel)
    .tanks(vec![
        FuelTank::new(Volume::l(168.8), Length::m(1.22)),
    ])
    .cg_envelope(vec![
        CGLimit::new(Mass::kg(0.0), Length::m(0.89)),
        CGLimit::new(Mass::kg(885.0), Length::m(0.89)),
        CGLimit::new(Mass::kg(1111.0), Length::m(1.02)),
        CGLimit::new(Mass::kg(1111.0), Length::m(1.20)),
        CGLimit::new(Mass::kg(0.0), Length::m(1.20)),
    ])
    .build()
    .unwrap();

// now we can calculate the mass & balance for a flight with one pilot on
// board and 20 Liter fuel consumption that is distributed equally across
// all tanks
let mb = ac.mb_from_const_mass_and_equally_distributed_fuel(
    &vec![
        // we're in the front
        Mass::kg(80.0),
        // and no mass on the other stations
        Mass::kg(0.0),
        Mass::kg(0.0),
        Mass::kg(0.0)
    ],
    // we start our flight with 80 Liter of Diesel
    &diesel!(Volume::l(80.0)),
    // and land with 60 Liter remaining in our tank
    &diesel!(Volume::l(60.0)),
);

// finally we can check if the aircraft is balanced throughout the flight
assert!(ac.is_balanced(&mb.unwrap()));

Implementations§

Source§

impl Aircraft

Source

pub fn builder() -> AircraftBuilder

Returns a builder to build an aircraft.

Examples found in repository?
examples/flightplanner.rs (line 116)
38fn main() -> Result<()> {
39    // Performance setting with 65% load in cruise. This is the performance
40    // profile of a Cessna C172 with an TAE125-02-114 Diesel engine.
41    let perf = Performance::from_fn(
42        |vd| {
43            let tas = if *vd >= VerticalDistance::Altitude(10000) {
44                Speed::kt(114.0)
45            } else if *vd >= VerticalDistance::Altitude(8000) {
46                Speed::kt(112.0)
47            } else if *vd >= VerticalDistance::Altitude(6000) {
48                Speed::kt(110.0)
49            } else if *vd >= VerticalDistance::Altitude(4000) {
50                Speed::kt(109.0)
51            } else {
52                Speed::kt(107.0)
53            };
54
55            let ff = FuelFlow::PerHour(diesel!(Volume::l(21.0)));
56
57            (tas, ff)
58        },
59        // The data end at 10000 ft so we don't need to create the Performance
60        // with more values.
61        VerticalDistance::Altitude(10000),
62    );
63
64    let takeoff_perf = TakeoffLandingPerformance::builder(vec![
65        (
66            VerticalDistance::PressureAltitude(0),
67            Temperature::c(0.0),
68            Length::ft(845.0),
69            Length::ft(1510.0),
70        ),
71        (
72            VerticalDistance::PressureAltitude(0),
73            Temperature::c(10.0),
74            Length::ft(910.0),
75            Length::ft(1625.0),
76        ),
77        (
78            VerticalDistance::PressureAltitude(0),
79            Temperature::c(20.0),
80            Length::ft(980.0),
81            Length::ft(1745.0),
82        ),
83        (
84            VerticalDistance::PressureAltitude(0),
85            Temperature::c(30.0),
86            Length::ft(1055.0),
87            Length::ft(1875.0),
88        ),
89        (
90            VerticalDistance::PressureAltitude(0),
91            Temperature::c(40.0),
92            Length::ft(1135.0),
93            Length::ft(2015.0),
94        ),
95    ])
96    .factors(vec![
97        // Decrease distances 10% for each 9 knots headwind. For operation
98        // with tail winds up to 10 knots, increase distances by 10% for
99        // each 2 knots.
100        AlteringFactor::DecreaseHeadwind(FactorOfEffect::Rate {
101            numerator: 0.1,
102            denominator: Speed::kt(9.0),
103        }),
104        AlteringFactor::IncreaseTailwind(FactorOfEffect::Rate {
105            numerator: 0.1,
106            denominator: Speed::kt(2.0),
107        }),
108        // For operation on dry, grass runway, increase distances by 15% of
109        // the "ground roll" figure.
110        AlteringFactor::IncreaseRWYCC(HashMap::from([
111            ((None, Some(RunwaySurface::Grass)), 0.15), // we'll add 15% on any grass
112        ])),
113    ])
114    .build();
115
116    let aircraft = Aircraft::builder()
117        .registration("N12345".to_string())
118        .stations(vec![
119            Station::new(Length::m(0.94), Some(String::from("front seats"))),
120            Station::new(Length::m(1.85), Some(String::from("back seats"))),
121            Station::new(
122                Length::m(2.41),
123                Some(String::from("first cargo compartment")),
124            ),
125            Station::new(
126                Length::m(3.12),
127                Some(String::from("second cargo compartment")),
128            ),
129        ])
130        .empty_mass(Mass::kg(807.0))
131        .empty_balance(Length::m(1.0))
132        .fuel_type(FuelType::Diesel)
133        .tanks(vec![FuelTank::new(Volume::l(168.8), Length::m(1.22))])
134        .cg_envelope(vec![
135            CGLimit::new(Mass::kg(0.0), Length::m(0.89)),
136            CGLimit::new(Mass::kg(885.0), Length::m(0.89)),
137            CGLimit::new(Mass::kg(1111.0), Length::m(1.02)),
138            CGLimit::new(Mass::kg(1111.0), Length::m(1.20)),
139            CGLimit::new(Mass::kg(0.0), Length::m(1.20)),
140        ])
141        .build()
142        .expect("all required aircraft parameter should be configured");
143
144    let mut fms = FMS::new();
145
146    // read the ARINC database
147    let ed_nd = NavigationData::try_from_arinc424(ARINC_424_RECORDS)?;
148
149    fms.modify_nd(|nd| nd.append(ed_nd))?;
150
151    // decode a route from EDDH to EDHF with winds at 20 kt from 290° and
152    // cruising speed of 107 kt and an altitude of 2500 ft. Takeoff runway in
153    // EDDH is runway 33 and landing runway in EDHF is 20.
154    fms.decode("29020KT N0107 A0250 EDDH33 N2 N1 DCT EDHF20".to_string())?;
155
156    // Now we can enter some data into the flight planning to get a fuel planning
157    // and mass & balance calculation.
158    let mut builder = FlightPlanning::builder();
159
160    builder
161        .aircraft(aircraft)
162        .mass(vec![
163            // we're in the front
164            Mass::kg(80.0),
165            // and no mass on the other stations
166            Mass::kg(0.0),
167            Mass::kg(0.0),
168            Mass::kg(0.0),
169        ])
170        .policy(FuelPolicy::ManualFuel(diesel!(Volume::l(80.0))))
171        .taxi(diesel!(Volume::l(10.0)))
172        .reserve(Reserve::Manual(Duration::s(1800))) // 30 min
173        .perf(perf)
174        .takeoff_perf(takeoff_perf)
175        // we use the route's wind so no need to specify it here
176        .origin_rwycc(RunwayConditionCode::Six)
177        .origin_temperature(Temperature::c(20.0));
178
179    fms.set_flight_planning(builder)?;
180
181    println!("{}", fms.print(40));
182
183    Ok(())
184}
Source

pub fn registration(&self) -> &str

The unique registration code of the aircraft aka tail number.

Source

pub fn icao_type(&self) -> &str

The aircraft type designator according to ICAO Doc. 8643.

Source

pub fn stations(&self) -> &[Station]

The distances from a reference datum at which mass can be loaded e.g. the position of a seat.

Source

pub fn empty_mass(&self) -> &Mass

The mass of the empty aircraft taken from the last mass and balance report.

Source

pub fn empty_balance(&self) -> &Length

The center of gravity of the empty aircraft taken from the last mass and balance report.

Source

pub fn fuel_type(&self) -> &FuelType

The aircraft’s fuel type.

Source

pub fn tanks(&self) -> &[FuelTank]

The fuel tanks with their usable capacity.

Source

pub fn cg_envelope(&self) -> &CGEnvelope

The center of gravity envelope which must contains the CG at a mass for the aircraft to be balanced.

Source

pub fn notes(&self) -> Option<&str>

Notes regarding the aircraft e.g. when the empty mass and balance were determined.

Source

pub fn usable_fuel(&self) -> Option<Fuel>

Returns the usable fuel.

The usable fuel is the sum of all tank capacities with the aircraft’s fuel type or None if no tank is defined.

Source

pub fn is_balanced(&self, mb: &MassAndBalance) -> bool

Tests if the mass & balance is within the aircraft’s CGEnvelope.

Source

pub fn mb( &self, mass_on_ramp: &[Mass], mass_after_landing: &[Mass], fuel_on_ramp: &[Fuel], fuel_after_landing: &[Fuel], ) -> Result<MassAndBalance, Error>

Returns the mass & balance on ramp and after landing.

The mass vectors are mapped to the station arms by position e.g. the mass at index 0 is placed at the station arm at index 0. Thus, the length of the mass vectors must match the length of the station arms. The same goes for the fuel, however the fuel vectors are mapped to the tanks.

§Errors

If the length of any mass vector doesn’t match the length of the station arms or the length of any fuel vector doesn’t match the tanks length, an error is returned.

Source

pub fn mb_from_equally_distributed_fuel( &self, mass_on_ramp: &[Mass], mass_after_landing: &[Mass], on_ramp: &Fuel, after_landing: &Fuel, ) -> Result<MassAndBalance, Error>

Source

pub fn mb_from_const_mass_and_equally_distributed_fuel( &self, mass: &[Mass], on_ramp: &Fuel, after_landing: &Fuel, ) -> Result<MassAndBalance, Error>

Trait Implementations§

Source§

impl Clone for Aircraft

Source§

fn clone(&self) -> Aircraft

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Aircraft

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Aircraft

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for Aircraft

Source§

fn eq(&self, other: &Aircraft) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for Aircraft

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Eq for Aircraft

Source§

impl StructuralPartialEq for Aircraft

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<G1, G2> Within<G2> for G1
where G2: Contains<G1>,

Source§

fn is_within(&self, b: &G2) -> bool

Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,