Skip to content

Commit c89cf80

Browse files
author
mattnischan
committed
2024 SU1 Release
1 parent 673b468 commit c89cf80

File tree

279 files changed

+4825
-1452
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

279 files changed

+4825
-1452
lines changed

src/garminsdk/flightplan/Fms.ts

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ export class Fms<ID extends string = any> {
644644
let approachRnavTypeFlags: RnavTypeFlags = RnavTypeFlags.None;
645645
let approachIsCircling = false;
646646
let approachIsVtf = false;
647+
let approachIsRnpAr = false;
647648
let referenceFacility: VorFacility | null = null;
648649
let approachRunway: OneWayRunway | null = null;
649650

@@ -667,6 +668,7 @@ export class Fms<ID extends string = any> {
667668
approachRnavTypeFlags = approach.rnavTypeFlags;
668669
approachIsCircling = !approach.runway;
669670
approachIsVtf = plan.procedureDetails.approachTransitionIndex < 0;
671+
approachIsRnpAr = ApproachUtils.isRnpAr(approach);
670672
if (FmsUtils.approachHasNavFrequency(approach)) {
671673
referenceFacility = (await ApproachUtils.getReferenceFacility(approach, this.facLoader) as VorFacility | undefined) ?? null;
672674
}
@@ -697,7 +699,18 @@ export class Fms<ID extends string = any> {
697699
}
698700
}
699701

700-
this.setApproachDetails(false, approachLoaded, approachType, approachRnavType, approachRnavTypeFlags, approachIsCircling, approachIsVtf, referenceFacility, approachRunway);
702+
this.setApproachDetails(
703+
false,
704+
approachLoaded,
705+
approachType,
706+
approachRnavType,
707+
approachRnavTypeFlags,
708+
approachIsCircling,
709+
approachIsVtf,
710+
approachIsRnpAr,
711+
referenceFacility,
712+
approachRunway,
713+
);
701714
}
702715

703716
/**
@@ -1456,7 +1469,7 @@ export class Fms<ID extends string = any> {
14561469
}
14571470

14581471
++this.updateApproachDetailsOpId;
1459-
this.setApproachDetails(true, false, ApproachType.APPROACH_TYPE_UNKNOWN, RnavTypeFlags.None, RnavTypeFlags.None, false, false, null, null);
1472+
this.setApproachDetails(true, false, ApproachType.APPROACH_TYPE_UNKNOWN, RnavTypeFlags.None, RnavTypeFlags.None, false, false, false, null, null);
14601473
plan.deleteUserData(Fms.VTF_FAF_DATA_KEY);
14611474

14621475
plan.calculate(0);
@@ -2105,19 +2118,21 @@ export class Fms<ID extends string = any> {
21052118
}
21062119
}
21072120

2108-
const approachType = visualRunway ? AdditionalApproachType.APPROACH_TYPE_VISUAL : facility.approaches[approachIndex].approachType;
2109-
const bestRnavType = visualRunway ? RnavTypeFlags.None : FmsUtils.getBestRnavType(facility.approaches[approachIndex].rnavTypeFlags);
2110-
const rnavTypeFlags = visualRunway ? RnavTypeFlags.None : facility.approaches[approachIndex].rnavTypeFlags;
2111-
const approachIsCircling = !visualRunway && !facility.approaches[approachIndex].runway ? true : false;
2121+
const approach = facility.approaches[approachIndex];
2122+
const approachType = visualRunway ? AdditionalApproachType.APPROACH_TYPE_VISUAL : approach.approachType;
2123+
const bestRnavType = visualRunway ? RnavTypeFlags.None : FmsUtils.getBestRnavType(approach.rnavTypeFlags);
2124+
const rnavTypeFlags = visualRunway ? RnavTypeFlags.None : approach.rnavTypeFlags;
2125+
const approachIsCircling = !visualRunway && !approach.runway ? true : false;
21122126
const isVtf = approachTransitionIndex < 0;
2127+
const isRnpAr = visualRunway ? false : ApproachUtils.isRnpAr(approach);
21132128

21142129
let referenceFacility: VorFacility | null = null;
2115-
if (!visualRunway && FmsUtils.approachHasNavFrequency(facility.approaches[approachIndex])) {
2116-
referenceFacility = (await ApproachUtils.getReferenceFacility(facility.approaches[approachIndex], this.facLoader) as VorFacility | undefined) ?? null;
2130+
if (!visualRunway && FmsUtils.approachHasNavFrequency(approach)) {
2131+
referenceFacility = (await ApproachUtils.getReferenceFacility(approach, this.facLoader) as VorFacility | undefined) ?? null;
21172132
}
21182133

21192134
++this.updateApproachDetailsOpId;
2120-
this.setApproachDetails(true, true, approachType, bestRnavType, rnavTypeFlags, approachIsCircling, isVtf, referenceFacility, approachRunway);
2135+
this.setApproachDetails(true, true, approachType, bestRnavType, rnavTypeFlags, approachIsCircling, isVtf, isRnpAr, referenceFacility, approachRunway);
21212136

21222137
this.autoDesignateProcedureConstraints(plan, approachSegment.segmentIndex);
21232138

@@ -2187,6 +2202,8 @@ export class Fms<ID extends string = any> {
21872202
? FmsUtils.buildVisualApproach(facility, visualRunway!, this.visualApproachOptions.finalFixDistance, this.visualApproachOptions.strghtFixDistance)
21882203
: facility.approaches[approachIndex];
21892204

2205+
const finalLegs = approach.finalLegs;
2206+
21902207
const transition = approach.transitions[approachTransitionIndex];
21912208
const isVtf = approachTransitionIndex < 0;
21922209
const insertProcedureObject: InsertProcedureObject = { procedureLegs: [] };
@@ -2200,14 +2217,33 @@ export class Fms<ID extends string = any> {
22002217
}
22012218
}
22022219

2203-
const lastTransitionLeg = insertProcedureObject.procedureLegs[insertProcedureObject.procedureLegs.length - 1];
2220+
const lastTransitionLeg = insertProcedureObject.procedureLegs[insertProcedureObject.procedureLegs.length - 1] as InsertProcedureObjectLeg | undefined;
2221+
2222+
let finalLegsStartIndex = 0;
22042223

22052224
if (isVtf) {
22062225
insertProcedureObject.procedureLegs.push(FlightPlan.createLeg({ type: LegType.ThruDiscontinuity }));
2226+
} else if (lastTransitionLeg) {
2227+
// Check if the last transition leg is an XF leg that terminates at the FACF (flagged as IF) or FAF. If so, then
2228+
// we need to merge the last transition leg with the FACF/FAF leg and skip every leg in the final legs array
2229+
// before the latter.
2230+
if (FlightPlanUtils.isToFixLeg(lastTransitionLeg.type) && !ICAO.isValueEmpty(lastTransitionLeg.fixIcaoStruct)) {
2231+
for (let i = 0; i < finalLegs.length; i++) {
2232+
const leg = finalLegs[i];
2233+
if (
2234+
BitFlags.isAny(leg.fixTypeFlags, FixTypeFlags.IF | FixTypeFlags.FAF)
2235+
&& FlightPlanUtils.isToFixLeg(leg.type)
2236+
&& ICAO.valueEquals(leg.fixIcaoStruct, lastTransitionLeg.fixIcaoStruct)
2237+
) {
2238+
insertProcedureObject.procedureLegs[insertProcedureObject.procedureLegs.length - 1] = this.mergeDuplicateLegData(lastTransitionLeg, leg);
2239+
finalLegsStartIndex = i + 1;
2240+
break;
2241+
}
2242+
}
2243+
}
22072244
}
22082245

2209-
const finalLegs = approach.finalLegs;
2210-
for (let i = 0; i < finalLegs.length; i++) {
2246+
for (let i = finalLegsStartIndex; i < finalLegs.length; i++) {
22112247
const leg = FlightPlanUtils.convertLegRunwayIcaosToSdkFormat(FlightPlan.createLeg(finalLegs[i]));
22122248
if (i === 0 && lastTransitionLeg && this.isDuplicateIFLeg(lastTransitionLeg, leg)) {
22132249
insertProcedureObject.procedureLegs[insertProcedureObject.procedureLegs.length - 1] = this.mergeDuplicateLegData(lastTransitionLeg, leg);
@@ -2425,7 +2461,7 @@ export class Fms<ID extends string = any> {
24252461
}
24262462

24272463
++this.updateApproachDetailsOpId;
2428-
this.setApproachDetails(true, true, approachType, bestRnavType, rnavTypeFlags, approachIsCircling, isVtf, referenceFacility, approachRunway);
2464+
this.setApproachDetails(true, true, approachType, bestRnavType, rnavTypeFlags, approachIsCircling, isVtf, false, referenceFacility, approachRunway);
24292465

24302466
await plan.calculate();
24312467

@@ -3347,7 +3383,7 @@ export class Fms<ID extends string = any> {
33473383
const plan = this.getFlightPlan();
33483384

33493385
++this.updateApproachDetailsOpId;
3350-
this.setApproachDetails(true, false, ApproachType.APPROACH_TYPE_UNKNOWN, RnavTypeFlags.None, RnavTypeFlags.None, false, false, null, null);
3386+
this.setApproachDetails(true, false, ApproachType.APPROACH_TYPE_UNKNOWN, RnavTypeFlags.None, RnavTypeFlags.None, false, false, false, null, null);
33513387
plan.deleteUserData(Fms.VTF_FAF_DATA_KEY);
33523388

33533389
const hasArrival = plan.procedureDetails.arrivalIndex >= 0;
@@ -4264,7 +4300,7 @@ export class Fms<ID extends string = any> {
42644300
plan.deleteUserData(FmsFplUserDataKey.ApproachSkipCourseReversal);
42654301

42664302
++this.updateApproachDetailsOpId;
4267-
this.setApproachDetails(true, false, ApproachType.APPROACH_TYPE_UNKNOWN, RnavTypeFlags.None, RnavTypeFlags.None, false, false, null, null);
4303+
this.setApproachDetails(true, false, ApproachType.APPROACH_TYPE_UNKNOWN, RnavTypeFlags.None, RnavTypeFlags.None, false, false, false, null, null);
42684304
plan.deleteUserData(Fms.VTF_FAF_DATA_KEY);
42694305

42704306
plan.setCalculatingLeg(0);
@@ -5941,18 +5977,32 @@ export class Fms<ID extends string = any> {
59415977
}
59425978

59435979
/**
5944-
* Merges two duplicate legs such that the new merged leg contains the fix type and altitude data from the source leg
5945-
* and all other data is derived from the target leg.
5980+
* Merges two duplicate legs. The merged leg will be identical to the target leg with the following exceptions:
5981+
* - The merged leg's fix type flags are the union of those of the target and source legs.
5982+
* - The merged leg's altitude restriction data are equal to those of the source leg if the source leg defines an
5983+
* altitude restriction. Otherwise the merged leg's altitude restriction data are equal to those of the target leg.
5984+
* - The merged leg's speed restriction data are equal to those of the source leg if the source leg defines a speed
5985+
* restriction. Otherwise the merged leg's speed restriction data are equal to those of the target leg.
59465986
* @param target The target leg.
59475987
* @param source The source leg.
5948-
* @returns the merged leg.
5988+
* @returns The merged leg.
59495989
*/
59505990
private mergeDuplicateLegData(target: FlightPlanLeg, source: FlightPlanLeg): FlightPlanLeg {
59515991
const merged = FlightPlan.createLeg(target);
5992+
59525993
merged.fixTypeFlags |= source.fixTypeFlags;
5953-
merged.altDesc = source.altDesc;
5954-
merged.altitude1 = source.altitude1;
5955-
merged.altitude2 = source.altitude2;
5994+
5995+
if (source.altDesc !== AltitudeRestrictionType.Unused) {
5996+
merged.altDesc = source.altDesc;
5997+
merged.altitude1 = source.altitude1;
5998+
merged.altitude2 = source.altitude2;
5999+
}
6000+
6001+
if (source.speedRestrictionDesc !== SpeedRestrictionType.Unused) {
6002+
merged.speedRestrictionDesc = source.speedRestrictionDesc;
6003+
merged.speedRestriction = source.speedRestriction;
6004+
}
6005+
59566006
return merged;
59576007
}
59586008

@@ -6165,6 +6215,7 @@ export class Fms<ID extends string = any> {
61656215
* @param rnavTypeFlags The RNAV minimum type flags for the approach.
61666216
* @param isCircling Whether the approach is a circling approach.
61676217
* @param isVtf Whether the approach is a vectors-to-final approach.
6218+
* @param isRnpAr Whether the approach is an RNP-AR approach.
61686219
* @param referenceFacility The approach's reference facility.
61696220
* @param runway The assigned runway for the approach
61706221
*/
@@ -6176,6 +6227,7 @@ export class Fms<ID extends string = any> {
61766227
rnavTypeFlags?: RnavTypeFlags,
61776228
isCircling?: boolean,
61786229
isVtf?: boolean,
6230+
isRnpAr?: boolean,
61796231
referenceFacility?: VorFacility | null,
61806232
runway?: OneWayRunway | null
61816233
): void {
@@ -6185,14 +6237,12 @@ export class Fms<ID extends string = any> {
61856237
rnavTypeFlags !== undefined && this.approachDetails.set('rnavTypeFlags', rnavTypeFlags);
61866238
isCircling !== undefined && this.approachDetails.set('isCircling', isCircling);
61876239
isVtf !== undefined && this.approachDetails.set('isVtf', isVtf);
6240+
isRnpAr !== undefined && this.approachDetails.set('isRnpAr', isRnpAr);
61886241
referenceFacility !== undefined && this.approachDetails.set('referenceFacility', referenceFacility);
61896242
runway !== undefined && this.approachDetails.set('runway', runway);
61906243

61916244
const approachDetails = this.approachDetails.get();
61926245

6193-
// If an approach is flagged as RNAV but has no defined RNAV minima, assume it is an RNP (AR) approach if it is not circling.
6194-
this.approachDetails.set('isRnpAr', approachDetails.type === ApproachType.APPROACH_TYPE_RNAV && approachDetails.bestRnavType === 0 && !approachDetails.isCircling);
6195-
61966246
if (this.needPublishApproachDetails) {
61976247
this.needPublishApproachDetails = false;
61986248

src/garminsdk/flightplan/FmsUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ export class FmsUtils {
203203
);
204204

205205
const runwayCode = RunwayUtils.getRunwayCode(runway.direction);
206-
const runwayLetter = RunwayUtils.getDesignatorLetter(runway.runwayDesignator).padStart(1, ' ');
206+
const runwayLetter = RunwayUtils.getDesignatorLetter(runway.runwayDesignator).padStart(1, '-');
207207
initialLegIdent ??= 'STRGHT';
208208

209209
const iafFixIcao = ICAO.value(IcaoType.VisualApproach, `${runwayCode}${runwayLetter}`, airport.icaoStruct.ident, initialLegIdent);

src/garminsdk/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@microsoft/msfs-garminsdk",
3-
"version": "2.0.5",
3+
"version": "2.0.7",
44
"description": "Common files for the Garmin series of avionics systems",
55
"main": "garminsdk.js",
66
"scripts": {
@@ -11,7 +11,7 @@
1111
"author": "Working Title Simulations",
1212
"license": "MIT",
1313
"devDependencies": {
14-
"@microsoft/msfs-sdk": "2.0.5",
14+
"@microsoft/msfs-sdk": "2.0.7",
1515
"@microsoft/msfs-types": "1.14.6",
1616
"@rollup/plugin-node-resolve": "^15.0.1",
1717
"@types/node": "^18.11.18",

src/sdk/autopilot/calculators/SmoothingPathCalculator.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -851,9 +851,7 @@ export class SmoothingPathCalculator implements VNavPathCalculator {
851851
}
852852
}
853853

854-
if (constraint.index === verticalPlan.lastDescentConstraintLegIndex) {
855-
constraint.isPathEnd = true;
856-
constraint.isTarget = true;
854+
if (constraint.isPathEnd) {
857855
constraintIsBod = true;
858856
}
859857

@@ -1080,15 +1078,20 @@ export class SmoothingPathCalculator implements VNavPathCalculator {
10801078
}
10811079

10821080
/**
1083-
* Populates a vertical flight plan's constraints with legs and updates the constraint distances and VNAV path
1084-
* eligibility data.
1081+
* Populates a vertical flight plan's constraints with legs, updates the constraint distances and VNAV path
1082+
* eligibility data, and resets the constraint path and FPA data.
10851083
* @param verticalPlan The vertical flight plan for which to populate constraints.
10861084
*/
10871085
protected populateConstraints(verticalPlan: VerticalFlightPlan): void {
10881086
for (let constraintIndex = 0; constraintIndex < verticalPlan.constraints.length; constraintIndex++) {
10891087
const constraint = verticalPlan.constraints[constraintIndex];
10901088
const previousConstraint = verticalPlan.constraints[constraintIndex + 1];
10911089

1090+
constraint.isPathEnd = false;
1091+
constraint.isTarget = false;
1092+
constraint.fpa = 0;
1093+
constraint.targetAltitude = 0;
1094+
10921095
constraint.legs.length = 0;
10931096

10941097
constraint.distance = VNavUtils.getConstraintDistanceFromLegs(constraint, previousConstraint, verticalPlan);
@@ -1151,6 +1154,8 @@ export class SmoothingPathCalculator implements VNavPathCalculator {
11511154
return false;
11521155
}
11531156

1157+
let hasFoundPathEndConstraint = false;
1158+
11541159
for (let targetConstraintIndex = lastDescentConstraintIndex; targetConstraintIndex <= firstDescentConstraintIndex; targetConstraintIndex++) {
11551160
const constraint = verticalPlan.constraints[targetConstraintIndex];
11561161

@@ -1169,6 +1174,11 @@ export class SmoothingPathCalculator implements VNavPathCalculator {
11691174
currentTargetConstraint = constraint;
11701175
currentTargetConstraint.targetAltitude = constraint.minAltitude > Number.NEGATIVE_INFINITY ? constraint.minAltitude : constraint.maxAltitude;
11711176
currentTargetConstraint.isTarget = true;
1177+
1178+
if (!hasFoundPathEndConstraint) {
1179+
currentTargetConstraint.isPathEnd = true;
1180+
hasFoundPathEndConstraint = true;
1181+
}
11721182
} else { continue; }
11731183
}
11741184

src/sdk/autopilot/directors/APAltCapDirector.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { SimVarValueType } from '../../data/SimVars';
44
import { MathUtils } from '../../math/MathUtils';
55
import { UnitType } from '../../math/NumberUnit';
6+
import { DebounceTimer } from '../../utils/time/DebounceTimer';
67
import { APValues } from '../APValues';
78
import { VNavUtils } from '../vnav/VNavUtils';
89
import { DirectorState, PlaneDirector } from './PlaneDirector';
@@ -21,6 +22,14 @@ export type APAltCapDirectorOptions = {
2122
* A function that returns true if the capturing shall start.
2223
*/
2324
shouldActivate: APAltCapDirectorActivationFunc | undefined;
25+
26+
/**
27+
* The time to inhibit altitude capture when the target altitude is changed, in ms.
28+
* Setting the time to null disables inhibition.
29+
* Defaults to 500 ms.
30+
* Note that if alt capture is already active when the target is changed, this will have no effect.
31+
*/
32+
targetChangeInhibitTime: number | null;
2433
};
2534

2635
/**
@@ -57,6 +66,11 @@ export type APAltCapDirectorActivationFunc = (
5766
* An altitude capture autopilot director.
5867
*/
5968
export class APAltCapDirector implements PlaneDirector {
69+
private static readonly DEFAULT_TARGET_CHANGE_INHIBIT_MS = 500;
70+
private static readonly EMPTY_FUNCTION = (): void => {};
71+
72+
private readonly targetChangeInhibitTimer?: DebounceTimer;
73+
private readonly targetChangeInhibitTime: number | null = APAltCapDirector.DEFAULT_TARGET_CHANGE_INHIBIT_MS;
6074

6175
public state: DirectorState;
6276

@@ -73,6 +87,11 @@ export class APAltCapDirector implements PlaneDirector {
7387
private readonly captureAltitude: APAltCapDirectorCaptureFunc = APAltCapDirector.captureAltitude;
7488
private readonly shouldActivate: APAltCapDirectorActivationFunc = APAltCapDirector.shouldActivate;
7589

90+
/**
91+
* Inhibits altitude capture actrivation for {@link APAltCapDirector.targetChangeInhibitTime}.
92+
*/
93+
private inhibitAltCapture?: () => void;
94+
7695
/**
7796
* Creates an instance of the APAltCapDirector.
7897
* @param apValues Autopilot data for this director.
@@ -81,6 +100,10 @@ export class APAltCapDirector implements PlaneDirector {
81100
* defined, a default function is used.
82101
* --> captureAltitude: An optional function which calculates desired pitch angles to capture a target altitude. If not
83102
* defined, a default function is used.
103+
* --> targetChangeInhibitTime: The time to inhibit altitude capture when the target altitude is changed, in ms.
104+
* Setting the time to null disables inhibition.
105+
* Defaults to 500 ms.
106+
* Note that if alt capture is already active when the target is changed, this will have no effect.
84107
*/
85108
constructor(
86109
private readonly apValues: APValues,
@@ -93,8 +116,18 @@ export class APAltCapDirector implements PlaneDirector {
93116
if (options?.shouldActivate !== undefined) {
94117
this.shouldActivate = options.shouldActivate;
95118
}
96-
}
119+
if (options?.targetChangeInhibitTime !== undefined) {
120+
this.targetChangeInhibitTime = options.targetChangeInhibitTime;
121+
}
97122

123+
if (this.targetChangeInhibitTime !== null) {
124+
this.targetChangeInhibitTimer = new DebounceTimer();
125+
this.inhibitAltCapture = () => {
126+
this.targetChangeInhibitTimer?.schedule(APAltCapDirector.EMPTY_FUNCTION, this.targetChangeInhibitTime!);
127+
};
128+
this.apValues.selectedAltitude.sub(this.inhibitAltCapture, false);
129+
}
130+
}
98131

99132
/**
100133
* Activates this director.
@@ -157,6 +190,10 @@ export class APAltCapDirector implements PlaneDirector {
157190
* Attempts to activate altitude capture.
158191
*/
159192
private tryActivate(): void {
193+
if (this.targetChangeInhibitTimer?.isPending()) {
194+
return;
195+
}
196+
160197
const selectedAltitude = this.apValues.selectedAltitude.get();
161198
const vs = SimVar.GetSimVarValue('VERTICAL SPEED', SimVarValueType.FPM);
162199
const alt = SimVar.GetSimVarValue('INDICATED ALTITUDE', SimVarValueType.Feet);
@@ -226,4 +263,4 @@ export class APAltCapDirector implements PlaneDirector {
226263
const desiredFpa = MathUtils.clamp(Math.asin(desiredVs / UnitType.KNOT.convertTo(tas, UnitType.FPM)) * Avionics.Utils.RAD2DEG, -initialFpaAbs, initialFpaAbs);
227264
return MathUtils.clamp(desiredFpa, -15, 15);
228265
}
229-
}
266+
}

0 commit comments

Comments
 (0)