From 92652ffe95e8d46cb6ab6139fa84775c526de3e2 Mon Sep 17 00:00:00 2001 From: Erik Quinn Date: Sun, 9 Feb 2020 08:39:05 -0500 Subject: [PATCH 1/4] bugfix/1398 - Improve aircraft slowing behavior on final approach --- .../scripts/client/aircraft/AircraftModel.js | 15 +++++++--- .../client/constants/aircraftConstants.js | 30 ++++++++----------- .../client/constants/airportConstants.js | 2 +- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/assets/scripts/client/aircraft/AircraftModel.js b/src/assets/scripts/client/aircraft/AircraftModel.js index 31bf3579f9..e0704672b0 100644 --- a/src/assets/scripts/client/aircraft/AircraftModel.js +++ b/src/assets/scripts/client/aircraft/AircraftModel.js @@ -1040,10 +1040,14 @@ export default class AircraftModel { * @return {boolean} */ isOnFinal() { + if (!this.isEstablishedOnCourse()) { + return false; + } + const approachDistanceNm = this.positionModel.distanceToPosition(this.mcp.nav1Datum); const maxDistanceConsideredOnFinalNm = AIRPORT_CONSTANTS.FINAL_APPROACH_FIX_DISTANCE_NM; - return this.isEstablishedOnCourse() && approachDistanceNm <= maxDistanceConsideredOnFinalNm; + return approachDistanceNm <= maxDistanceConsideredOnFinalNm; } /** @@ -2227,6 +2231,8 @@ export default class AircraftModel { const runwayModel = this.fms.arrivalRunwayModel; const offset = getOffset(this, runwayModel.relativePosition, runwayModel.angle); const distanceOnFinal_nm = nm(offset[1]); + const stableApproachTimeHours = PERFORMANCE.STABLE_APPROACH_TIME_SECONDS * TIME.ONE_SECOND_IN_HOURS; + const stableApproachDistance = this.model.speed.landing * stableApproachTimeHours; if (distanceOnFinal_nm <= 0 && this.isOnGround()) { return 0; @@ -2237,9 +2243,9 @@ export default class AircraftModel { } const nextSpeed = extrapolate_range_clamp( - AIRPORT_CONSTANTS.LANDING_FINAL_APPROACH_SPEED_DISTANCE_NM, + stableApproachDistance, distanceOnFinal_nm, - AIRPORT_CONSTANTS.LANDING_ASSIGNED_SPEED_DISTANCE_NM, + AIRPORT_CONSTANTS.FINAL_APPROACH_FIX_DISTANCE_NM, this.model.speed.landing, startSpeed ); @@ -2443,7 +2449,8 @@ export default class AircraftModel { // TODO: this should be an available property on the `AircraftTypeDefinitionModel` let descentRate = this.model.rate.descent * PERFORMANCE.TYPICAL_DESCENT_FACTOR; - if (this.target.expedite) { + // TODO: target.expedite... if it's not used, remove it! + if (this.target.expedite || this.mcp.shouldExpediteAltitudeChange || this.isOnFinal()) { descentRate = this.model.rate.descent; } diff --git a/src/assets/scripts/client/constants/aircraftConstants.js b/src/assets/scripts/client/constants/aircraftConstants.js index d7f7c66308..899d7083f2 100644 --- a/src/assets/scripts/client/constants/aircraftConstants.js +++ b/src/assets/scripts/client/constants/aircraftConstants.js @@ -133,24 +133,6 @@ export const PERFORMANCE = { */ DECELERATION_FACTOR_DUE_TO_GROUND_BRAKING: 3.5, - /* - * Distance from landing threshold at which to establish on final approach speed - * - * @property LANDING_FINAL_APPROACH_SPEED_DISTANCE_NM - * @type {number} - * @final - */ - LANDING_FINAL_APPROACH_SPEED_DISTANCE_NM: 1, - - /** - * Distance from landing threshold outside of which you must maintain assigned speed - * - * @property LANDING_ASSIGNED_SPEED_DISTANCE_NM - * @type {number} - * @final - */ - LANDING_ASSIGNED_SPEED_DISTANCE_NM: 5, - /** * Maximum vertical distance between the aircraft and the glidepath to * consider the aircraft to be "established on the glidepath" @@ -223,6 +205,18 @@ export const PERFORMANCE = { */ INSTRUMENT_APPROACH_MINIMUM_DESCENT_ALTITUDE: 200, + /** + * Length of time individual aircraft will require themselves to be established at Vref + * (their landing speed) before landing. If they cannot reach that speed by that time, they + * will not consider themselves on a "stable approach", and will likely go around. + * + * @memberof PERFORMANCE + * @property STABLE_APPROACH_TIME_SECONDS + * @type {number} + * @final + */ + STABLE_APPROACH_TIME_SECONDS: 60, + /** * Altitude above the runway at which aircraft begin their on-course turn, in feet * diff --git a/src/assets/scripts/client/constants/airportConstants.js b/src/assets/scripts/client/constants/airportConstants.js index 383f679b24..40f62faf5d 100644 --- a/src/assets/scripts/client/constants/airportConstants.js +++ b/src/assets/scripts/client/constants/airportConstants.js @@ -36,7 +36,7 @@ export const AIRPORT_CONSTANTS = { * @type {number} * @final */ - FINAL_APPROACH_FIX_DISTANCE_NM: 4, + FINAL_APPROACH_FIX_DISTANCE_NM: 5, /** * Maximum allowable indicated airspeed for aircraft below 10,000 feet MSL From 03c5156147daef04479d36eb807071f1df818967 Mon Sep 17 00:00:00 2001 From: Erik Quinn Date: Sun, 9 Feb 2020 16:14:54 -0500 Subject: [PATCH 2/4] bugfix/1398 - Allow a/c to self-expedite on approach and remove redundant expedite code --- .../scripts/client/aircraft/AircraftModel.js | 24 ++++++------------- .../scripts/client/aircraft/Pilot/Pilot.js | 1 + 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/assets/scripts/client/aircraft/AircraftModel.js b/src/assets/scripts/client/aircraft/AircraftModel.js index e0704672b0..a0cb2be1a9 100644 --- a/src/assets/scripts/client/aircraft/AircraftModel.js +++ b/src/assets/scripts/client/aircraft/AircraftModel.js @@ -472,7 +472,6 @@ export default class AircraftModel { // not given a heading directly, but has a fix or is following an ILS path this.target = { altitude: 0, - expedite: false, heading: null, turn: null, speed: 0 @@ -1406,7 +1405,6 @@ export default class AircraftModel { * @method updateTarget */ updateTarget() { - this.target.expedite = _defaultTo(this.fms.currentWaypoint.expedite, false); this.target.altitude = _defaultTo(this._calculateTargetedAltitude(), this.target.altitude); this._updateTargetedDirectionality(); @@ -1426,7 +1424,6 @@ export default class AircraftModel { case FLIGHT_PHASE.APRON: // TODO: Is this needed? // this.target.altitude = this.altitude; - // this.target.expedite = false; // this.targetHeading = this.heading; // this.target.speed = 0; @@ -1435,7 +1432,6 @@ export default class AircraftModel { case FLIGHT_PHASE.TAXI: // TODO: Is this needed? // this.target.altitude = this.altitude; - // this.target.expedite = false; // this.targetHeading = this.heading; // this.target.speed = 0; @@ -1444,7 +1440,6 @@ export default class AircraftModel { case FLIGHT_PHASE.WAITING: // TODO: Is this needed? // this.target.altitude = this.altitude; - // this.target.expedite = false; // this.targetHeading = this.heading; // this.target.speed = 0; @@ -1457,7 +1452,6 @@ export default class AircraftModel { this.target.altitude = this.model.ceiling; } - this.target.expedite = false; this.targetHeading = this.heading; this.target.speed = this.model.speed.min; @@ -2425,12 +2419,6 @@ export default class AircraftModel { updateAltitudePhysics() { this.trend = 0; - // TODO: Is this needed? - // // TODO: abstract to class method - // if (this.speed <= this.model.speed.min && this.mcp.speedMode === MCP_MODE.SPEED.N1) { - // return; - // } - if (this.target.altitude < this.altitude) { this.decreaseAircraftAltitude(); } else if (this.target.altitude > this.altitude) { @@ -2446,11 +2434,9 @@ export default class AircraftModel { */ decreaseAircraftAltitude() { const altitude_diff = this.altitude - this.target.altitude; - // TODO: this should be an available property on the `AircraftTypeDefinitionModel` let descentRate = this.model.rate.descent * PERFORMANCE.TYPICAL_DESCENT_FACTOR; - // TODO: target.expedite... if it's not used, remove it! - if (this.target.expedite || this.mcp.shouldExpediteAltitudeChange || this.isOnFinal()) { + if (this.mcp.shouldExpediteAltitudeChange || this.isEstablishedOnCourse()) { descentRate = this.model.rate.descent; } @@ -2459,6 +2445,7 @@ export default class AircraftModel { if (abs(altitude_diff) < feetDescended) { this.altitude = this.target.altitude; + this.mcp.shouldExpediteAltitudeChange = false; } else { this.altitude -= feetDescended; } @@ -2476,7 +2463,8 @@ export default class AircraftModel { const altitude_diff = this.altitude - this.target.altitude; let climbRate = this.getClimbRate() * PERFORMANCE.TYPICAL_CLIMB_FACTOR; - if (this.target.expedite) { + // TODO: Ensure expediting is STOPPED when the altitude is reached + if (this.mcp.shouldExpediteAltitudeChange || this.isTakeoff()) { climbRate = this.model.rate.climb; } @@ -2485,6 +2473,7 @@ export default class AircraftModel { if (abs(altitude_diff) < abs(feetClimbed)) { this.altitude = this.target.altitude; + this.mcp.shouldExpediteAltitudeChange = false; } else { this.altitude += feetClimbed; } @@ -2493,7 +2482,8 @@ export default class AircraftModel { } /** - * This updates the speed for the instance of the aircraft by checking the difference between current speed and requested speed + * This updates the speed for the instance of the aircraft by checking the + * difference between current speed and requested speed * * @for AircraftModel * @method updateWarning diff --git a/src/assets/scripts/client/aircraft/Pilot/Pilot.js b/src/assets/scripts/client/aircraft/Pilot/Pilot.js index ebc9e179f4..4c7edde1e0 100644 --- a/src/assets/scripts/client/aircraft/Pilot/Pilot.js +++ b/src/assets/scripts/client/aircraft/Pilot/Pilot.js @@ -145,6 +145,7 @@ export default class Pilot { this.cancelApproachClearance(aircraftModel); this._mcp.setAltitudeFieldValue(clampedAltitude); this._mcp.setAltitudeHold(); + this._mcp.shouldExpediteAltitudeChange = false; // Build readback const readbackAltitude = _floor(clampedAltitude, -2); From 035eb329b00d83c046c7d928733c608a86de45b5 Mon Sep 17 00:00:00 2001 From: Erik Quinn Date: Sun, 9 Feb 2020 16:36:18 -0500 Subject: [PATCH 3/4] bugfix/1398 - Update tests --- test/aircraft/Pilot/Pilot.spec.js | 8 ++++---- test/airport/runway/RunwayModel.spec.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/aircraft/Pilot/Pilot.spec.js b/test/aircraft/Pilot/Pilot.spec.js index 517c764c3e..7cc732b906 100644 --- a/test/aircraft/Pilot/Pilot.spec.js +++ b/test/aircraft/Pilot/Pilot.spec.js @@ -562,10 +562,10 @@ ava('.conductInstrumentApproach() returns failure message when no runway is prov ava('.conductInstrumentApproach() returns failure message when assigned altitude is lower than minimum glideslope intercept altitude', (t) => { const expectedResult = [false, { - log: 'unable ILS 19L, our assigned altitude is below the minimum glideslope ' - + 'intercept altitude, request climb to 3400', - say: 'unable ILS one niner left, our assigned altitude is below the minimum ' - + 'glideslope intercept altitude, request climb to three thousand four hundred' + log: 'unable ILS 19L, our assigned altitude is below the minimum glideslope ' + + 'intercept altitude, request climb to 3700', + say: 'unable ILS one niner left, our assigned altitude is below the minimum ' + + 'glideslope intercept altitude, request climb to three thousand seven hundred' }]; const aircraftModel = new AircraftModel(ARRIVAL_AIRCRAFT_INIT_PROPS_MOCK, createNavigationLibraryFixture()); diff --git a/test/airport/runway/RunwayModel.spec.js b/test/airport/runway/RunwayModel.spec.js index 63edfe188a..95b1292f3f 100644 --- a/test/airport/runway/RunwayModel.spec.js +++ b/test/airport/runway/RunwayModel.spec.js @@ -83,7 +83,7 @@ ava('.getGlideslopeAltitude() returns glideslope altitude at the specified dista ava('.getGlideslopeAltitudeAtFinalApproachFix() returns glideslope altitude at the final approach fix', (t) => { const model = new RunwayModel(runway07L25R, 0, airportPositionFixtureKLAS); - const expectedResult = 3452.7428770628912; + const expectedResult = 3771.178596328614; const result = model.getGlideslopeAltitudeAtFinalApproachFix(); t.true(result === expectedResult); @@ -91,7 +91,7 @@ ava('.getGlideslopeAltitudeAtFinalApproachFix() returns glideslope altitude at t ava('.getMinimumGlideslopeInterceptAltitude() returns glideslope altitude at the final approach fix', (t) => { const model = new RunwayModel(runway07L25R, 0, airportPositionFixtureKLAS); - const expectedResult = 3500; + const expectedResult = 3800; const result = model.getMinimumGlideslopeInterceptAltitude(); t.true(result === expectedResult); From 8c304601c5877f459210ceac3691ad0c25757740 Mon Sep 17 00:00:00 2001 From: Erik Quinn Date: Tue, 11 Feb 2020 16:11:08 -0500 Subject: [PATCH 4/4] bugfix/1398 - Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87315f61ba..0ceaea6675 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - #1389 - Prevent unfair penalty at KDCA on IRONS7 arrivals by deactivating R6601B/C - #1218 - Ensure proper removal of aircraft who collide with terrain/traffic - #1513 - Add missing fleets to AFR/LDM airlines that were crashing EDDM/EIDW +- #1398 - Fix go-arounds from aircraft with low descent rates ### Enhancements & Refactors - #1486 - Update AFL airline