diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 96fc487e2d2..3a82a8cb8e2 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -110,6 +110,7 @@ 1. [FAC] Move Speedscale computation to FAC - @lukecologne (luke) 1. [EFB] Added deboarding button to flyPad Payload - @frankkopp (Frank Kopp) 1. [EFB] Improved simbridge-client connection handling - @frankkopp (Frank Kopp) +1. [HYD] Tilting gear mechanism - @Crocket63 (crocket) ## 0.8.0 diff --git a/src/systems/a380_systems/src/hydraulic/mod.rs b/src/systems/a380_systems/src/hydraulic/mod.rs index db5d09066a2..b33475a3a76 100644 --- a/src/systems/a380_systems/src/hydraulic/mod.rs +++ b/src/systems/a380_systems/src/hydraulic/mod.rs @@ -43,7 +43,7 @@ use systems::{ ElectricPump, EngineDrivenPump, HydraulicCircuit, HydraulicCircuitController, HydraulicPressureSensors, PressureSwitch, PressureSwitchType, PumpController, Reservoir, }, - landing_gear::{GearSystemSensors, LandingGearControlInterfaceUnitSet}, + landing_gear::{GearSystemSensors, LandingGearControlInterfaceUnitSet, TiltingGear}, overhead::{AutoOffFaultPushButton, AutoOnFaultPushButton}, shared::{ interpolation, @@ -67,6 +67,59 @@ use flaps_computer::SlatFlapComplex; #[cfg(test)] use systems::hydraulic::PressureSwitchState; +struct A380TiltingGearsFactory {} +impl A380TiltingGearsFactory { + fn new_a380_body_gear(context: &mut InitContext, is_left: bool) -> TiltingGear { + let mut x_offset_meters = 2.85569; + let y_offset_meters = -5.04847; + let z_offset_meters = -0.235999; + + if is_left { + x_offset_meters *= -1.; + } + + TiltingGear::new( + context, + Length::new::(0.280065), + if is_left { 1 } else { 2 }, + Vector3::new(x_offset_meters, y_offset_meters, z_offset_meters), + Angle::new::(9.89), + ) + } + + fn new_a380_wing_gear(context: &mut InitContext, is_left: bool) -> TiltingGear { + let mut x_offset_meters = 6.18848; + let y_offset_meters = -4.86875; + let z_offset_meters = 2.6551; + + if is_left { + x_offset_meters *= -1.; + } + + TiltingGear::new( + context, + Length::new::(0.134608), + if is_left { 3 } else { 4 }, + Vector3::new(x_offset_meters, y_offset_meters, z_offset_meters), + Angle::new::(9.), + ) + } + + fn new_a380_tilt_assembly(context: &mut InitContext) -> A380TiltingGears { + let left_body_gear = Self::new_a380_body_gear(context, true); + let right_body_gear = Self::new_a380_body_gear(context, false); + let left_wing_gear = Self::new_a380_wing_gear(context, true); + let right_wing_gear = Self::new_a380_wing_gear(context, false); + + A380TiltingGears::new( + left_body_gear, + right_body_gear, + left_wing_gear, + right_wing_gear, + ) + } +} + struct A380HydraulicReservoirFactory {} impl A380HydraulicReservoirFactory { fn new_green_reservoir(context: &mut InitContext) -> Reservoir { @@ -1232,6 +1285,8 @@ pub(super) struct A380Hydraulic { trim_assembly: TrimmableHorizontalStabilizerAssembly, epump_auto_logic: A380ElectricPumpAutoLogic, + + tilting_gears: A380TiltingGears, } impl A380Hydraulic { const FLAP_FPPU_TO_SURFACE_ANGLE_BREAKPTS: [f64; 12] = [ @@ -1539,6 +1594,8 @@ impl A380Hydraulic { ), epump_auto_logic: A380ElectricPumpAutoLogic::default(), + + tilting_gears: A380TiltingGearsFactory::new_a380_tilt_assembly(context), } } @@ -1754,6 +1811,8 @@ impl A380Hydraulic { engine1: &impl Engine, engine2: &impl Engine, ) { + self.tilting_gears.update(context); + self.nose_steering.update( context, self.yellow_circuit.system_section(), @@ -2285,6 +2344,8 @@ impl SimulationElement for A380Hydraulic { self.trim_controller.accept(visitor); self.trim_assembly.accept(visitor); + self.tilting_gears.accept(visitor); + visitor.visit(self); } } @@ -5329,6 +5390,45 @@ impl SimulationElement for A380TrimInputController { } } +struct A380TiltingGears { + left_body_gear: TiltingGear, + right_body_gear: TiltingGear, + left_wing_gear: TiltingGear, + right_wing_gear: TiltingGear, +} +impl A380TiltingGears { + fn new( + left_body_gear: TiltingGear, + right_body_gear: TiltingGear, + left_wing_gear: TiltingGear, + right_wing_gear: TiltingGear, + ) -> Self { + Self { + left_body_gear, + right_body_gear, + left_wing_gear, + right_wing_gear, + } + } + + fn update(&mut self, context: &UpdateContext) { + self.left_body_gear.update(context); + self.right_body_gear.update(context); + self.left_wing_gear.update(context); + self.right_wing_gear.update(context); + } +} +impl SimulationElement for A380TiltingGears { + fn accept(&mut self, visitor: &mut T) { + self.left_body_gear.accept(visitor); + self.right_body_gear.accept(visitor); + self.left_wing_gear.accept(visitor); + self.right_wing_gear.accept(visitor); + + visitor.visit(self); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/systems/systems/src/landing_gear/mod.rs b/src/systems/systems/src/landing_gear/mod.rs index ac2818da4aa..0d91791d07a 100644 --- a/src/systems/systems/src/landing_gear/mod.rs +++ b/src/systems/systems/src/landing_gear/mod.rs @@ -4,8 +4,9 @@ use crate::{ failures::{Failure, FailureType}, shared::arinc429::{Arinc429Word, SignStatus}, shared::{ - ElectricalBusType, ElectricalBuses, GearWheel, LandingGearHandle, LgciuDoorPosition, - LgciuGearControl, LgciuGearExtension, LgciuId, LgciuInterface, LgciuWeightOnWheels, + height_over_ground, ElectricalBusType, ElectricalBuses, GearWheel, LandingGearHandle, + LgciuDoorPosition, LgciuGearControl, LgciuGearExtension, LgciuId, LgciuInterface, + LgciuWeightOnWheels, }, simulation::{ InitContext, Read, SimulationElement, SimulationElementVisitor, SimulatorReader, @@ -13,10 +14,13 @@ use crate::{ }, }; use uom::si::{ + angle::degree, f64::*, + length::meter, ratio::{percent, ratio}, }; +use nalgebra::Vector3; pub trait GearSystemSensors { fn is_wheel_id_up_and_locked(&self, wheel_id: GearWheel, lgciu_id: LgciuId) -> bool; fn is_wheel_id_down_and_locked(&self, wheel_id: GearWheel, lgciu_id: LgciuId) -> bool; @@ -24,6 +28,104 @@ pub trait GearSystemSensors { fn is_door_id_down_and_locked(&self, wheel_id: GearWheel, lgciu_id: LgciuId) -> bool; } +pub struct TiltingGear { + tilt_animation_id: VariableIdentifier, + compression_id: VariableIdentifier, + + tilt_height_from_low_to_up: Length, + contact_point_offset_from_datum_ref_meters: Vector3, + tilting_max_angle: Angle, + + current_compression: Ratio, + tilt_position: Ratio, +} +impl TiltingGear { + // Indicates the tilt angle already used with plane on ground standing still + const PLANE_PITCH_OFFSET_ON_GROUND_DEGREES: f64 = 0.8; + + const HEIGHT_TO_ACTIVATE_GROUND_COLLISION_METER: f64 = 0.0005; + + // Max speed at which tilt can move if gear is instantly in the air + const TILT_SPEED_WHEN_AIRBORN_RATIO_PER_SECOND: f64 = 0.5; + + pub fn new( + context: &mut InitContext, + tilt_height_from_low_to_up: Length, + contact_point_id: usize, + contact_point_offset_from_datum_ref_meters: Vector3, + tilting_max_angle: Angle, + ) -> Self { + Self { + tilt_animation_id: context + .get_identifier(format!("GEAR_{}_TILT_POSITION", contact_point_id)), + compression_id: context + .get_identifier(format!("GEAR ANIMATION POSITION:{}", contact_point_id)), + tilt_height_from_low_to_up, + contact_point_offset_from_datum_ref_meters, + tilting_max_angle, + + current_compression: Ratio::default(), + tilt_position: Ratio::default(), + } + } + + pub fn update(&mut self, context: &UpdateContext) { + let current_tire_height = + height_over_ground(context, self.contact_point_offset_from_datum_ref_meters); + + self.tilt_position = if current_tire_height.get::() + <= Self::HEIGHT_TO_ACTIVATE_GROUND_COLLISION_METER + { + let ground_tilt_raw = Ratio::new::( + (1. - (current_tire_height.abs() / self.tilt_height_from_low_to_up).get::()) + .min(1.) + .max(0.), + ); + + ground_tilt_raw.max(self.max_ground_tilt_from_plane_pitch(context)) + } else { + // Tilt for positive Gs else untilt for negative Gs + let delta_tilt = if context.acceleration_plane_reference_filtered_ms2_vector()[1] <= 0. + { + Ratio::new::( + Self::TILT_SPEED_WHEN_AIRBORN_RATIO_PER_SECOND * context.delta_as_secs_f64(), + ) + } else { + Ratio::new::( + -1. * Self::TILT_SPEED_WHEN_AIRBORN_RATIO_PER_SECOND + * context.delta_as_secs_f64(), + ) + }; + + (self.tilt_position + delta_tilt) + .min(Ratio::new::(1.)) + .max(Ratio::new::(0.)) + }; + } + + fn max_ground_tilt_from_plane_pitch(&self, context: &UpdateContext) -> Ratio { + let plane_pitch = -context.pitch(); + + let pitch_offset = Angle::new::(-Self::PLANE_PITCH_OFFSET_ON_GROUND_DEGREES); + + let offset_pitch = plane_pitch - pitch_offset; + + offset_pitch + .max(Angle::new::(0.)) + .min(self.tilting_max_angle) + / self.tilting_max_angle + } +} +impl SimulationElement for TiltingGear { + fn write(&self, writer: &mut SimulatorWriter) { + writer.write(&self.tilt_animation_id, self.tilt_position.get::()); + } + + fn read(&mut self, reader: &mut SimulatorReader) { + self.current_compression = reader.read(&self.compression_id); + } +} + /// Represents a landing gear on Airbus aircraft. /// Note that this type somewhat hides the gear's position. /// The real aircraft also can only check whether or not the gear is up and @@ -1551,6 +1653,130 @@ mod tests { assert!(test_bed.query(|a| a.lgcius.lgciu1().status) == LgciuStatus::Ok); } + #[test] + fn tilting_gear_does_not_tilt_when_no_pitch_on_ground() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(test_tilting_gear_left)) + .with_update_before_power_distribution(|el, context, _| { + el.update(context); + }); + + test_bed.write_by_name("PLANE PITCH DEGREES", 0.); + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(0.5)); + + test_bed.run(); + + let tilt_position = Ratio::new::(test_bed.read_by_name("GEAR_1_TILT_POSITION")); + assert!(tilt_position.get::() <= 0.1); + } + + #[test] + fn tilting_gear_tilts_when_up_pitch_on_ground() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(test_tilting_gear_left)) + .with_update_before_power_distribution(|el, context, _| { + el.update(context); + }); + + test_bed.write_by_name("PLANE PITCH DEGREES", -5.); + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(0.5)); + + test_bed.run(); + + let tilt_position = Ratio::new::(test_bed.read_by_name("GEAR_1_TILT_POSITION")); + assert!(tilt_position.get::() >= 0.2 && tilt_position.get::() <= 0.8); + } + + #[test] + fn tilting_gear_tilts_at_max_angle_when_high_up_pitch_on_ground() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(test_tilting_gear_left)) + .with_update_before_power_distribution(|el, context, _| { + el.update(context); + }); + + test_bed.write_by_name("PLANE PITCH DEGREES", -15.); + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(0.5)); + + test_bed.run(); + + let tilt_position = Ratio::new::(test_bed.read_by_name("GEAR_1_TILT_POSITION")); + assert!(tilt_position.get::() >= 0.99); + } + + #[test] + fn tilting_gear_tilts_at_max_angle_when_not_touching_ground() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(test_tilting_gear_left)) + .with_update_before_power_distribution(|el, context, _| { + el.update(context); + }); + + test_bed.write_by_name("PLANE PITCH DEGREES", -15.); + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(20.)); + + // Give time to tilt mechanism to go down + test_bed.run_with_delta(Duration::from_secs(2)); + + let tilt_position = Ratio::new::(test_bed.read_by_name("GEAR_1_TILT_POSITION")); + assert!(tilt_position.get::() >= 0.99); + } + + #[test] + fn tilting_gear_untilts_when_plane_inverted() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(test_tilting_gear_left)) + .with_update_before_power_distribution(|el, context, _| { + el.update(context); + }); + + test_bed.write_by_name("PLANE BANK DEGREES", -180.); + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(20.)); + + // Give time to tilt mechanism to go down + test_bed.run_with_delta(Duration::from_secs(2)); + + let tilt_position = Ratio::new::(test_bed.read_by_name("GEAR_1_TILT_POSITION")); + assert!(tilt_position.get::() <= 0.01); + } + + #[test] + fn tilting_gear_at_max_tilt_when_not_compressed_and_just_touching_ground() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(test_tilting_gear_left)) + .with_update_before_power_distribution(|el, context, _| { + el.update(context); + }); + + test_bed.write_by_name("PLANE PITCH DEGREES", 0.); + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(2.)); + + test_bed.run(); + + let tilt_position = Ratio::new::(test_bed.read_by_name("GEAR_1_TILT_POSITION")); + assert!(tilt_position.get::() >= 0.99); + } + + #[test] + fn tilting_gear_start_tilting_when_touching_ground() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(test_tilting_gear_left)) + .with_update_before_power_distribution(|el, context, _| { + el.update(context); + }); + + test_bed.write_by_name("PLANE PITCH DEGREES", 0.); + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(1.9)); + + test_bed.run(); + + let tilt_position = Ratio::new::(test_bed.read_by_name("GEAR_1_TILT_POSITION")); + assert!(tilt_position.get::() < 1. && tilt_position.get::() > 0.); + } + + fn test_tilting_gear_left(context: &mut InitContext) -> TiltingGear { + TiltingGear::new( + context, + Length::new::(0.28), + 1, + Vector3::new(-5., -2., -5.), + Angle::new::(9.), + ) + } + fn run_test_bed_on_with_compression( left: Ratio, center: Ratio, diff --git a/src/systems/systems/src/shared/mod.rs b/src/systems/systems/src/shared/mod.rs index 5c3df93c658..6c0376550a9 100644 --- a/src/systems/systems/src/shared/mod.rs +++ b/src/systems/systems/src/shared/mod.rs @@ -4,6 +4,7 @@ use crate::{ simulation::UpdateContext, }; +use nalgebra::Vector3; use num_derive::FromPrimitive; use std::{cell::Ref, fmt::Display, time::Duration}; use uom::si::{ @@ -566,6 +567,21 @@ pub fn to_bool(value: f64) -> bool { (value - 1.).abs() < f64::EPSILON } +/// Returns the height over the ground of any point of the plane considering its current attitude +/// Offset parameter is the position of the point in plane reference with respect to datum reference point +/// X positive from left to right +/// Y positive from down to up +/// Z positive from aft to front +pub fn height_over_ground( + context: &UpdateContext, + offset_from_plane_reference: Vector3, +) -> Length { + let offset_including_plane_rotation = context.attitude().pitch_rotation_transform() + * (context.attitude().bank_rotation_transform().inverse() * offset_from_plane_reference); + + Length::new::(offset_including_plane_rotation[1]) + context.plane_height_over_ground() +} + pub struct InternationalStandardAtmosphere; impl InternationalStandardAtmosphere { const TEMPERATURE_LAPSE_RATE: f64 = 0.0065; @@ -1070,6 +1086,7 @@ mod delayed_pulse_true_logic_gate_tests { assert!(!test_bed.query(|a| a.gate_output())); } } + #[cfg(test)] mod interpolation_tests { use super::*; @@ -1255,3 +1272,195 @@ mod average_tests { assert_eq!(average, Pressure::new::(200.)); } } + +#[cfg(test)] +mod height_over_ground { + use super::*; + + use crate::simulation::{ + test::{ElementCtorFn, SimulationTestBed, WriteByName}, + SimulationElement, + }; + use uom::si::angle::degree; + + use ntest::assert_about_eq; + + #[derive(Default)] + struct DummyObject {} + impl DummyObject {} + impl SimulationElement for DummyObject {} + + #[test] + fn at_zero_altitude_zero_reference_default_attitude() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(|_| DummyObject::default())) + .with_update_after_power_distribution(|_, context| { + assert!(height_over_ground(context, Vector3::new(0., 0., 0.)).get::() == 0.); + assert!( + height_over_ground(context, Vector3::new(0., 10., 0.)).get::() == 10. + ); + assert!( + height_over_ground(context, Vector3::new(0., -10., 0.)).get::() == -10. + ); + + assert!( + height_over_ground(context, Vector3::new(-10., 0., 0.)).get::() == 0. + ); + assert!( + height_over_ground(context, Vector3::new(10., -10., 0.)).get::() == -10. + ); + + assert!( + height_over_ground(context, Vector3::new(-10., 0., 10.)).get::() == 0. + ); + assert!( + height_over_ground(context, Vector3::new(10., -10., -10.)).get::() + == -10. + ); + }); + + test_bed.run_with_delta(Duration::from_secs(0)); + } + + #[test] + fn at_10_altitude_with_default_attitude() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(|_| DummyObject::default())) + .with_update_after_power_distribution(|_, context| { + assert_about_eq!( + height_over_ground(context, Vector3::new(0., 0., 0.)).get::(), + 10. + ); + assert_about_eq!( + height_over_ground(context, Vector3::new(0., 10., 0.)).get::(), + 20. + ); + assert_about_eq!( + height_over_ground(context, Vector3::new(0., -10., 0.)).get::(), + 0. + ); + }); + + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(10.)); + + test_bed.run_with_delta(Duration::from_secs(0)); + } + + #[test] + fn at_10_altitude_with_45_right_bank_attitude() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(|_| DummyObject::default())) + .with_update_after_power_distribution(|_, context| { + assert_about_eq!( + height_over_ground(context, Vector3::new(0., 0., 0.)).get::(), + 10. + ); + assert!(height_over_ground(context, Vector3::new(5., 0., 0.)).get::() < 8.); + assert!( + height_over_ground(context, Vector3::new(-5., 0., 0.)).get::() > 12. + ); + assert_about_eq!( + height_over_ground(context, Vector3::new(0., 0., -10.)).get::(), + 10. + ); + assert_about_eq!( + height_over_ground(context, Vector3::new(0., 0., 10.)).get::(), + 10. + ); + assert!(height_over_ground(context, Vector3::new(0., 5., 0.)).get::() < 15.); + }); + + // MSFS bank right is negative angle + test_bed.write_by_name("PLANE BANK DEGREES", Angle::new::(-45.)); + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(10.)); + + test_bed.run_with_delta(Duration::from_secs(0)); + } + + #[test] + fn at_10_altitude_with_45_left_bank_attitude() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(|_| DummyObject::default())) + .with_update_after_power_distribution(|_, context| { + assert_about_eq!( + height_over_ground(context, Vector3::new(0., 0., 0.)).get::(), + 10. + ); + assert!(height_over_ground(context, Vector3::new(5., 0., 0.)).get::() > 12.); + assert!(height_over_ground(context, Vector3::new(-5., 0., 0.)).get::() < 8.); + assert_about_eq!( + height_over_ground(context, Vector3::new(0., 0., -10.)).get::(), + 10. + ); + assert_about_eq!( + height_over_ground(context, Vector3::new(0., 0., 10.)).get::(), + 10. + ); + assert!(height_over_ground(context, Vector3::new(0., 5., 0.)).get::() < 15.); + }); + + // MSFS bank right is negative angle + test_bed.write_by_name("PLANE BANK DEGREES", Angle::new::(45.)); + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(10.)); + + test_bed.run_with_delta(Duration::from_secs(0)); + } + + #[test] + fn at_10_altitude_with_45_up_pitch_attitude() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(|_| DummyObject::default())) + .with_update_after_power_distribution(|_, context| { + assert_about_eq!( + height_over_ground(context, Vector3::new(0., 0., 0.)).get::(), + 10. + ); + assert_about_eq!( + height_over_ground(context, Vector3::new(5., 0., 0.)).get::(), + 10. + ); + assert_about_eq!( + height_over_ground(context, Vector3::new(-5., 0., 0.)).get::(), + 10. + ); + assert!( + height_over_ground(context, Vector3::new(0., 0., -10.)).get::() < 8. + ); + assert!( + height_over_ground(context, Vector3::new(0., 0., 10.)).get::() > 12. + ); + assert!(height_over_ground(context, Vector3::new(0., 5., 0.)).get::() < 15.); + }); + + // MSFS bank right is negative angle + test_bed.write_by_name("PLANE PITCH DEGREES", Angle::new::(-45.)); + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(10.)); + + test_bed.run_with_delta(Duration::from_secs(0)); + } + + #[test] + fn at_10_altitude_with_45_down_pitch_attitude() { + let mut test_bed = SimulationTestBed::from(ElementCtorFn(|_| DummyObject::default())) + .with_update_after_power_distribution(|_, context| { + assert_about_eq!( + height_over_ground(context, Vector3::new(0., 0., 0.)).get::(), + 10. + ); + assert_about_eq!( + height_over_ground(context, Vector3::new(5., 0., 0.)).get::(), + 10. + ); + assert_about_eq!( + height_over_ground(context, Vector3::new(-5., 0., 0.)).get::(), + 10. + ); + assert!( + height_over_ground(context, Vector3::new(0., 0., -10.)).get::() > 12. + ); + assert!(height_over_ground(context, Vector3::new(0., 0., 10.)).get::() < 8.); + assert!(height_over_ground(context, Vector3::new(0., 5., 0.)).get::() < 15.); + }); + + // MSFS bank right is negative angle + test_bed.write_by_name("PLANE PITCH DEGREES", Angle::new::(45.)); + test_bed.write_by_name("PLANE ALT ABOVE GROUND", Length::new::(10.)); + + test_bed.run_with_delta(Duration::from_secs(0)); + } +} diff --git a/src/systems/systems/src/simulation/update_context.rs b/src/systems/systems/src/simulation/update_context.rs index fb44d727687..9fabc753a96 100644 --- a/src/systems/systems/src/simulation/update_context.rs +++ b/src/systems/systems/src/simulation/update_context.rs @@ -159,6 +159,7 @@ pub struct UpdateContext { plane_bank_id: VariableIdentifier, plane_true_heading_id: VariableIdentifier, mach_number_id: VariableIdentifier, + plane_height_id: VariableIdentifier, delta: Delta, simulation_time: f64, @@ -181,6 +182,7 @@ pub struct UpdateContext { mach_number: MachNumber, air_density: MassDensity, true_heading: Angle, + plane_height_over_ground: Length, } impl UpdateContext { pub(crate) const IS_READY_KEY: &'static str = "IS_READY"; @@ -205,6 +207,7 @@ impl UpdateContext { pub(crate) const LOCAL_LATERAL_SPEED_KEY: &'static str = "VELOCITY BODY X"; pub(crate) const LOCAL_LONGITUDINAL_SPEED_KEY: &'static str = "VELOCITY BODY Z"; pub(crate) const LOCAL_VERTICAL_SPEED_KEY: &'static str = "VELOCITY BODY Y"; + pub(crate) const ALT_ABOVE_GROUND_KEY: &'static str = "PLANE ALT ABOVE GROUND"; // Plane accelerations can become crazy with msfs collision handling. // Having such filtering limits high frequencies transients in accelerations used for physics @@ -256,6 +259,7 @@ impl UpdateContext { plane_bank_id: context.get_identifier(Self::PLANE_BANK_KEY.to_owned()), plane_true_heading_id: context.get_identifier(Self::TRUE_HEADING_KEY.to_owned()), mach_number_id: context.get_identifier(Self::MACH_NUMBER_KEY.to_owned()), + plane_height_id: context.get_identifier(Self::ALT_ABOVE_GROUND_KEY.to_owned()), delta: delta.into(), simulation_time, @@ -296,6 +300,7 @@ impl UpdateContext { mach_number, air_density: MassDensity::new::(1.22), true_heading: Default::default(), + plane_height_over_ground: Length::default(), } } @@ -323,6 +328,7 @@ impl UpdateContext { plane_bank_id: context.get_identifier("PLANE BANK DEGREES".to_owned()), plane_true_heading_id: context.get_identifier("PLANE HEADING DEGREES TRUE".to_owned()), mach_number_id: context.get_identifier("AIRSPEED MACH".to_owned()), + plane_height_id: context.get_identifier("PLANE ALT ABOVE GROUND".to_owned()), delta: Default::default(), simulation_time: Default::default(), @@ -361,6 +367,7 @@ impl UpdateContext { mach_number: Default::default(), air_density: MassDensity::new::(1.22), true_heading: Default::default(), + plane_height_over_ground: Length::default(), } } @@ -414,6 +421,8 @@ impl UpdateContext { self.true_heading = reader.read(&self.plane_true_heading_id); + self.plane_height_over_ground = reader.read(&self.plane_height_id); + self.update_relative_wind(); self.update_local_acceleration_plane_reference(delta); @@ -587,6 +596,10 @@ impl UpdateContext { pub fn true_heading_rotation_transform(&self) -> Rotation3 { Rotation3::from_axis_angle(&Vector3::y_axis(), self.true_heading.get::()) } + + pub fn plane_height_over_ground(&self) -> Length { + self.plane_height_over_ground + } } impl DeltaContext for UpdateContext {