Skip to content

Commit

Permalink
fix(a380/mfd): RTE RSV, AMI defaults, trip fuel, FOB & ETA in fuel an…
Browse files Browse the repository at this point in the history
…d load (#9540)

* fix(mfd): dest eta & extra fuel in fuel & load

* mfd: update AMI defaults for taxi fuel & rsv max

* fix(mfd): add trip time & change eta based on phase

* fix(mfd): remove hardcoded rte reserve percentage

* fix trip time eta. Hook up RTE RSV to percentages

* fix manual entry input size of percentage

* fix FOB display & manual route reserve entry

* clear pilot route reserve on percentage entry

* clean RTE RSV fields on takeoff phase. Fix fuel unit conversion

* fix extra fuel in non preflight phase

* Update CHANGELOG.md

* handle NaN input in mfd ETA utils function

---------

Co-authored-by: alepouna <[email protected]>
  • Loading branch information
BravoMike99 and alepouna authored Jan 12, 2025
1 parent 77296a8 commit a90b1ae
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 46 deletions.
1 change: 1 addition & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
1. [A380X/FMS] Added SURV Status & Switching page with TCAS fault indication - @Frenkii (Moritz)
1. [FMS] Fix T/D not showing in selected speed - @BlueberryKing (BlueberryKing)
1. [FMS] Fix Pause at T/D not working in selected speed - @BlueberryKing (BlueberryKing)
1. [A380X/MFD] Various Fuel & Load Fixes - @BravoMike99 (bruno_pt99)
1. [MISC] Replaced brake temperature simulation with physics based model of brakes - @Gurgel100 (Pascal)
1. [A32NX/MCDU] Suppress TMPY FPLN when no modifications made in airways page - @robertxing2004 (robeet)
1. [A380X/FWS] No "auto brake off" callout when double pressing A/THR instinctive disconnect - @flogross89 (floridude)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,9 +664,9 @@ export class FlightManagementComputer implements FmcInterface {
this.acInterface.updateThrustReductionAcceleration();
}

pd.taxiFuelPilotEntry.set(null);
pd.routeReserveFuelPercentagePilotEntry.set(0.00001);
pd.routeReserveFuelWeightPilotEntry.set(0.00001);
pd.routeReserveFuelWeightPilotEntry.set(null);
pd.routeReserveFuelPercentagePilotEntry.set(0);
pd.routeReserveFuelWeightCalculated.set(0);

this.fmgc.data.climbPredictionsReferenceAutomatic.set(
this.guidanceController.verticalProfileComputationParametersObserver.get().fcuAltitude,
Expand Down
21 changes: 14 additions & 7 deletions fbw-a380x/src/systems/instruments/src/MFD/FMC/fmgc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,21 @@ export class FmgcData {
/** in percent. null if not set. */
public readonly routeReserveFuelPercentagePilotEntry = Subject.create<number | null>(null);

public readonly routeReserveFuelPercentage = this.routeReserveFuelPercentagePilotEntry.map((it) =>
it === null ? AirlineModifiableInformation.EK.rteRsv : it,
); // in percent

public readonly routeReserveFuelIsPilotEntered = MappedSubject.create(
([fuel, time]) => fuel !== null || time !== null,
this.routeReserveFuelWeightPilotEntry,
public readonly routeReserveFuelIsPilotEntered = this.routeReserveFuelWeightPilotEntry.map((it) => it !== null);

public readonly routeReserveFuelPercentage = MappedSubject.create(
([percentagePilotEntry, reservePilotEntry]) =>
reservePilotEntry !== null
? null
: percentagePilotEntry === null
? AirlineModifiableInformation.EK.rteRsv
: percentagePilotEntry,
this.routeReserveFuelPercentagePilotEntry,
this.routeReserveFuelWeightPilotEntry,
);

public readonly routeReserveFuelPercentageIsPilotEntered = this.routeReserveFuelPercentagePilotEntry.map(
(v) => v !== null,
);

public readonly paxNumber = Subject.create<number | null>(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { MfdSimvars } from 'instruments/src/MFD/shared/MFDSimvarPublisher';
import { FmgcFlightPhase } from '@shared/flightphase';
import { AirlineModifiableInformation } from '@shared/AirlineModifiableInformation';
import { Units } from '@flybywiresim/fbw-sdk';
import { getEtaFromUtcOrPresent } from '../../shared/utils';

interface MfdFmsFuelLoadProps extends AbstractMfdPageProps {}

Expand All @@ -42,9 +43,13 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {

private fuelPlanningIsDisabled = Subject.create<boolean>(true);

private DestinationAlternateTimeHeader = this.activeFlightPhase.map((v) =>
v === FmgcFlightPhase.Preflight ? 'TIME' : 'UTC',
);

private tripFuelWeight = Subject.create<number | null>(null);

private tripFuelTime = Subject.create<number | null>(null);
private tripFuelTime = Subject.create('--:--');

private costIndex = Subject.create<number | null>(null);

Expand Down Expand Up @@ -101,15 +106,12 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {

if (this.loadedFlightPlan.destinationAirport) {
this.destIcao.set(this.loadedFlightPlan.destinationAirport.ident);

const destPred = this.props.fmcService.master.guidanceController.vnavDriver.getDestinationPrediction();
if (destPred) {
const utcTime = SimVar.GetGlobalVarValue('ZULU TIME', 'seconds');
const eta = new Date((utcTime + destPred.secondsFromPresent) * 1000);
this.destEta.set(
`${eta.getHours().toString().padStart(2, '0')}:${eta.getMinutes().toString().padStart(2, '0')}`,
);
}

// TODO Should display ETA if EET present
this.destEta.set(
getEtaFromUtcOrPresent(destPred?.secondsFromPresent, this.activeFlightPhase.get() == FmgcFlightPhase.Preflight),
);
const destEfob = this.props.fmcService.master.fmgc.getDestEFOB(true);
this.destEfob.set(destEfob !== null ? destEfob.toFixed(1) : '---.-');
this.destEfobBelowMin.set(
Expand Down Expand Up @@ -163,21 +165,25 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {
this.fuelOnBoard.set(this.props.fmcService.master.fmgc.getFOB());
}

const destPred = this.props.fmcService.master.guidanceController.vnavDriver.getDestinationPrediction();
if (this.activeFlightPhase.get() === FmgcFlightPhase.Preflight) {
const destPred = this.props.fmcService.master.guidanceController.vnavDriver.getDestinationPrediction();
// EXTRA = BLOCK - TAXI - TRIP - MIN FUEL DEST - RTE RSV
const fob = this.props.fmcService.master.fmgc.getFOB() * 1_000;
const tripFuel =
fob - (destPred?.estimatedFuelOnBoard ? Units.poundToKilogram(destPred?.estimatedFuelOnBoard) : fob);
this.tripFuelWeight.set(tripFuel);
this.tripFuelTime.set(getEtaFromUtcOrPresent(destPred?.secondsFromPresent, true));

// Calculate Rte Rsv fuel for 5.0% reserve
// Calculate Rte Rsv fuel if not manually entered
const pilotEnteredReserveFuel = this.props.fmcService.master.fmgc.data.routeReserveFuelIsPilotEntered.get();
this.props.fmcService.master.fmgc.data.routeReserveFuelWeightCalculated.set(
tripFuel ? tripFuel * 0.05 : null,
);
this.props.fmcService.master.fmgc.data.routeReserveFuelWeightPilotEntry.set(
tripFuel ? tripFuel * 0.05 : null,
!pilotEnteredReserveFuel && tripFuel
? (tripFuel * this.props.fmcService.master.fmgc.data.routeReserveFuelPercentage.get()!) / 100
: null,
);
if (!pilotEnteredReserveFuel) {
this.props.fmcService.master.fmgc.data.routeReserveFuelWeightPilotEntry.set(null);
}

const block = this.props.fmcService.master.fmgc.data.blockFuel.get() ?? 0;
this.extraFuelWeight.set(
Expand All @@ -188,12 +194,16 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {
(this.props.fmcService.master.fmgc.data.routeReserveFuelWeight.get() ?? 0),
);
} else {
// EXTRA = FOB - TRIP - MIN FUEL DEST
this.extraFuelWeight.set(
(this.fuelOnBoard.get() ?? 0) -
((this.tripFuelWeight.get() ?? 0) -
(this.props.fmcService.master.fmgc.data.minimumFuelAtDestination.get() ?? 0)),
);
if (destPred) {
const fobKg = this.props.fmcService.master.fmgc.getFOB() * 1000;
const destFuelKg = Units.poundToKilogram(destPred?.estimatedFuelOnBoard);
const remainingTripFuel = fobKg - destFuelKg;
this.tripFuelWeight.set(remainingTripFuel);
this.tripFuelTime.set(getEtaFromUtcOrPresent(destPred.secondsFromPresent, true));
this.extraFuelWeight.set(
destFuelKg - (this.props.fmcService.master.fmgc.data.minimumFuelAtDestination.get() ?? 0),
);
}
}

this.updateDestAndAltnPredictions();
Expand Down Expand Up @@ -231,7 +241,7 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {
</div>
<div class="mfd-label-value-container">
<span class="mfd-label mfd-spacing-right">FOB</span>
<span class="mfd-value">{this.fuelOnBoard.map((it) => (it ? (it / 1000).toFixed(1) : '---.-'))}</span>
<span class="mfd-value">{this.fuelOnBoard.map((it) => (it ? it.toFixed(1) : '---.-'))}</span>
<span class="mfd-label-unit mfd-unit-trailing">T</span>
</div>
</div>
Expand Down Expand Up @@ -337,7 +347,7 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {
<span class="mfd-label-unit mfd-unit-trailing">T</span>
</div>
<div style="display: flex; justify-content: center; margin-bottom: 20px;">
<span class="mfd-value">{this.tripFuelTime.map((it) => new TimeHHMMFormat().format(it ?? 0))}</span>
<span class="mfd-value">{this.tripFuelTime}</span>
</div>
<div class="mfd-label mfd-spacing-right middleGrid">CI</div>
<div style="margin-bottom: 20px;">
Expand All @@ -359,7 +369,7 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {
<div class="mfd-label mfd-spacing-right middleGrid">RTE RSV</div>
<div style="margin-bottom: 20px;">
<InputField<number>
disabled={Subject.create(true)}
disabled={this.activeFlightPhase.map((it) => it >= FmgcFlightPhase.Takeoff)}
dataEntryFormat={
new WeightFormat(
Subject.create(AirlineModifiableInformation.EK.rsvMin),
Expand All @@ -371,7 +381,6 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {
}
enteredByPilot={this.props.fmcService.master.fmgc.data.routeReserveFuelIsPilotEntered}
value={this.props.fmcService.master.fmgc.data.routeReserveFuelWeight}
inactive={this.activeFlightPhase.map((it) => it >= FmgcFlightPhase.Takeoff)}
alignText="flex-end"
containerStyle="width: 150px;"
errorHandler={(e) => this.props.fmcService.master?.showFmsErrorMessage(e)}
Expand All @@ -381,14 +390,14 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {
</div>
<div style="margin-bottom: 20px; margin-left: 5px">
<InputField<number>
disabled={Subject.create(true)}
disabled={this.activeFlightPhase.map((it) => it >= FmgcFlightPhase.Takeoff)}
dataEntryFormat={new PercentageFormat(Subject.create(0), Subject.create(maxRteRsvFuelPerc))}
dataHandlerDuringValidation={async (v) =>
this.props.fmcService.master?.fmgc.data.routeReserveFuelPercentagePilotEntry.set(v)
}
enteredByPilot={this.props.fmcService.master.fmgc.data.routeReserveFuelIsPilotEntered}
dataHandlerDuringValidation={async (v) => {
this.props.fmcService.master?.fmgc.data.routeReserveFuelWeightPilotEntry.set(null);
this.props.fmcService.master?.fmgc.data.routeReserveFuelPercentagePilotEntry.set(v);
}}
enteredByPilot={this.props.fmcService.master.fmgc.data.routeReserveFuelPercentageIsPilotEntered}
value={this.props.fmcService.master.fmgc.data.routeReserveFuelPercentage}
inactive={this.activeFlightPhase.map((it) => it >= FmgcFlightPhase.Takeoff)}
alignText="center"
containerStyle="width: 120px;"
errorHandler={(e) => this.props.fmcService.master?.showFmsErrorMessage(e)}
Expand Down Expand Up @@ -476,7 +485,9 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {
<div style="display: grid; grid-template-columns: auto auto auto auto;">
<div class="mfd-fms-fuel-load-dest-grid-top-cell" />
<div class="mfd-fms-fuel-load-dest-grid-top-cell" />
<div class="mfd-label mfd-fms-fuel-load-dest-grid-top-cell">UTC</div>
<div class="mfd-label mfd-fms-fuel-load-dest-grid-top-cell">
{this.DestinationAlternateTimeHeader}
</div>
<div class="mfd-label mfd-fms-fuel-load-dest-grid-top-cell">EFOB</div>
<div class="mfd-label mfd-fms-fuel-load-dest-grid-middle-cell">DEST</div>
<div class="mfd-label bigger green mfd-fms-fuel-load-dest-grid-middle-cell">{this.destIcao}</div>
Expand Down
7 changes: 5 additions & 2 deletions fbw-a380x/src/systems/instruments/src/MFD/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

import { Approach, ApproachType } from '@flybywiresim/fbw-sdk';

export function getEtaFromUtcOrPresent(seconds: number | null, fromPresent: boolean) {
if (seconds === null) {
export function getEtaFromUtcOrPresent(seconds: number | null | undefined, fromPresent: boolean) {
if (seconds === null || seconds === undefined) {
return '--:--';
} else if (Number.isNaN(seconds)) {
console.error('[MFD] NaN input received for eta format');
return '--:--';
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ export const AirlineModifiableInformation: AmiCollection = {
perfFactor: 0, // %
idleFactor: 0, // %
perfCode: 'ARM',
taxiFuel: 200, // kg
taxiFuel: 1500, // kg
rteRsv: 5, // %
rsvMin: 0, // kg
rsvMax: 10_000, // kg
rsvMax: 50_000, // kg
rsvInflt: true, // YES / NO
rsvAltn: false, // YES / NO
finalTg: 30, // minutes
Expand Down

0 comments on commit a90b1ae

Please sign in to comment.