Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@
1. [A380X/Fuel] Only try to equalize feedtanks when each is below transfer limit - @Maximilian-Reuter (\_chaoz_)
1. [A380X/MFD] Added IMMEDIATE EXIT/RESUME HOLD button to the F-PLN page - @BravoMike99 (bruno_pt99)
1. [A32NX/FWS] Add message grouping and order messages based on priority - @FozzieHi (fozzie)
1. [FMS] Fixed an issue where waypoints around lateral discontinuities and at enroute/procedure boundaries were sometimes not sent to the ND - @tracernz (Mike)
1. [FMS] Fixed an issue where the WPT overlay on the ND showed terminal waypoints in enroute range - @tracernz (Mike)

## 0.14.0

Expand Down
128 changes: 83 additions & 45 deletions fbw-a32nx/src/systems/fmgc/src/efis/EfisSymbols.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// @ts-strict-ignore
// Copyright (c) 2021-2023 FlyByWire Simulations
// Copyright (c) 2021-2026 FlyByWire Simulations
// SPDX-License-Identifier: GPL-3.0

import {
Expand All @@ -26,6 +25,7 @@ import {
VdSymbol,
FmsData,
NdPwpSymbolTypeFlags,
SectionCode,
MagVar,
} from '@flybywiresim/fbw-sdk';

Expand Down Expand Up @@ -338,7 +338,7 @@ export class EfisSymbols<T extends number> {
symbol.location = symbol.location ?? oldSymbol.location;
symbol.type |= oldSymbol.type;
if (oldSymbol.typePwp) {
symbol.typePwp |= oldSymbol.typePwp;
symbol.typePwp = (symbol.typePwp ?? 0) | oldSymbol.typePwp;
}
if (oldSymbol.radials) {
if (symbol.radials) {
Expand Down Expand Up @@ -406,7 +406,10 @@ export class EfisSymbols<T extends number> {
}
if ((efisOption & EfisOption.Waypoints) > 0) {
for (const wp of this.nearbyWaypointMonitor.getCurrentFacilities()) {
if (this.isWithinEditArea(wp.location, mapReferencePoint, mapOrientation, editArea)) {
if (
(range < 160 || wp.sectionCode !== SectionCode.Airport) &&
this.isWithinEditArea(wp.location, mapReferencePoint, mapOrientation, editArea)
) {
upsertSymbol({
databaseId: wp.databaseId,
ident: wp.ident,
Expand Down Expand Up @@ -439,11 +442,16 @@ export class EfisSymbols<T extends number> {
// eslint-disable-next-line no-lone-blocks

// ACTIVE
if (this.flightPlanService.hasActive && this.guidanceController.hasGeometryForFlightPlan(FlightPlanIndex.Active)) {
const activeGeometry =
this.flightPlanService.hasActive && this.guidanceController.hasGeometryForFlightPlan(FlightPlanIndex.Active)
? this.guidanceController.activeGeometry
: undefined;

if (activeGeometry) {
const symbols = this.getFlightPlanSymbols(
false,
this.flightPlanService.active,
this.guidanceController.activeGeometry,
activeGeometry,
range,
efisOption,
mode,
Expand All @@ -461,15 +469,18 @@ export class EfisSymbols<T extends number> {
}

// ACTIVE ALTN
if (
const activeAltnGeoemtry =
this.flightPlanService.active.alternateFlightPlan.legCount > 0 &&
this.guidanceController.hasGeometryForFlightPlan(FlightPlanIndex.Active) &&
this.efisInterface.shouldTransmitAlternate(FlightPlanIndex.Active, mode === EfisNdMode.PLAN)
) {
? this.guidanceController.getGeometryForFlightPlan(FlightPlanIndex.Active, true)
: undefined;

if (activeAltnGeoemtry) {
const symbols = this.getFlightPlanSymbols(
true,
this.flightPlanService.active.alternateFlightPlan,
this.guidanceController.getGeometryForFlightPlan(FlightPlanIndex.Active, true),
activeAltnGeoemtry,
range,
efisOption,
mode,
Expand All @@ -488,14 +499,16 @@ export class EfisSymbols<T extends number> {
}

// TMPY
if (
this.flightPlanService.hasTemporary &&
this.guidanceController.hasGeometryForFlightPlan(FlightPlanIndex.Temporary)
) {
const tmpyGeometry =
this.flightPlanService.hasTemporary && this.guidanceController.hasGeometryForFlightPlan(FlightPlanIndex.Temporary)
? this.guidanceController.temporaryGeometry
: undefined;

if (tmpyGeometry) {
const symbols = this.getFlightPlanSymbols(
false,
this.flightPlanService.temporary,
this.guidanceController.temporaryGeometry,
tmpyGeometry,
range,
efisOption,
mode,
Expand All @@ -514,15 +527,18 @@ export class EfisSymbols<T extends number> {

// SEC
for (let secIndex = 1; secIndex <= this.efisInterface.numSecondaryFlightPlans; secIndex++) {
if (
const secGeometry =
this.flightPlanService.hasSecondary(secIndex) &&
this.guidanceController.hasGeometryForFlightPlan(FlightPlanIndex.FirstSecondary + secIndex - 1) &&
this.efisInterface.shouldTransmitSecondary(secIndex)
) {
? this.guidanceController.secondaryGeometry(secIndex)
: undefined;

if (secGeometry) {
const symbols = this.getFlightPlanSymbols(
false,
this.flightPlanService.secondary(secIndex),
this.guidanceController.secondaryGeometry(secIndex),
secGeometry,
range,
efisOption,
mode,
Expand All @@ -539,18 +555,21 @@ export class EfisSymbols<T extends number> {
}

// SEC ALTN
if (
const secAltnGeometry =
this.flightPlanService.secondary(secIndex).alternateFlightPlan.legCount > 0 &&
this.guidanceController.hasGeometryForFlightPlan(FlightPlanIndex.FirstSecondary + secIndex - 1) &&
this.efisInterface.shouldTransmitAlternate(
FlightPlanIndex.FirstSecondary + secIndex - 1,
mode === EfisNdMode.PLAN,
)
) {
? this.guidanceController.getGeometryForFlightPlan(FlightPlanIndex.FirstSecondary + secIndex - 1, true)
: undefined;

if (secAltnGeometry) {
const symbols = this.getFlightPlanSymbols(
true,
this.flightPlanService.secondary(secIndex).alternateFlightPlan,
this.guidanceController.getGeometryForFlightPlan(FlightPlanIndex.FirstSecondary + secIndex - 1, true),
secAltnGeometry,
range,
efisOption,
mode,
Expand Down Expand Up @@ -584,11 +603,15 @@ export class EfisSymbols<T extends number> {
let direction: number | undefined = undefined;

if (
pwp.efisPwpSymbolFlag !== undefined &&
pwp.efisPwpSymbolFlag & NdPwpSymbolTypeFlags.PwpEndOfVdMarker &&
this.guidanceController.activeGeometry.legs.has(pwp.alongLegIndex)
this.guidanceController.activeGeometry?.legs.has(pwp.alongLegIndex)
) {
const leg = this.guidanceController.activeGeometry.legs.get(pwp.alongLegIndex);
const orientation = Geometry.getLegOrientationAtDistanceFromEnd(leg, pwp.distanceFromLegTermination);
const orientation = leg
? Geometry.getLegOrientationAtDistanceFromEnd(leg, pwp.distanceFromLegTermination)
: null;

if (orientation !== null) {
direction = orientation;
}
Expand Down Expand Up @@ -650,7 +673,7 @@ export class EfisSymbols<T extends number> {

private getFlightPlanSymbols(
isAlternate: boolean,
flightPlan: BaseFlightPlan,
flightPlan: BaseFlightPlan | AlternateFlightPlan,
geometry: Geometry,
range: NauticalMiles,
efisOption: EfisOption,
Expand Down Expand Up @@ -680,8 +703,11 @@ export class EfisSymbols<T extends number> {
// FP legs
for (let i = flightPlan.legCount - 1; i >= flightPlan.fromLegIndex && i >= 0; i--) {
const isBeforeActiveLeg = i < flightPlan.activeLegIndex;
const isActiveLeg = i === flightPlan.activeLegIndex;

const leg = flightPlan.elementAt(i);
const prevLeg = flightPlan.maybeElementAt(i - 1);
const nextLeg = flightPlan.maybeElementAt(i + 1);

if (leg.isDiscontinuity === true) {
continue;
Expand All @@ -696,18 +722,20 @@ export class EfisSymbols<T extends number> {
}

// no symbols for manual legs, except FM leg with no leg before it
if (
leg.definition.type === LegType.VM ||
(leg.definition.type === LegType.FM && !flightPlan.maybeElementAt(i - 1)?.isDiscontinuity)
) {
if (leg.definition.type === LegType.VM || (leg.definition.type === LegType.FM && !prevLeg?.isDiscontinuity)) {
continue;
}

// if range >= 160, don't include terminal waypoints, except at enroute boundary
if (range >= 160) {
// FIXME the enroute boundary condition has been removed in fms-v2...
const [segment] = flightPlan.segmentPositionForIndex(i);
if (segment.class === SegmentClass.Departure || segment.class === SegmentClass.Arrival) {
// if range >= 160, don't include terminal waypoints, except at enroute and discont boundaries, and TO WPT
if (!isActiveLeg && range >= 160) {
if (
prevLeg &&
nextLeg &&
nextLeg.isDiscontinuity === false &&
prevLeg.isDiscontinuity === false &&
prevLeg.segment.class === leg.segment.class &&
(leg.segment.class === SegmentClass.Departure || leg.segment.class === SegmentClass.Arrival)
) {
continue;
}
}
Expand Down Expand Up @@ -738,7 +766,7 @@ export class EfisSymbols<T extends number> {
databaseId = leg.terminationWaypoint()?.databaseId;
}

if (!location) {
if (!location || !databaseId) {
continue;
}

Expand Down Expand Up @@ -808,23 +836,33 @@ export class EfisSymbols<T extends number> {
case AltitudeDescriptor.AtAlt1:
case AltitudeDescriptor.AtAlt1GsIntcptAlt2:
case AltitudeDescriptor.AtAlt1AngleAlt2:
constraints.push(formatConstraintAlt(altConstraint.altitude1, descent));
if (altConstraint.altitude1 !== undefined) {
constraints.push(formatConstraintAlt(altConstraint.altitude1, descent));
}
break;
case AltitudeDescriptor.AtOrAboveAlt1:
case AltitudeDescriptor.AtOrAboveAlt1GsIntcptAlt2:
case AltitudeDescriptor.AtOrAboveAlt1AngleAlt2:
constraints.push(formatConstraintAlt(altConstraint.altitude1, descent, '+'));
if (altConstraint.altitude1 !== undefined) {
constraints.push(formatConstraintAlt(altConstraint.altitude1, descent, '+'));
}
break;
case AltitudeDescriptor.AtOrBelowAlt1:
case AltitudeDescriptor.AtOrBelowAlt1AngleAlt2:
constraints.push(formatConstraintAlt(altConstraint.altitude1, descent, '-'));
if (altConstraint.altitude1 !== undefined) {
constraints.push(formatConstraintAlt(altConstraint.altitude1, descent, '-'));
}
break;
case AltitudeDescriptor.BetweenAlt1Alt2:
constraints.push(formatConstraintAlt(altConstraint.altitude1, descent, '-'));
constraints.push(formatConstraintAlt(altConstraint.altitude2, descent, '+'));
if (altConstraint.altitude1 !== undefined && altConstraint.altitude2 !== undefined) {
constraints.push(formatConstraintAlt(altConstraint.altitude1, descent, '-'));
constraints.push(formatConstraintAlt(altConstraint.altitude2, descent, '+'));
}
break;
case AltitudeDescriptor.AtOrAboveAlt2:
constraints.push(formatConstraintAlt(altConstraint.altitude2, descent, '+'));
if (altConstraint.altitude2 !== undefined) {
constraints.push(formatConstraintAlt(altConstraint.altitude2, descent, '+'));
}
break;
default:
// No constraint
Expand All @@ -834,7 +872,7 @@ export class EfisSymbols<T extends number> {

const speedConstraint = leg.speedConstraint;

if (speedConstraint) {
if (speedConstraint?.speed !== undefined) {
constraints.push(formatConstraintSpeed(speedConstraint.speed));
}
}
Expand All @@ -846,12 +884,12 @@ export class EfisSymbols<T extends number> {

const distanceFromAirplane =
predictions && predictions.waypointPredictions.has(i)
? predictions.waypointPredictions.get(i).distanceFromAircraft
? predictions.waypointPredictions.get(i)?.distanceFromAircraft
: undefined;

const predictedAltitude =
predictions && predictions.waypointPredictions.has(i)
? predictions.waypointPredictions.get(i).altitude
? predictions.waypointPredictions.get(i)?.altitude
: undefined;

ret.push({
Expand All @@ -863,7 +901,7 @@ export class EfisSymbols<T extends number> {
altConstraint: leg.altitudeConstraint,
isAltitudeConstraintMet:
predictions && predictions.waypointPredictions.has(i)
? predictions.waypointPredictions.get(i).isAltitudeConstraintMet
? predictions.waypointPredictions.get(i)?.isAltitudeConstraintMet
: true,
direction,
distanceFromAirplane,
Expand Down Expand Up @@ -906,7 +944,7 @@ export class EfisSymbols<T extends number> {
const databaseId = `A${airport.ident}${planAltnStr}${planIndexStr}${runwayIdentStr}`;

const distanceFromAirplane =
(segment.lastLeg?.calculated?.cumulativeDistanceWithTransitions ??
(segment?.lastLeg?.calculated?.cumulativeDistanceWithTransitions ??
distanceTo(this.lastPpos, airport.location)) -
(this.guidanceController.vnavDriver.mcduProfile?.distanceToPresentPosition ?? 0);

Expand Down Expand Up @@ -1062,7 +1100,7 @@ export class EfisSymbols<T extends number> {
}

const geometry = this.guidanceController.getGeometryForFlightPlan(focusedWpFpIndex, focusedWpInAlternate);
const matchingGeometryLeg = geometry.legs.get(matchingLeg.isVectors() ? focusedWpIndex - 1 : focusedWpIndex);
const matchingGeometryLeg = geometry?.legs.get(matchingLeg.isVectors() ? focusedWpIndex - 1 : focusedWpIndex);

if (!matchingGeometryLeg?.terminationWaypoint) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { FlightPlanPerformanceData } from '@fmgc/flightplanning/plans/performanc
/**
* An alternate flight plan shares its origin with the destination of a regular flight plan
*/
export class AlternateFlightPlan<P extends FlightPlanPerformanceData> extends BaseFlightPlan<P> {
export class AlternateFlightPlan<
P extends FlightPlanPerformanceData = FlightPlanPerformanceData,
> extends BaseFlightPlan<P> {
constructor(
context: FlightPlanContext,
index: number,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Copyright (c) 2025 FlyByWire Simulations
// Copyright (c) 2025-2026 FlyByWire Simulations
// SPDX-License-Identifier: GPL-3.0

import { Coordinates } from 'msfs-geo';

import { ElevatedCoordinates } from '../shared/types/Common';
import { SectionCode } from '../shared/types/SectionCode';
import { VhfNavaidType, VorClass } from '../shared/types/VhfNavaid';
import { ElevatedCoordinates } from '@flybywiresim/fbw-sdk';

export enum NearbyFacilityType {
VhfNavaid = 1 << 0,
Expand All @@ -17,13 +19,15 @@ export interface NearbyFacility {
location: Coordinates & Partial<ElevatedCoordinates>;
ident: string;
databaseId: string;
sectionCode: SectionCode;
}

export interface NearbyAirportFacility extends NearbyFacility {
/**
* The airport magvar in degrees, or null when the airport is true referenced.
*/
magVar: number | null;
sectionCode: SectionCode.Airport;
}

export interface NearbyVhfFacility extends NearbyFacility {
Expand Down
Loading
Loading