From b0809299567b89125bd6fe3603a8cafe34ca778a Mon Sep 17 00:00:00 2001 From: Michael Corcoran Date: Sat, 25 Jan 2025 23:56:44 +1300 Subject: [PATCH] feat(navdata): allow airports to be used as fixes (#9792) * feat(navdata): allow airports to be used as fixes Needed for some procedures e.g. the EWR5 transitions. * fix(navdata): load airport as fix in 2020 * fix(navdata): add airport to fix type * fix: automatic import... need to fix tsconfig --- .github/CHANGELOG.md | 1 + .../client/backends/Msfs/FacilityCache.ts | 60 ++++++++++++++++++- .../navdata/client/backends/Msfs/Mapping.ts | 18 +++++- .../systems/navdata/shared/types/Airport.ts | 5 ++ .../systems/navdata/shared/types/BaseFix.ts | 3 +- 5 files changed, 81 insertions(+), 6 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 5a0c0ce554f..6810cf8daaf 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -138,6 +138,7 @@ 1. [A380X/MFD] Fix wrong Landing weight calculation & block fuel not editable across flights in FUEL & LOAD - @BravoMike99 (bruno_pt99) 1. [A32NX/FMS] Add terminal area database holds for MSFS2024 - @tracernz (Mike) 1. [EFB] Set EFB Auto Brightness to default to On - @MrJigs7 (MrJigs) +1. [FMS] Allow airport to be loaded as fixes in instrument procedures - @tracernz (Mike) ## 0.12.0 diff --git a/fbw-common/src/systems/navdata/client/backends/Msfs/FacilityCache.ts b/fbw-common/src/systems/navdata/client/backends/Msfs/FacilityCache.ts index 24e2d39211a..5d9411f7648 100644 --- a/fbw-common/src/systems/navdata/client/backends/Msfs/FacilityCache.ts +++ b/fbw-common/src/systems/navdata/client/backends/Msfs/FacilityCache.ts @@ -1,4 +1,4 @@ -// Copyright (c) 2021, 2022 FlyByWire Simulations +// Copyright (c) 2021, 2022, 2025 FlyByWire Simulations // SPDX-License-Identifier: GPL-3.0 /* eslint-disable camelcase */ @@ -19,8 +19,10 @@ import { JS_FacilityIntersection, JS_FacilityNDB, JS_FacilityVOR, + JS_Leg, } from './FsTypes'; import { Airport, NdbNavaid, VhfNavaid, Waypoint } from '../../../shared'; +import { isMsfs2024 } from '../../../../shared/src/MsfsDetect'; // @microsoft/msfs-sdk does not export this, so we declare it declare class CoherentNearestSearchSession implements NearestSearchSession { @@ -232,7 +234,7 @@ export class FacilityCache { private insert(key: string, facility: JS_Facility): void { if (this.facilityCache.size > FacilityCache.cacheSize - 1) { - const oldestKey: string = this.facilityCache.keys().next().value; + const oldestKey: string = this.facilityCache.keys().next().value!; this.facilityCache.delete(oldestKey); } this.facilityCache.set(key, facility); @@ -246,6 +248,56 @@ export class FacilityCache { return icao.substring(7).trim(); } + private static readonly AIRPORT_REGION_REGEX = /^A[A-Z0-9]{2}/; + private static readonly AIRPORT_REGION_REPLACE = 'A '; + + /** Removes the region code from any airport ICAOs in an array of procedure legs. */ + private static fixupLegAirportRegions(legs: JS_Leg[]): void { + for (const leg of legs) { + leg.fixIcao = leg.fixIcao.replace(FacilityCache.AIRPORT_REGION_REGEX, FacilityCache.AIRPORT_REGION_REPLACE); + leg.originIcao = leg.originIcao.replace(FacilityCache.AIRPORT_REGION_REGEX, FacilityCache.AIRPORT_REGION_REPLACE); + leg.arcCenterFixIcao = leg.arcCenterFixIcao.replace( + FacilityCache.AIRPORT_REGION_REGEX, + FacilityCache.AIRPORT_REGION_REPLACE, + ); + } + } + + /** + * Fix up airport ICAOs to a format that MSFS2020 can understand and load. + * Note: this is **not** required for MSFS2024. + * @param airport The airport facility to fix up. + */ + private static fixupAirportRegions(airport: JS_FacilityAirport): void { + for (const appr of airport.approaches) { + for (const trans of appr.transitions) { + FacilityCache.fixupLegAirportRegions(trans.legs); + } + FacilityCache.fixupLegAirportRegions(appr.finalLegs); + FacilityCache.fixupLegAirportRegions(appr.missedLegs); + } + + for (const sid of airport.departures) { + for (const trans of sid.runwayTransitions) { + FacilityCache.fixupLegAirportRegions(trans.legs); + } + FacilityCache.fixupLegAirportRegions(sid.commonLegs); + for (const trans of sid.enRouteTransitions) { + FacilityCache.fixupLegAirportRegions(trans.legs); + } + } + + for (const star of airport.arrivals) { + for (const trans of star.enRouteTransitions) { + FacilityCache.fixupLegAirportRegions(trans.legs); + } + FacilityCache.fixupLegAirportRegions(star.commonLegs); + for (const trans of star.runwayTransitions) { + FacilityCache.fixupLegAirportRegions(trans.legs); + } + } + } + private receiveFacility(facility: JS_Facility): void { let loadType: LoadType; switch (facility.icao.charAt(0)) { @@ -270,6 +322,10 @@ export class FacilityCache { this.addToAirwayCache(facility as any as JS_FacilityIntersection); } + if (!isMsfs2024() && loadType === LoadType.Airport) { + FacilityCache.fixupAirportRegions(facility as JS_FacilityAirport); + } + const key = FacilityCache.key(facility.icao, loadType); this.insert(key, facility); } diff --git a/fbw-common/src/systems/navdata/client/backends/Msfs/Mapping.ts b/fbw-common/src/systems/navdata/client/backends/Msfs/Mapping.ts index 66519246fd9..3d637e45603 100644 --- a/fbw-common/src/systems/navdata/client/backends/Msfs/Mapping.ts +++ b/fbw-common/src/systems/navdata/client/backends/Msfs/Mapping.ts @@ -88,7 +88,9 @@ type FacilityType = T extends JS_FacilityIntersection ? NdbNavaid : T extends JS_FacilityVOR ? VhfNavaid - : never; + : T extends JS_FacilityAirport + ? Airport + : never; export class MsfsMapping { private static readonly letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; @@ -148,11 +150,15 @@ export class MsfsMapping { ? Math.round(msAirport.transitionLevel / 30.48) : undefined; + const ident = this.mapAirportIdent(msAirport); + return { databaseId: msAirport.icao, sectionCode: SectionCode.Airport, subSectionCode: AirportSubsectionCode.ReferencePoints, - ident: this.mapAirportIdent(msAirport), + area: WaypointArea.Terminal, + ident, + airportIdent: ident, // needed for terminal fix icaoCode: msAirport.icao.substring(1, 3), // TODO name: Utils.Translate(msAirport.name), location: { lat: msAirport.lat, long: msAirport.lon, alt: elevation }, @@ -936,6 +942,10 @@ export class MsfsMapping { const icaos = Array.from(icaoSet); + const airports = await this.cache.getFacilities( + icaos.filter((icao) => icao.charAt(0) === 'A'), + LoadType.Airport, + ); const vors = await this.cache.getFacilities( icaos.filter((icao) => icao.charAt(0) === 'V'), LoadType.Vor, @@ -949,7 +959,7 @@ export class MsfsMapping { LoadType.Intersection, ); - return new Map([...wps, ...ndbs, ...vors]); + return new Map([...wps, ...ndbs, ...vors, ...airports]); } private mapApproachTransitions( @@ -1290,6 +1300,8 @@ export class MsfsMapping { class: this.mapVorClass(vor), } as unknown as FacilityType; } + case 'A': + return this.mapAirport(facility as JS_FacilityAirport) as FacilityType; case 'W': default: return { diff --git a/fbw-common/src/systems/navdata/shared/types/Airport.ts b/fbw-common/src/systems/navdata/shared/types/Airport.ts index d5c73a4b272..61a331e2033 100644 --- a/fbw-common/src/systems/navdata/shared/types/Airport.ts +++ b/fbw-common/src/systems/navdata/shared/types/Airport.ts @@ -2,6 +2,7 @@ import { Feet, Metres, NauticalMiles } from 'msfs-geo'; import { DatabaseItem, Knots, FlightLevel, ElevatedCoordinates } from './Common'; import { RunwaySurfaceType } from './Runway'; import { AirportSubsectionCode, SectionCode } from './SectionCode'; +import { WaypointArea } from './Waypoint'; export interface Airport extends DatabaseItem { subSectionCode: AirportSubsectionCode.ReferencePoints; @@ -43,4 +44,8 @@ export interface Airport extends DatabaseItem { * Distance from centre location for nearby airport query */ distance?: NauticalMiles; + + // These two are needed to satisfy the terminal fix interface, for use as procedure fix. + area: WaypointArea.Terminal; + airportIdent: string; } diff --git a/fbw-common/src/systems/navdata/shared/types/BaseFix.ts b/fbw-common/src/systems/navdata/shared/types/BaseFix.ts index c034a8545de..b1023355cef 100644 --- a/fbw-common/src/systems/navdata/shared/types/BaseFix.ts +++ b/fbw-common/src/systems/navdata/shared/types/BaseFix.ts @@ -4,6 +4,7 @@ import { VhfNavaid } from './VhfNavaid'; import { SectionCode } from './SectionCode'; import { Waypoint, WaypointArea } from './Waypoint'; import { NdbNavaid } from './NdbNavaid'; +import { Airport } from './Airport'; export interface BaseFix extends DatabaseItem { location: Coordinates; @@ -14,7 +15,7 @@ export interface BaseFix extends DatabaseItem { /** * Union of all possible fix interfaces */ -export type Fix = VhfNavaid | NdbNavaid | Waypoint; +export type Fix = Airport | NdbNavaid | VhfNavaid | Waypoint; export function isFix(o: any): o is Fix { return typeof o === 'object' && 'location' in o && 'databaseId' in o;