Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(a380/mfd): trip fuel calculation & block not editable across flights in fuel & load #9788

Merged
1 change: 1 addition & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
1. [A380X/SD] Display oil quantity on ENG page - @flogross89 (floridude)
1. [A380X/FMS] Accept KCCU plus/minus key as first entry into field - @flogross89 (floridude)
1. [ATSU] Add MSFS as TAF source in MSFS2024 - @tracernz (Mike)
1. [A380X/MFD] Fix wrong Landing weight calculation & block fuel not editable across flights in FUEL & LOAD - @BravoMike99 (bruno_pt99)

## 0.12.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
EfisSide,
Fix,
NXDataStore,
Units,
UpdateThrottler,
Waypoint,
a380EfisRangeSettings,
Expand Down Expand Up @@ -268,6 +269,7 @@ export class FlightManagementComputer implements FmcInterface {
!Number.isFinite(this.flightPlanService.active.performanceData.costIndex)
) {
this.flightPlanService.active.setPerformanceData('costIndex', 0);
this.addMessageToQueue(NXSystemMessages.costIndexInUse.getModifiedMessage('0'));
}
}),
this.legacyFmsIsHealthy.sub((v) => {
Expand Down Expand Up @@ -372,7 +374,7 @@ export class FlightManagementComputer implements FmcInterface {
// TOW before engine start: TOW = ZFW + BLOCK - TAXI
const zfw = this.fmgc.data.zeroFuelWeight.get() ?? maxZfw;
if (this.fmgc.data.zeroFuelWeight.get() && this.fmgc.data.blockFuel.get() && this.fmgc.data.taxiFuel.get()) {
return zfw + (this.fmgc.data.blockFuel.get() ?? maxBlockFuel) - this.fmgc.data.taxiFuel.get();
return zfw + (this.fmgc.data.blockFuel.get() ?? maxBlockFuel) - (this.fmgc.data.taxiFuel.get() ?? 0);
}
return null;
}
Expand All @@ -388,7 +390,35 @@ export class FlightManagementComputer implements FmcInterface {
}

public getTripFuel(): number | null {
return 25_000; // FIXME Dummy value
const destPred = this.guidanceController.vnavDriver.getDestinationPrediction();
if (destPred) {
const fob = this.fmgc.getFOB() * 1_000;
const destFuelKg = Units.poundToKilogram(destPred.estimatedFuelOnBoard);
return fob - destFuelKg;
}
return null;
}

public getExtraFuel(): number | null {
const destPred = this.guidanceController.vnavDriver.getDestinationPrediction();
if (destPred) {
if (this.flightPhase.get() === FmgcFlightPhase.Preflight) {
// EXTRA = BLOCK - TAXI - TRIP - MIN FUEL DEST - RTE RSV
return (
(this.enginesWereStarted.get() ? this.fmgc.getFOB() * 1_000 : this.fmgc.data.blockFuel.get() ?? 0) -
(this.fmgc.data.taxiFuel.get() ?? 0) -
(this.getTripFuel() ?? 0) -
(this.fmgc.data.minimumFuelAtDestination.get() ?? 0) -
(this.fmgc.data.routeReserveFuelWeight.get() ?? 0)
);
} else {
return (
Units.poundToKilogram(destPred.estimatedFuelOnBoard) - (this.fmgc.data.minimumFuelAtDestination.get() ?? 0)
);
}
}

return null;
}

public getRecMaxFlightLevel(): number | null {
Expand Down Expand Up @@ -664,6 +694,8 @@ export class FlightManagementComputer implements FmcInterface {
this.acInterface.updateThrustReductionAcceleration();
}

pd.taxiFuelPilotEntry.set(null);
pd.defaultTaxiFuel.set(null);
pd.routeReserveFuelWeightPilotEntry.set(null);
pd.routeReserveFuelPercentagePilotEntry.set(0);
pd.routeReserveFuelWeightCalculated.set(0);
Expand Down Expand Up @@ -934,6 +966,15 @@ export class FlightManagementComputer implements FmcInterface {
if (throttledDt !== -1) {
this.navigation.update(throttledDt);
if (this.flightPlanService.hasActive) {
const flightPhase = this.flightPhase.get();
this.enginesWereStarted.set(
flightPhase >= FmgcFlightPhase.Takeoff ||
(flightPhase == FmgcFlightPhase.Preflight && SimVar.GetSimVarValue('L:A32NX_ENGINE_N2:1', 'number') > 20) ||
SimVar.GetSimVarValue('L:A32NX_ENGINE_N2:2', 'number') > 20 ||
SimVar.GetSimVarValue('L:A32NX_ENGINE_N2:3', 'number') > 20 ||
SimVar.GetSimVarValue('L:A32NX_ENGINE_N2:4', 'number') > 20,
);

this.acInterface.updateThrustReductionAcceleration();
this.acInterface.updateTransitionAltitudeLevel();
this.acInterface.updatePerformanceData();
Expand All @@ -956,19 +997,6 @@ export class FlightManagementComputer implements FmcInterface {
this.acInterface.updateMinimums(destPred.distanceFromAircraft);
}
this.acInterface.updateIlsCourse(this.navigation.getNavaidTuner().getMmrRadioTuningStatus(1));

if (!this.enginesWereStarted.get()) {
const flightPhase = this.fmgc.getFlightPhase();
const oneEngineWasStarted =
SimVar.GetSimVarValue('L:A32NX_ENGINE_N2:1', 'percent') > 20 ||
SimVar.GetSimVarValue('L:A32NX_ENGINE_N2:2', 'percent') > 20 ||
SimVar.GetSimVarValue('L:A32NX_ENGINE_N2:3', 'percent') > 20 ||
SimVar.GetSimVarValue('L:A32NX_ENGINE_N2:4', 'percent') > 20;
this.enginesWereStarted.set(
flightPhase >= FmgcFlightPhase.Takeoff ||
(flightPhase === FmgcFlightPhase.Preflight && oneEngineWasStarted),
);
}
}
this.checkGWParams();
this.updateMessageQueue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ export class FmcAircraftInterface {
case FmgcFlightPhase.GoAround: {
if (this.fmaVerticalMode.get() === VerticalMode.SRS_GA) {
const speed = Math.min(
this.fmgc.data.approachVls.get() + (engineOut ? 15 : 25),
this.fmgc.data.approachVls.get() ?? Infinity + (engineOut ? 15 : 25),
Math.max(
SimVar.GetSimVarValue('L:A32NX_GOAROUND_INIT_SPEED', 'number'),
this.fmgc.data.approachSpeed.get() ?? 0,
Expand Down Expand Up @@ -1233,7 +1233,7 @@ export class FmcAircraftInterface {
SimVar.SetSimVarValue('L:A32NX_FM_GROSS_WEIGHT', 'Number', gw);
}

if (this.fmc.enginesWereStarted.get()) {
if (this.fmc.enginesWereStarted.get() && this.flightPhase.get() !== FmgcFlightPhase.Done) {
this.fmc.fmgc.data.blockFuel.set(this.fmc.fmgc.getFOB() * 1_000);
}
}
Expand Down
3 changes: 3 additions & 0 deletions fbw-a380x/src/systems/instruments/src/MFD/FMC/FmcInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ export interface FmcInterface extends FlightPhaseManagerProxyInterface, DataInte
/** in kilograms */
getTripFuel(): number | null;

/** in kilograms */
getExtraFuel(): number | null;

/** as flight level */
getRecMaxFlightLevel(): number | null;

Expand Down
17 changes: 10 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 @@ -93,12 +93,15 @@ export class FmgcData {
/** in kg. null if not set. */
public readonly taxiFuelPilotEntry = Subject.create<number | null>(null);

/** in kg */
public readonly taxiFuel = this.taxiFuelPilotEntry.map((it) =>
it === null ? AirlineModifiableInformation.EK.taxiFuel : it,
);
public readonly taxiFuelIsPilotEntered = this.taxiFuelPilotEntry.map((v) => v !== null);

public readonly defaultTaxiFuel = Subject.create<number | null>(AirlineModifiableInformation.EK.taxiFuel);

public readonly taxiFuelIsPilotEntered = this.taxiFuelPilotEntry.map((it) => it !== null);
public readonly taxiFuel = MappedSubject.create(
([pilotEntryTaxiFuel, defaultTaxiFuel]) => (pilotEntryTaxiFuel !== null ? pilotEntryTaxiFuel : defaultTaxiFuel),
this.taxiFuelPilotEntry,
this.defaultTaxiFuel,
);

/** in kg. null if not set. */
public readonly routeReserveFuelWeightPilotEntry = Subject.create<number | null>(null);
Expand Down Expand Up @@ -304,11 +307,11 @@ export class FmgcData {
/** in feet. null if not set. */
public readonly approachRadioMinimum = Subject.create<number | null>(null);

public readonly approachVref = Subject.create<Knots>(129);
public readonly approachVref = Subject.create<Knots | null>(null);

public readonly approachFlapConfig = Subject.create<FlapConf>(FlapConf.CONF_FULL);

public readonly approachVls = Subject.create<Knots>(134);
public readonly approachVls = Subject.create<Knots | null>(null);

/**
* Estimated take-off time, in seconds. Displays as HH:mm:ss. Null if not set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { FmsPage } from 'instruments/src/MFD/pages/common/FmsPage';
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 Down Expand Up @@ -166,14 +165,11 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {
}

const destPred = this.props.fmcService.master.guidanceController.vnavDriver.getDestinationPrediction();
const tripFuel = this.props.fmcService.master.getTripFuel();
this.tripFuelWeight.set(tripFuel);
this.tripFuelTime.set(getEtaFromUtcOrPresent(destPred?.secondsFromPresent, true));
this.extraFuelWeight.set(this.props.fmcService.master.getExtraFuel());
if (this.activeFlightPhase.get() === FmgcFlightPhase.Preflight) {
// 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 if not manually entered
const pilotEnteredReserveFuel = this.props.fmcService.master.fmgc.data.routeReserveFuelIsPilotEntered.get();
this.props.fmcService.master.fmgc.data.routeReserveFuelWeightCalculated.set(
Expand All @@ -184,28 +180,7 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {
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(
(this.props.fmcService.master.enginesWereStarted.get() ? fob : block) -
(this.props.fmcService.master.fmgc.data.taxiFuel.get() ?? 0) -
(this.tripFuelWeight.get() ?? 0) -
(this.props.fmcService.master.fmgc.data.minimumFuelAtDestination.get() ?? 0) -
(this.props.fmcService.master.fmgc.data.routeReserveFuelWeight.get() ?? 0),
);
} else {
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 @@ -306,12 +281,12 @@ export class MfdFmsFuelLoad extends FmsPage<MfdFmsFuelLoadProps> {
<div style="margin-bottom: 20px;">
<InputField<number>
dataEntryFormat={new WeightFormat(Subject.create(0), Subject.create(maxTaxiFuel))}
dataHandlerDuringValidation={async (v) =>
this.props.fmcService.master?.fmgc.data.taxiFuelPilotEntry.set(v)
}
dataHandlerDuringValidation={async (v) => {
this.props.fmcService.master?.fmgc.data.taxiFuelPilotEntry.set(v);
}}
enteredByPilot={this.props.fmcService.master.fmgc.data.taxiFuelIsPilotEntered}
value={this.props.fmcService.master.fmgc.data.taxiFuel}
inactive={this.activeFlightPhase.map((it) => it >= FmgcFlightPhase.Takeoff)}
disabled={this.activeFlightPhase.map((it) => it >= FmgcFlightPhase.Takeoff)}
alignText="flex-end"
containerStyle="width: 150px;"
errorHandler={(e) => this.props.fmcService.master?.showFmsErrorMessage(e)}
Expand Down
2 changes: 1 addition & 1 deletion fbw-a380x/src/systems/instruments/src/MFD/pages/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Status legend:
| Status | URI | Sprint/Prio | Missing functionality |
| ------------- | ------------- | ------------- | ------------- |
| ✅ | fms/\*/init | 1 | CPNY WIND REQUEST, RTE SEL, ALTN RTE SEL, CPNY T.O REQUEST |
| ✅ | fms/\*/fuel-load | 1 | RTE RSV, correct fuel calculation, FUEL PLANNING |
| ✅ | fms/\*/fuel-load | 1 | correct fuel calculation, FUEL PLANNING |
| ✅ | fms/\*/perf | 1 | OPT FL, REC MAX, EO behavior, display of type of speed restriction (when MANAGED), PRED TO |
| | | | |
| ✅ | fms/\*/f-pln | 1 | F-PLN INFO button, exit of hold not possible via button |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const NXSystemMessages = {
checkToData: new TypeIIMessage('CHECK TAKE OFF DATA', true),
comUnavailable: new TypeIMessage('COM UNAVAILABLE'),
cstrDelUpToWpt: new TypeIIMessage('CONSTRAINTS BEFORE WWWWW : DELETED', false, 'WWWWW'),
costIndexInUse: new TypeIMessage('COST INDEX-NNN IN USE', false, 'NNN'),
databaseCodingError: new TypeIIMessage('DATABASE CODING ERROR'),
dcduFileFull: new TypeIMessage('DCDU FILE FULL'),
destEfobBelowMin: new TypeIIMessage('DEST EFOB BELOW MIN', true),
Expand Down
Loading