From 3313a8d463851c2579c82f3c976c7b47ec729f68 Mon Sep 17 00:00:00 2001 From: Sven Czarnian Date: Wed, 13 Oct 2021 18:13:00 +0200 Subject: [PATCH] calculate the descend profiles based on the arrival constraints --- aman/types/Inbound.py | 101 +++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 22 deletions(-) diff --git a/aman/types/Inbound.py b/aman/types/Inbound.py index ae692e5..9045ecf 100644 --- a/aman/types/Inbound.py +++ b/aman/types/Inbound.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import pytz +import sys from datetime import datetime, timedelta @@ -62,50 +63,106 @@ class Inbound: return timedelta(seconds = 0) # calculate remaining trackmiles - remainingDistanceNM = self.Report.distanceToIAF + trackmiles = self.Report.distanceToIAF start = self.PlannedStar.Route[0] - for i in range(1, len(self.PlannedStar.Route)): - remainingDistanceNM += start.haversine(self.PlannedStar.Route[i]) * 0.539957 + turnIndices = [ -1, -1 ] + constraints = [] + for i in range(0, len(self.PlannedStar.Route)): + # identified the base turn + if True == self.PlannedStar.Route[i].BaseTurn: + turnIndices[0] = i + # identified the final turn + elif -1 != turnIndices[0] and True == self.PlannedStar.Route[i].FinalTurn: + turnIndices[1] = i + # skip waypoints until the final turn point is found + elif -1 != turnIndices[0] and -1 == turnIndices[1]: + continue + + trackmiles += start.haversine(self.PlannedStar.Route[i]) * 0.539957 + + # check if a new constraint is defined + altitude = -1 + speed = -1 + if None != self.PlannedStar.Route[i].Altitude: + altitude = self.PlannedStar.Route[i].Altitude + if None != self.PlannedStar.Route[i].Speed: + speed = self.PlannedStar.Route[i].Speed + if -1 != altitude or -1 != speed: + constraints.append([ trackmiles, altitude, speed ]) + start = self.PlannedStar.Route[i] + # add the remaining distance from the last waypoint to the runway threshold + trackmiles += start.haversine(self.PlannedRunway.Runway.Start) + + if turnIndices[0] > turnIndices[1] or (-1 == turnIndices[1] and -1 != turnIndices[0]): + sys.stderr.write('Invalid constraint definition found for ' + self.PlannedStar.Name) + sys.exit(-1) + # calculate descend profile - flightTimeSeconds = 0 currentHeading = Waypoint(latitude = self.Report.position.latitude, longitude = self.Report.position.longitude).bearing(self.PlannedStar.Route[0]) - distanceToWaypoint = self.Report.distanceToIAF - nextWaypointIndex = 0 + currentIAS = self.PerformanceData.ias(self.Report.dynamics.altitude, trackmiles) currentPosition = [ self.Report.dynamics.altitude, self.Report.dynamics.groundSpeed ] - while 0 < currentPosition[0]: - lastDistance = remainingDistanceNM + distanceToWaypoint = self.Report.distanceToIAF + flightTimeSeconds = 0 + nextWaypointIndex = 0 + flownDistance = 0.0 - # TODO integrate speed and altitude constraints + while True: + # check if a constraint cleanup is needed and if a speed-update is needed + if 0 != len(constraints) and flownDistance >= constraints[0][0]: + if -1 != constraints[0][2]: + currentIAS = min(constraints[0][2], self.PerformanceData.ias(self.Report.dynamics.altitude, trackmiles - flownDistance)) + currentPosition[1] = min(weather.calculateGS(currentPosition[0], currentIAS, currentHeading), currentPosition[1]) + constraints.pop(0) - # calculate the next position after 10 seconds - if (currentPosition[0] / 1000 * 3) > remainingDistanceNM: + # search next altitude constraint + altitudeDistance = 0 + nextAltitude = 0 + for constraint in constraints: + if -1 != constraint[1]: + altitudeDistance = constraint[0] + nextAltitude = constraint[1] + break + + # check if update of altitude and speed is needed on 3° glide + if currentPosition[0] > nextAltitude and ((currentPosition[0] - nextAltitude) / 1000 * 3) > (altitudeDistance - flownDistance): oldGroundspeed = currentPosition[1] descendRate = (currentPosition[1] / 60) / 3 * 1000 / 6 newAltitude = currentPosition[0] - descendRate if 0 > newAltitude: newAltitude = 0 - # get the planned IAS and calculate the new altitude and GS out of the predicted information - # we assume that the aircraft only decelerates - ias = self.PerformanceData.ias(newAltitude, remainingDistanceNM) - currentPosition = [ newAltitude, min(weather.calculateGS(newAltitude, int(ias), currentHeading), currentPosition[1]) ] - - # use the average between last and current speed to estimate the remaining distance - remainingDistanceNM -= (currentPosition[1] + oldGroundspeed) / 2 / 60 / 6 + currentPosition = [ newAltitude, min(weather.calculateGS(newAltitude, currentIAS, currentHeading), currentPosition[1]) ] + distance = (currentPosition[1] + oldGroundspeed) / 2 / 60 / 6 else: - remainingDistanceNM -= currentPosition[1] / 60 / 6 + distance = currentPosition[1] / 60 / 6 + + # update the statistics + distanceToWaypoint -= distance + flownDistance += distance + newIAS = min(currentIAS, self.PerformanceData.ias(currentPosition[0], trackmiles - flownDistance)) + if newIAS < currentIAS: + currentPosition[1] = min(weather.calculateGS(currentPosition[0], newIAS, currentHeading), currentPosition[1]) + currentIAS = newIAS flightTimeSeconds += 10 - if 0 > remainingDistanceNM: + if flownDistance >= trackmiles: break # check if we follow a new waypoint pair - distanceToWaypoint -= abs(lastDistance - remainingDistanceNM) if 0 >= distanceToWaypoint: + lastWaypointIndex = nextWaypointIndex nextWaypointIndex += 1 + + # check if a skip from base to final turn waypoints is needed + if -1 != turnIndices[0] and nextWaypointIndex > turnIndices[0] and nextWaypointIndex < turnIndices[1]: + nextWaypointIndex = turnIndices[1] + + # update the statistics if nextWaypointIndex < len(self.PlannedStar.Route): - currentHeading = self.PlannedStar.Route[nextWaypointIndex - 1].bearing(self.PlannedStar.Route[nextWaypointIndex]) + distanceToWaypoint = self.PlannedStar.Route[lastWaypointIndex].haversine(self.PlannedStar.Route[nextWaypointIndex]) * 0.539957 + currentHeading = self.PlannedStar.Route[lastWaypointIndex].bearing(self.PlannedStar.Route[nextWaypointIndex]) + currentPosition[1] = min(weather.calculateGS(newAltitude, currentIAS, currentHeading), currentPosition[1]) return timedelta(seconds = flightTimeSeconds)