11use crate :: air_conditioning:: AdirsToAirCondInterface ;
2+ use crate :: payload:: BoardingRate ;
23use crate :: shared:: InternationalStandardAtmosphere ;
34use crate :: simulation:: { InitContext , VariableIdentifier } ;
45use crate :: {
@@ -249,6 +250,12 @@ struct AdirsSimulatorData {
249250 baro_correction_2_id : VariableIdentifier ,
250251 /// Baro correction for fo's side in hPa from the FCU
251252 baro_correction_2 : Arinc429Word < f64 > ,
253+
254+ is_boarding_started_by_user_id : VariableIdentifier ,
255+ boarding_rate_id : VariableIdentifier ,
256+
257+ is_boarding_started_by_user : bool ,
258+ boarding_rate : BoardingRate ,
252259}
253260impl AdirsSimulatorData {
254261 const MACH : & ' static str = "AIRSPEED MACH" ;
@@ -270,6 +277,8 @@ impl AdirsSimulatorData {
270277 const ANGLE_OF_ATTACK : & ' static str = "INCIDENCE ALPHA" ;
271278 const BARO_CORRECTION_1_HPA : & ' static str = "FCU_LEFT_EIS_BARO_HPA" ;
272279 const BARO_CORRECTION_2_HPA : & ' static str = "FCU_RIGHT_EIS_BARO_HPA" ;
280+ const BOARDING_STARTED_BY_USR : & ' static str = "BOARDING_STARTED_BY_USR" ;
281+ const BOARDING_RATE : & ' static str = "BOARDING_RATE" ;
273282
274283 fn new ( context : & mut InitContext ) -> Self {
275284 Self {
@@ -330,6 +339,13 @@ impl AdirsSimulatorData {
330339
331340 baro_correction_2_id : context. get_identifier ( Self :: BARO_CORRECTION_2_HPA . to_owned ( ) ) ,
332341 baro_correction_2 : Arinc429Word :: new ( 1013. , SignStatus :: FailureWarning ) ,
342+
343+ is_boarding_started_by_user_id : context
344+ . get_identifier ( Self :: BOARDING_STARTED_BY_USR . to_owned ( ) ) ,
345+ boarding_rate_id : context. get_identifier ( Self :: BOARDING_RATE . to_owned ( ) ) ,
346+
347+ is_boarding_started_by_user : false ,
348+ boarding_rate : BoardingRate :: Instant ,
333349 }
334350 }
335351}
@@ -359,6 +375,8 @@ impl SimulationElement for AdirsSimulatorData {
359375 self . angle_of_attack = reader. read ( & self . angle_of_attack_id ) ;
360376 self . baro_correction_1 = reader. read_arinc429 ( & self . baro_correction_1_id ) ;
361377 self . baro_correction_2 = reader. read_arinc429 ( & self . baro_correction_2_id ) ;
378+ self . is_boarding_started_by_user = reader. read ( & self . is_boarding_started_by_user_id ) ;
379+ self . boarding_rate = reader. read ( & self . boarding_rate_id ) ;
362380 }
363381}
364382
@@ -1470,6 +1488,7 @@ struct InertialReference {
14701488 extreme_latitude : bool ,
14711489 body_velocity_filter : LowPassFilter < Vector2 < f64 > > ,
14721490 excess_motion : bool ,
1491+ excess_motion_inhibit_time : Option < Duration > ,
14731492 quick_realign_remaining_available_time : Duration ,
14741493 alignment_failed : bool ,
14751494
@@ -1508,6 +1527,7 @@ struct InertialReference {
15081527 fault_warn_discrete : AdirsDiscreteOutput < bool > ,
15091528}
15101529impl InertialReference {
1530+ const EXCESS_MOTION_INHIBIT_TIME : Duration = Duration :: from_secs ( 2 ) ;
15111531 const FAST_ALIGNMENT_TIME_IN_SECS : f64 = 90. ;
15121532 const IR_FAULT_FLASH_DURATION : Duration = Duration :: from_millis ( 50 ) ;
15131533 const ATTITUDE_INITIALISATION_DURATION : Duration = Duration :: from_secs ( 28 ) ;
@@ -1564,6 +1584,7 @@ impl InertialReference {
15641584 extreme_latitude : false ,
15651585 body_velocity_filter : LowPassFilter :: new ( Self :: ALIGNMENT_VELOCITY_TIME_CONSTANT ) ,
15661586 excess_motion : false ,
1587+ excess_motion_inhibit_time : None ,
15671588 quick_realign_remaining_available_time : Duration :: default ( ) ,
15681589 alignment_failed : false ,
15691590
@@ -1684,6 +1705,15 @@ impl InertialReference {
16841705 simulator_data. latitude . abs ( ) . get :: < degree > ( ) <= Self :: MAX_LATITUDE_FOR_ALIGNMENT
16851706 }
16861707
1708+ fn is_instant_or_fast_boarding_in_progress ( simulator_data : AdirsSimulatorData ) -> bool {
1709+ match simulator_data. boarding_rate {
1710+ BoardingRate :: Instant | BoardingRate :: Fast => {
1711+ simulator_data. is_boarding_started_by_user
1712+ }
1713+ _ => false ,
1714+ }
1715+ }
1716+
16871717 fn update_remaining_align_duration (
16881718 & mut self ,
16891719 context : & UpdateContext ,
@@ -1703,6 +1733,14 @@ impl InertialReference {
17031733 . saturating_sub ( context. delta ( ) ) ;
17041734 }
17051735
1736+ // work around instant/fast boarding upsetting the body velocity and interrupting IR alignment
1737+ if InertialReference :: is_instant_or_fast_boarding_in_progress ( simulator_data) {
1738+ self . excess_motion_inhibit_time = Some ( InertialReference :: EXCESS_MOTION_INHIBIT_TIME ) ;
1739+ } else if let Some ( excess_motion_inhibit_time) = self . excess_motion_inhibit_time {
1740+ self . excess_motion_inhibit_time =
1741+ excess_motion_inhibit_time. checked_sub ( context. delta ( ) ) ;
1742+ }
1743+
17061744 // If the align time setting has been changed to instant during alignment,
17071745 // then set remaining time to 0. This allows to implement a "Instant Align" button in the EFB
17081746 // for users who want to align the ADIRS instantly but do not want to change the default
@@ -1713,6 +1751,7 @@ impl InertialReference {
17131751 // If we exceeded the max alignment velocity, the alignment is restarted
17141752 if self . is_aligning ( )
17151753 && self . body_velocity_filter . output ( ) . max ( ) > Self :: MAX_ALIGNMENT_VELOCITY_FPS
1754+ && self . excess_motion_inhibit_time . is_none ( )
17161755 {
17171756 self . remaining_align_duration = None ;
17181757 self . excess_motion = true ;
@@ -2593,6 +2632,18 @@ mod tests {
25932632 self
25942633 }
25952634
2635+ fn boarding_rate_of ( mut self , rate : BoardingRate ) -> Self {
2636+ self . write_by_name ( AdirsSimulatorData :: BOARDING_RATE , rate) ;
2637+
2638+ self
2639+ }
2640+
2641+ fn boarding_started_of ( mut self , started : bool ) -> Self {
2642+ self . write_by_name ( AdirsSimulatorData :: BOARDING_STARTED_BY_USR , started) ;
2643+
2644+ self
2645+ }
2646+
25962647 fn ir_fault_light_illuminated ( & mut self , number : usize ) -> bool {
25972648 self . read_by_name ( & OnOffFaultPushButton :: has_fault_id ( & format ! (
25982649 "ADIRS_IR_{}" ,
@@ -3356,6 +3407,72 @@ mod tests {
33563407 ) ;
33573408 }
33583409
3410+ #[ rstest]
3411+ fn adirs_detects_excess_motion_during_alignment_with_instant_boarding_selected ( ) {
3412+ let mut test_bed = all_adirus_unaligned_test_bed_with ( )
3413+ . boarding_rate_of ( BoardingRate :: Instant )
3414+ . body_lateral_velocity_of ( Velocity :: new :: < foot_per_second > ( 0.1 ) )
3415+ . ir_mode_selector_set_to ( 1 , InertialReferenceMode :: Navigation ) ;
3416+ test_bed. run ( ) ;
3417+ test_bed. run ( ) ;
3418+
3419+ let maint_word_flags = IrMaintFlags :: from_bits ( test_bed. maint_word ( 1 ) . value ( ) ) ;
3420+ assert_eq ! (
3421+ maint_word_flags. unwrap( ) & IrMaintFlags :: EXCESS_MOTION_ERROR ,
3422+ IrMaintFlags :: EXCESS_MOTION_ERROR
3423+ ) ;
3424+ }
3425+
3426+ #[ rstest]
3427+ fn adirs_detects_excess_motion_during_alignment_with_fast_boarding_selected ( ) {
3428+ let mut test_bed = all_adirus_unaligned_test_bed_with ( )
3429+ . boarding_rate_of ( BoardingRate :: Fast )
3430+ . body_lateral_velocity_of ( Velocity :: new :: < foot_per_second > ( 0.1 ) )
3431+ . ir_mode_selector_set_to ( 1 , InertialReferenceMode :: Navigation ) ;
3432+ test_bed. run ( ) ;
3433+ test_bed. run ( ) ;
3434+
3435+ let maint_word_flags = IrMaintFlags :: from_bits ( test_bed. maint_word ( 1 ) . value ( ) ) ;
3436+ assert_eq ! (
3437+ maint_word_flags. unwrap( ) & IrMaintFlags :: EXCESS_MOTION_ERROR ,
3438+ IrMaintFlags :: EXCESS_MOTION_ERROR
3439+ ) ;
3440+ }
3441+
3442+ #[ rstest]
3443+ fn adirs_does_not_detect_excess_motion_during_instant_boarding ( ) {
3444+ let mut test_bed = all_adirus_unaligned_test_bed_with ( )
3445+ . boarding_rate_of ( BoardingRate :: Instant )
3446+ . boarding_started_of ( true )
3447+ . body_lateral_velocity_of ( Velocity :: new :: < foot_per_second > ( 0.1 ) )
3448+ . ir_mode_selector_set_to ( 1 , InertialReferenceMode :: Navigation ) ;
3449+ test_bed. run ( ) ;
3450+ test_bed. run ( ) ;
3451+
3452+ let maint_word_flags = IrMaintFlags :: from_bits ( test_bed. maint_word ( 1 ) . value ( ) ) ;
3453+ assert_eq ! (
3454+ maint_word_flags. unwrap( ) & IrMaintFlags :: EXCESS_MOTION_ERROR ,
3455+ IrMaintFlags :: empty( )
3456+ ) ;
3457+ }
3458+
3459+ #[ rstest]
3460+ fn adirs_does_not_detect_excess_motion_during_fast_boarding ( ) {
3461+ let mut test_bed = all_adirus_unaligned_test_bed_with ( )
3462+ . boarding_rate_of ( BoardingRate :: Fast )
3463+ . boarding_started_of ( true )
3464+ . body_lateral_velocity_of ( Velocity :: new :: < foot_per_second > ( 0.1 ) )
3465+ . ir_mode_selector_set_to ( 1 , InertialReferenceMode :: Navigation ) ;
3466+ test_bed. run ( ) ;
3467+ test_bed. run ( ) ;
3468+
3469+ let maint_word_flags = IrMaintFlags :: from_bits ( test_bed. maint_word ( 1 ) . value ( ) ) ;
3470+ assert_eq ! (
3471+ maint_word_flags. unwrap( ) & IrMaintFlags :: EXCESS_MOTION_ERROR ,
3472+ IrMaintFlags :: empty( )
3473+ ) ;
3474+ }
3475+
33593476 #[ rstest]
33603477 #[ case( Angle :: new:: <degree>( 80. ) ) ]
33613478 #[ case( Angle :: new:: <degree>( -80. ) ) ]
0 commit comments