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(a380x/fms): Fix mach crossover altitude & T/O perf characteristic speeds #9769

Merged
merged 11 commits into from
Jan 18, 2025
1 change: 1 addition & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
1. [A380X/FCU] Add TRUE indication on FCU when TRUE North reference is selected on AFS CP - @heclak (Heclak)
1. [A380X/MFD] Add airport data page into the MFD (DATA > AIRPORT) - @bulenteroglu (senolitam)
1. [A380X/EFB] Adds PRIM/SEC/FCDC failures to EFB - @flogross89 (floridude)
1. [A380X/FMS] Use cruise mach above crossover altitude - @flogross89 (floridude)


## 0.12.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0

import { ConsumerValue, EventBus, GameStateProvider, SimVarValueType, Subject, UnitType } from '@microsoft/msfs-sdk';
import { Arinc429SignStatusMatrix, Arinc429Word, FmsOansData } from '@flybywiresim/fbw-sdk';
import { Arinc429SignStatusMatrix, Arinc429Word, FmsOansData, MathUtils } from '@flybywiresim/fbw-sdk';
import { FlapConf } from '@fmgc/guidance/vnav/common';
import { FlightPlanService } from '@fmgc/index';
import { MmrRadioTuningStatus } from '@fmgc/navigation/NavaidTuner';
Expand Down Expand Up @@ -1170,17 +1170,16 @@ export class FmcAircraftInterface {
this.fmgc.data.approachFlapRetractionSpeed.set(Math.ceil(approachSpeeds.f3));
this.speedVapp.set(Math.round(approachSpeeds.vapp));

// Retrieve CAS and altitude from ADRs
const cas = this.fmc.navigation.getComputedAirspeed();
// Retrieve altitude from ADRs
const alt = this.fmc.navigation.getPressureAltitude();

if (cas !== null && alt !== null) {
// Only update speeds if ADR data valid
if (alt !== null) {
// Only update speeds if ADR altitude data valid.

const flapLever = SimVar.GetSimVarValue('L:A32NX_FLAPS_HANDLE_INDEX', 'Enum');
const speeds = new A380OperatingSpeeds(
grossWeight,
cas,
this.fmc.navigation.getComputedAirspeed() ?? 0, // CAS is NCD for low speeds/standstill, leading to null here
flapLever,
this.flightPhase.get(),
this.fmgc.getV2Speed(),
Expand Down Expand Up @@ -1612,10 +1611,21 @@ export class FmcAircraftInterface {
return SimVar.GetSimVarValue('AUTOPILOT ALTITUDE SLOT INDEX', 'number') === 2;
}

getManagedTargets(v: number, m: number) {
const alt = ADIRS.getBaroCorrectedAltitude();
const vM = SimVar.GetGameVarValue('FROM MACH TO KIAS', 'number', m);
return alt && alt.isNormalOperation() && alt.value > 20_000 && v > vM ? [vM, true] : [v, false];
getManagedTargets(v: number, m: number): [number, boolean] {
const sat = ADIRS.getStaticAirTemperature();
const press = ADIRS.getCorrectedAverageStaticPressure();

if (
sat !== undefined &&
(sat.isNormalOperation() || sat.isFunctionalTest()) &&
press !== undefined &&
(press.isNormalOperation() || press.isFunctionalTest())
) {
const vM = MathUtils.convertMachToKCas(m, press.value);
return v > vM ? [vM, true] : [v, false];
} else {
return [v, false];
}
}

// TODO/VNAV: Speed constraint
Expand Down
15 changes: 10 additions & 5 deletions fbw-a380x/src/systems/instruments/src/MFD/FMC/fmgc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export enum ClimbDerated {
* Temporary place for data which is found nowhere else. Not associated to flight plans right now, which should be the case for some of these values
*/
export class FmgcData {
static fmcFormatSpeed(sub: Subscribable<number | null>) {
return sub.map((it) => (it !== null ? it.toFixed(0) : '---'));
}

public readonly cpnyFplnAvailable = Subject.create(false);

public readonly cpnyFplnUplinkInProgress = Subject.create(false);
Expand Down Expand Up @@ -217,11 +221,11 @@ export class FmgcData {

public readonly takeoffFlapsSetting = Subject.create<FlapConf>(FlapConf.CONF_1);

public readonly flapRetractionSpeed = Subject.create<Knots | null>(141);
public readonly flapRetractionSpeed = Subject.create<Knots | null>(null);

public readonly slatRetractionSpeed = Subject.create<Knots | null>(159);
public readonly slatRetractionSpeed = Subject.create<Knots | null>(null);

public readonly greenDotSpeed = Subject.create<Knots | null>(190);
public readonly greenDotSpeed = Subject.create<Knots | null>(null);

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

Expand Down Expand Up @@ -454,10 +458,11 @@ export class FmgcDataService implements Fmgc {
return preSel;
}

if (this.flightPlanService.has(FlightPlanIndex.Active)) {
// FIXME need to rework the cost index based speed calculations
/* if (this.flightPlanService.has(FlightPlanIndex.Active)) {
const dCI = ((this.flightPlanService.active.performanceData.costIndex ?? 100) / 999) ** 2;
return 290 * (1 - dCI) + 330 * dCI;
}
}*/
return 310;
}

Expand Down
54 changes: 39 additions & 15 deletions fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { maxCertifiedAlt, Mmo, Vmo } from '@shared/PerformanceConstants';
import { ConfirmationDialog } from 'instruments/src/MFD/pages/common/ConfirmationDialog';
import { FmsPage } from 'instruments/src/MFD/pages/common/FmsPage';
import { FmgcFlightPhase } from '@shared/flightphase';
import { TakeoffDerated, TakeoffPowerSetting } from 'instruments/src/MFD/FMC/fmgc';
import { FmgcData, TakeoffDerated, TakeoffPowerSetting } from 'instruments/src/MFD/FMC/fmgc';
import { ConditionalComponent } from 'instruments/src/MFD/pages/common/ConditionalComponent';
import { MfdSimvars } from 'instruments/src/MFD/shared/MFDSimvarPublisher';
import { VerticalCheckpointReason } from '@fmgc/guidance/vnav/profile/NavGeometryProfile';
Expand Down Expand Up @@ -1055,7 +1055,9 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> {
</div>
<div class="mfd-label-value-container">
<div ref={this.vSpeedsConfirmationRef[0]}>
<span class="mfd-value tmpy">{this.props.fmcService.master.fmgc.data.v1ToBeConfirmed}</span>
<span class="mfd-value tmpy">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.v1ToBeConfirmed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
</div>
Expand Down Expand Up @@ -1093,7 +1095,9 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> {
</div>
<div ref={this.flapSpeedsRef[0]} class="mfd-label-value-container">
<span class="mfd-label mfd-spacing-right">F</span>
<span class="mfd-value">{this.props.fmcService.master.fmgc.data.flapRetractionSpeed}</span>
<span class="mfd-value">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.flapRetractionSpeed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
<div class="mfd-label-value-container">
Expand All @@ -1115,13 +1119,17 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> {
</div>
<div class="mfd-label-value-container">
<div ref={this.vSpeedsConfirmationRef[1]}>
<span class="mfd-value tmpy">{this.props.fmcService.master.fmgc.data.vrToBeConfirmed}</span>
<span class="mfd-value tmpy">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.vrToBeConfirmed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
</div>
<div ref={this.flapSpeedsRef[1]} class="mfd-label-value-container">
<span class="mfd-label mfd-spacing-right">S</span>
<span class="mfd-value">{this.props.fmcService.master.fmgc.data.slatRetractionSpeed}</span>
<span class="mfd-value">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.slatRetractionSpeed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
<div class="mfd-label-value-container">
Expand All @@ -1143,7 +1151,9 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> {
</div>
<div class="mfd-label-value-container">
<div ref={this.vSpeedsConfirmationRef[2]}>
<span class="mfd-value tmpy">{this.props.fmcService.master.fmgc.data.v2ToBeConfirmed}</span>
<span class="mfd-value tmpy">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.v2ToBeConfirmed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
</div>
Expand All @@ -1153,7 +1163,9 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> {
<circle cx="6" cy="6" r="5" stroke="#00ff00" stroke-width="2" />
</svg>
</span>
<span class="mfd-value">{this.props.fmcService.master.fmgc.data.greenDotSpeed}</span>
<span class="mfd-value">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.greenDotSpeed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
</div>
Expand Down Expand Up @@ -2557,26 +2569,30 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> {
<circle cx="6" cy="6" r="5" stroke="#00ff00" stroke-width="2" />
</svg>
</span>
<span class="mfd-value">{this.props.fmcService.master.fmgc.data.approachGreenDotSpeed}</span>
<span class="mfd-value">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.approachGreenDotSpeed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
<div class="mfd-label-value-container">
<span class="mfd-label mfd-spacing-right mfd-fms-perf-appr-flap-speeds">S</span>
<span class="mfd-value">
{this.props.fmcService.master.fmgc.data.approachSlatRetractionSpeed}
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.approachSlatRetractionSpeed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
<div class="mfd-label-value-container">
<span class="mfd-label mfd-spacing-right mfd-fms-perf-appr-flap-speeds">F</span>
<span class="mfd-value">
{this.props.fmcService.master.fmgc.data.approachFlapRetractionSpeed}
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.approachFlapRetractionSpeed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
<div class="mfd-label-value-container" style="padding-top: 15px;">
<span class="mfd-label mfd-spacing-right mfd-fms-perf-appr-flap-speeds">VREF</span>
<span class="mfd-value">{this.props.fmcService.master.fmgc.data.approachVref}</span>
<span class="mfd-value">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.approachVref)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
</div>
Expand All @@ -2594,7 +2610,9 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> {
/>
<div class="mfd-label-value-container" style="margin-top: 10px;">
<span class="mfd-label mfd-spacing-right">VLS</span>
<span class="mfd-value">{this.props.fmcService.master.fmgc.data.approachVls}</span>
<span class="mfd-value">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.approachVls)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
</div>
Expand Down Expand Up @@ -2653,12 +2671,16 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> {
<div style="margin: 60px 0px 100px 200px; display: flex; flex-direction: column;">
<div class="mfd-label-value-container">
<span class="mfd-label mfd-spacing-right">F</span>
<span class="mfd-value">{this.props.fmcService.master.fmgc.data.approachFlapRetractionSpeed}</span>
<span class="mfd-value">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.approachFlapRetractionSpeed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
<div class="mfd-label-value-container">
<span class="mfd-label mfd-spacing-right">S</span>
<span class="mfd-value">{this.props.fmcService.master.fmgc.data.approachSlatRetractionSpeed}</span>
<span class="mfd-value">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.approachSlatRetractionSpeed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
<div class="mfd-label-value-container">
Expand All @@ -2667,7 +2689,9 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> {
<circle cx="6" cy="6" r="5" stroke="#00ff00" stroke-width="2" />
</svg>
</span>
<span class="mfd-value">{this.props.fmcService.master.fmgc.data.approachGreenDotSpeed}</span>
<span class="mfd-value">
{FmgcData.fmcFormatSpeed(this.props.fmcService.master.fmgc.data.approachGreenDotSpeed)}
</span>
<span class="mfd-label-unit mfd-unit-trailing">KT</span>
</div>
</div>
Expand Down
17 changes: 17 additions & 0 deletions fbw-a380x/src/systems/instruments/src/MFD/shared/Adirs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,35 +31,52 @@ export class ADIRS {
);
}

/** in degrees */
static getLatitude() {
return ADIRS.getFromAnyAdiru('IR', 'LATITUDE');
}

/** in degrees */
static getLongitude() {
return ADIRS.getFromAnyAdiru('IR', 'LONGITUDE');
}

/** in degrees */
static getTrueTrack() {
return ADIRS.getFromAnyAdiru('IR', 'TRUE_TRACK');
}

/** in knots */
static getTrueAirspeed() {
return ADIRS.getFromAnyAdiru('ADR', 'TRUE_AIRSPEED');
}

/** in knots */
static getCalibratedAirspeed() {
return ADIRS.getFromAnyAdiru('ADR', 'COMPUTED_AIRSPEED');
}

/** in knots */
static getGroundSpeed() {
return ADIRS.getFromAnyAdiru('IR', 'GROUND_SPEED');
}

// FIXME there should be baro corrected altitude 1 (capt) and 2 (f/o)
/** in feet */
static getBaroCorrectedAltitude() {
return ADIRS.getFromAnyAdiru('ADR', 'ALTITUDE');
}

/** in degrees celsius */
static getStaticAirTemperature() {
return ADIRS.getFromAnyAdiru('ADR', 'STATIC_AIR_TEMPERATURE');
}

/** in hPa */
static getCorrectedAverageStaticPressure() {
return ADIRS.getFromAnyAdiru('ADR', 'CORRECTED_AVERAGE_STATIC_PRESSURE');
}

/**
*
* @param type IR or ADR
Expand Down
22 changes: 5 additions & 17 deletions fbw-a380x/src/systems/shared/src/OperatingSpeeds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { MathUtils } from '@flybywiresim/fbw-sdk';
import { Mmo, VfeF1, VfeF1F, VfeF2, VfeF3, VfeFF, Vmcl, Vmo } from '@shared/PerformanceConstants';
import { FmgcFlightPhase } from '@shared/flightphase';
import { LerpLookupTable } from '@microsoft/msfs-sdk';
import { ADIRS } from 'instruments/src/MFD/shared/Adirs';

export enum ApproachConf {
CONF_1 = 1,
Expand Down Expand Up @@ -402,29 +403,16 @@ function getVfeNIdx(fi: number): number {
}
}

/**
* Convert degrees Celsius into Kelvin
* @param T degrees Celsius
* @returns degrees Kelvin
*/
function convertCtoK(T: number): number {
return T + 273.15;
}

/**
* Get correct Vmax for Vmo and Mmo in knots
* @returns Min(Vmo, Mmo)
* @private
*/
function getVmo() {
return Math.min(
Vmo,
MathUtils.convertMachToKCas(
Mmo,
convertCtoK(Simplane.getAmbientTemperature()),
SimVar.GetSimVarValue('AMBIENT PRESSURE', 'millibar'),
),
);
// FIXME use ADR corrected average static pressure
const adrPressure = ADIRS.getCorrectedAverageStaticPressure();
const ambientPressure = adrPressure !== undefined ? adrPressure.valueOr(1013.25) : 1013.25;
return Math.min(Vmo, MathUtils.convertMachToKCas(Mmo, ambientPressure));
}

export class A380OperatingSpeeds {
Expand Down
10 changes: 10 additions & 0 deletions fbw-common/src/systems/shared/src/MathUtils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,14 @@ describe('MathUtils.correctMsfsLocaliserError', () => {
expect(MathUtils.correctMsfsLocaliserError(177.5)).toBeCloseTo(2.5);
expect(MathUtils.correctMsfsLocaliserError(90.1)).toBeCloseTo(89.9);
});

describe('MathUtils.convertMachToKCas', () => {
it('correctly converts mach to CAS', () => {
expect(MathUtils.convertMachToKCas(0, 1013.25)).toBeCloseTo(0);
expect(MathUtils.convertMachToKCas(0.84, 1013.25)).toBeCloseTo(555.634);
// FL350 = 238.423 hPa
expect(MathUtils.convertMachToKCas(0, 238.423)).toBeCloseTo(0);
expect(MathUtils.convertMachToKCas(0.84, 238.423)).toBeCloseTo(287.097);
});
});
});
12 changes: 7 additions & 5 deletions fbw-common/src/systems/shared/src/MathUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ export class MathUtils {
return (
1479.1 *
Math.sqrt(
((pressure / 1013) * ((1 + (1 / (oat / 288.15)) * (tas / 1479.1) ** 2) ** 3.5 - 1) + 1) ** (1 / 3.5) - 1,
((pressure / 1013.25) * ((1 + (1 / (oat / 288.15)) * (tas / 1479.1) ** 2) ** 3.5 - 1) + 1) ** (1 / 3.5) - 1,
)
);
}
Expand All @@ -306,20 +306,22 @@ export class MathUtils {
1479.1 *
Math.sqrt(
(oat / 288.15) *
(((1 / (pressure / 1013)) * ((1 + 0.2 * (kcas / 661.4786) ** 2) ** 3.5 - 1) + 1) ** (1 / 3.5) - 1),
(((1 / (pressure / 1013.25)) * ((1 + 0.2 * (kcas / 661.4786) ** 2) ** 3.5 - 1) + 1) ** (1 / 3.5) - 1),
)
);
}

/**
* Convert Mach to Calibrated Air Speed
* @param mach Mach
* @param oat Kelvin
* @param pressure current pressure hpa
* @returns Calibrated Air Speed
*/
public static convertMachToKCas(mach: number, oat: number, pressure: number): number {
return MathUtils.convertTasToKCas(MathUtils.convertMachToKTas(mach, oat), oat, pressure);
public static convertMachToKCas(mach: number, pressure: number): number {
// Formula from Jet Transport Performance Methods 2009.
return (
1479.1 * Math.sqrt(Math.pow((pressure / 1013.25) * (Math.pow(0.2 * mach * mach + 1, 3.5) - 1) + 1, 1 / 3.5) - 1)
);
}

/**
Expand Down
Loading