Skip to content

Commit

Permalink
feat(bleed): Ground air start (#8240)
Browse files Browse the repository at this point in the history
* Initial start of implementation

* Implemented ASU with Simvar, moved MSFS bleed activation logic to WASM

* Implemented EFB Groundservice and added FADEC control over Engine starter

* Simplified MSFS APU management

* forgot smth

* Engines now only keep starting when bleed demand is being sastisfied.
Connected APU SD page Pressure indication to pneumatic system

* Document Simvars and add Changelog, add tests and remove unused APU.js

* Fixed mixed case variable and fixed up testcase

* fix lint issue

* check which folders take up most space

* try different check

* revert changes

* reworked engine start and shutdown logic to better interface with the starter valve

* Changed ASU valve to purely pneumatic

* removed leftover debug statement

* Apply suggestions from code review

Co-authored-by: Saschl <[email protected]>

* Removed import of deleted JS file

* made sure not to provide bleed pressure in flight

* removed non functional behavior code

* Applied code review

* fix lint and build errors

* fix: changlog 0.12

* fix: remove added newline

* Changed Icon

* Update .github/CHANGELOG.md

Co-authored-by: Michael Corcoran <[email protected]>

* changed initialization of OPVs

* Tweak to WAI

* Connected starter valve indication on ENG SD
page with pneumatics + corrected starter valve closing logic

* adjusted parameters for ignition start and startervalve closeing

---------

Co-authored-by: Saschl <[email protected]>
Co-authored-by: Michael Corcoran <[email protected]>
  • Loading branch information
3 people authored Nov 6, 2023
1 parent 39b3842 commit 1b6309d
Show file tree
Hide file tree
Showing 31 changed files with 507 additions and 223 deletions.
1 change: 1 addition & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
1. [PFD] The ILS frequency is now visible even when a LOC is not received - @tracernz (Mike)
1. [ATSU] Fixed GROUND REQ page not updating after successful station insert - @BravoMike99 (bruno_pt99)
1. [MCDU] Dash alternate time predictions if alternate fuel is manually inserted @BravoMike99 (bruno_pt99)
1. [BLEED] Add Air starter unit to enable ground starts - @Maximilian-Reuter (\_Chaoz_)

## 0.11.0

Expand Down
8 changes: 8 additions & 0 deletions fbw-a32nx/docs/a320-simvars.md
Original file line number Diff line number Diff line change
Expand Up @@ -2824,6 +2824,10 @@ In the variables below, {number} should be replaced with one item in the set: {

## Pneumatic

- A32NX_ASU_TURNED_ON:
- Turns the Air Starter Unit on or off
- Bool

- A32NX_PNEU_ENG_{number}_IP_PRESSURE:
- Pressure in intermediate pressure compression chamber
- PSI
Expand Down Expand Up @@ -2934,6 +2938,10 @@ In the variables below, {number} should be replaced with one item in the set: {
- 1
- 2

- A32NX_PNEU_APU_BLEED_CONTAINER_PRESSURE:
- Indicates the APU internal bleed pressure.
- PSI absolute

- A32NX_PNEU_XBLEED_VALVE_FULLY_OPEN:
- Indicates whether the cross bleed air valve is fully open
- Bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ Junction.2 = Name:APUJunction#OutputOnlyLines:JuncAPUToAPUPump
Junction.3 = Name:RightJunction#InputOnlyLines:PumpRight1ToJuncRight,PumpRight2ToJuncRight#OutputOnlyLines:JuncRightToEngValveRight
Junction.4 = Name:CenterTransferJunctionL#InputOnlyLines:CenterXferDisableValveLToCenterXferJunctionL#OutputOnlyLines:CenterXferJunctionLToAutoCenterXferValveL,CenterXferJunctionLToLeftInner#Option:CenterXferJunctionLToAutoCenterXferValveL#Option:CenterXferJunctionLToLeftInner
Junction.5 = Name:CenterTransferJunctionR#InputOnlyLines:CenterXferDisableValveRToCenterXferJunctionR#OutputOnlyLines:CenterXferJunctionRToAutoCenterXferValveR,CenterXferJunctionRToRightInner#Option:CenterXferJunctionRToAutoCenterXferValveR#Option:CenterXferJunctionRToRightInner
Valve.1 = Name:LeftEngineValve#OpeningTime:3#Circuit:1
Valve.2 = Name:RightEngineValve#OpeningTime:3#Circuit:2
Valve.1 = Name:LeftEngineValve#OpeningTime:1.7#Circuit:1
Valve.2 = Name:RightEngineValve#OpeningTime:1.7#Circuit:2
Valve.3 = Name:CrossFeedValve#OpeningTime:3#Circuit:3
Valve.4 = Name:LeftTransferValve2#Circuit:4
Valve.5 = Name:RightTransferValve2#Circuit:5
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ class A32NX_Core {
module: new A32NX_ADIRS(),
updateInterval: 100,
},
{
name: 'APU',
module: new A32NX_APU(),
updateInterval: 100,
},
{
name: 'BaroSelector',
module: new A32NX_BaroSelector(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class A32NX_FADEC {
this.lastActiveIgniterAutostart ^= 1; // toggles Igniter
}

this.igniting = ignitionState && (engineState === 2 || engineState === 3) && n2Percent > 18 && n2Percent < 55;
this.igniting = ignitionState && (engineState === 2 || engineState === 3) && n2Percent > 25 && n2Percent < 55;

if (this.lastIgnitionState !== ignitionState && !ignitionState) {
this.fadecTimer = Math.max(30, this.fadecTimer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@
<script type="text/html" import-script="/Pages/A32NX_Core/A32NX_SoundManager.js"></script>
<script type="text/html" import-script="/Pages/A32NX_Core/A32NX_BaroSelector.js"></script>
<script type="text/html" import-script="/Pages/A32NX_Core/A32NX_ADIRS.js"></script>
<script type="text/html" import-script="/Pages/A32NX_Core/A32NX_APU.js"></script>
<script type="text/html" import-script="/Pages/A32NX_Core/A32NX_BrakeTemp.js"></script>
<script type="text/html" import-script="/Pages/A32NX_Core/A32NX_Refuel.js"></script>
<script type="text/html" import-script="/Pages/A32NX_Core/A32NX_DMC.js"></script>
Expand Down
13 changes: 0 additions & 13 deletions fbw-a32nx/src/behavior/src/A32NX_Interior_Misc.xml
Original file line number Diff line number Diff line change
Expand Up @@ -319,28 +319,15 @@
<ANIMTIP_1>TT:COCKPIT.TOOLTIPS.ENGINE#ID#_MASTER_TURN_OFF</ANIMTIP_1>
</DefaultTemplateParameters>
<Component ID="#NODE_ID#" Node="#NODE_ID#">
<Update Frequency="5">
(* This Update Ensures that the state of some simvars that are linked to the same button is consistent. *)
(A:FUELSYSTEM VALVE SWITCH:#VALVE_ID#, Bool) sp1
(A:GENERAL ENG STARTER:#ID#, Bool) l1 != if{
(&gt;K:TOGGLE_STARTER#ID#)
}
</Update>
<UseTemplate Name="ASOBO_GT_Switch_Code">
<ANIM_CODE>
(A:FUELSYSTEM VALVE SWITCH:#VALVE_ID#, Bool) 100 *
</ANIM_CODE>
<LEFT_SINGLE_CODE>
(A:FUELSYSTEM VALVE SWITCH:#VALVE_ID#, Bool) if{
#VALVE_ID# (&gt;K:FUELSYSTEM_VALVE_CLOSE)
(A:GENERAL ENG STARTER:#ID#, Bool) if{
(&gt;K:TOGGLE_STARTER#ID#)
}
} els{
#VALVE_ID# (&gt;K:FUELSYSTEM_VALVE_OPEN)
(A:GENERAL ENG STARTER:#ID#, Bool) ! if{
(&gt;K:TOGGLE_STARTER#ID#)
}
}
</LEFT_SINGLE_CODE>
</UseTemplate>
Expand Down
3 changes: 2 additions & 1 deletion fbw-a32nx/src/localization/flypad/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@
"FuelTruck": "Fuel Truck",
"JetBridge": "Jet Bridge",
"Title": "Services",
"WheelChocks": "Wheel Chocks"
"WheelChocks": "Wheel Chocks",
"AirStarterUnit": "Air Starter Unit"
},
"Title": "Ground"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
TriangleFill as Chock,
Truck,
VinylFill as Wheel,
Fan,
} from 'react-bootstrap-icons';
import { ActionCreatorWithOptionalPayload } from '@reduxjs/toolkit';
import { t } from '../../../../translation';
Expand All @@ -30,6 +31,7 @@ import {
setFuelTruckButtonState,
setGpuButtonState,
setJetWayButtonState,
setAsuButtonState,
} from '../../../../Store/features/groundServicePage';

interface ServiceButtonWrapperProps {
Expand Down Expand Up @@ -59,7 +61,8 @@ enum ServiceButton {
BaggageTruck,
AftLeftDoor,
AftRightDoor,
CateringTruck
CateringTruck,
AirStarterUnit
}

// Possible states of buttons
Expand Down Expand Up @@ -123,6 +126,7 @@ export const A320Services: React.FC = () => {
const [cargoDoorOpen] = useSimVar('A:INTERACTIVE POINT OPEN:5', 'Percent over 100', 100);
const [gpuActive] = useSimVar('A:INTERACTIVE POINT OPEN:8', 'Percent over 100', 100);
const [fuelingActive] = useSimVar('A:INTERACTIVE POINT OPEN:9', 'Percent over 100', 100);
const [asuActive, setAsuActive] = useSimVar('L:A32NX_ASU_TURNED_ON', 'Bool', 100);

// Wheel Chocks and Cones
const [isGroundEquipmentVisible] = useSimVar('L:A32NX_GND_EQP_IS_VISIBLE', 'bool', 500);
Expand All @@ -145,6 +149,7 @@ export const A320Services: React.FC = () => {
const toggleCateringTruck = () => SimVar.SetSimVarValue('K:REQUEST_CATERING', 'bool', true);
const toggleFuelTruck = () => SimVar.SetSimVarValue('K:REQUEST_FUEL_KEY', 'bool', true);
const toggleGpu = () => SimVar.SetSimVarValue('K:REQUEST_POWER_SUPPLY', 'bool', true);
const toggleAsu = () => setAsuActive(!asuActive);

// Button states
const {
Expand All @@ -158,6 +163,7 @@ export const A320Services: React.FC = () => {
gpuButtonState,
baggageButtonState,
cateringButtonState,
asuButtonState,
} = useAppSelector((state) => state.groundServicePage);

// Required so these can be used inside the useTimeout callback
Expand Down Expand Up @@ -324,6 +330,10 @@ export const A320Services: React.FC = () => {
aftRightDoorOpen);
toggleCateringTruck();
break;
case ServiceButton.AirStarterUnit:
handleSimpleService(ServiceButton.AirStarterUnit, asuButtonState, setAsuButtonState);
toggleAsu();
break;
default:
break;
}
Expand Down Expand Up @@ -451,6 +461,11 @@ export const A320Services: React.FC = () => {
);
}, [aftRightDoorOpen]);

// Asu
useEffect(() => {
simpleServiceListenerHandling(asuButtonState, setAsuButtonState, asuActive);
}, [asuActive]);

// Pushback or movement start --> disable buttons and close doors
// Enable buttons if all have been disabled before
useEffect(() => {
Expand All @@ -465,6 +480,8 @@ export const A320Services: React.FC = () => {
dispatch(setBoarding3DoorButtonState(ServiceButtonState.DISABLED));
dispatch(setServiceDoorButtonState(ServiceButtonState.DISABLED));
dispatch(setCateringButtonState(ServiceButtonState.DISABLED));
dispatch(setAsuButtonState(ServiceButtonState.DISABLED));

if (cabinLeftDoorOpen === 1) {
toggleCabinLeftDoor();
}
Expand Down Expand Up @@ -492,7 +509,8 @@ export const A320Services: React.FC = () => {
fuelTruckButtonState,
gpuButtonState,
baggageButtonState,
cateringButtonState]
cateringButtonState,
asuButtonState]
.every((buttonState) => buttonState === ServiceButtonState.DISABLED)
) {
dispatch(setBoarding1DoorButtonState(ServiceButtonState.INACTIVE));
Expand Down Expand Up @@ -549,6 +567,15 @@ export const A320Services: React.FC = () => {
<Truck size={36} />
</GroundServiceButton>

{/* Air Starter Unit */}
<GroundServiceButton
name={t('Ground.Services.AirStarterUnit')}
state={asuButtonState}
onClick={() => handleButtonClick(ServiceButton.AirStarterUnit)}
>
<Fan size={36} />
</GroundServiceButton>

</ServiceButtonWrapper>

<ServiceButtonWrapper xr={930} y={600} className="">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface ButtonSelectionState {
gpuButtonState: ServiceButtonState
baggageButtonState: ServiceButtonState
cateringButtonState: ServiceButtonState
asuButtonState: ServiceButtonState
}

// hack to fix initialization issue for ACE/vite
Expand All @@ -34,6 +35,7 @@ let initialState: ButtonSelectionState = {
jetWayButtonState: ServiceButtonState.DISABLED,
baggageButtonState: ServiceButtonState.DISABLED,
cateringButtonState: ServiceButtonState.DISABLED,
asuButtonState: ServiceButtonState.DISABLED,
};

const setInitialState = () => {
Expand All @@ -48,6 +50,7 @@ const setInitialState = () => {
jetWayButtonState: ServiceButtonState.INACTIVE,
baggageButtonState: ServiceButtonState.INACTIVE,
cateringButtonState: ServiceButtonState.INACTIVE,
asuButtonState: (SimVar.GetSimVarValue('L:A32NX_ASU_TURNED_ON', 'Bool') === 1.0 ? ServiceButtonState.ACTIVE : ServiceButtonState.INACTIVE),
};
};

Expand Down Expand Up @@ -92,6 +95,9 @@ export const buttonsSlice = createSlice({
setCateringButtonState: (state, action: PayloadAction<ServiceButtonState>) => {
state.cateringButtonState = action.payload;
},
setAsuButtonState: (state, action: PayloadAction<ServiceButtonState>) => {
state.asuButtonState = action.payload;
},
},
});

Expand All @@ -106,6 +112,7 @@ export const {
setGpuButtonState,
setBaggageButtonState,
setCateringButtonState,
setAsuButtonState,
} = buttonsSlice.actions;

export default buttonsSlice.reducer;
8 changes: 6 additions & 2 deletions fbw-a32nx/src/systems/instruments/src/SD/Pages/Apu/Apu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,12 @@ const ApuBleed = ({ x, y } : ComponentPositionProps) => {
const [apuBleedPbOn] = useSimVar('L:A32NX_OVHD_PNEU_APU_BLEED_PB_IS_ON', 'Bool', 1000);
const [apuBleedPbOnConfirmed, setApuBleedPbOnConfirmed] = useState(false);
const [apuBleedOpen] = useSimVar('L:A32NX_APU_BLEED_AIR_VALVE_OPEN', 'Bool', 1000);
const [apuBleedPressure] = useSimVar('L:APU_BLEED_PRESSURE', 'PSI', 1000);
const displayedBleedPressure = Math.round(apuBleedPressure / 2) * 2; // APU bleed pressure is shown in steps of two.
const [apuBleedPressureAbsolute] = useSimVar('L:A32NX_PNEU_APU_BLEED_CONTAINER_PRESSURE', 'PSI', 1000);
// FIXME Since APU pressure is constant right now we also subtract a 1 bar / 14.7 psi static pressure to arrive at the correct pressure
// Should be ADIRU static pressure
const apuBleedPressureGauge = apuBleedPressureAbsolute - 14.7;
// APU bleed pressure is shown in steps of two.
const displayedBleedPressure = apuBleedOpen ? Math.round(apuBleedPressureGauge / 2) * 2 : 0;
// This assumes that the SD is displayed by DMC 1, which is the case during normal operation.
const [attHdgPosition] = useSimVar('L:A32NX_ATT_HDG_SWITCHING_KNOB', 'Position', 100);
const adrSource = attHdgPosition === 0 ? 3 : 1;
Expand Down
24 changes: 3 additions & 21 deletions fbw-a32nx/src/systems/instruments/src/SD/Pages/Eng/Eng.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,8 @@ const QuantityGauge = ({ x, y, engineNumber, fadecOn }: ComponentPositionProps)
};

const ValveGroup = ({ x, y, engineNumber, fadecOn }: ComponentPositionProps) => {
const [isValveOpen, setIsValveOpen] = useState(false);
const [isValveOpen] = useSimVar(`L:A32NX_PNEU_ENG_${engineNumber}_STARTER_VALVE_OPEN`, 'bool', 250);
const [n2Percent] = useSimVar(`ENG N2 RPM:${engineNumber}`, 'percent', 50);
const [isEngineStarting] = useSimVar(`GENERAL ENG STARTER:${engineNumber}`, 'bool', 300);
const [engSelectorPosition] = useSimVar('L:XMLVAR_ENG_MODE_SEL', 'Enum');
const [igniterAactive] = useSimVar(`L:A32NX_FADEC_IGNITER_A_ACTIVE_ENG${engineNumber}`, 'bool', 300);
const [igniterBactive] = useSimVar(`L:A32NX_FADEC_IGNITER_B_ACTIVE_ENG${engineNumber}`, 'bool', 300);
Expand All @@ -253,28 +252,11 @@ const ValveGroup = ({ x, y, engineNumber, fadecOn }: ComponentPositionProps) =>

const activeVisibility = fadecOn ? 'visible' : 'hidden';
const inactiveVisibility = fadecOn ? 'hidden' : 'visible';
// This useEffect ensures that the valve is only opened if the engine mode selector is set to IGN/START, the engine is starting, and n2% is below 50
useEffect(() => {
let timeout: number;
if (isEngineStarting && n2Percent < 50 && engSelectorPosition === 2) {
timeout = setTimeout(() => setIsValveOpen(true), 1200);
} else {
timeout = setTimeout(() => setIsValveOpen(false), 1200);
}

return () => clearTimeout(timeout);
}, [isEngineStarting, engSelectorPosition]);

useEffect(() => {
if (n2Percent >= 50) {
setIsValveOpen(false);
}
}, [n2Percent]);

return (
<SvgGroup x={0} y={0} className={`${engSelectorPosition !== 2 && 'Hidden'}`}>
<text x={x - 7} y={y} className={`FillGreen FontMedium TextCenter ${!(isValveOpen && igniterAactive) && 'Hidden'}`}>A</text>
<text x={x + 7} y={y} className={`FillGreen FontMedium TextCenter ${!(isValveOpen && igniterBactive) && 'Hidden'}`}>B</text>
<text x={x - 7} y={y} className={`FillGreen FontMedium TextCenter ${!igniterAactive && 'Hidden'}`}>A</text>
<text x={x + 7} y={y} className={`FillGreen FontMedium TextCenter ${!igniterBactive && 'Hidden'}`}>B</text>
<g className="StartValveDiagram">
{/* 375 to 30 */}
<circle r={14} cx={x} cy={y + 30} />
Expand Down
1 change: 1 addition & 0 deletions fbw-a32nx/src/wasm/fadec_a320/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ include_directories(
./src
${FBW_ROOT}/fbw-common/src/wasm/fadec_common/src
${FBW_ROOT}/fbw-common/src/wasm/fbw_common/src
${FBW_ROOT}/fbw-common/src/wasm/fbw_common/src/inih
)

add_executable(flybywire-a32nx-fadec
Expand Down
4 changes: 3 additions & 1 deletion fbw-a32nx/src/wasm/fadec_a320/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ clang++ \
-I "${COMMON_DIR}/fadec_common/src" \
-I "${COMMON_DIR}/fbw_common/src/inih" \
-I "${DIR}/common" \
"${DIR}/src/FadecGauge.cpp"
"${DIR}/src/FadecGauge.cpp" \
"${DIR}/src/Arinc429.cpp" \
"${DIR}/src/Arinc429Utils.cpp"

# restore directory
popd
Expand Down
1 change: 1 addition & 0 deletions fbw-a32nx/src/wasm/fadec_a320/src/Arinc429.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "../../fbw_a320/src/Arinc429.cpp"
1 change: 1 addition & 0 deletions fbw-a32nx/src/wasm/fadec_a320/src/Arinc429.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "../../fbw_a320/src/Arinc429.h"
1 change: 1 addition & 0 deletions fbw-a32nx/src/wasm/fadec_a320/src/Arinc429Utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "../../fbw_a320/src/Arinc429Utils.cpp"
1 change: 1 addition & 0 deletions fbw-a32nx/src/wasm/fadec_a320/src/Arinc429Utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "../../fbw_a320/src/Arinc429Utils.h"
Loading

0 comments on commit 1b6309d

Please sign in to comment.