From 7db9163ddf3f81e0788c48b5f44bfb54c4a20200 Mon Sep 17 00:00:00 2001 From: Michael Corcoran Date: Sun, 4 Dec 2022 02:23:15 +1300 Subject: [PATCH 01/15] feat(adiru): polar heading/track --- Cargo.lock | 1 + docs/a320-simvars.md | 10 +- src/systems/systems/Cargo.toml | 1 + src/systems/systems/src/navigation/adirs.rs | 205 ++++++++++++++++++-- 4 files changed, 199 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 793f28bc77c..9f683563b4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -944,6 +944,7 @@ dependencies = [ name = "systems" version = "0.1.0" dependencies = [ + "bitflags", "bounded-vec-deque", "fxhash", "nalgebra", diff --git a/docs/a320-simvars.md b/docs/a320-simvars.md index 4f6e4920011..00044bb5282 100644 --- a/docs/a320-simvars.md +++ b/docs/a320-simvars.md @@ -1431,6 +1431,7 @@ In the variables below, {number} should be replaced with one item in the set: { - 0 is CAPT, 1 is NORM, 2 is F/O - A32NX_ADIRS_ADIRU_{number}_STATE + - TODO delete - Enum - The Inertial Reference alignment state. Description | Value @@ -1440,6 +1441,7 @@ In the variables below, {number} should be replaced with one item in the set: { Aligned | 2 - A32NX_ADIRS_REMAINING_IR_ALIGNMENT_TIME + - TODO delete - Seconds - The remaining alignment duration. Zero seconds when the system is aligned or the system is not aligning. @@ -1493,7 +1495,7 @@ In the variables below, {number} should be replaced with one item in the set: { - A32NX_ADIRS_IR_{number}_HEADING - Arinc429Word - - The inertial heading of the aircraft. + - The magnetic heading of the aircraft (true in polar region). - A32NX_ADIRS_IR_{number}_TRUE_HEADING - Arinc429Word @@ -1501,7 +1503,7 @@ In the variables below, {number} should be replaced with one item in the set: { - A32NX_ADIRS_IR_{number}_TRACK - Arinc429Word - - The inertial track of the aircraft. + - The magnetic track of the aircraft (true in polar region). - A32NX_ADIRS_IR_{number}_TRUE_TRACK - Arinc429Word @@ -1587,6 +1589,10 @@ In the variables below, {number} should be replaced with one item in the set: { - Arinc429Word - The roll rate (φ^dot) of the aircraft +- A32NX_ADIRS_IR_{number}_MAINT_WORD + - Arinc429Word + - TODO flag table + - A32NX_ADIRS_USES_GPS_AS_PRIMARY - Bool - Whether or not the GPS is used as the primary means of navigation/position determination. diff --git a/src/systems/systems/Cargo.toml b/src/systems/systems/Cargo.toml index 83ee08a0c1f..f85ca38a212 100644 --- a/src/systems/systems/Cargo.toml +++ b/src/systems/systems/Cargo.toml @@ -14,6 +14,7 @@ num-traits = "0.2.14" nalgebra = "0.25.0" bounded-vec-deque = "0.1.1" fxhash = "0.2.1" +bitflags = "1.3.2" [dev-dependencies] rstest = "0.10.0" diff --git a/src/systems/systems/src/navigation/adirs.rs b/src/systems/systems/src/navigation/adirs.rs index e578174f4d3..7d96b3d8195 100644 --- a/src/systems/systems/src/navigation/adirs.rs +++ b/src/systems/systems/src/navigation/adirs.rs @@ -12,6 +12,7 @@ use crate::{ }, }; use nalgebra::{Rotation2, Vector2}; +use bitflags::bitflags; use std::{fmt::Display, time::Duration}; use uom::si::acceleration::meter_per_second_squared; use uom::si::{ @@ -931,6 +932,35 @@ impl From for AlignTime { } } +bitflags! { + #[derive(Default)] + struct IrMaintFlags: u32 { + const AlignmentNotReady = 0b0000000000000000001; + const RevAttMode = 0b0000000000000000010; + const NavMode = 0b0000000000000000100; + const ValidSetHeading = 0b0000000000000001000; + const AttitudeInvalid = 0b0000000000000010000; + const DcFail = 0b0000000000000100000; + const OnDc = 0b0000000000001000000; + const AdrFault = 0b0000000000010000000; + const IrFault = 0b0000000000100000000; + const DcFailOnDc = 0b0000000001000000000; + const AlignFault = 0b0000000010000000000; + const NoIrsInitial = 0b0000000100000000000; + const ExcessMotionError = 0b0000001000000000000; + const AdrIrFault = 0b0000010000000000000; + const ExtremeLatitude = 0b0000100000000000000; + const Align710Minutes = 0b0111000000000000000; + const Align6Minutes = 0b0110000000000000000; + const Align5Minutes = 0b0101000000000000000; + const Align4Minutes = 0b0100000000000000000; + const Align3Minutes = 0b0011000000000000000; + const Align2Minutes = 0b0010000000000000000; + const Align1Minutes = 0b0001000000000000000; + const ComputedLatitudeMiscompare = 0b1000000000000000000; + } +} + struct InertialReference { number: usize, is_on: bool, @@ -940,6 +970,7 @@ struct InertialReference { ir_fault_flash_duration: Option, remaining_attitude_initialisation_duration: Option, wind_velocity: LowPassFilter>, + extreme_latitude: bool, pitch: AdirsData, roll: AdirsData, @@ -970,6 +1001,7 @@ struct InertialReference { wind_direction_bnr: AdirsData, latitude: AdirsData, longitude: AdirsData, + maint_word: AdirsData, } impl InertialReference { const FAST_ALIGNMENT_TIME_IN_SECS: f64 = 90.; @@ -1000,6 +1032,7 @@ impl InertialReference { const WIND_SPEED_BNR: &'static str = "WIND_SPEED_BNR"; const LATITUDE: &'static str = "LATITUDE"; const LONGITUDE: &'static str = "LONGITUDE"; + const MAINT_WORD: &'static str = "MAINT_WORD"; const MINIMUM_TRUE_AIRSPEED_FOR_WIND_DETERMINATION_KNOTS: f64 = 100.; const MINIMUM_GROUND_SPEED_FOR_TRACK_KNOTS: f64 = 50.; @@ -1016,6 +1049,7 @@ impl InertialReference { // Start fully initialised. remaining_attitude_initialisation_duration: Some(Duration::from_secs(0)), wind_velocity: LowPassFilter::new(Self::WIND_VELOCITY_TIME_CONSTANT), + extreme_latitude: false, pitch: AdirsData::new_ir(context, number, Self::PITCH), roll: AdirsData::new_ir(context, number, Self::ROLL), @@ -1042,6 +1076,8 @@ impl InertialReference { wind_speed_bnr: AdirsData::new_ir(context, number, Self::WIND_SPEED_BNR), latitude: AdirsData::new_ir(context, number, Self::LATITUDE), longitude: AdirsData::new_ir(context, number, Self::LONGITUDE), + /// label 270 + maint_word: AdirsData::new_ir(context, number, Self::MAINT_WORD), } } @@ -1064,9 +1100,11 @@ impl InertialReference { simulator_data, ); + self.update_latitude(simulator_data); self.update_attitude_values(context, simulator_data); self.update_heading_values(overhead, simulator_data); self.update_non_attitude_values(context, true_airspeed_source, overhead, simulator_data); + self.update_maint_word(context, overhead); } fn update_fault_flash_duration( @@ -1118,6 +1156,19 @@ impl InertialReference { }; } + fn update_latitude(&mut self, simulator_data: AdirsSimulatorData) { + let latitude = simulator_data.latitude.get::(); + let longitude = simulator_data.longitude.get::(); + + let hysteresis_sign = if self.extreme_latitude { 1. } else { -1. }; + + self.extreme_latitude = !((-60. + hysteresis_sign * 0.5) <= latitude + && (latitude <= (73. - hysteresis_sign * 0.5) + || (latitude <= (82. - hysteresis_sign * 0.5) + && (longitude <= (-120. - hysteresis_sign * 2.5) + || longitude >= (-90. + hysteresis_sign * 2.5))))) + } + fn update_attitude_values( &mut self, context: &UpdateContext, @@ -1171,31 +1222,43 @@ impl InertialReference { overhead: &AirDataInertialReferenceSystemOverheadPanel, simulator_data: AdirsSimulatorData, ) { - let ssm = if self.is_on - && (self.is_fully_aligned() - || (overhead.mode_of(self.number) == InertialReferenceMode::Attitude - && self.is_attitude_aligned())) - { - SignStatus::NormalOperation - } else { - SignStatus::NoComputedData - }; + // TODO BNR labels (that most things use) are actually +/- 180 - let true_heading_ssm = if self.is_on + // TODO tests for when should be mag or true in mag labels + + let heading_available = self.is_on && (self.is_fully_aligned() || (overhead.mode_of(self.number) == InertialReferenceMode::Navigation && self .remaining_align_duration - .map_or(false, |duration| duration.as_secs() < 120))) - { + .map_or(false, |duration| duration.as_secs() < 120))); + + let true_heading_ssm = if heading_available { SignStatus::NormalOperation } else { SignStatus::NoComputedData }; - self.true_heading .set_value(simulator_data.true_heading, true_heading_ssm); - self.heading.set_value(simulator_data.heading, ssm); + + // TODO in ATT mode NCD until heading initialised on MCDU + let magnetic_heading_ssm = if self.is_on + && (heading_available + || (overhead.mode_of(self.number) == InertialReferenceMode::Attitude + && self.is_attitude_aligned())) + { + SignStatus::NormalOperation + } else { + SignStatus::NoComputedData + }; + self.heading.set_value( + if self.has_magnetic_data() { + simulator_data.heading + } else { + simulator_data.true_heading + }, + magnetic_heading_ssm, + ); } fn update_wind_velocity( @@ -1315,9 +1378,17 @@ impl InertialReference { self.track.set_value( if ground_speed_above_minimum_threshold { - simulator_data.track + if self.has_magnetic_data() { + simulator_data.track + } else { + simulator_data.true_track + } } else { - simulator_data.heading + if self.has_magnetic_data() { + simulator_data.heading + } else { + simulator_data.true_heading + } }, ssm, ); @@ -1365,6 +1436,103 @@ impl InertialReference { self.update_wind_velocity(context, true_airspeed_source, overhead, simulator_data); } + fn update_maint_word( + &mut self, + context: &UpdateContext, + overhead: &AirDataInertialReferenceSystemOverheadPanel, + ) { + // TODO check status of these during mode transitions (first need to implement mode FSM) + let mut maint_word: IrMaintFlags = IrMaintFlags::default(); + + if !self.is_on { + // FIXME should be no transmission (can we just do this at a higher level...) + self.maint_word + .set_value(maint_word.bits(), SignStatus::FailureWarning); + return; + } + + if !self.is_fully_aligned() { + maint_word |= IrMaintFlags::AlignmentNotReady + } + + if overhead.mode_of(self.number) == InertialReferenceMode::Attitude { + maint_word |= IrMaintFlags::RevAttMode; + } + + if self.is_fully_aligned() { + maint_word |= IrMaintFlags::NavMode; + } + + // TODO request heading setting in att mode if not set + + // TODO attitude invalid fault + + // TODO dc < 18 V + + // TODO on DC (re-implement ON BAT light properly at the same time) + + // TODO ADR input data fault + + // TODO unimportant nav fault + + // TODO DC fault during DC operation last power up + + // TODO align fault + + // TODO No IRS initial pos + + // TODO excess motion during align + + // TODO ADR data not received or parity error + + if self.extreme_latitude { + maint_word |= IrMaintFlags::ExtremeLatitude; + } + + if self.is_aligning() { + if self + .remaining_align_duration() + .map_or(false, |duration| duration.as_secs() <= 60) + { + maint_word |= IrMaintFlags::Align1Minutes; + } else if self + .remaining_align_duration() + .map_or(false, |duration| duration.as_secs() <= 120) + { + maint_word |= IrMaintFlags::Align2Minutes; + } else if self + .remaining_align_duration() + .map_or(false, |duration| duration.as_secs() <= 180) + { + maint_word |= IrMaintFlags::Align3Minutes; + } else if self + .remaining_align_duration() + .map_or(false, |duration| duration.as_secs() <= 240) + { + maint_word |= IrMaintFlags::Align4Minutes; + } else if self + .remaining_align_duration() + .map_or(false, |duration| duration.as_secs() <= 300) + { + maint_word |= IrMaintFlags::Align5Minutes; + } else if self + .remaining_align_duration() + .map_or(false, |duration| duration.as_secs() <= 360) + { + maint_word |= IrMaintFlags::Align6Minutes; + } else { + maint_word |= IrMaintFlags::Align710Minutes; + } + } + + // TODO sin/cos test discrepancy + + self.maint_word + .set_value(maint_word.bits(), SignStatus::NormalOperation); + + // TODO tests! + } + fn alignment_starting(&self, selected_mode: InertialReferenceMode) -> bool { selected_mode != InertialReferenceMode::Off && self.remaining_attitude_initialisation_duration.is_none() @@ -1404,6 +1572,10 @@ impl InertialReference { fn has_fault(&self) -> bool { self.ir_fault_flash_duration.is_some() } + + fn has_magnetic_data(&self) -> bool { + !self.extreme_latitude + } } impl SimulationElement for InertialReference { fn write(&self, writer: &mut SimulatorWriter) { @@ -1442,6 +1614,7 @@ impl SimulationElement for InertialReference { self.wind_speed_bnr.write_to(writer); self.latitude.write_to(writer); self.longitude.write_to(writer); + self.maint_word.write_to(writer); } } impl GroundSpeed for InertialReference { From dc707ef34eca16ac283df92f0d9fd31770ff671f Mon Sep 17 00:00:00 2001 From: Michael Corcoran Date: Mon, 5 Dec 2022 22:41:23 +1300 Subject: [PATCH 02/15] feat(font): diamond for eis font --- .../html_ui/Fonts/ECAMFontRegular.ttf | Bin 13796 -> 13924 bytes src/fonts/ECAMFontRegular_Source.sfd | 81 +++++++++++++----- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/flybywire-aircraft-a320-neo/html_ui/Fonts/ECAMFontRegular.ttf b/flybywire-aircraft-a320-neo/html_ui/Fonts/ECAMFontRegular.ttf index 3f6607411e8e8d4006f3fd2d77b4650bb89a670f..0d701898a9a1c47b0cf66be8ceef75947f9e001f 100644 GIT binary patch delta 1234 zcmaEo{UoQJfsuiMfsdhqftew}%`L=t#+5%m85qnwfTA+)F0O72>WoW)d<7t1%|BS* zsAIv}*9;7d89;tQa&BUQ$=b6u3=Aw67#LWClFLdI7&^F=7#O%zfbuNqiNyu~{{u~C zFp~iCInr|~)5Mv)q8J!>H5eFVr(~oi)~BeJO}7C`z5!yh44{a3E4LBQ0+0bJ8M!4D zj`adhf&3E;49sjf`N@gL+s>^6S^zYFnISi^qJZ%j!$P3E2T;92USe+Qn!s273=CR( z7#Nr)7UUO~Y?DY%WMI$@0NQ^H>{+0PWmP9e#`D{JWsnyV0P164xErPt2cqw9l9Zh+ zz-Y@@KiQ8lmht%HHH<46FKrHEDrXAZ3sL~`3>eFR&0=8o0P;XVD8sCN~PGx$z1Fm0CO&E#Rd^#3QPENcQu<@9WMHWH$4ZfrNsdMA&mCqpW)?f=dr4>m*!;}nL3+;G7$qKFf%cMGaC<(C&nNL Q<}otXP2OUv!ve~>0Miu^H~;_u delta 1104 zcmaEo^CY{TfsuiMfsdhqftew}%`L=t+Giyj1_sj;Kv5ZY7gsk1b;cz?{sJIh%|BS* zsAIv}%M1*R89;tQa&BV5OZ|^?85mg3Ffg!sC6|>bFm!M!F)(mR0OeWI6N?M}{|B1P zU^)ZH=Sa_~O#7qMwu*s)SB8N>RwpAhu|7q4%AR_lgSRWfo* zDjbvqo&xzm2QjncQ{^V+upXaUdwrhmDK6$Ok(85RQNJ%H*J@)C1X*95-uXJF6* z@|h+Ub(w0{rSvp^5?2rJjd^V@u7kQWjF>SJKI8>SKmrZ3nfOcr3Y zWvrX*#~91Fe)1Z|m5fH4!VZPwz=CwCqoPmDnh%wuG%oor&J!*UmxaR4K!=^Ow6 diff --git a/src/fonts/ECAMFontRegular_Source.sfd b/src/fonts/ECAMFontRegular_Source.sfd index c3abe91931d..ceaca3ab1c7 100644 --- a/src/fonts/ECAMFontRegular_Source.sfd +++ b/src/fonts/ECAMFontRegular_Source.sfd @@ -22,7 +22,7 @@ OS2Version: 4 OS2_WeightWidthSlopeOnly: 0 OS2_UseTypoMetrics: 1 CreationTime: 1630565342 -ModificationTime: 1634010783 +ModificationTime: 1670146205 PfmFamily: 17 TTFWeight: 400 TTFWidth: 5 @@ -89,8 +89,15 @@ NameList: AGL For New Fonts DisplaySize: -48 AntiAlias: 1 FitToEm: 0 -WinInfo: 0 29 12 -BeginChars: 65538 382 +WinInfo: 9541 29 12 +Grid +-4096 3112.08334351 m 0 + 8192 3112.08334351 l 1024 + Named: "top" +-4053.02087402 5324 m 0 + -4053.02087402 -2868 l 1024 +EndSplineSet +BeginChars: 65538 383 StartChar: .notdef Encoding: 65536 -1 0 @@ -537,7 +544,7 @@ StartChar: period Encoding: 46 46 11 Width: 2568 GlyphClass: 2 -Flags: WO +Flags: W LayerCount: 2 Fore SplineSet @@ -2497,24 +2504,24 @@ Flags: W LayerCount: 2 Fore SplineSet -120 2969 m 2,0,1 - 120 3028 120 3028 161.5 3070 c 128,-1,2 - 203 3112 203 3112 263 3112 c 2,3,-1 - 1949 3112 l 2,4,5 - 2008 3112 2008 3112 2050 3070.5 c 128,-1,6 - 2092 3029 2092 3029 2092 2969 c 2,7,-1 - 2092 144 l 2,8,9 - 2092 85 2092 85 2050 43 c 128,-1,10 - 2008 1 2008 1 1949 1 c 2,11,-1 - 263 1 l 2,12,13 - 204 1 204 1 162 43 c 128,-1,14 - 120 85 120 85 120 144 c 2,15,-1 - 120 2969 l 2,0,1 -402 2829 m 1,16,-1 - 402 284 l 1,17,-1 - 1809 284 l 1,18,-1 - 1809 2829 l 1,19,-1 - 402 2829 l 1,16,-1 +120 2969 m 6,0,1 + 120 3028 120 3028 161.5 3070 c 132,-1,2 + 203 3112 203 3112 263 3112 c 6,3,-1 + 1949 3112 l 6,4,5 + 2008 3112 2008 3112 2050 3070.5 c 132,-1,6 + 2092 3029 2092 3029 2092 2969 c 6,7,-1 + 2092 144 l 6,8,9 + 2092 85 2092 85 2050 43 c 132,-1,10 + 2008 1 2008 1 1949 1 c 6,11,-1 + 263 1 l 6,12,13 + 204 1 204 1 162 43 c 132,-1,14 + 120 85 120 85 120 144 c 6,15,-1 + 120 2969 l 6,0,1 +402 2829 m 5,16,-1 + 402 284 l 5,17,-1 + 1809 284 l 5,18,-1 + 1809 2829 l 5,19,-1 + 402 2829 l 5,16,-1 EndSplineSet Validated: 1 EndChar @@ -2946,7 +2953,7 @@ EndChar StartChar: at Encoding: 64 64 98 Width: 2568 -Flags: WO +Flags: W LayerCount: 2 Fore SplineSet @@ -5521,5 +5528,33 @@ LayerCount: 2 Fore Validated: 1 EndChar + +StartChar: uni25C7 +Encoding: 9671 9671 382 +Width: 2568 +Flags: WO +LayerCount: 2 +Fore +SplineSet +1188.15136719 3063.99804688 m 2,0,1 + 1221.84960938 3112.5625 1221.84960938 3112.5625 1269.76855469 3113.11035156 c 0,2,3 + 1317.45019531 3113.72949219 1317.45019531 3113.72949219 1351.95898438 3063.99804688 c 2,4,-1 + 2317.62597656 1672.34667969 l 2,5,6 + 2351.64257812 1623.06738281 2351.64257812 1623.06738281 2351.70605469 1554.72460938 c 0,7,8 + 2351.64257812 1485.29980469 2351.64257812 1485.29980469 2317.62597656 1436.27734375 c 2,9,-1 + 1352.53222656 45.453125 l 2,10,11 + 1318.89257812 -3.001953125 1318.89257812 -3.001953125 1270.51660156 -3.0849609375 c 0,12,13 + 1222.34667969 -3.001953125 1222.34667969 -3.001953125 1188.72363281 45.453125 c 2,14,-1 + 223.056640625 1437.10449219 l 2,15,16 + 189.584960938 1485.33984375 189.584960938 1485.33984375 191.372070312 1560.03125 c 0,17,18 + 192.993164062 1630.0703125 192.993164062 1630.0703125 223.056640625 1673.17285156 c 2,19,-1 + 1188.15136719 3063.99804688 l 2,0,1 +1269.48242188 2715.67285156 m 1,20,-1 + 464.759765625 1555.96386719 l 1,21,-1 + 1270.62792969 394.603515625 l 1,22,-1 + 2075.35058594 1554.31347656 l 1,23,-1 + 1269.48242188 2715.67285156 l 1,20,-1 +EndSplineSet +EndChar EndChars EndSplineFont From c67cbf5651e03366ab9347bf56cc5a0847e77051 Mon Sep 17 00:00:00 2001 From: Michael Corcoran Date: Mon, 5 Dec 2022 22:44:29 +1300 Subject: [PATCH 03/15] feat(nd): true ref Still work to be done around radio nav indications. --- src/fmgc/src/guidance/lnav/LnavDriver.ts | 5 +- src/instruments/src/Common/utils.tsx | 6 +- .../src/ND/elements/ApproachMessage.tsx | 26 ------- .../src/ND/elements/RadioNavInfo.tsx | 2 + .../src/ND/elements/ToWaypointIndicator.tsx | 8 +- .../src/ND/elements/TopMessages.tsx | 76 +++++++++++++++++++ src/instruments/src/ND/pages/ArcMode.tsx | 40 +++++----- src/instruments/src/ND/pages/PlanMode.tsx | 16 ++-- src/instruments/src/ND/pages/RoseMode.tsx | 44 ++++++----- src/instruments/src/ND/styles.scss | 8 +- 10 files changed, 153 insertions(+), 78 deletions(-) delete mode 100644 src/instruments/src/ND/elements/ApproachMessage.tsx create mode 100644 src/instruments/src/ND/elements/TopMessages.tsx diff --git a/src/fmgc/src/guidance/lnav/LnavDriver.ts b/src/fmgc/src/guidance/lnav/LnavDriver.ts index 104bba241d3..2ca914590aa 100644 --- a/src/fmgc/src/guidance/lnav/LnavDriver.ts +++ b/src/fmgc/src/guidance/lnav/LnavDriver.ts @@ -411,8 +411,9 @@ export class LnavDriver implements GuidanceComponent { private updateEfisData(activeLeg: Leg, gs: Knots) { const termination = activeLeg instanceof XFLeg ? activeLeg.fix.infos.coordinates : activeLeg.getPathEndPoint(); + const efisTrueBearing = termination ? Avionics.Utils.computeGreatCircleHeading(this.ppos, termination) : -1; const efisBearing = termination ? A32NX_Util.trueToMagnetic( - Avionics.Utils.computeGreatCircleHeading(this.ppos, termination), + efisTrueBearing, Facilities.getMagVar(this.ppos.lat, this.ppos.long), ) : -1; @@ -423,10 +424,12 @@ export class LnavDriver implements GuidanceComponent { // FIXME should be NCD if no FM position SimVar.SetSimVarValue('L:A32NX_EFIS_L_TO_WPT_BEARING', 'Degrees', efisBearing); + SimVar.SetSimVarValue('L:A32NX_EFIS_L_TO_WPT_TRUE_BEARING', 'Degrees', efisTrueBearing); SimVar.SetSimVarValue('L:A32NX_EFIS_L_TO_WPT_DISTANCE', 'Number', efisDistance); SimVar.SetSimVarValue('L:A32NX_EFIS_L_TO_WPT_ETA', 'Seconds', efisEta); SimVar.SetSimVarValue('L:A32NX_EFIS_R_TO_WPT_BEARING', 'Degrees', efisBearing); + SimVar.SetSimVarValue('L:A32NX_EFIS_R_TO_WPT_TRUE_BEARING', 'Degrees', efisTrueBearing); SimVar.SetSimVarValue('L:A32NX_EFIS_R_TO_WPT_DISTANCE', 'Number', efisDistance); SimVar.SetSimVarValue('L:A32NX_EFIS_R_TO_WPT_ETA', 'Seconds', efisEta); } diff --git a/src/instruments/src/Common/utils.tsx b/src/instruments/src/Common/utils.tsx index bfaa693e404..b9d07f44784 100644 --- a/src/instruments/src/Common/utils.tsx +++ b/src/instruments/src/Common/utils.tsx @@ -1,9 +1,9 @@ import React from 'react'; -export type LayerProps = { x: number, y: number, id?: string, className?: string } +export type LayerProps = { x: number, y: number, id?: string, className?: string, visibility?: string } -export const Layer: React.FC = ({ x = 0, y = 0, id, className, children }) => ( - +export const Layer: React.FC = ({ x = 0, y = 0, id, className, children, visibility = 'visible' }) => ( + {children} ); diff --git a/src/instruments/src/ND/elements/ApproachMessage.tsx b/src/instruments/src/ND/elements/ApproachMessage.tsx deleted file mode 100644 index 7bdcdea63e9..00000000000 --- a/src/instruments/src/ND/elements/ApproachMessage.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import { Layer } from '@instruments/common/utils'; -import { useSimVar } from '@instruments/common/simVars'; -import { SimVarString } from '@shared/simvar'; -import { EfisSide } from '@shared/NavigationDisplay'; - -type ApproachMessageProps = { - side: EfisSide, -} - -export const ApproachMessage: React.FC = ({ side }) => { - const [apprMsg0] = useSimVar(`L:A32NX_EFIS_${side}_APPR_MSG_0`, 'number', 5000); - const [apprMsg1] = useSimVar(`L:A32NX_EFIS_${side}_APPR_MSG_1`, 'number', 5000); - - const apprMsg = SimVarString.unpack([apprMsg0, apprMsg1]); - - if (apprMsg.length < 1) { - return null; - } - - return ( - - {apprMsg} - - ); -}; diff --git a/src/instruments/src/ND/elements/RadioNavInfo.tsx b/src/instruments/src/ND/elements/RadioNavInfo.tsx index 31439bcd2bc..a1cb64f9a05 100644 --- a/src/instruments/src/ND/elements/RadioNavInfo.tsx +++ b/src/instruments/src/ND/elements/RadioNavInfo.tsx @@ -9,6 +9,8 @@ export enum NavAidMode { VOR, } +// TODO true ref + export type RadioNavInfoProps = { index: 1 | 2, side: EfisSide } const TuningModeIndicator: React.FC<{ index: 1 | 2, frequency: number }> = ({ index, frequency }) => { diff --git a/src/instruments/src/ND/elements/ToWaypointIndicator.tsx b/src/instruments/src/ND/elements/ToWaypointIndicator.tsx index 1d11831fa80..e6f8d7f96b9 100644 --- a/src/instruments/src/ND/elements/ToWaypointIndicator.tsx +++ b/src/instruments/src/ND/elements/ToWaypointIndicator.tsx @@ -6,15 +6,17 @@ import { SimVarString } from '@shared/simvar'; export type ToWaypointIndicatorProps = { side: EfisSide, + trueRef: boolean, } -export const ToWaypointIndicator: FC = memo(({ side }) => { +export const ToWaypointIndicator: FC = memo(({ side, trueRef }) => { // TODO replace with appropriate ARINC 429 labels const [ident, setIdent] = useState(null); const [ident0] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_IDENT_0`, 'number', 500); const [ident1] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_IDENT_1`, 'number', 500); const [bearing] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_BEARING`, 'Degrees'); + const [trueBearing] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_TRUE_BEARING`, 'Degrees'); const [distance] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_DISTANCE`, 'Number'); const [eta] = useSimVar(`L:A32NX_EFIS_${side}_TO_WPT_ETA`, 'Seconds'); @@ -55,8 +57,8 @@ export const ToWaypointIndicator: FC = memo(({ side }) {bearing && bearing !== -1 && Number.isFinite(bearing) && ( <> - {(Math.round(bearing)).toString().padStart(3, '0')} - ° + {(Math.round(trueRef ? trueBearing : bearing)).toString().padStart(3, '0')} + {trueRef ? 'T' : '°'} )} diff --git a/src/instruments/src/ND/elements/TopMessages.tsx b/src/instruments/src/ND/elements/TopMessages.tsx new file mode 100644 index 00000000000..f049413256c --- /dev/null +++ b/src/instruments/src/ND/elements/TopMessages.tsx @@ -0,0 +1,76 @@ +import React, { useEffect, useState } from 'react'; +import { Layer } from '@instruments/common/utils'; +import { EfisSide } from '@shared/NavigationDisplay'; +import { Arinc429Word } from '@shared/arinc429'; +import { SimVarString } from '@shared/simvar'; +import { useSimVar } from '@instruments/common/simVars'; + +type TopMessagesProps = { + side: EfisSide, + ppos: LatLongData, + trueTrack: Arinc429Word, + trueRef: boolean, +} + +type GridTrackProps = { + gridTrack: number, +}; + +const GridTrack: React.FC = ({ gridTrack }) => ( + <> + + + + ◇ + {gridTrack?.toFixed(0).padStart(3, '0') ?? ''} + + °G + + +); + +type TrueFlagProps = { + xOffset?: number, + box: boolean, +}; + +const TrueFlag: React.FC = ({ xOffset = 0, box }) => ( + <> + + TRUE + +); + +export const TopMessages: React.FC = ({ side, ppos, trueTrack, trueRef }) => { + const [apprMsg0] = useSimVar(`L:A32NX_EFIS_${side}_APPR_MSG_0`, 'number', 5000); + const [apprMsg1] = useSimVar(`L:A32NX_EFIS_${side}_APPR_MSG_1`, 'number', 5000); + const [apprMsg, setApprMsg] = useState(null); + + const [gridTrack, setGridTrack] = useState(null); + useEffect(() => { + if (trueTrack.isNormalOperation() && Math.abs(ppos.lat) > 65) { + setGridTrack(Math.round(Avionics.Utils.clampAngle(trueTrack.value - Math.sign(ppos.lat) * ppos.long)) % 360); + } else { + setGridTrack(null); + } + }, [ppos.lat.toFixed(0), ppos.long.toFixed(1), trueTrack.value.toFixed(0), trueTrack.ssm]); + + useEffect(() => { + const msg = SimVarString.unpack([apprMsg0, apprMsg1]); + setApprMsg(msg.length > 0 ? msg : null); + }, [apprMsg0, apprMsg1]); + + return ( + <> + + {apprMsg ?? ''} + + + + + + + + + ); +}; diff --git a/src/instruments/src/ND/pages/ArcMode.tsx b/src/instruments/src/ND/pages/ArcMode.tsx index 83f9c8ee327..25041deeeb4 100644 --- a/src/instruments/src/ND/pages/ArcMode.tsx +++ b/src/instruments/src/ND/pages/ArcMode.tsx @@ -2,14 +2,14 @@ import React, { memo, useEffect, useState } from 'react'; import { useSimVar } from '@instruments/common/simVars'; import { getSmallestAngle } from '@instruments/common/utils'; import { MathUtils } from '@shared/MathUtils'; -import { LatLongData } from '@typings/fs-base-ui/html_ui/JS/Types'; import { RangeSetting, Mode, EfisSide, NdSymbol } from '@shared/NavigationDisplay'; import { ArmedLateralMode, isArmed, LateralMode } from '@shared/autopilot'; +import { useArinc429Var } from '@instruments/common/arinc429'; +import { TopMessages } from '../elements/TopMessages'; import { FlightPlan } from '../elements/FlightPlan'; import { MapParameters } from '../utils/MapParameters'; import { RadioNeedle } from '../elements/RadioNeedles'; import { ToWaypointIndicator } from '../elements/ToWaypointIndicator'; -import { ApproachMessage } from '../elements/ApproachMessage'; import { CrossTrack } from '../elements/CrossTrack'; import { TrackLine } from '../elements/TrackLine'; import { Traffic } from '../elements/Traffic'; @@ -25,36 +25,38 @@ export interface ArcModeProps { } export const ArcMode: React.FC = ({ symbols, adirsAlign, rangeSetting, side, ppos, mapHidden }) => { - const [magHeading] = useSimVar('PLANE HEADING DEGREES MAGNETIC', 'degrees'); - const [magTrack] = useSimVar('GPS GROUND MAGNETIC TRACK', 'degrees'); - const [trueHeading] = useSimVar('PLANE HEADING DEGREES TRUE', 'degrees'); + // TODO arinc var selector + const magHeading = useArinc429Var('L:A32NX_ADIRS_IR_1_HEADING'); + const magTrack = useArinc429Var('L:A32NX_ADIRS_IR_1_TRACK'); + const trueHeading = useArinc429Var('L:A32NX_ADIRS_IR_1_TRUE_HEADING'); + const trueTrack = useArinc429Var('L:A32NX_ADIRS_IR_1_TRUE_TRACK'); const [tcasMode] = useSimVar('L:A32NX_SWITCH_TCAS_Position', 'number'); const [selectedHeading] = useSimVar('L:A32NX_AUTOPILOT_HEADING_SELECTED', 'degrees'); const [lsCourse] = useSimVar('L:A32NX_FM_LS_COURSE', 'number'); const [lsDisplayed] = useSimVar(`L:BTN_LS_${side === 'L' ? 1 : 2}_FILTER_ACTIVE`, 'bool'); // TODO rename simvar const [fmaLatMode] = useSimVar('L:A32NX_FMA_LATERAL_MODE', 'enum', 200); const [armedLateralBitmask] = useSimVar('L:A32NX_FMA_LATERAL_ARMED', 'enum', 200); - const [groundSpeed] = useSimVar('GPS GROUND SPEED', 'Meters per second', 200); + const irMaint = useArinc429Var('L:A32NX_ADIRS_IR_1_MAINT_WORD'); + const [trueRefPb] = useSimVar('L:A32NX_PUSH_TRUE_REF', 'bool'); + const [trueRef, setTrueRef] = useState(false); - const heading = Number(MathUtils.fastToFixed(magHeading, 2)); - let track = Number(MathUtils.fastToFixed(magTrack, 2)); - - // Workaround for bug with gps ground track simvar - if (groundSpeed < 40) { - track = (0.025 * groundSpeed + 0.00005) * track + (1 - (0.025 * groundSpeed + 0.00005)) * heading; - track = Number(MathUtils.fastToFixed(track, 2)); - } + const heading = Number(MathUtils.fastToFixed((trueRef ? trueHeading.value : magHeading.value), 2)); + const track = Number(MathUtils.fastToFixed((trueRef ? trueTrack.value : magTrack.value), 2)); const [mapParams] = useState(() => { const params = new MapParameters(); - params.compute(ppos, rangeSetting, 492, trueHeading); + params.compute(ppos, rangeSetting, 492, trueHeading.value); return params; }); useEffect(() => { - mapParams.compute(ppos, rangeSetting, 492, trueHeading); - }, [ppos.lat, ppos.long, trueHeading, rangeSetting].map((n) => MathUtils.fastToFixed(n, 6))); + mapParams.compute(ppos, rangeSetting, 492, trueHeading.value); + }, [ppos.lat, ppos.long, trueHeading.value, rangeSetting].map((n) => MathUtils.fastToFixed(n, 6))); + + useEffect(() => { + setTrueRef((irMaint.getBitValueOr(15, false) || trueRefPb) && !irMaint.getBitValueOr(2, false)); + }, [irMaint.value, trueRefPb]); if (adirsAlign) { return ( @@ -89,9 +91,9 @@ export const ArcMode: React.FC = ({ symbols, adirsAlign, rangeSett - + - + { lsDisplayed && } diff --git a/src/instruments/src/ND/pages/PlanMode.tsx b/src/instruments/src/ND/pages/PlanMode.tsx index bd54e210bd8..ef47849f436 100644 --- a/src/instruments/src/ND/pages/PlanMode.tsx +++ b/src/instruments/src/ND/pages/PlanMode.tsx @@ -2,6 +2,7 @@ import React, { FC, memo, useEffect, useState } from 'react'; import { useSimVar } from '@instruments/common/simVars'; import { Coordinates } from '@fmgc/flightplanning/data/geo'; import { EfisSide, NdSymbol } from '@shared/NavigationDisplay'; +import { useArinc429Var } from '@instruments/common/arinc429'; import { CrossTrack } from '../elements/CrossTrack'; import { ToWaypointIndicator } from '../elements/ToWaypointIndicator'; import { FlightPlan } from '../elements/FlightPlan'; @@ -19,15 +20,20 @@ export interface PlanModeProps { export const PlanMode: FC = ({ side, symbols, adirsAlign, rangeSetting, ppos, mapHidden }) => { const [planCentreLat] = useSimVar('L:A32NX_SELECTED_WAYPOINT_LAT', 'Degrees'); const [planCentreLong] = useSimVar('L:A32NX_SELECTED_WAYPOINT_LONG', 'Degrees'); - - const [trueHeading] = useSimVar('PLANE HEADING DEGREES TRUE', 'degrees'); - + const trueHeading = useArinc429Var('L:A32NX_ADIRS_IR_1_TRUE_HEADING'); + const irMaint = useArinc429Var('L:A32NX_ADIRS_IR_1_MAINT_WORD'); + const [trueRefPb] = useSimVar('L:A32NX_PUSH_TRUE_REF', 'bool'); + const [trueRef, setTrueRef] = useState(false); const [mapParams] = useState(new MapParameters()); useEffect(() => { mapParams.compute({ lat: planCentreLat, long: planCentreLong }, rangeSetting / 2, 250, 0); }, [planCentreLat, planCentreLong, rangeSetting]); + useEffect(() => { + setTrueRef((irMaint.getBitValueOr(15, false) || trueRefPb) && !irMaint.getBitValueOr(2, false)); + }, [irMaint.value, trueRefPb]); + return ( <> @@ -46,10 +52,10 @@ export const PlanMode: FC = ({ side, symbols, adirsAlign, rangeSe {adirsAlign && !mapHidden && mapParams.valid && ( - + )} - + diff --git a/src/instruments/src/ND/pages/RoseMode.tsx b/src/instruments/src/ND/pages/RoseMode.tsx index cda40cec103..e6b2f8558f3 100644 --- a/src/instruments/src/ND/pages/RoseMode.tsx +++ b/src/instruments/src/ND/pages/RoseMode.tsx @@ -5,11 +5,12 @@ import { MathUtils } from '@shared/MathUtils'; import { TuningMode } from '@fmgc/radionav'; import { Mode, EfisSide, NdSymbol } from '@shared/NavigationDisplay'; import { ArmedLateralMode, isArmed, LateralMode } from '@shared/autopilot'; +import { useArinc429Var } from '@instruments/common/arinc429'; +import { TopMessages } from '../elements/TopMessages'; import { ToWaypointIndicator } from '../elements/ToWaypointIndicator'; import { FlightPlan } from '../elements/FlightPlan'; import { MapParameters } from '../utils/MapParameters'; import { RadioNeedle } from '../elements/RadioNeedles'; -import { ApproachMessage } from '../elements/ApproachMessage'; import { CrossTrack } from '../elements/CrossTrack'; import { TrackLine } from '../elements/TrackLine'; import { Traffic } from '../elements/Traffic'; @@ -26,35 +27,37 @@ export interface RoseModeProps { } export const RoseMode: FC = ({ symbols, adirsAlign, rangeSetting, mode, side, ppos, mapHidden }) => { - const [magHeading] = useSimVar('PLANE HEADING DEGREES MAGNETIC', 'degrees'); - const [magTrack] = useSimVar('GPS GROUND MAGNETIC TRACK', 'degrees'); - const [trueHeading] = useSimVar('PLANE HEADING DEGREES TRUE', 'degrees'); + const magHeading = useArinc429Var('L:A32NX_ADIRS_IR_1_HEADING'); + const magTrack = useArinc429Var('L:A32NX_ADIRS_IR_1_TRACK'); + const trueHeading = useArinc429Var('L:A32NX_ADIRS_IR_1_TRUE_HEADING'); + const trueTrack = useArinc429Var('L:A32NX_ADIRS_IR_1_TRUE_TRACK'); const [tcasMode] = useSimVar('L:A32NX_SWITCH_TCAS_Position', 'number'); const [selectedHeading] = useSimVar('L:A32NX_AUTOPILOT_HEADING_SELECTED', 'degrees'); const [lsCourse] = useSimVar('L:A32NX_FM_LS_COURSE', 'number'); const [lsDisplayed] = useSimVar(`L:BTN_LS_${side === 'L' ? 1 : 2}_FILTER_ACTIVE`, 'bool'); // TODO rename simvar const [fmaLatMode] = useSimVar('L:A32NX_FMA_LATERAL_MODE', 'enum', 200); const [armedLateralBitmask] = useSimVar('L:A32NX_FMA_LATERAL_ARMED', 'enum', 200); - const [groundSpeed] = useSimVar('GPS GROUND SPEED', 'Meters per second', 200); + const irMaint = useArinc429Var('L:A32NX_ADIRS_IR_1_MAINT_WORD'); + const [trueRefPb] = useSimVar('L:A32NX_PUSH_TRUE_REF', 'bool'); + const [trueRef, setTrueRef] = useState(false); - const heading = Math.round(Number(MathUtils.fastToFixed(magHeading, 1)) * 1000) / 1000; - let track = Math.round(Number(MathUtils.fastToFixed(magTrack, 1)) * 1000) / 1000; - - // Workaround for bug with gps ground track simvar - if (groundSpeed < 40) { - track = (0.025 * groundSpeed + 0.00005) * track + (1 - (0.025 * groundSpeed + 0.00005)) * heading; - } + const heading = Number(MathUtils.fastToFixed((trueRef ? trueHeading.value : magHeading.value), 2)); + const track = Number(MathUtils.fastToFixed((trueRef ? trueTrack.value : magTrack.value), 2)); const [mapParams] = useState(() => { const params = new MapParameters(); - params.compute(ppos, rangeSetting / 2, 250, trueHeading); + params.compute(ppos, rangeSetting / 2, 250, trueHeading.value); return params; }); useEffect(() => { - mapParams.compute(ppos, rangeSetting / 2, 250, trueHeading); - }, [ppos.lat, ppos.long, trueHeading, rangeSetting].map((n) => MathUtils.fastToFixed(n, 6))); + mapParams.compute(ppos, rangeSetting / 2, 250, trueHeading.value); + }, [ppos.lat, ppos.long, trueHeading.value, rangeSetting].map((n) => MathUtils.fastToFixed(n, 6))); + + useEffect(() => { + setTrueRef((irMaint.getBitValueOr(15, false) || trueRefPb) && !irMaint.getBitValueOr(2, false)); + }, [irMaint.value, trueRefPb]); if (adirsAlign) { return ( @@ -89,15 +92,15 @@ export const RoseMode: FC = ({ symbols, adirsAlign, rangeSetting, - { mode === Mode.ROSE_VOR && } + { mode === Mode.ROSE_VOR && } - { mode === Mode.ROSE_ILS && } + { mode === Mode.ROSE_ILS && } - { mode === Mode.ROSE_NAV && } + { mode === Mode.ROSE_NAV && } { mode === Mode.ROSE_VOR && } { mode === Mode.ROSE_ILS && } - + { mode === Mode.ROSE_NAV && lsDisplayed && } @@ -548,6 +551,7 @@ const MapFailOverlay: FC> = memo(({ rangeSett )); +// TODO true ref const VorCaptureOverlay: React.FC<{ heading: number, side: EfisSide, @@ -628,6 +632,7 @@ const VorCaptureOverlay: React.FC<{ ); }; +// TODO true ref const IlsCaptureOverlay: React.FC<{ heading: number, _side: EfisSide, @@ -713,6 +718,7 @@ const TrackBug: React.FC<{heading: number, track: number}> = memo(({ heading, tr ); }); +// TODO true ref const LsCourseBug: React.FC<{heading: number, lsCourse: number}> = ({ heading, lsCourse }) => { if (lsCourse < 0) { return null; diff --git a/src/instruments/src/ND/styles.scss b/src/instruments/src/ND/styles.scss index a545f0acda7..432be1ff60a 100644 --- a/src/instruments/src/ND/styles.scss +++ b/src/instruments/src/ND/styles.scss @@ -49,7 +49,9 @@ text.Magenta { } .Cyan text, -text.Cyan { +text.Cyan, +.Cyan tspan, +tspan.Cyan { fill: $display-cyan; stroke: none; } @@ -86,7 +88,9 @@ text.White { } .Green text, -text.Green { +text.Green, +.Green tspan, +tspan.Green { fill: $display-green; stroke: none; } From 3a1caad8f3ca8500d503dbe4a84377f2c3dd84e8 Mon Sep 17 00:00:00 2001 From: Michael Corcoran Date: Mon, 5 Dec 2022 22:46:57 +1300 Subject: [PATCH 04/15] feat(pfd): true ref Still QFU dagger to be done (I think) --- .../src/PFD/AttitudeIndicatorHorizon.tsx | 5 +- src/instruments/src/PFD/HeadingIndicator.tsx | 34 ++++++++-- src/instruments/src/PFD/HorizontalTape.tsx | 5 +- .../src/PFD/LandingSystemIndicator.tsx | 1 + src/instruments/src/PFD/PFD.svg | 39 +++++++++-- src/instruments/src/PFD/PFD.tsx | 5 +- src/instruments/src/PFD/instrument.tsx | 15 +++-- .../src/PFD/shared/AdirsValueProvider.tsx | 7 +- .../src/PFD/shared/ArincValueProvider.ts | 31 ++++++--- .../PFD/shared/DisplayManagementComputer.tsx | 66 +++++++++++++++++++ .../src/PFD/shared/PFDSimvarPublisher.tsx | 30 +++++---- 11 files changed, 195 insertions(+), 43 deletions(-) create mode 100644 src/instruments/src/PFD/shared/DisplayManagementComputer.tsx diff --git a/src/instruments/src/PFD/AttitudeIndicatorHorizon.tsx b/src/instruments/src/PFD/AttitudeIndicatorHorizon.tsx index bed75bfc199..3d03ab482d0 100644 --- a/src/instruments/src/PFD/AttitudeIndicatorHorizon.tsx +++ b/src/instruments/src/PFD/AttitudeIndicatorHorizon.tsx @@ -1,6 +1,7 @@ import { ClockEvents, DisplayComponent, EventBus, FSComponent, Subject, Subscribable, VNode } from 'msfssdk'; import { Arinc429Word } from '@shared/arinc429'; +import { DisplayManagementComputerEvents } from 'instruments/src/PFD/shared/DisplayManagementComputer'; import { calculateHorizonOffsetFromPitch, calculateVerticalOffsetFromRoll, @@ -44,7 +45,7 @@ class HeadingBug extends DisplayComponent<{bus: EventBus, isCaptainSide: boolean onAfterRender(node: VNode): void { super.onAfterRender(node); - const sub = this.props.bus.getSubscriber(); + const sub = this.props.bus.getSubscriber(); sub.on('selectedHeading').whenChanged().handle((s) => { this.selectedHeading = s; @@ -53,7 +54,7 @@ class HeadingBug extends DisplayComponent<{bus: EventBus, isCaptainSide: boolean } }); - sub.on('headingAr').handle((h) => { + sub.on('heading').handle((h) => { this.heading = h; if (this.isActive) { this.calculateAndSetOffset(); diff --git a/src/instruments/src/PFD/HeadingIndicator.tsx b/src/instruments/src/PFD/HeadingIndicator.tsx index f7d00bf78c5..8f2d09b41cb 100644 --- a/src/instruments/src/PFD/HeadingIndicator.tsx +++ b/src/instruments/src/PFD/HeadingIndicator.tsx @@ -1,4 +1,5 @@ import { DisplayComponent, EventBus, FSComponent, HEvent, Subject, Subscribable, VNode } from 'msfssdk'; +import { DisplayManagementComputerEvents } from 'instruments/src/PFD/shared/DisplayManagementComputer'; import { HorizontalTape } from './HorizontalTape'; import { getSmallestAngle } from './PFDUtils'; import { PFDSimvars } from './shared/PFDSimvarPublisher'; @@ -60,9 +61,9 @@ export class HeadingOfftape extends DisplayComponent<{ bus: EventBus, failed: Su onAfterRender(node: VNode): void { super.onAfterRender(node); - const sub = this.props.bus.getSubscriber(); + const sub = this.props.bus.getSubscriber(); - sub.on('headingAr').handle((h) => { + sub.on('heading').handle((h) => { this.heading.set(h.value); if (h.isNormalOperation()) { @@ -99,6 +100,7 @@ export class HeadingOfftape extends DisplayComponent<{ bus: EventBus, failed: Su + ); @@ -207,9 +209,9 @@ class GroundTrackBug extends DisplayComponent { onAfterRender(node: VNode): void { super.onAfterRender(node); - const sub = this.props.bus.getSubscriber(); + const sub = this.props.bus.getSubscriber(); - sub.on('groundTrackAr').handle((groundTrack) => { + sub.on('track').handle((groundTrack) => { // if (groundTrack.isNormalOperation()) { const offset = getSmallestAngle(groundTrack.value, this.props.heading.get()) * DistanceSpacing / ValueSpacing; this.trackIndicator.instance.style.display = 'inline'; @@ -339,3 +341,27 @@ class QFUIndicator extends DisplayComponent<{ ILSCourse: Subscribable, h ); } } + +interface TrueFlagProps { + bus: EventBus; +} + +class TrueFlag extends DisplayComponent { + private trueRefActive = Subject.create(false); + + /** @inheritdoc */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onAfterRender(node: VNode): void { + this.props.bus.getSubscriber().on('trueRefActive').whenChanged().handle((v) => this.trueRefActive.set(v)); + } + + /** @inheritdoc */ + render(): VNode { + return ( + + + TRUE + + ); + } +} diff --git a/src/instruments/src/PFD/HorizontalTape.tsx b/src/instruments/src/PFD/HorizontalTape.tsx index 07bba69d261..72cd6f08a05 100644 --- a/src/instruments/src/PFD/HorizontalTape.tsx +++ b/src/instruments/src/PFD/HorizontalTape.tsx @@ -1,3 +1,4 @@ +import { DisplayManagementComputerEvents } from 'instruments/src/PFD/shared/DisplayManagementComputer'; import { EventBus, DisplayComponent, FSComponent, NodeReference, VNode, Subscribable } from 'msfssdk'; import { Arinc429Values } from './shared/ArincValueProvider'; @@ -118,14 +119,14 @@ export class HorizontalTape extends DisplayComponent { onAfterRender(node: VNode): void { super.onAfterRender(node); - const pf = this.props.bus.getSubscriber(); + const pf = this.props.bus.getSubscriber(); this.props.yOffset?.sub((yOffset) => { this.yOffset = yOffset; this.refElement.instance.style.transform = `translate3d(${this.tapeOffset}px, ${yOffset}px, 0px)`; }); - pf.on('headingAr').handle((newVal) => { + pf.on('heading').handle((newVal) => { const multiplier = 100; const currentValueAtPrecision = Math.round(newVal.value * multiplier) / multiplier; const tapeOffset = -currentValueAtPrecision % 10 * this.props.distanceSpacing / this.props.valueSpacing; diff --git a/src/instruments/src/PFD/LandingSystemIndicator.tsx b/src/instruments/src/PFD/LandingSystemIndicator.tsx index 96c87d480d7..08822d2acb4 100644 --- a/src/instruments/src/PFD/LandingSystemIndicator.tsx +++ b/src/instruments/src/PFD/LandingSystemIndicator.tsx @@ -5,6 +5,7 @@ import { Arinc429Values } from './shared/ArincValueProvider'; import { PFDSimvars } from './shared/PFDSimvarPublisher'; import { LagFilter } from './PFDUtils'; +// TODO true ref export class LandingSystem extends DisplayComponent<{ bus: EventBus, instrument: BaseInstrument }> { private lsButtonPressedVisibility = false; diff --git a/src/instruments/src/PFD/PFD.svg b/src/instruments/src/PFD/PFD.svg index e3624420549..e99d3577ad3 100644 --- a/src/instruments/src/PFD/PFD.svg +++ b/src/instruments/src/PFD/PFD.svg @@ -13,7 +13,7 @@ version="1.1" id="svg1876" sodipodi:docname="PFD.svg" - inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)"> + inkscape:version="1.0.2 (e86c870879, 2021-01-15, custom)">