diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index e941b20c566..8750b398b7b 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -105,6 +105,7 @@
1. [ND] Fix color of navaids at the active leg termination - @BlueberryKing (BlueberryKing)
1. [EFB] Added a takeoff performance calculator - @donstim (donbikes), @tracernz (Mike)
1. [MCDU] Removed V-speed auto-fill function - @tracernz (Mike)
+1. [PFD] Implement alerts within artificial horizon (ROP, ROW, OANS, stall, windshear) @flogross89 (Flo)
## 0.11.0
diff --git a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/sound/sound.xml b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/sound/sound.xml
index 1927b61bc40..eddb808bf5b 100644
--- a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/sound/sound.xml
+++ b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/sound/sound.xml
@@ -2306,6 +2306,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2313,8 +2350,6 @@
-
-
@@ -2388,13 +2423,6 @@
-
-
-
-
-
-
-
diff --git a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/A32NX_Core/A32NX_GPWS.js b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/A32NX_Core/A32NX_GPWS.js
index 81b35706a18..8db734859b8 100644
--- a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/A32NX_Core/A32NX_GPWS.js
+++ b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/A32NX_Core/A32NX_GPWS.js
@@ -98,7 +98,7 @@ class A32NX_GPWS {
{},
],
onChange: (current, _) => {
- SimVar.SetSimVarValue("L:A32NX_GPWS_GS_Warning_Active", "Bool", current >= 1);
+ this.setGlideSlopeWarning(current >= 1);
}
}
];
@@ -109,6 +109,44 @@ class A32NX_GPWS {
this.AltCallState.setState("ground");
this.RetardState = A32NX_Util.createMachine(RetardStateMachine);
this.RetardState.setState("landed");
+
+ this.egpwsAlertDiscreteWord1 = Arinc429Word.empty();
+ this.egpwsAlertDiscreteWord2 = Arinc429Word.empty();
+ }
+
+ gpwsUpdateDiscreteWords() {
+ this.egpwsAlertDiscreteWord1.ssm = Arinc429Word.SignStatusMatrix.NormalOperation;
+ this.egpwsAlertDiscreteWord1.setBitValue(11, this.modes[0].current === 1);
+ this.egpwsAlertDiscreteWord1.setBitValue(12, this.modes[0].current === 2);
+ this.egpwsAlertDiscreteWord1.setBitValue(13, this.modes[1].current === 1);
+ this.egpwsAlertDiscreteWord1.setBitValue(12, this.modes[1].current === 2);
+ this.egpwsAlertDiscreteWord1.setBitValue(14, this.modes[2].current === 1);
+ this.egpwsAlertDiscreteWord1.setBitValue(15, this.modes[3].current === 1);
+ this.egpwsAlertDiscreteWord1.setBitValue(16, this.modes[3].current === 2);
+ this.egpwsAlertDiscreteWord1.setBitValue(17, this.modes[3].current === 3);
+ this.egpwsAlertDiscreteWord1.setBitValue(18, this.modes[4].current === 1);
+ Arinc429Word.toSimVarValue('L:A32NX_EGPWS_ALERT_1_DISCRETE_WORD_1', this.egpwsAlertDiscreteWord1.value, this.egpwsAlertDiscreteWord1.ssm);
+ Arinc429Word.toSimVarValue('L:A32NX_EGPWS_ALERT_2_DISCRETE_WORD_1', this.egpwsAlertDiscreteWord1.value, this.egpwsAlertDiscreteWord1.ssm);
+
+ this.egpwsAlertDiscreteWord2.ssm = Arinc429Word.SignStatusMatrix.NormalOperation;
+ this.egpwsAlertDiscreteWord2.setBitValue(14, false);
+ Arinc429Word.toSimVarValue('L:A32NX_EGPWS_ALERT_1_DISCRETE_WORD_2', this.egpwsAlertDiscreteWord2.value, this.egpwsAlertDiscreteWord2.ssm);
+ Arinc429Word.toSimVarValue('L:A32NX_EGPWS_ALERT_2_DISCRETE_WORD_2', this.egpwsAlertDiscreteWord2.value, this.egpwsAlertDiscreteWord2.ssm);
+ }
+
+ setGlideSlopeWarning(state) {
+ SimVar.SetSimVarValue('L:A32NX_GPWS_GS_Warning_Active', 'Bool', state ? 1 : 0); // Still need this for XML
+ this.egpwsAlertDiscreteWord2.setBitValue(11, state);
+ Arinc429Word.toSimVarValue('L:A32NX_EGPWS_ALERT_1_DISCRETE_WORD_2', this.egpwsAlertDiscreteWord2.value, this.egpwsAlertDiscreteWord2.ssm);
+ Arinc429Word.toSimVarValue('L:A32NX_EGPWS_ALERT_2_DISCRETE_WORD_2', this.egpwsAlertDiscreteWord2.value, this.egpwsAlertDiscreteWord2.ssm);
+ }
+
+ setGpwsWarning(state) {
+ SimVar.SetSimVarValue('L:A32NX_GPWS_Warning_Active', 'Bool', state ? 1 : 0); // Still need this for XML
+ this.egpwsAlertDiscreteWord2.setBitValue(12, state);
+ this.egpwsAlertDiscreteWord2.setBitValue(13, state);
+ Arinc429Word.toSimVarValue('L:A32NX_EGPWS_ALERT_1_DISCRETE_WORD_2', this.egpwsAlertDiscreteWord2.value, this.egpwsAlertDiscreteWord2.ssm);
+ Arinc429Word.toSimVarValue('L:A32NX_EGPWS_ALERT_2_DISCRETE_WORD_2', this.egpwsAlertDiscreteWord2.value, this.egpwsAlertDiscreteWord2.ssm);
}
init() {
@@ -116,8 +154,8 @@ class A32NX_GPWS {
this.radnav.init(NavMode.FOUR_SLOTS);
- SimVar.SetSimVarValue("L:A32NX_GPWS_GS_Warning_Active", "Bool", 0);
- SimVar.SetSimVarValue("L:A32NX_GPWS_Warning_Active", "Bool", 0);
+ this.setGlideSlopeWarning(false);
+ this.setGpwsWarning(false);
NXDataStore.getAndSubscribe('CONFIG_A32NX_FWC_RADIO_AUTO_CALL_OUT_PINS', (k, v) => k === 'CONFIG_A32NX_FWC_RADIO_AUTO_CALL_OUT_PINS' && (this.autoCallOutPins = v), DEFAULT_RADIO_AUTO_CALL_OUTS);
}
@@ -171,11 +209,12 @@ class A32NX_GPWS {
this.Mode4MaxRAAlt = NaN;
}
- SimVar.SetSimVarValue("L:A32NX_GPWS_GS_Warning_Active", "Bool", 0);
- SimVar.SetSimVarValue("L:A32NX_GPWS_Warning_Active", "Bool", 0);
+ this.setGlideSlopeWarning(false);
+ this.setGpwsWarning(false);
}
this.GPWSComputeLightsAndCallouts();
+ this.gpwsUpdateDiscreteWords();
if ((mda !== 0 || (dh !== -1 && dh !== -2) && phase === FmgcFlightPhases.APPROACH)) {
let minimumsDA; //MDA or DH
@@ -276,7 +315,7 @@ class A32NX_GPWS {
}
const illuminateGpwsLight = activeTypes.some((type) => type.gpwsLight);
- SimVar.SetSimVarValue("L:A32NX_GPWS_Warning_Active", "Bool", illuminateGpwsLight);
+ this.setGpwsWarning(illuminateGpwsLight);
}
/**
diff --git a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/NavSystems/A320_Neo/A32NX_NavSystem.js b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/NavSystems/A320_Neo/A32NX_NavSystem.js
index 1ea84ca0d35..3b6c427fabf 100644
--- a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/NavSystems/A320_Neo/A32NX_NavSystem.js
+++ b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/NavSystems/A320_Neo/A32NX_NavSystem.js
@@ -438,7 +438,9 @@ class NavSystem extends BaseInstrument {
}
try {
this.onUpdate(this.accumulatedDeltaTime);
- } catch (e) {}
+ } catch (e) {
+ console.log("Uncaught exception", e);
+ }
const t = performance.now() - t0;
NavSystem.maxTimeUpdateAllTime = Math.max(t, NavSystem.maxTimeUpdateAllTime);
NavSystem.maxTimeUpdate = Math.max(t, NavSystem.maxTimeUpdate);
diff --git a/fbw-a32nx/src/systems/instruments/src/EWD/PseudoFWC.ts b/fbw-a32nx/src/systems/instruments/src/EWD/PseudoFWC.ts
index 1a40cf57230..6b90ea4a62f 100644
--- a/fbw-a32nx/src/systems/instruments/src/EWD/PseudoFWC.ts
+++ b/fbw-a32nx/src/systems/instruments/src/EWD/PseudoFWC.ts
@@ -12,10 +12,13 @@ import {
ConsumerSubject,
SimVarValueType,
SubscribableMapFunctions,
+ StallWarningEvents,
} from '@microsoft/msfs-sdk';
import {
Arinc429Register,
+ Arinc429RegisterSubject,
+ Arinc429SignStatusMatrix,
Arinc429Word,
NXDataStore,
NXLogicClockNode,
@@ -32,6 +35,15 @@ export function xor(a: boolean, b: boolean): boolean {
return !!((a ? 1 : 0) ^ (b ? 1 : 0));
}
+/**
+ * Counts the number of truthy values in an array of booleans
+ * @param args
+ * @returns
+ */
+function countTrue(...args: boolean[]): number {
+ return args.reduce((accu, b) => (b ? accu + 1 : accu), 0);
+}
+
interface EWDItem {
flightPhaseInhib: number[];
/** warning is active */
@@ -59,7 +71,9 @@ enum FwcAuralWarning {
}
export class PseudoFWC {
- private readonly sub = this.bus.getSubscriber();
+ private readonly sub = this.bus.getSubscriber();
+ /** Time to inhibit master warnings and cautions during startup in ms */
+ private static readonly FWC_STARTUP_TIME = 5000;
/** Time to inhibit SCs after one is trigger in ms */
private static readonly AURAL_SC_INHIBIT_TIME = 2000;
@@ -88,6 +102,9 @@ export class PseudoFWC {
);
/* PSEUDO FWC VARIABLES */
+ private readonly startupTimer = new DebounceTimer();
+
+ private readonly startupCompleted = Subject.create(false);
private readonly allCurrentFailures: string[] = [];
@@ -117,10 +134,13 @@ export class PseudoFWC {
private nonCancellableWarningCount = 0;
+ private readonly stallWarning = Subject.create(false);
+
private readonly masterWarningOutput = MappedSubject.create(
SubscribableMapFunctions.or(),
this.masterWarning,
this.fireActive,
+ this.stallWarning,
);
private readonly auralCrcOutput = MappedSubject.create(
@@ -129,6 +149,8 @@ export class PseudoFWC {
this.fireActive,
);
+ private readonly fwcOut126 = Arinc429RegisterSubject.createEmpty();
+
/* 21 - AIR CONDITIONING AND PRESSURIZATION */
private readonly acsc1DiscreteWord1 = Arinc429Register.empty();
@@ -584,7 +606,7 @@ export class PseudoFWC {
/* LANDING GEAR AND LIGHTS */
- private readonly aircraftOnGround = Subject.create(0);
+ private readonly aircraftOnGround = Subject.create(false);
private readonly antiskidActive = Subject.create(false);
@@ -604,6 +626,10 @@ export class PseudoFWC {
private readonly lgciu2DiscreteWord1 = Arinc429Register.empty();
+ private readonly lgciu1DiscreteWord2 = Arinc429Register.empty();
+
+ private readonly lgciu2DiscreteWord2 = Arinc429Register.empty();
+
private readonly nwSteeringDisc = Subject.create(false);
private readonly parkBrake = Subject.create(false);
@@ -618,6 +644,28 @@ export class PseudoFWC {
private readonly lgNotDownPulse2 = new NXLogicPulseNode();
+ private readonly lgciu1OnGroundDisagreeConf = new NXLogicConfirmNode(1, true);
+
+ private readonly lgciu1OnGroundAgreeConf = new NXLogicConfirmNode(0.5, true);
+
+ private readonly lgciu1OnGroundDisagreeMem = new NXLogicMemoryNode(true);
+
+ private readonly lgciu2OnGroundDisagreeConf = new NXLogicConfirmNode(1, true);
+
+ private readonly lgciu2OnGroundAgreeConf = new NXLogicConfirmNode(0.5, true);
+
+ private readonly lgciu2OnGroundDisagreeMem = new NXLogicMemoryNode(true);
+
+ private readonly ra1OnGroundMem = new NXLogicMemoryNode(true);
+
+ private readonly ra2OnGroundMem = new NXLogicMemoryNode(true);
+
+ private readonly ignoreRaOnGroundTrigger = new NXLogicTriggeredMonostableNode(10, true);
+
+ private readonly onGroundConf = new NXLogicConfirmNode(1, true);
+
+ private onGroundImmediate = false;
+
/* NAVIGATION */
private readonly adirsRemainingAlignTime = Subject.create(0);
@@ -628,9 +676,13 @@ export class PseudoFWC {
private readonly adiru3State = Subject.create(0);
- private readonly computedAirSpeed = Subject.create(Arinc429Word.empty());
+ private readonly adr1Cas = Subject.create(Arinc429Word.empty());
+
+ private readonly adr2Cas = Arinc429Register.empty();
+
+ private readonly adr3Cas = Arinc429Register.empty();
- private readonly computedAirSpeedToNearest2 = this.computedAirSpeed.map((it) => Math.round(it.value / 2) * 2);
+ private readonly computedAirSpeedToNearest2 = this.adr1Cas.map((it) => Math.round(it.value / 2) * 2);
private readonly height1Failed = Subject.create(false);
@@ -642,6 +694,8 @@ export class PseudoFWC {
private readonly flapsIndex = Subject.create(0);
+ private stallWarningRaw = ConsumerValue.create(this.sub.on('stall_warning_on'), false);
+
/** ENGINE AND THROTTLE */
private readonly engine1Master = ConsumerSubject.create(this.sub.on('engine1Master'), 0);
@@ -884,11 +938,13 @@ export class PseudoFWC {
this.auralCrcOutput.sub((crc) => SimVar.SetSimVarValue('L:A32NX_FWC_CRC', 'bool', crc));
this.masterCaution.sub((caution) => {
- SimVar.SetSimVarValue('L:A32NX_MASTER_CAUTION', 'bool', caution);
+ // Inhibit master warning/cautions until FWC startup has been completed
+ SimVar.SetSimVarValue('L:A32NX_MASTER_CAUTION', 'bool', this.startupCompleted.get() ? caution : false);
}, true);
this.masterWarningOutput.sub((warning) => {
- SimVar.SetSimVarValue('L:A32NX_MASTER_WARNING', 'Bool', warning);
+ // Inhibit master warning/cautions until FWC startup has been completed
+ SimVar.SetSimVarValue('L:A32NX_MASTER_WARNING', 'Bool', this.startupCompleted.get() ? warning : false);
}, true);
// L/G lever red arrow sinking outputs
@@ -898,6 +954,21 @@ export class PseudoFWC {
SimVar.SetSimVarValue('L:A32NX_FWC_2_LG_RED_ARROW', SimVarValueType.Bool, on);
}, true);
+ this.stallWarning.sub((v) => {
+ this.fwcOut126.setBitValue(17, v);
+ // set the sound on/off
+ SimVar.SetSimVarValue('L:A32NX_AUDIO_STALL_WARNING', 'bool', v);
+ }, true);
+ this.aircraftOnGround.sub((v) => this.fwcOut126.setBitValue(28, v));
+
+ this.fwcOut126.sub((v) => {
+ Arinc429Word.toSimVarValue('L:A32NX_FWC_1_DISCRETE_WORD_126', v.value, v.ssm);
+ Arinc429Word.toSimVarValue('L:A32NX_FWC_2_DISCRETE_WORD_126', v.value, v.ssm);
+ }, true);
+
+ // FIXME depend on FWC state
+ this.fwcOut126.setSsm(Arinc429SignStatusMatrix.NormalOperation);
+
const sub = this.bus.getSubscriber();
this.fuelCtrTankModeSelMan.setConsumer(sub.on('fuel_ctr_tk_mode_sel_man'));
@@ -911,6 +982,25 @@ export class PseudoFWC {
this.rightOuterInnerValve.setConsumer(sub.on('fuel_valve_open_5'));
this.rightFuelPump1Auto.setConsumer(sub.on('fuel_pump_switch_3'));
this.rightFuelPump2Auto.setConsumer(sub.on('fuel_pump_switch_6'));
+
+ // Inhibit single chimes for the first two seconds after power-on
+ this.auralSingleChimeInhibitTimer.schedule(
+ () => (this.auralSingleChimePending = false),
+ PseudoFWC.AURAL_SC_INHIBIT_TIME,
+ );
+
+ this.acESSBusPowered.sub((v) => {
+ if (v) {
+ this.startupTimer.schedule(() => {
+ this.startupCompleted.set(true);
+ console.log('PseudoFWC startup completed.');
+ }, PseudoFWC.FWC_STARTUP_TIME);
+ } else {
+ this.startupTimer.clear();
+ this.startupCompleted.set(false);
+ console.log('PseudoFWC shut down.');
+ }
+ });
}
mapOrder(array, order): [] {
@@ -1007,6 +1097,7 @@ export class PseudoFWC {
this.flightPhase129.set([1, 2, 9].includes(this.fwcFlightPhase.get()));
this.flightPhase67.set([6, 7].includes(this.fwcFlightPhase.get()));
this.flightPhase78.set([7, 8].includes(this.fwcFlightPhase.get()));
+ const flightPhase567 = [5, 6, 7].includes(this.fwcFlightPhase.get());
// TO CONFIG button
this.toConfigTestRaw = SimVar.GetSimVarValue('L:A32NX_BTN_TOCONFIG', 'bool') > 0;
@@ -1049,12 +1140,22 @@ export class PseudoFWC {
this.flapsIndex.set(SimVar.GetSimVarValue('L:A32NX_FLAPS_CONF_INDEX', 'number'));
- this.computedAirSpeed.set(Arinc429Word.fromSimVarValue('L:A32NX_ADIRS_ADR_1_COMPUTED_AIRSPEED'));
+ this.adr1Cas.set(Arinc429Word.fromSimVarValue('L:A32NX_ADIRS_ADR_1_COMPUTED_AIRSPEED'));
+ this.adr2Cas.setFromSimVar('L:A32NX_ADIRS_ADR_2_COMPUTED_AIRSPEED');
+ this.adr3Cas.setFromSimVar('L:A32NX_ADIRS_ADR_3_COMPUTED_AIRSPEED');
- // needs to happen before dual eng failure check
- this.aircraftOnGround.set(SimVar.GetSimVarValue('SIM ON GROUND', 'Bool'));
+ // RA acquisition
+ this.radioHeight1.setFromSimVar('L:A32NX_RA_1_RADIO_ALTITUDE');
+ this.radioHeight2.setFromSimVar('L:A32NX_RA_2_RADIO_ALTITUDE');
+
+ /* ELECTRICAL acquisition */
+ this.dcESSBusPowered.set(SimVar.GetSimVarValue('L:A32NX_ELEC_DC_ESS_BUS_IS_POWERED', 'bool'));
+ this.dc2BusPowered.set(SimVar.GetSimVarValue('L:A32NX_ELEC_DC_2_BUS_IS_POWERED', 'bool'));
+ this.ac1BusPowered.set(SimVar.GetSimVarValue('L:A32NX_ELEC_AC_1_BUS_IS_POWERED', 'bool'));
+ this.ac2BusPowered.set(SimVar.GetSimVarValue('L:A32NX_ELEC_AC_2_BUS_IS_POWERED', 'bool'));
+ this.acESSBusPowered.set(SimVar.GetSimVarValue('L:A32NX_ELEC_AC_ESS_BUS_IS_POWERED', 'bool'));
- /* ENGINE AND THROTTLE */
+ /* ENGINE AND THROTTLE acquisition */
this.engine1State.set(SimVar.GetSimVarValue('L:A32NX_ENGINE_STATE:1', 'Enum'));
this.engine2State.set(SimVar.GetSimVarValue('L:A32NX_ENGINE_STATE:2', 'Enum'));
@@ -1063,6 +1164,7 @@ export class PseudoFWC {
this.N2Eng1.set(SimVar.GetSimVarValue('L:A32NX_ENGINE_N2:1', 'number'));
this.N2Eng2.set(SimVar.GetSimVarValue('L:A32NX_ENGINE_N2:2', 'number'));
this.N1IdleEng.set(SimVar.GetSimVarValue('L:A32NX_ENGINE_IDLE_N1', 'number'));
+ // FIXME move out of acquisition to logic below
const oneEngineAboveMinPower = this.engine1AboveIdle.get() || this.engine2AboveIdle.get();
this.engine1Generator.set(SimVar.GetSimVarValue('L:A32NX_ELEC_ENG_GEN_1_POTENTIAL_NORMAL', 'bool'));
@@ -1075,8 +1177,6 @@ export class PseudoFWC {
this.apuBleedValveOpen.set(SimVar.GetSimVarValue('L:A32NX_APU_BLEED_AIR_VALVE_OPEN', 'bool'));
this.radioAlt.set(SimVar.GetSimVarValue('PLANE ALT ABOVE GROUND MINUS CG', 'feet'));
- this.radioHeight1.setFromSimVar('L:A32NX_RA_1_RADIO_ALTITUDE');
- this.radioHeight2.setFromSimVar('L:A32NX_RA_2_RADIO_ALTITUDE');
this.fac1Failed.set(SimVar.GetSimVarValue('L:A32NX_FBW_FAC_FAILED:1', 'boost psi'));
@@ -1096,27 +1196,8 @@ export class PseudoFWC {
this.autoThrustStatus.set(SimVar.GetSimVarValue('L:A32NX_AUTOTHRUST_STATUS', 'enum'));
this.autothrustLeverWarningFlex.set(SimVar.GetSimVarValue('L:A32NX_AUTOTHRUST_THRUST_LEVER_WARNING_FLEX', 'bool'));
this.autothrustLeverWarningToga.set(SimVar.GetSimVarValue('L:A32NX_AUTOTHRUST_THRUST_LEVER_WARNING_TOGA', 'bool'));
- this.thrustLeverNotSet.set(this.autothrustLeverWarningFlex.get() || this.autothrustLeverWarningToga.get());
- // FIXME ECU doesn't have the necessary output words so we go purely on TLA
- const flexThrustLimit = SimVar.GetSimVarValue('L:A32NX_AUTOTHRUST_THRUST_LIMIT_TYPE', 'number') === 3;
- const toPower =
- this.throttle1Position.get() >= 45 ||
- (this.throttle1Position.get() >= 35 && flexThrustLimit) ||
- this.throttle2Position.get() >= 45 ||
- (this.throttle2Position.get() >= 35 && flexThrustLimit);
- this.eng1Or2TakeoffPowerConfirm.write(toPower, deltaTime);
- const raAbove1500 = this.radioHeight1.valueOr(0) > 1500 || this.radioHeight2.valueOr(0) > 1500;
- this.eng1Or2TakeoffPower.set(toPower || (this.eng1Or2TakeoffPowerConfirm.read() && !raAbove1500));
-
- this.engDualFault.set(
- !this.aircraftOnGround.get() &&
- ((this.fireButton1.get() && this.fireButton2.get()) ||
- (!this.engine1ValueSwitch.get() && !this.engine2ValueSwitch.get()) ||
- (this.engine1State.get() === 0 && this.engine2State.get() === 0) ||
- (!this.engine1CoreAtOrAboveMinIdle.get() && !this.engine2CoreAtOrAboveMinIdle.get())),
- );
- /* HYDRAULICS */
+ /* HYDRAULICS acquisition */
this.blueElecPumpPBAuto.set(SimVar.GetSimVarValue('L:A32NX_OVHD_HYD_EPUMPB_PB_IS_AUTO', 'bool'));
this.blueLP.set(SimVar.GetSimVarValue('L:A32NX_HYD_BLUE_EDPUMP_LOW_PRESS', 'bool'));
@@ -1137,7 +1218,7 @@ export class PseudoFWC {
const greenSysPressurised = SimVar.GetSimVarValue('L:A32NX_HYD_GREEN_SYSTEM_1_SECTION_PRESSURE_SWITCH', 'bool');
const yellowSysPressurised = SimVar.GetSimVarValue('L:A32NX_HYD_YELLOW_SYSTEM_1_SECTION_PRESSURE_SWITCH', 'bool');
- /* ADIRS */
+ /* ADIRS acquisition */
this.adirsRemainingAlignTime.set(SimVar.GetSimVarValue('L:A32NX_ADIRS_REMAINING_IR_ALIGNMENT_TIME', 'Seconds'));
@@ -1155,32 +1236,11 @@ export class PseudoFWC {
this.height1Failed.set(height1.isFailureWarning());
this.height2Failed.set(height2.isFailureWarning());
// overspeed
- const adr3Cas = Arinc429Word.fromSimVarValue('L:A32NX_ADIRS_ADR_3_COMPUTED_AIRSPEED');
const adr3MaxCas = Arinc429Word.fromSimVarValue('L:A32NX_ADIRS_ADR_3_MAX_AIRSPEED');
- let overspeedWarning = this.adr3OverspeedWarning.write(
- adr3Cas.isNormalOperation() && adr3MaxCas.isNormalOperation() && adr3Cas.value > adr3MaxCas.value + 8,
- this.aircraftOnGround.get() ||
- !(adr3Cas.isNormalOperation() && adr3MaxCas.isNormalOperation()) ||
- adr3Cas.value < adr3MaxCas.value + 4,
- );
const adr1Discrete1 = Arinc429Word.fromSimVarValue('L:A32NX_ADIRS_ADR_1_DISCRETE_WORD_1');
const adr2Discrete1 = Arinc429Word.fromSimVarValue('L:A32NX_ADIRS_ADR_2_DISCRETE_WORD_1');
- if (
- !(adr1Discrete1.isNormalOperation() || adr1Discrete1.isFunctionalTest()) ||
- !(adr2Discrete1.isNormalOperation() || adr2Discrete1.isFunctionalTest())
- ) {
- const adr3Discrete1 = Arinc429Word.fromSimVarValue('L:A32NX_ADIRS_ADR_3_DISCRETE_WORD_1');
- overspeedWarning ||= adr3Discrete1.getBitValueOr(9, false);
- }
- overspeedWarning ||= adr1Discrete1.getBitValueOr(9, false) || adr2Discrete1.getBitValueOr(9, false);
- this.overspeedWarning.set(overspeedWarning);
-
- /* LANDING GEAR AND LIGHTS */
- // const [left1LandingGear] = useSimVar('L:A32NX_LGCIU_1_LEFT_GEAR_COMPRESSED', 'bool', 500);
- // const [right1LandingGear] = useSimVar('L:A32NX_LGCIU_1_RIGHT_GEAR_COMPRESSED', 'bool', 500);
- // const aircraftOnGround = left1LandingGear === 1 || right1LandingGear === 1;
- // FIXME The landing gear triggers the dual engine failure on loading
+ /* LANDING GEAR AND LIGHTS acquisition */
this.antiskidActive.set(SimVar.GetSimVarValue('ANTISKID BRAKES ACTIVE', 'bool'));
this.brakeFan.set(SimVar.GetSimVarValue('L:A32NX_BRAKE_FAN', 'bool'));
@@ -1193,9 +1253,17 @@ export class PseudoFWC {
this.lgciu1Fault.set(SimVar.GetSimVarValue('L:A32NX_LGCIU_1_FAULT', 'bool'));
this.lgciu2Fault.set(SimVar.GetSimVarValue('L:A32NX_LGCIU_2_FAULT', 'bool'));
this.lgciu1DiscreteWord1.setFromSimVar('L:A32NX_LGCIU_1_DISCRETE_WORD_1');
- this.lgciu2DiscreteWord1.setFromSimVar('L:A32NX_LGCIU_1_DISCRETE_WORD_1');
+ this.lgciu2DiscreteWord1.setFromSimVar('L:A32NX_LGCIU_2_DISCRETE_WORD_1');
+ this.lgciu1DiscreteWord2.setFromSimVar('L:A32NX_LGCIU_1_DISCRETE_WORD_2');
+ this.lgciu2DiscreteWord2.setFromSimVar('L:A32NX_LGCIU_2_DISCRETE_WORD_2');
this.parkBrake.set(SimVar.GetSimVarValue('L:A32NX_PARK_BRAKE_LEVER_POS', 'Bool'));
this.nwSteeringDisc.set(SimVar.GetSimVarValue('L:A32NX_HYD_NW_STRG_DISC_ECAM_MEMO', 'Bool'));
+ const leftCompressedHardwireLgciu1 =
+ this.dcESSBusPowered.get() && SimVar.GetSimVarValue('A32NX_LGCIU_1_L_GEAR_COMPRESSED', 'bool') > 0;
+ const leftCompressedHardwireLgciu2 =
+ this.dc2BusPowered.get() && SimVar.GetSimVarValue('A32NX_LGCIU_2_L_GEAR_COMPRESSED', 'bool') > 0;
+
+ // General logic
const mainGearDownlocked =
(this.lgciu1DiscreteWord1.bitValueOr(23, false) || this.lgciu2DiscreteWord1.bitValueOr(23, false)) &&
(this.lgciu1DiscreteWord1.bitValueOr(24, false) || this.lgciu2DiscreteWord1.bitValueOr(24, false));
@@ -1204,6 +1272,83 @@ export class PseudoFWC {
mainGearDownlocked &&
(this.lgciu1DiscreteWord1.bitValueOr(25, false) || this.lgciu2DiscreteWord1.bitValueOr(25, false));
+ // on ground logic
+ const lgciu1Disagree = xor(leftCompressedHardwireLgciu1, this.lgciu1DiscreteWord2.bitValue(13));
+ this.lgciu1OnGroundDisagreeConf.write(lgciu1Disagree, deltaTime);
+ this.lgciu1OnGroundAgreeConf.write(!lgciu1Disagree, deltaTime);
+ this.lgciu1OnGroundDisagreeMem.write(
+ (!this.lgciu1DiscreteWord2.isNormalOperation() && !this.lgciu1DiscreteWord2.isFunctionalTest()) ||
+ this.lgciu1OnGroundDisagreeConf.read(),
+ this.lgciu1OnGroundAgreeConf.read(),
+ );
+ const lgciu2Disagree = xor(leftCompressedHardwireLgciu2, this.lgciu2DiscreteWord2.bitValue(13));
+ this.lgciu2OnGroundDisagreeConf.write(lgciu2Disagree, deltaTime);
+ this.lgciu2OnGroundAgreeConf.write(!lgciu2Disagree, deltaTime);
+ this.lgciu2OnGroundDisagreeMem.write(
+ (!this.lgciu2DiscreteWord2.isNormalOperation() && !this.lgciu2DiscreteWord2.isFunctionalTest()) ||
+ this.lgciu2OnGroundDisagreeConf.read(),
+ this.lgciu2OnGroundAgreeConf.read(),
+ );
+ const lgciuOnGroundDisagree = this.lgciu1OnGroundDisagreeMem.read() || this.lgciu2OnGroundDisagreeMem.read();
+ const onGroundA =
+ leftCompressedHardwireLgciu1 &&
+ this.lgciu1DiscreteWord2.bitValue(13) &&
+ leftCompressedHardwireLgciu2 &&
+ this.lgciu2DiscreteWord2.bitValue(13);
+
+ // TODO some renaming
+ this.ignoreRaOnGroundTrigger.write(
+ this.radioHeight1.isNoComputedData() && this.radioHeight2.isNoComputedData() && !lgciuOnGroundDisagree,
+ deltaTime,
+ );
+ this.ra1OnGroundMem.write(
+ this.radioHeight1.value < 5,
+ !leftCompressedHardwireLgciu1 || !leftCompressedHardwireLgciu2,
+ );
+ this.ra2OnGroundMem.write(
+ this.radioHeight2.value < 5,
+ !leftCompressedHardwireLgciu1 || !leftCompressedHardwireLgciu2,
+ );
+ const ra1OnGround =
+ (this.radioHeight1.isNormalOperation() || this.radioHeight1.isFunctionalTest()) &&
+ (this.radioHeight1.value < 5 || this.ra1OnGroundMem.read());
+ const ra2OnGround =
+ (this.radioHeight2.isNormalOperation() || this.radioHeight2.isFunctionalTest()) &&
+ (this.radioHeight2.value < 5 || this.ra2OnGroundMem.read());
+ const onGroundCount = countTrue(
+ leftCompressedHardwireLgciu1,
+ leftCompressedHardwireLgciu2,
+ ra1OnGround,
+ ra2OnGround,
+ );
+ const raInvalid = this.radioHeight1.isFailureWarning() || this.radioHeight2.isFailureWarning();
+ this.onGroundImmediate =
+ (onGroundA && this.ignoreRaOnGroundTrigger.read()) ||
+ (onGroundCount > 2 && !raInvalid) ||
+ (onGroundCount > 1 && raInvalid);
+ this.aircraftOnGround.set(this.onGroundConf.write(this.onGroundImmediate, deltaTime));
+
+ // Engine Logic
+ this.thrustLeverNotSet.set(this.autothrustLeverWarningFlex.get() || this.autothrustLeverWarningToga.get());
+ // FIXME ECU doesn't have the necessary output words so we go purely on TLA
+ const flexThrustLimit = SimVar.GetSimVarValue('L:A32NX_AUTOTHRUST_THRUST_LIMIT_TYPE', 'number') === 3;
+ const toPower =
+ this.throttle1Position.get() >= 45 ||
+ (this.throttle1Position.get() >= 35 && flexThrustLimit) ||
+ this.throttle2Position.get() >= 45 ||
+ (this.throttle2Position.get() >= 35 && flexThrustLimit);
+ this.eng1Or2TakeoffPowerConfirm.write(toPower, deltaTime);
+ const raAbove1500 = this.radioHeight1.valueOr(0) > 1500 || this.radioHeight2.valueOr(0) > 1500;
+ this.eng1Or2TakeoffPower.set(toPower || (this.eng1Or2TakeoffPowerConfirm.read() && !raAbove1500));
+
+ this.engDualFault.set(
+ !this.aircraftOnGround.get() &&
+ ((this.fireButton1.get() && this.fireButton2.get()) ||
+ (!this.engine1ValueSwitch.get() && !this.engine2ValueSwitch.get()) ||
+ (this.engine1State.get() === 0 && this.engine2State.get() === 0) ||
+ (!this.engine1CoreAtOrAboveMinIdle.get() && !this.engine2CoreAtOrAboveMinIdle.get())),
+ );
+
/* 22 - AUTOFLIGHT */
const fm1DiscreteWord3 = Arinc429Word.fromSimVarValue('L:A32NX_FM1_DISCRETE_WORD_3');
const fm2DiscreteWord3 = Arinc429Word.fromSimVarValue('L:A32NX_FM2_DISCRETE_WORD_3');
@@ -1214,6 +1359,22 @@ export class PseudoFWC {
this.toConfigCheckedInPhase2Or3 = true;
}
+ let overspeedWarning = this.adr3OverspeedWarning.write(
+ this.adr3Cas.isNormalOperation() && adr3MaxCas.isNormalOperation() && this.adr3Cas.value > adr3MaxCas.value + 8,
+ this.aircraftOnGround.get() ||
+ !(this.adr3Cas.isNormalOperation() && adr3MaxCas.isNormalOperation()) ||
+ this.adr3Cas.value < adr3MaxCas.value + 4,
+ );
+ if (
+ !(adr1Discrete1.isNormalOperation() || adr1Discrete1.isFunctionalTest()) ||
+ !(adr2Discrete1.isNormalOperation() || adr2Discrete1.isFunctionalTest())
+ ) {
+ const adr3Discrete1 = Arinc429Word.fromSimVarValue('L:A32NX_ADIRS_ADR_3_DISCRETE_WORD_1');
+ overspeedWarning ||= adr3Discrete1.getBitValueOr(9, false);
+ }
+ overspeedWarning ||= adr1Discrete1.getBitValueOr(9, false) || adr2Discrete1.getBitValueOr(9, false);
+ this.overspeedWarning.set(overspeedWarning);
+
// TO SPEEDS NOT INSERTED
const fmToSpeedsNotInserted =
fm1DiscreteWord3.getBitValueOr(18, false) && fm2DiscreteWord3.getBitValueOr(18, false);
@@ -1266,14 +1427,6 @@ export class PseudoFWC {
/** MCDU TO CONF 3 selected */
const mcduToFlapPos3 = fm1DiscreteWord2.getBitValueOr(16, false) || fm2DiscreteWord2.getBitValueOr(16, false);
- /* ELECTRICAL */
-
- this.dcESSBusPowered.set(SimVar.GetSimVarValue('L:A32NX_ELEC_DC_ESS_BUS_IS_POWERED', 'bool'));
- this.dc2BusPowered.set(SimVar.GetSimVarValue('L:A32NX_ELEC_DC_2_BUS_IS_POWERED', 'bool'));
- this.ac1BusPowered.set(SimVar.GetSimVarValue('L:A32NX_ELEC_AC_1_BUS_IS_POWERED', 'bool'));
- this.ac2BusPowered.set(SimVar.GetSimVarValue('L:A32NX_ELEC_AC_2_BUS_IS_POWERED', 'bool'));
- this.acESSBusPowered.set(SimVar.GetSimVarValue('L:A32NX_ELEC_AC_ESS_BUS_IS_POWERED', 'bool'));
-
/* 21 - AIR CONDITIONING AND PRESSURIZATION */
this.acsc1DiscreteWord1.setFromSimVar('L:A32NX_COND_ACSC_1_DISCRETE_WORD_1');
@@ -1926,6 +2079,20 @@ export class PseudoFWC {
!((flightPhase6 && !bothRaInvalid) || flightPhase45) && (this.lgNotDownNoCancel.get() || this.lgNotDown.get());
this.lgLeverRedArrow.set(redArrow);
+ // 32 - Surveillance Logic
+ const isNormalLaw = fcdc1DiscreteWord1.getBitValue(11) || fcdc2DiscreteWord1.getBitValue(11);
+ // we need to check this since the MSFS SDK stall warning does not.
+ const isCasAbove60 =
+ this.adr1Cas.get().valueOr(0) > 60 || this.adr2Cas.valueOr(0) > 60 || this.adr3Cas.valueOr(0) > 60;
+ this.stallWarning.set(
+ !isNormalLaw &&
+ isCasAbove60 &&
+ this.stallWarningRaw.get() &&
+ flightPhase567 &&
+ this.radioHeight1.valueOr(Infinity) > 1500 &&
+ this.radioHeight2.valueOr(Infinity) > 1500,
+ );
+
/* FIRE */
this.fireButton1.set(SimVar.GetSimVarValue('L:A32NX_FIRE_BUTTON_ENG1', 'bool'));
@@ -2324,6 +2491,38 @@ export class PseudoFWC {
PseudoFWC.AURAL_SC_PLAY_TIME,
);
}
+
+ this.updateRowRopWarnings();
+ }
+
+ updateRowRopWarnings() {
+ const w = Arinc429Word.fromSimVarValue('L:A32NX_ROW_ROP_WORD_1');
+
+ // ROW
+ SimVar.SetSimVarValue('L:A32NX_AUDIO_ROW_RWY_TOO_SHORT', 'bool', w.getBitValueOr(15, false));
+
+ // ROP
+ // MAX BRAKING, only for manual braking, if maximum pedal braking is not applied
+ const maxBrakingSet =
+ SimVar.GetSimVarValue('L:A32NX_LEFT_BRAKE_PEDAL_INPUT', 'number') > 90 ||
+ SimVar.GetSimVarValue('L:A32NX_RIGHT_BRAKE_PEDAL_INPUT', 'number') > 90;
+ const maxBraking = w.getBitValueOr(13, false) && !maxBrakingSet;
+ SimVar.SetSimVarValue('L:A32NX_AUDIO_ROP_MAX_BRAKING', 'bool', maxBraking);
+
+ // SET MAX REVERSE, if not already max. reverse set and !MAX_BRAKING
+ const maxReverseSet =
+ SimVar.GetSimVarValue('L:XMLVAR_Throttle1Position', 'number') < 0.1 &&
+ SimVar.GetSimVarValue('L:XMLVAR_Throttle2Position', 'number') < 0.1;
+ const maxReverse = (w.getBitValueOr(12, false) || w.getBitValueOr(13, false)) && !maxReverseSet;
+ SimVar.SetSimVarValue('L:A32NX_AUDIO_ROW_SET_MAX_REVERSE', 'bool', !maxBraking && maxReverse);
+
+ // At 80kt, KEEP MAX REVERSE once, if max. reversers deployed
+ const ias = SimVar.GetSimVarValue('AIRSPEED INDICATED', 'knots');
+ SimVar.SetSimVarValue(
+ 'L:A32NX_AUDIO_ROP_KEEP_MAX_REVERSE',
+ 'bool',
+ ias <= 80 && ias > 4 && (w.getBitValueOr(12, false) || w.getBitValueOr(13, false)),
+ );
}
ewdMessageFailures: EWDMessageDict = {
diff --git a/fbw-a32nx/src/systems/instruments/src/EWD/instrument.tsx b/fbw-a32nx/src/systems/instruments/src/EWD/instrument.tsx
index db4206e2d0e..b00d8b5b1c2 100644
--- a/fbw-a32nx/src/systems/instruments/src/EWD/instrument.tsx
+++ b/fbw-a32nx/src/systems/instruments/src/EWD/instrument.tsx
@@ -2,7 +2,14 @@
//
// SPDX-License-Identifier: GPL-3.0
-import { Clock, EventBus, FSComponent, InstrumentBackplane } from '@microsoft/msfs-sdk';
+import {
+ AdcPublisher,
+ Clock,
+ EventBus,
+ FSComponent,
+ InstrumentBackplane,
+ StallWarningPublisher,
+} from '@microsoft/msfs-sdk';
import { FuelSystemPublisher } from 'instruments/src/MsfsAvionicsCommon/providers/FuelSystemPublisher';
import { ArincValueProvider } from './shared/ArincValueProvider';
import { EwdComponent } from './EWD';
@@ -24,6 +31,10 @@ class A32NX_EWD extends BaseInstrument {
private readonly fuelSystemPublisher = new FuelSystemPublisher(this.bus);
+ private readonly adcPublisher = new AdcPublisher(this.bus);
+
+ private readonly stallWarningPublisher = new StallWarningPublisher(this.bus, 0.9);
+
private readonly pseudoFwc = new PseudoFWC(this.bus, this);
constructor() {
@@ -32,6 +43,8 @@ class A32NX_EWD extends BaseInstrument {
this.backplane.addInstrument('Clock', this.clock);
this.backplane.addPublisher('SimVars', this.simVarPublisher);
this.backplane.addPublisher('FuelSystem', this.fuelSystemPublisher);
+ this.backplane.addPublisher('adc', this.adcPublisher);
+ this.backplane.addPublisher('stallWarning', this.stallWarningPublisher);
this.backplane.addInstrument('Fwc', this.pseudoFwc);
}
diff --git a/fbw-a32nx/src/systems/instruments/src/PFD/PFD.tsx b/fbw-a32nx/src/systems/instruments/src/PFD/PFD.tsx
index 9d92c3adafd..f90f2cc01db 100644
--- a/fbw-a32nx/src/systems/instruments/src/PFD/PFD.tsx
+++ b/fbw-a32nx/src/systems/instruments/src/PFD/PFD.tsx
@@ -12,6 +12,7 @@ import {
} from '@flybywiresim/fbw-sdk';
import { A320Failure } from '@failures';
+import { AttitudeIndicatorWarnings } from '@flybywiresim/pfd';
import { DmcLogicEvents } from '../MsfsAvionicsCommon/providers/DmcPublisher';
import { LagFilter } from './PFDUtils';
import { Arinc429Values } from './shared/ArincValueProvider';
@@ -173,6 +174,7 @@ export class PFDComponent extends DisplayComponent {
+
72;
+
+ if (showLower) {
+ this.lowerRef.instance.setAttribute('visibility', 'visible');
+ } else {
+ this.lowerRef.instance.setAttribute('visibility', 'hidden');
+ }
+ }
+ }
+
onAfterRender(node: VNode): void {
super.onAfterRender(node);
@@ -399,6 +428,7 @@ export class AirspeedIndicatorOfftape extends DisplayComponent<{ bus: ArincEvent
.handle((g) => {
this.leftMainGearCompressed = g;
this.onGround = this.rightMainGearCompressed || g;
+ this.setOutline();
});
sub
@@ -407,36 +437,15 @@ export class AirspeedIndicatorOfftape extends DisplayComponent<{ bus: ArincEvent
.handle((g) => {
this.rightMainGearCompressed = g;
this.onGround = this.leftMainGearCompressed || g;
+ this.setOutline();
});
sub
.on('speedAr')
.withArinc429Precision(2)
.handle((speed) => {
- let airspeedValue: number;
- if (speed.isFailureWarning() || (speed.isNoComputedData() && !this.onGround)) {
- airspeedValue = NaN;
- } else if (speed.isNoComputedData()) {
- airspeedValue = 30;
- } else {
- airspeedValue = speed.value;
- }
- if (Number.isNaN(airspeedValue)) {
- this.offTapeRef.instance.classList.add('HiddenElement');
- this.offTapeFailedRef.instance.classList.remove('HiddenElement');
- } else {
- this.offTapeRef.instance.classList.remove('HiddenElement');
- this.offTapeFailedRef.instance.classList.add('HiddenElement');
-
- const clampedSpeed = Math.max(Math.min(airspeedValue, 660), 30);
- const showLower = clampedSpeed > 72;
-
- if (showLower) {
- this.lowerRef.instance.setAttribute('visibility', 'visible');
- } else {
- this.lowerRef.instance.setAttribute('visibility', 'hidden');
- }
- }
+ this.airSpeed = speed;
+ this.setOutline();
});
sub
diff --git a/fbw-a32nx/src/systems/instruments/src/PFD/animations.scss b/fbw-a32nx/src/systems/instruments/src/PFD/animations.scss
index 1a2f5c3f992..b437cc1eb88 100644
--- a/fbw-a32nx/src/systems/instruments/src/PFD/animations.scss
+++ b/fbw-a32nx/src/systems/instruments/src/PFD/animations.scss
@@ -7,10 +7,10 @@
@mixin GenericPulsingStroke($color, $name) {
@keyframes #{$name} {
- 0% {stroke: scale-color($color, $lightness: -30%);}
- 50% {stroke: scale-color($color, $lightness: -30%);}
- 51% {stroke: scale-color($color, $lightness: 30%);}
- 100% {stroke: scale-color($color, $lightness: 30%);}
+ 0% {stroke: scale-color($color, $lightness: -50%);}
+ 50% {stroke: scale-color($color, $lightness: -50%);}
+ 51% {stroke: scale-color($color, $lightness: 50%);}
+ 100% {stroke: scale-color($color, $lightness: 50%);}
}
animation-name: $name;
}
@@ -75,3 +75,9 @@
animation-duration: 200ms;
animation-iteration-count: infinite;
}
+
+.RwyAheadAnimation {
+ @include GenericPulsingStroke($display-yellow, pulse-yellow-stroke);
+ animation-duration: 1s;
+ animation-iteration-count: infinite;
+}
diff --git a/fbw-a32nx/src/systems/instruments/src/PFD/config.json b/fbw-a32nx/src/systems/instruments/src/PFD/config.json
index ade492578f8..5fd4d2dc9f4 100644
--- a/fbw-a32nx/src/systems/instruments/src/PFD/config.json
+++ b/fbw-a32nx/src/systems/instruments/src/PFD/config.json
@@ -1,4 +1,8 @@
{
"index": "./instrument.tsx",
- "isInteractive": false
+ "isInteractive": false,
+ "extraDeps": [
+ "../MsfsAvionicsCommon",
+ "fbw-common/src/systems/instruments/src/PFD"
+ ]
}
diff --git a/fbw-a32nx/src/systems/instruments/src/PFD/instrument.tsx b/fbw-a32nx/src/systems/instruments/src/PFD/instrument.tsx
index 5e18ae5db8c..98a6f2310ab 100644
--- a/fbw-a32nx/src/systems/instruments/src/PFD/instrument.tsx
+++ b/fbw-a32nx/src/systems/instruments/src/PFD/instrument.tsx
@@ -2,11 +2,12 @@
//
// SPDX-License-Identifier: GPL-3.0
-import { Clock, FSComponent, HEventPublisher, Subject } from '@microsoft/msfs-sdk';
+import { Clock, FSComponent, HEventPublisher, InstrumentBackplane, Subject } from '@microsoft/msfs-sdk';
import { ArincEventBus } from '@flybywiresim/fbw-sdk';
+import { FwcPublisher, RopRowOansPublisher } from '@flybywiresim/msfs-avionics-common';
-import { DmcPublisher } from 'instruments/src/MsfsAvionicsCommon/providers/DmcPublisher';
-import { FmsDataPublisher } from 'instruments/src/MsfsAvionicsCommon/providers/FmsDataPublisher';
+import { FmsDataPublisher } from '../MsfsAvionicsCommon/providers/FmsDataPublisher';
+import { DmcPublisher } from '../MsfsAvionicsCommon/providers/DmcPublisher';
import { getDisplayIndex, PFDComponent } from './PFD';
import { AdirsValueProvider } from '../MsfsAvionicsCommon/AdirsValueProvider';
import { ArincValueProvider } from './shared/ArincValueProvider';
@@ -18,24 +19,32 @@ import './style.scss';
// TODO move this whole thing to InstrumentBackplane and GameStateProvider
class A32NX_PFD extends BaseInstrument {
- private bus: ArincEventBus;
+ private readonly bus = new ArincEventBus();
- private simVarPublisher: PFDSimvarPublisher;
+ private readonly backplane = new InstrumentBackplane();
- private readonly hEventPublisher;
+ private readonly simVarPublisher = new PFDSimvarPublisher(this.bus);
- private readonly arincProvider: ArincValueProvider;
+ private readonly hEventPublisher = new HEventPublisher(this.bus);
- private readonly simplaneValueProvider: SimplaneValueProvider;
+ private readonly arincProvider = new ArincValueProvider(this.bus);
- private readonly clock: Clock;
+ private readonly simplaneValueProvider = new SimplaneValueProvider(this.bus);
+ private readonly clock = new Clock(this.bus);
+
+ // FIXME fit this into the normal backplane pattern
private adirsValueProvider: AdirsValueProvider;
- private readonly dmcPublisher: DmcPublisher;
+ private readonly dmcPublisher = new DmcPublisher(this.bus);
+ // FIXME fit this into the normal backplane pattern
private fmsDataPublisher: FmsDataPublisher;
+ private readonly ropRowOansPublisher = new RopRowOansPublisher(this.bus);
+
+ private readonly fwcPublisher = new FwcPublisher(this.bus);
+
/**
* "mainmenu" = 0
* "loading" = 1
@@ -46,13 +55,15 @@ class A32NX_PFD extends BaseInstrument {
constructor() {
super();
- this.bus = new ArincEventBus();
- this.simVarPublisher = new PFDSimvarPublisher(this.bus);
- this.hEventPublisher = new HEventPublisher(this.bus);
- this.arincProvider = new ArincValueProvider(this.bus);
- this.simplaneValueProvider = new SimplaneValueProvider(this.bus);
- this.clock = new Clock(this.bus);
- this.dmcPublisher = new DmcPublisher(this.bus);
+
+ this.backplane.addPublisher('PfdSimvars', this.simVarPublisher);
+ this.backplane.addPublisher('hEvent', this.hEventPublisher);
+ this.backplane.addInstrument('arincProvider', this.arincProvider);
+ this.backplane.addInstrument('simPlane', this.simplaneValueProvider);
+ this.backplane.addInstrument('Clock', this.clock);
+ this.backplane.addPublisher('Dmc', this.dmcPublisher);
+ this.backplane.addPublisher('RopRowOans', this.ropRowOansPublisher);
+ this.backplane.addPublisher('Fwc', this.fwcPublisher);
}
get templateID(): string {
@@ -79,9 +90,7 @@ class A32NX_PFD extends BaseInstrument {
const stateSubject = Subject.create<'L' | 'R'>(getDisplayIndex() === 1 ? 'L' : 'R');
this.fmsDataPublisher = new FmsDataPublisher(this.bus, stateSubject);
- this.arincProvider.init();
- this.clock.init();
- this.dmcPublisher.startPublish();
+ this.backplane.init();
FSComponent.render(, document.getElementById('PFD_CONTENT'));
@@ -95,21 +104,16 @@ class A32NX_PFD extends BaseInstrument {
public Update(): void {
super.Update();
+ this.backplane.onUpdate();
+
if (this.gameState !== 3) {
const gamestate = this.getGameState();
if (gamestate === 3) {
- this.simVarPublisher.startPublish();
- this.hEventPublisher.startPublish();
this.adirsValueProvider.start();
- this.dmcPublisher.startPublish();
this.fmsDataPublisher.startPublish();
}
this.gameState = gamestate;
} else {
- this.simVarPublisher.onUpdate();
- this.simplaneValueProvider.onUpdate();
- this.clock.onUpdate();
- this.dmcPublisher.onUpdate();
this.fmsDataPublisher.onUpdate();
}
}
diff --git a/fbw-a32nx/src/systems/instruments/src/PFD/shared/ArincValueProvider.ts b/fbw-a32nx/src/systems/instruments/src/PFD/shared/ArincValueProvider.ts
index d10f241fb84..5fafda83e16 100644
--- a/fbw-a32nx/src/systems/instruments/src/PFD/shared/ArincValueProvider.ts
+++ b/fbw-a32nx/src/systems/instruments/src/PFD/shared/ArincValueProvider.ts
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: GPL-3.0
-import { ConsumerSubject, MathUtils, Publisher, Subscription } from '@microsoft/msfs-sdk';
+import { ConsumerSubject, Instrument, MathUtils, Publisher, Subscription } from '@microsoft/msfs-sdk';
import { ArincEventBus, Arinc429Register, Arinc429Word, Arinc429WordData } from '@flybywiresim/fbw-sdk';
import { getDisplayIndex } from 'instruments/src/PFD/PFD';
@@ -55,7 +55,7 @@ export interface Arinc429Values {
fmTransAltRaw: number;
fmTransLvlRaw: number;
}
-export class ArincValueProvider {
+export class ArincValueProvider implements Instrument {
private roll = new Arinc429Word(0);
private pitch = Arinc429Register.empty();
@@ -120,6 +120,7 @@ export class ArincValueProvider {
constructor(private readonly bus: ArincEventBus) {}
+ /** @inheritdoc */
public init() {
const publisher = this.bus.getPublisher();
const subscriber = this.bus.getSubscriber();
@@ -518,6 +519,11 @@ export class ArincValueProvider {
this.fm2Healthy.sub(this.determineFmToUse.bind(this), true);
}
+ /** @inheritdoc */
+ public onUpdate(): void {
+ // noop
+ }
+
private determineAndPublishChosenRadioAltitude(publisher: Publisher) {
const ownRadioAltitudeHasData =
!this.ownRadioAltitude.isFailureWarning() && !this.ownRadioAltitude.isNoComputedData();
diff --git a/fbw-a32nx/src/systems/instruments/src/PFD/shared/SimplaneValueProvider.ts b/fbw-a32nx/src/systems/instruments/src/PFD/shared/SimplaneValueProvider.ts
index 076fc005874..5bd89b4827f 100644
--- a/fbw-a32nx/src/systems/instruments/src/PFD/shared/SimplaneValueProvider.ts
+++ b/fbw-a32nx/src/systems/instruments/src/PFD/shared/SimplaneValueProvider.ts
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: GPL-3.0
-import { Publisher } from '@microsoft/msfs-sdk';
+import { Instrument, Publisher } from '@microsoft/msfs-sdk';
import { ArincEventBus } from '@flybywiresim/fbw-sdk';
export type SimplaneBaroMode = 'QNH' | 'QFE' | 'STD';
@@ -17,14 +17,20 @@ export interface SimplaneValues {
selectedAltitude: number;
baroMode: SimplaneBaroMode;
}
-export class SimplaneValueProvider {
+export class SimplaneValueProvider implements Instrument {
private publisher: Publisher;
constructor(private readonly bus: ArincEventBus) {
this.publisher = this.bus.getPublisher();
}
- public onUpdate() {
+ /** @inheritdoc */
+ public init(): void {
+ // noop
+ }
+
+ /** @inheritdoc */
+ public onUpdate(): void {
const units = Simplane.getPressureSelectedUnits();
const pressure = Simplane.getPressureValue(units);
const isSelected = Simplane.getAutoPilotAirspeedSelected();
diff --git a/fbw-a32nx/src/systems/instruments/src/PFD/tsconfig.json b/fbw-a32nx/src/systems/instruments/src/PFD/tsconfig.json
index 2d56720368e..85632976a98 100644
--- a/fbw-a32nx/src/systems/instruments/src/PFD/tsconfig.json
+++ b/fbw-a32nx/src/systems/instruments/src/PFD/tsconfig.json
@@ -27,7 +27,9 @@
"@shared/*": ["./shared/src/*"],
"@tcas/*": ["./tcas/src/*"],
"@typings/*": ["../../../fbw-common/src/typings/*"],
- "@flybywiresim/fbw-sdk": ["../../../fbw-common/src/systems/index-no-react.ts"]
+ "@flybywiresim/fbw-sdk": ["../../../fbw-common/src/systems/index-no-react.ts"],
+ "@flybywiresim/pfd": ["../../../fbw-common/src/systems/instruments/src/PFD/index.ts"],
+ "@flybywiresim/msfs-avionics-common": ["../../../fbw-common/src/systems/instruments/src/MsfsAvionicsCommon/index.ts"]
}
}
}
diff --git a/fbw-a32nx/src/systems/tsconfig.json b/fbw-a32nx/src/systems/tsconfig.json
index a97b05c32e6..8da0d744708 100644
--- a/fbw-a32nx/src/systems/tsconfig.json
+++ b/fbw-a32nx/src/systems/tsconfig.json
@@ -28,7 +28,8 @@
"@flybywiresim/fbw-sdk": ["../../../fbw-common/src/systems/index.ts"],
"@flybywiresim/flypad": ["../../../fbw-common/src/systems/instruments/src/EFB/index.ts"],
"@flybywiresim/clock": ["../../../fbw-common/src/systems/instruments/src/Clock/index.ts"],
- "@flybywiresim/bat": ["../../../fbw-common/src/systems/instruments/src/BAT/index.ts"]
+ "@flybywiresim/bat": ["../../../fbw-common/src/systems/instruments/src/BAT/index.ts"],
+ "@flybywiresim/pfd": ["../../../fbw-common/src/systems/instruments/src/PFD/index.ts"]
}
},
}