Skip to content

Commit

Permalink
Merge bugfix/1398 (openscope#1530)
Browse files Browse the repository at this point in the history
Improve aircraft slowing behavior on final approach
  • Loading branch information
erikquinn authored Feb 11, 2020
2 parents 7e8d063 + 8c30460 commit c061dd8
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- <a href="https://github.com/openscope/openscope/issues/1389" target="_blank">#1389</a> - Prevent unfair penalty at KDCA on IRONS7 arrivals by deactivating R6601B/C
- <a href="https://github.com/openscope/openscope/issues/1218" target="_blank">#1218</a> - Ensure proper removal of aircraft who collide with terrain/traffic
- <a href="https://github.com/openscope/openscope/issues/1513" target="_blank">#1513</a> - Add missing fleets to AFR/LDM airlines that were crashing EDDM/EIDW
- <a href="https://github.com/openscope/openscope/issues/1398" target="_blank">#1398</a> - Fix go-arounds from aircraft with low descent rates

### Enhancements & Refactors
- <a href="https://github.com/openscope/openscope/issues/1486" target="_blank">#1486</a> - Update AFL airline
Expand Down
35 changes: 16 additions & 19 deletions src/assets/scripts/client/aircraft/AircraftModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1040,10 +1039,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;
}

/**
Expand Down Expand Up @@ -1402,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();
Expand All @@ -1422,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;

Expand All @@ -1431,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;

Expand All @@ -1440,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;

Expand All @@ -1453,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;

Expand Down Expand Up @@ -2227,6 +2225,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;
Expand All @@ -2237,9 +2237,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
);
Expand Down Expand Up @@ -2419,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) {
Expand All @@ -2440,10 +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;

if (this.target.expedite) {
if (this.mcp.shouldExpediteAltitudeChange || this.isEstablishedOnCourse()) {
descentRate = this.model.rate.descent;
}

Expand All @@ -2452,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;
}
Expand All @@ -2469,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;
}

Expand All @@ -2478,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;
}
Expand All @@ -2486,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
Expand Down
1 change: 1 addition & 0 deletions src/assets/scripts/client/aircraft/Pilot/Pilot.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
30 changes: 12 additions & 18 deletions src/assets/scripts/client/constants/aircraftConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
*
Expand Down
2 changes: 1 addition & 1 deletion src/assets/scripts/client/constants/airportConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions test/aircraft/Pilot/Pilot.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
4 changes: 2 additions & 2 deletions test/airport/runway/RunwayModel.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ 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);
});

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);
Expand Down

0 comments on commit c061dd8

Please sign in to comment.