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

feat(navdata): allow airports to be used as fixes #9792

Merged
merged 7 commits into from
Jan 25, 2025
Merged
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
1 change: 1 addition & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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 */
Expand All @@ -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<string, string> {
Expand Down Expand Up @@ -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);
Expand All @@ -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)) {
Expand All @@ -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);
}
Expand Down
18 changes: 15 additions & 3 deletions fbw-common/src/systems/navdata/client/backends/Msfs/Mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ type FacilityType<T> = 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';
Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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,
Expand All @@ -949,7 +959,7 @@ export class MsfsMapping {
LoadType.Intersection,
);

return new Map<string, JS_Facility>([...wps, ...ndbs, ...vors]);
return new Map<string, JS_Facility>([...wps, ...ndbs, ...vors, ...airports]);
}

private mapApproachTransitions(
Expand Down Expand Up @@ -1290,6 +1300,8 @@ export class MsfsMapping {
class: this.mapVorClass(vor),
} as unknown as FacilityType<T>;
}
case 'A':
return this.mapAirport(facility as JS_FacilityAirport) as FacilityType<T>;
case 'W':
default:
return {
Expand Down
5 changes: 5 additions & 0 deletions fbw-common/src/systems/navdata/shared/types/Airport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<SectionCode.Airport> {
subSectionCode: AirportSubsectionCode.ReferencePoints;
Expand Down Expand Up @@ -43,4 +44,8 @@ export interface Airport extends DatabaseItem<SectionCode.Airport> {
* 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;
}
3 changes: 2 additions & 1 deletion fbw-common/src/systems/navdata/shared/types/BaseFix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends SectionCode> extends DatabaseItem<T> {
location: Coordinates;
Expand All @@ -14,7 +15,7 @@ export interface BaseFix<T extends SectionCode> extends DatabaseItem<T> {
/**
* 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;
Expand Down
Loading