define member variables with capital letters
This commit is contained in:
55
aman/AMAN.py
55
aman/AMAN.py
@@ -27,12 +27,11 @@ class AMAN:
|
||||
|
||||
def __init__(self):
|
||||
# default initialization of members
|
||||
self.systemConfig = None
|
||||
self.aircraftPerformance = None
|
||||
self.receiver = None
|
||||
self.weather = None
|
||||
self.workers = []
|
||||
self.inbounds = {}
|
||||
self.SystemConfig = None
|
||||
self.AircraftPerformance = None
|
||||
self.Receiver = None
|
||||
self.Weather = None
|
||||
self.Workers = []
|
||||
|
||||
def __del__(self):
|
||||
self.release()
|
||||
@@ -41,19 +40,19 @@ class AMAN:
|
||||
configPath = AMAN.findConfigPath()
|
||||
|
||||
# read all system relevant configuration files
|
||||
self.systemConfig = System(os.path.join(configPath, 'System.ini'))
|
||||
self.SystemConfig = System(os.path.join(configPath, 'System.ini'))
|
||||
print('Parsed System.ini')
|
||||
|
||||
# read the aircraft performance data
|
||||
self.aircraftPerformance = AircraftPerformance(os.path.join(configPath, 'PerformanceData.ini'))
|
||||
if None == self.aircraftPerformance:
|
||||
self.AircraftPerformance = AircraftPerformance(os.path.join(configPath, 'PerformanceData.ini'))
|
||||
if None == self.AircraftPerformance:
|
||||
sys.stderr.write('No aircraft performance data found!')
|
||||
sys.exit(-1)
|
||||
else:
|
||||
print('Parsed PerformanceData.ini. Extracted ' + str(len(self.aircraftPerformance.Aircrafts)) + ' aircrafts')
|
||||
print('Parsed PerformanceData.ini. Extracted ' + str(len(self.AircraftPerformance.Aircrafts)) + ' aircrafts')
|
||||
|
||||
self.weather = Weather()
|
||||
self.weather.acquire(self.systemConfig.Weather)
|
||||
self.Weather = Weather()
|
||||
self.Weather.acquire(self.SystemConfig.Weather)
|
||||
|
||||
# find the airport configurations and create the workers
|
||||
airportsPath = os.path.join(os.path.join(configPath, 'airports'), '*.ini')
|
||||
@@ -65,33 +64,33 @@ class AMAN:
|
||||
|
||||
# initialize the worker thread
|
||||
worker = Worker()
|
||||
worker.acquire(icao, airportConfig, self.weather, self.aircraftPerformance)
|
||||
self.workers.append(worker)
|
||||
worker.acquire(icao, airportConfig, self.Weather, self.AircraftPerformance)
|
||||
self.Workers.append(worker)
|
||||
print('Started worker for ' + icao)
|
||||
|
||||
# create the EuroScope receiver
|
||||
self.receiver = Euroscope()
|
||||
self.receiver.acquire(configPath, self.systemConfig.Server, self)
|
||||
self.Receiver = Euroscope()
|
||||
self.Receiver.acquire(configPath, self.SystemConfig.Server, self)
|
||||
|
||||
def release(self):
|
||||
if None != self.receiver:
|
||||
self.receiver.release()
|
||||
self.receiver = None
|
||||
if None != self.Receiver:
|
||||
self.Receiver.release()
|
||||
self.Receiver = None
|
||||
|
||||
if None != self.weather:
|
||||
self.weather.release()
|
||||
self.weather = None
|
||||
if None != self.Weather:
|
||||
self.Weather.release()
|
||||
self.Weather = None
|
||||
|
||||
if None != self.workers:
|
||||
for worker in self.workers:
|
||||
if None != self.Workers:
|
||||
for worker in self.Workers:
|
||||
worker.release()
|
||||
self.workers = None
|
||||
self.Workers = None
|
||||
|
||||
def updateAircraftReport(self, report : AircraftReport_pb2.AircraftReport):
|
||||
# find the correct worker for the inbound
|
||||
for worker in self.workers:
|
||||
if worker.icao == report.destination:
|
||||
for worker in self.Workers:
|
||||
if worker.Icao == report.destination:
|
||||
worker.acquireLock()
|
||||
worker.reportQueue[report.aircraft.callsign] = report
|
||||
worker.ReportQueue[report.aircraft.callsign] = report
|
||||
worker.releaseLock()
|
||||
break
|
||||
@@ -19,8 +19,8 @@ from datetime import datetime as dt
|
||||
# - third element of wind data tuple: wind speed (KT)
|
||||
class DwdCrawler():
|
||||
def __init__(self):
|
||||
self.updateTime = None
|
||||
self.windData = None
|
||||
self.UpdateTime = None
|
||||
self.WindData = None
|
||||
|
||||
def parseGaforAreas(areas : str):
|
||||
areas = areas.replace(':', '')
|
||||
@@ -125,8 +125,8 @@ class DwdCrawler():
|
||||
return nextUpdate, windInformation
|
||||
|
||||
def receiveWindData(self):
|
||||
self.updateTime = None
|
||||
self.windData = None
|
||||
self.UpdateTime = None
|
||||
self.WindData = None
|
||||
|
||||
with urllib.request.urlopen('https://www.dwd.de/DE/fachnutzer/luftfahrt/teaser/luftsportberichte/luftsportberichte_node.html') as site:
|
||||
data = site.read().decode('utf-8')
|
||||
@@ -141,17 +141,17 @@ class DwdCrawler():
|
||||
pages.append('https://www.dwd.de/' + link['href'].split(';')[0])
|
||||
|
||||
# receive the wind data
|
||||
self.updateTime = None
|
||||
self.windData = []
|
||||
self.UpdateTime = None
|
||||
self.WindData = []
|
||||
for page in pages:
|
||||
next, wind = self.parseGaforPage(page)
|
||||
if None != next:
|
||||
if None == self.updateTime or self.updateTime > next:
|
||||
self.updateTime = next
|
||||
self.windData.extend(wind)
|
||||
if None == self.UpdateTime or self.UpdateTime > next:
|
||||
self.UpdateTime = next
|
||||
self.WindData.extend(wind)
|
||||
|
||||
# indicate that new wind data is available
|
||||
if None != self.updateTime:
|
||||
if None != self.UpdateTime:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -16,20 +16,20 @@ from threading import Thread, _active
|
||||
class ReceiverThread(Thread):
|
||||
def __init__(self, socket, aman):
|
||||
Thread.__init__(self)
|
||||
self.socket = socket
|
||||
self.aman = aman
|
||||
self.Socket = socket
|
||||
self.AMAN = aman
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
try:
|
||||
msg = self.socket.recv(zmq.NOBLOCK)
|
||||
msg = self.Socket.recv(zmq.NOBLOCK)
|
||||
|
||||
# parse the received message
|
||||
report = AircraftReport_pb2.AircraftReport()
|
||||
report.ParseFromString(msg)
|
||||
|
||||
# try to associate the received aircraft to an airport
|
||||
self.aman.updateAircraftReport(report)
|
||||
self.AMAN.updateAircraftReport(report)
|
||||
|
||||
except zmq.ZMQError as error:
|
||||
if zmq.EAGAIN == error.errno:
|
||||
@@ -54,10 +54,10 @@ class ReceiverThread(Thread):
|
||||
# @brief Receives and sends messages to EuroScope plugins
|
||||
class Euroscope:
|
||||
def __init__(self):
|
||||
self.context = None
|
||||
self.receiverSocket = None
|
||||
self.receiverThread = None
|
||||
self.notificationSocket = None
|
||||
self.Context = None
|
||||
self.ReceiverSocket = None
|
||||
self.ReceiverThread = None
|
||||
self.NotificationSocket = None
|
||||
|
||||
def __del__(self):
|
||||
self.release()
|
||||
@@ -65,7 +65,7 @@ class Euroscope:
|
||||
# @brief Initializes the ZMQ socket
|
||||
# @param[in] config The server configuration
|
||||
def acquire(self, configPath : str, config : Server, aman):
|
||||
self.context = zmq.Context()
|
||||
self.Context = zmq.Context()
|
||||
|
||||
# find the key directories
|
||||
serverKeyPath = os.path.join(os.path.join(configPath, 'keys'), 'server')
|
||||
@@ -88,34 +88,36 @@ class Euroscope:
|
||||
keyPair = zmq.auth.load_certificate(keyPairPath[0])
|
||||
|
||||
# initialize the receiver
|
||||
self.receiverSocket = zmq.Socket(self.context, zmq.SUB)
|
||||
self.receiverSocket.setsockopt(zmq.CURVE_PUBLICKEY, keyPair[0])
|
||||
self.receiverSocket.setsockopt(zmq.CURVE_SECRETKEY, keyPair[1])
|
||||
self.receiverSocket.setsockopt(zmq.CURVE_SERVER, True)
|
||||
self.receiverSocket.bind('tcp://' + config.Address + ':' + str(config.PortReceiver))
|
||||
self.receiverSocket.setsockopt(zmq.SUBSCRIBE, b'')
|
||||
self.receiverThread = ReceiverThread(self.receiverSocket, aman)
|
||||
self.receiverThread.start()
|
||||
self.ReceiverSocket = zmq.Socket(self.Context, zmq.SUB)
|
||||
self.ReceiverSocket.setsockopt(zmq.CURVE_PUBLICKEY, keyPair[0])
|
||||
self.ReceiverSocket.setsockopt(zmq.CURVE_SECRETKEY, keyPair[1])
|
||||
self.ReceiverSocket.setsockopt(zmq.CURVE_SERVER, True)
|
||||
self.ReceiverSocket.bind('tcp://' + config.Address + ':' + str(config.PortReceiver))
|
||||
self.ReceiverSocket.setsockopt(zmq.SUBSCRIBE, b'')
|
||||
self.ReceiverThread = ReceiverThread(self.ReceiverSocket, aman)
|
||||
self.ReceiverThread.start()
|
||||
print('Listening to tcp://' + config.Address + ':' + str(config.PortReceiver))
|
||||
|
||||
# initialize the notification
|
||||
self.notificationSocket = zmq.Socket(self.context, zmq.PUB)
|
||||
self.notificationSocket.setsockopt(zmq.CURVE_PUBLICKEY, keyPair[0])
|
||||
self.notificationSocket.setsockopt(zmq.CURVE_SECRETKEY, keyPair[1])
|
||||
self.notificationSocket.setsockopt(zmq.CURVE_SERVER, True)
|
||||
self.notificationSocket.bind('tcp://' + config.Address + ':' + str(config.PortNotification))
|
||||
self.NotificationSocket = zmq.Socket(self.Context, zmq.PUB)
|
||||
self.NotificationSocket.setsockopt(zmq.CURVE_PUBLICKEY, keyPair[0])
|
||||
self.NotificationSocket.setsockopt(zmq.CURVE_SECRETKEY, keyPair[1])
|
||||
self.NotificationSocket.setsockopt(zmq.CURVE_SERVER, True)
|
||||
self.NotificationSocket.bind('tcp://' + config.Address + ':' + str(config.PortNotification))
|
||||
print('Publishing to tcp://' + config.Address + ':' + str(config.PortNotification))
|
||||
|
||||
def release(self):
|
||||
if None != self.receiverThread:
|
||||
self.receiverThread.stopThread()
|
||||
self.receiverThread.join()
|
||||
self.receiverThread = None
|
||||
if None != self.ReceiverThread:
|
||||
self.ReceiverThread.stopThread()
|
||||
self.ReceiverThread.join()
|
||||
self.ReceiverThread = None
|
||||
|
||||
if None != self.receiverSocket:
|
||||
self.receiverSocket.close()
|
||||
self.receiverSocket = None
|
||||
if None != self.ReceiverSocket:
|
||||
self.ReceiverSocket.close()
|
||||
self.ReceiverSocket = None
|
||||
|
||||
if None != self.notificationSocket:
|
||||
self.notificationSocket.close()
|
||||
self.notificationSocket = None
|
||||
if None != self.NotificationSocket:
|
||||
self.NotificationSocket.close()
|
||||
self.NotificationSocket = None
|
||||
|
||||
self.Context = None
|
||||
|
||||
@@ -14,19 +14,19 @@ class Weather(Thread):
|
||||
def __init__(self):
|
||||
Thread.__init__(self)
|
||||
|
||||
self.nextUpdate = None
|
||||
self.lastUpdateTried = None
|
||||
self.stopThread = False
|
||||
self.provider = None
|
||||
self.NextUpdate = None
|
||||
self.LastUpdateTried = None
|
||||
self.StopThread = False
|
||||
self.Provider = None
|
||||
|
||||
def acquire(self, config : aman.config.Weather.Weather):
|
||||
self.nextUpdate = dt.utcfromtimestamp(int(time.time()))
|
||||
self.lastUpdateTried = None
|
||||
self.stopThread = False
|
||||
self.provider = None
|
||||
self.NextUpdate = dt.utcfromtimestamp(int(time.time()))
|
||||
self.LastUpdateTried = None
|
||||
self.StopThread = False
|
||||
self.Provider = None
|
||||
|
||||
if 'DWD' == config.Provider.upper():
|
||||
self.provider = DwdCrawler()
|
||||
self.Provider = DwdCrawler()
|
||||
else:
|
||||
sys.stderr.write('Invalid or unknown weather-provider defined')
|
||||
sys.exit(-1)
|
||||
@@ -34,7 +34,7 @@ class Weather(Thread):
|
||||
self.start()
|
||||
|
||||
def release(self):
|
||||
self.stopThread = True
|
||||
self.StopThread = True
|
||||
self.join()
|
||||
|
||||
def currentClock():
|
||||
@@ -42,15 +42,15 @@ class Weather(Thread):
|
||||
return clock
|
||||
|
||||
def run(self):
|
||||
while False == self.stopThread and None != self.provider:
|
||||
while False == self.StopThread and None != self.Provider:
|
||||
now = Weather.currentClock()
|
||||
|
||||
# check if an update is required
|
||||
if None != self.provider.updateTime and self.provider.updateTime > now:
|
||||
if None != self.Provider.UpdateTime and self.Provider.UpdateTime > now:
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
if None == self.lastUpdateTried or self.lastUpdateTried <= now:
|
||||
if True == self.provider.receiveWindData():
|
||||
self.nextUpdate = self.provider.updateTime
|
||||
if None == self.LastUpdateTried or self.LastUpdateTried <= now:
|
||||
if True == self.Provider.receiveWindData():
|
||||
self.NextUpdate = self.Provider.UpdateTime
|
||||
print('Received new wind data')
|
||||
@@ -17,12 +17,12 @@ class AircraftPerformance:
|
||||
|
||||
aircraft = PerformanceData(key)
|
||||
|
||||
aircraft.speedAboveFL240 = config[key]['speedabovefl240']
|
||||
aircraft.rodAboveFL240 = config[key]['rodabovefl240']
|
||||
aircraft.speedAboveFL100 = config[key]['speedabovefl100']
|
||||
aircraft.rodAboveFL100 = config[key]['rodabovefl100']
|
||||
aircraft.speedBelowFL100 = config[key]['speedbelowfl100']
|
||||
aircraft.rodBelowFL100 = config[key]['rodbelowfl100']
|
||||
aircraft.speedApproach = config[key]['speedapproach']
|
||||
aircraft.SpeedAboveFL240 = config[key]['speedabovefl240']
|
||||
aircraft.RodAboveFL240 = config[key]['rodabovefl240']
|
||||
aircraft.SpeedAboveFL100 = config[key]['speedabovefl100']
|
||||
aircraft.RodAboveFL100 = config[key]['rodabovefl100']
|
||||
aircraft.SpeedBelowFL100 = config[key]['speedbelowfl100']
|
||||
aircraft.RodBelowFL100 = config[key]['rodbelowfl100']
|
||||
aircraft.SpeedApproach = config[key]['speedapproach']
|
||||
|
||||
self.Aircrafts[aircraft.icao] = aircraft
|
||||
self.Aircrafts[aircraft.Icao] = aircraft
|
||||
|
||||
@@ -43,7 +43,7 @@ class Airport:
|
||||
if None == planning.get('arrivalspacingdefault'):
|
||||
sys.stderr.write('No "arrivalspacingdefault" entry found!')
|
||||
sys.exit(-1)
|
||||
if not icao in self.GngData.runways:
|
||||
if not icao in self.GngData.Runways:
|
||||
sys.stderr.write('Unable to find' + icao + 'in the SCT data!')
|
||||
sys.exit(-1)
|
||||
|
||||
@@ -77,8 +77,8 @@ class Airport:
|
||||
sys.exit(-1)
|
||||
|
||||
found = False
|
||||
for runway in self.GngData.runways[icao]:
|
||||
if ident == runway.name:
|
||||
for runway in self.GngData.Runways[icao]:
|
||||
if ident == runway.Name:
|
||||
sequence = RunwaySequencing(runway)
|
||||
sequence.Spacing = spacings[ident]
|
||||
self.DefaultSequencing.activateRunway(sequence)
|
||||
@@ -103,9 +103,13 @@ class Airport:
|
||||
for i in range(0, len(dependencies), 2):
|
||||
self.DefaultSequencing.addDependency(dependencies[i], dependencies[i + 1])
|
||||
|
||||
def __init__(self, filepath : str, icao : str):
|
||||
self.arrivalRoutes = {}
|
||||
def parseConstraints(self, icao : str, planning):
|
||||
for key in planning:
|
||||
if True == key.startswith('constraints'):
|
||||
star = key.replace('constraints', '').upper()
|
||||
#print(star)
|
||||
|
||||
def __init__(self, filepath : str, icao : str):
|
||||
config = configparser.ConfigParser()
|
||||
config.read(filepath)
|
||||
|
||||
@@ -153,5 +157,6 @@ class Airport:
|
||||
sys.exit(-1)
|
||||
self.GaforId = dataConfig['gaforid']
|
||||
|
||||
# get the default sequencing data
|
||||
# get the default sequencing data and constraints
|
||||
self.parseDefaultSequencingConfiguration(icao, planningConfig)
|
||||
self.parseConstraints(icao, planningConfig)
|
||||
|
||||
@@ -13,14 +13,14 @@ class AirportSequencing:
|
||||
|
||||
def activateRunway(self, runway : RunwaySequencing):
|
||||
for active in self.ActiveArrivalRunways:
|
||||
if active.Runway.name == runway.Runway.name:
|
||||
self.ActiveArrivalRunways[runway.Runway.name] = runway
|
||||
if active.Runway.Name == runway.Runway.Name:
|
||||
self.ActiveArrivalRunways[runway.Runway.Name] = runway
|
||||
return
|
||||
self.ActiveArrivalRunways.append(runway)
|
||||
|
||||
def runwayIndex(self, identifier : str):
|
||||
for i in range(0, len(self.ActiveArrivalRunways)):
|
||||
if self.ActiveArrivalRunways[i].Runway.name == identifier:
|
||||
if self.ActiveArrivalRunways[i].Runway.Name == identifier:
|
||||
return i
|
||||
return -1
|
||||
|
||||
@@ -70,7 +70,7 @@ class AirportSequencing:
|
||||
|
||||
def findRunway(self, identifier : str):
|
||||
for runway in self.ActiveArrivalRunways:
|
||||
if runway.Runway.name == identifier:
|
||||
if runway.Runway.Name == identifier:
|
||||
return runway
|
||||
return None
|
||||
|
||||
|
||||
@@ -82,32 +82,32 @@ class SctEseFormat:
|
||||
# extract all waypoints
|
||||
for waypoint in config['VOR']:
|
||||
waypoint = SctEseFormat.parseWaypoint(waypoint, 0, 2, 3)
|
||||
self.waypoints.setdefault(waypoint.name, []).append(waypoint)
|
||||
self.Waypoints.setdefault(waypoint.Name, []).append(waypoint)
|
||||
for waypoint in config['NDB']:
|
||||
waypoint = SctEseFormat.parseWaypoint(waypoint, 0, 1, 2)
|
||||
self.waypoints.setdefault(waypoint.name, []).append(waypoint)
|
||||
self.Waypoints.setdefault(waypoint.Name, []).append(waypoint)
|
||||
for waypoint in config['FIXES']:
|
||||
waypoint = SctEseFormat.parseWaypoint(waypoint, 0, 1, 2)
|
||||
self.waypoints.setdefault(waypoint.name, []).append(waypoint)
|
||||
self.Waypoints.setdefault(waypoint.Name, []).append(waypoint)
|
||||
|
||||
# extract the airports
|
||||
for airport in config['AIRPORT']:
|
||||
airport = SctEseFormat.parseWaypoint(airport, 0, 2, 3)
|
||||
self.airports.setdefault(airport.name, []).append(airport)
|
||||
self.Airports.setdefault(airport.Name, []).append(airport)
|
||||
|
||||
# extract the runways
|
||||
for runway in config['RUNWAY']:
|
||||
airport, runway0, runway1 = SctEseFormat.parseRunway(runway)
|
||||
if None != airport:
|
||||
if not airport in self.runways:
|
||||
self.runways.setdefault(airport, [])
|
||||
self.runways[airport].append(runway0)
|
||||
self.runways[airport].append(runway1)
|
||||
if not airport in self.Runways:
|
||||
self.Runways.setdefault(airport, [])
|
||||
self.Runways[airport].append(runway0)
|
||||
self.Runways[airport].append(runway1)
|
||||
|
||||
def parseArrivalRoute(self, route : str, airport : Waypoint):
|
||||
# split the route and validate that it is a STAR for the airport
|
||||
split = route.split(':')
|
||||
if 5 != len(split) or 'STAR' != split[0] or split[1] != airport.name:
|
||||
if 5 != len(split) or 'STAR' != split[0] or split[1] != airport.Name:
|
||||
return None
|
||||
|
||||
# find all waypoints
|
||||
@@ -115,7 +115,7 @@ class SctEseFormat:
|
||||
route = list(filter(None, split[4].split(' ')))
|
||||
for waypoint in route:
|
||||
# find the waypoint in the route
|
||||
coordinates = self.waypoints[waypoint]
|
||||
coordinates = self.Waypoints[waypoint]
|
||||
# no waypoint with this name defined
|
||||
if None == coordinates:
|
||||
sys.stderr.write('Unable to find waypoint ' + waypoint)
|
||||
@@ -150,10 +150,10 @@ class SctEseFormat:
|
||||
foundSidsStars = False
|
||||
|
||||
# search the airport in the extracted list
|
||||
if not airport in self.airports:
|
||||
sys.stderr.write(airport + 'in self.airports', 'Unable to find the requested airport')
|
||||
if not airport in self.Airports:
|
||||
sys.stderr.write('Unable to find the requested airport')
|
||||
sys.exit(-1)
|
||||
airport = self.airports[airport][0]
|
||||
airport = self.Airports[airport][0]
|
||||
|
||||
for key in config:
|
||||
if 'SIDSSTARS' == key:
|
||||
@@ -166,14 +166,14 @@ class SctEseFormat:
|
||||
# parse all arrival routes
|
||||
for line in config['SIDSSTARS']:
|
||||
route = self.parseArrivalRoute(line, airport)
|
||||
if None != route and route.name in allowedRoutes:
|
||||
self.arrivalRoutes.setdefault(route.runway, []).append(route)
|
||||
if None != route and route.Name in allowedRoutes:
|
||||
self.ArrivalRoutes.setdefault(route.Runway, []).append(route)
|
||||
|
||||
def __init__(self, sctFilepath : str, eseFilepath : str, airport : str, allowedRoutes : list):
|
||||
self.arrivalRoutes = {}
|
||||
self.waypoints = {}
|
||||
self.airports = {}
|
||||
self.runways = {}
|
||||
self.ArrivalRoutes = {}
|
||||
self.Waypoints = {}
|
||||
self.Airports = {}
|
||||
self.Runways = {}
|
||||
|
||||
self.extractSctInformation(sctFilepath)
|
||||
self.extractArrivalRoutes(eseFilepath, airport, allowedRoutes)
|
||||
|
||||
@@ -7,13 +7,13 @@ import scipy.interpolate
|
||||
|
||||
class WeatherModel:
|
||||
def __init__(self, gaforId, weather : Weather):
|
||||
self.gafor = gaforId
|
||||
self.weather = weather
|
||||
self.windDirectionModel = None
|
||||
self.windSpeedModel = None
|
||||
self.lastWeatherUpdate = None
|
||||
self.minimumAltitude = 1000000
|
||||
self.maximumAltitude = -1
|
||||
self.Gafor = gaforId
|
||||
self.Weather = weather
|
||||
self.WindDirectionModel = None
|
||||
self.WindSpeedModel = None
|
||||
self.LastWeatherUpdate = None
|
||||
self.MinimumAltitude = 1000000
|
||||
self.MaximumAltitude = -1
|
||||
|
||||
# create the density interpolation model
|
||||
# the density model is based on https://aerotoolbox.com/atmcalc/
|
||||
@@ -95,51 +95,51 @@ class WeatherModel:
|
||||
return ias * math.sqrt(1.225 / self.densityModel(altitude).item())
|
||||
|
||||
def updateWindModel(self):
|
||||
if None == self.lastWeatherUpdate or self.lastWeatherUpdate != self.weather.provider.updateTime:
|
||||
self.lastWeatherUpdate = self.weather.provider.updateTime
|
||||
if None == self.LastWeatherUpdate or self.LastWeatherUpdate != self.Weather.Provider.UpdateTime:
|
||||
self.LastWeatherUpdate = self.Weather.Provider.UpdateTime
|
||||
|
||||
self.minimumAltitude = 1000000
|
||||
self.maximumAltitude = -1
|
||||
self.windDirectionModel = None
|
||||
self.windSpeedModel = None
|
||||
self.MinimumAltitude = 1000000
|
||||
self.MaximumAltitude = -1
|
||||
self.WindDirectionModel = None
|
||||
self.WindSpeedModel = None
|
||||
|
||||
if None != self.weather.provider.windData and self.gafor in self.weather.provider.windData:
|
||||
if None != self.Weather.Provider.WindData and self.Gafor in self.Weather.Provider.WindData:
|
||||
altitudes = []
|
||||
directions = []
|
||||
speeds = []
|
||||
|
||||
# collect the data for the wind model
|
||||
for level in self.weather.provider.windData[self.gafor]:
|
||||
for level in self.Weather.Provider.WindData[self.Gafor]:
|
||||
altitudes.append(level[0])
|
||||
directions.append(level[1])
|
||||
speeds.append(level[2])
|
||||
|
||||
# define the thresholds for later boundary checks
|
||||
if self.minimumAltitude > level[0]:
|
||||
self.minimumAltitude = level[0]
|
||||
if self.maximumAltitude < level[0]:
|
||||
self.maximumAltitude = level[0]
|
||||
if self.MinimumAltitude > level[0]:
|
||||
self.MinimumAltitude = level[0]
|
||||
if self.MaximumAltitude < level[0]:
|
||||
self.MaximumAltitude = level[0]
|
||||
|
||||
# calculate the models
|
||||
if 1 < len(altitudes):
|
||||
self.windDirectionModel = scipy.interpolate.interp1d(altitudes, directions)
|
||||
self.windSpeedModel = scipy.interpolate.interp1d(altitudes, speeds)
|
||||
self.WindDirectionModel = scipy.interpolate.interp1d(altitudes, directions)
|
||||
self.WindSpeedModel = scipy.interpolate.interp1d(altitudes, speeds)
|
||||
|
||||
def calculateGS(self, altitude : int, ias : int, heading : int):
|
||||
self.updateWindModel()
|
||||
tas = self.calculateTAS(altitude, ias)
|
||||
|
||||
# initialize the wind data
|
||||
if None != self.windDirectionModel and None != self.windSpeedModel:
|
||||
if None != self.WindDirectionModel and None != self.WindSpeedModel:
|
||||
direction = 0.0
|
||||
speed = 0.0
|
||||
if None != self.windSpeedModel and None != self.windDirectionModel:
|
||||
if self.maximumAltitude <= altitude:
|
||||
altitude = self.maximumAltitude - 1
|
||||
if self.minimumAltitude >= altitude:
|
||||
altitude = self.minimumAltitude + 1
|
||||
direction = self.windDirectionModel(altitude).item()
|
||||
speed = self.windSpeedModel(altitude).item()
|
||||
if None != self.WindSpeedModel and None != self.WindDirectionModel:
|
||||
if self.MaximumAltitude <= altitude:
|
||||
altitude = self.MaximumAltitude - 1
|
||||
if self.MinimumAltitude >= altitude:
|
||||
altitude = self.MinimumAltitude + 1
|
||||
direction = self.WindDirectionModel(altitude).item()
|
||||
speed = self.WindSpeedModel(altitude).item()
|
||||
else:
|
||||
speed = 0
|
||||
direction = 0
|
||||
|
||||
@@ -14,48 +14,46 @@ from aman.types.PerformanceData import PerformanceData
|
||||
class Worker(Thread):
|
||||
def __init__(self):
|
||||
Thread.__init__(self)
|
||||
self.stopThread = None
|
||||
self.icao = None
|
||||
self.configuration = None
|
||||
self.arrivalRoutes = None
|
||||
self.performanceData = None
|
||||
self.updateLock = None
|
||||
self.reportQueue = {}
|
||||
self.weatherModel = None
|
||||
self.StopThread = None
|
||||
self.Icao = None
|
||||
self.Configuration = None
|
||||
self.PerformanceData = None
|
||||
self.UpdateLock = None
|
||||
self.ReportQueue = {}
|
||||
self.WeatherModel = None
|
||||
self.RecedingHorizonControl = None
|
||||
|
||||
def __del__(self):
|
||||
self.release()
|
||||
|
||||
def acquire(self, icao : str, configuration : Airport, weather : Weather, performance : PerformanceData):
|
||||
self.stopThread = None
|
||||
self.icao = icao
|
||||
self.configuration = configuration
|
||||
self.sequencingConfiguration = self.configuration.DefaultSequencing
|
||||
self.performanceData = performance
|
||||
self.arrivalRoutes = configuration.GngData.arrivalRoutes
|
||||
self.updateLock = Lock()
|
||||
self.reportQueue = {}
|
||||
self.weatherModel = WeatherModel(self.configuration.GaforId, weather)
|
||||
self.RecedingHorizonControl = RecedingHorizonControl(self.configuration.RecedingHorizonControl)
|
||||
self.StopThread = None
|
||||
self.Icao = icao
|
||||
self.Configuration = configuration
|
||||
self.sequencingConfiguration = configuration.DefaultSequencing
|
||||
self.PerformanceData = performance
|
||||
self.UpdateLock = Lock()
|
||||
self.ReportQueue = {}
|
||||
self.WeatherModel = WeatherModel(configuration.GaforId, weather)
|
||||
self.RecedingHorizonControl = RecedingHorizonControl(configuration.RecedingHorizonControl)
|
||||
self.start()
|
||||
|
||||
def acquireLock(self):
|
||||
if None != self.updateLock:
|
||||
self.updateLock.acquire()
|
||||
if None != self.UpdateLock:
|
||||
self.UpdateLock.acquire()
|
||||
|
||||
def release(self):
|
||||
self.stopThread = True
|
||||
self.StopThread = True
|
||||
self.join()
|
||||
|
||||
def releaseLock(self):
|
||||
if None != self.updateLock:
|
||||
self.updateLock.release()
|
||||
if None != self.UpdateLock:
|
||||
self.UpdateLock.release()
|
||||
|
||||
def run(self):
|
||||
counter = 0
|
||||
|
||||
while None == self.stopThread:
|
||||
while None == self.StopThread:
|
||||
time.sleep(1)
|
||||
counter += 1
|
||||
if 0 != (counter % 10):
|
||||
@@ -67,17 +65,17 @@ class Worker(Thread):
|
||||
self.RecedingHorizonControl.cleanupWindows()
|
||||
|
||||
# update the aircraft information in RHC
|
||||
for callsign in self.reportQueue:
|
||||
report = self.reportQueue[callsign]
|
||||
for callsign in self.ReportQueue:
|
||||
report = self.ReportQueue[callsign]
|
||||
|
||||
if 0 != report.distanceToIAF and '' != report.initialApproachFix:
|
||||
inbound = Inbound(report, self.sequencingConfiguration, self.configuration.GngData, self.performanceData, self.weatherModel)
|
||||
inbound = Inbound(report, self.sequencingConfiguration, self.Configuration.GngData, self.PerformanceData, self.WeatherModel)
|
||||
if None != inbound.PlannedRunway and None != inbound.PlannedStar:
|
||||
self.RecedingHorizonControl.update(inbound)
|
||||
else:
|
||||
print('Unable to find all data of ' + report.aircraft.callsign)
|
||||
|
||||
self.reportQueue.clear()
|
||||
self.ReportQueue.clear()
|
||||
|
||||
if 0 != len(self.RecedingHorizonControl.Windows):
|
||||
print('FCFS run:')
|
||||
@@ -89,9 +87,9 @@ class Worker(Thread):
|
||||
# this is required to handle the overlap between windows
|
||||
preceedingInbounds = {}
|
||||
for runway in self.sequencingConfiguration.ActiveArrivalRunways:
|
||||
inbound = self.RecedingHorizonControl.lastFixedInboundOnRunway(runway.Runway.name)
|
||||
inbound = self.RecedingHorizonControl.lastFixedInboundOnRunway(runway.Runway.Name)
|
||||
if None != inbound:
|
||||
preceedingInbounds[runway.Runway.name] = inbound
|
||||
preceedingInbounds[runway.Runway.Name] = inbound
|
||||
|
||||
# search the ACO relevant aircrafts
|
||||
relevantInbounds = self.RecedingHorizonControl.optimizationRelevantInbounds()
|
||||
@@ -106,6 +104,6 @@ class Worker(Thread):
|
||||
# TODO perform the ACO run
|
||||
# TODO update the RHC stages based on the ACO run result
|
||||
else:
|
||||
print('No relevant inbounds found for the optimization in ' + self.icao)
|
||||
print('No relevant inbounds found for the optimization in ' + self.Icao)
|
||||
|
||||
self.releaseLock()
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
class ArrivalRoute:
|
||||
def __init__(self, name : str, runway : str, waypoints : list):
|
||||
self.name = name
|
||||
self.runway = runway
|
||||
self.iaf = waypoints[0]
|
||||
self.route = waypoints
|
||||
self.Name = name
|
||||
self.Runway = runway
|
||||
self.Iaf = waypoints[0]
|
||||
self.Route = waypoints
|
||||
|
||||
def __str__(self):
|
||||
return 'Name: ' + self.name + ', IAF: ' + self.iaf.name + ', RWY: ' + self.runway
|
||||
return 'Name: ' + self.Name + ', IAF: ' + self.Iaf.name + ', RWY: ' + self.Runway
|
||||
@@ -39,7 +39,7 @@ class Inbound:
|
||||
distance = 100000.0
|
||||
currentPosition = Waypoint('', self.Report.position.latitude, self.Report.position.longitude)
|
||||
for runway in sequencingConfig.ActiveArrivalRunways:
|
||||
candidateDistance = runway.Runway.start.haversine(currentPosition)
|
||||
candidateDistance = runway.Runway.Start.haversine(currentPosition)
|
||||
if distance > candidateDistance:
|
||||
self.PlannedRunway = runway
|
||||
distance = candidateDistance
|
||||
@@ -49,11 +49,11 @@ class Inbound:
|
||||
if None == self.PlannedRunway:
|
||||
return
|
||||
|
||||
for arrivalRunway in navData.arrivalRoutes:
|
||||
if arrivalRunway == self.PlannedRunway.Runway.name:
|
||||
stars = navData.arrivalRoutes[arrivalRunway]
|
||||
for arrivalRunway in navData.ArrivalRoutes:
|
||||
if arrivalRunway == self.PlannedRunway.Runway.Name:
|
||||
stars = navData.ArrivalRoutes[arrivalRunway]
|
||||
for star in stars:
|
||||
if 0 != len(star.route) and self.Report.initialApproachFix == star.iaf.name:
|
||||
if 0 != len(star.Route) and self.Report.initialApproachFix == star.Iaf.Name:
|
||||
self.PlannedStar = star
|
||||
return
|
||||
|
||||
@@ -63,14 +63,14 @@ class Inbound:
|
||||
|
||||
# calculate remaining trackmiles
|
||||
remainingDistanceNM = 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
|
||||
start = self.PlannedStar.route[i]
|
||||
start = self.PlannedStar.Route[0]
|
||||
for i in range(1, len(self.PlannedStar.Route)):
|
||||
remainingDistanceNM += start.haversine(self.PlannedStar.Route[i]) * 0.539957
|
||||
start = self.PlannedStar.Route[i]
|
||||
|
||||
# calculate descend profile
|
||||
flightTimeSeconds = 0
|
||||
currentHeading = Waypoint('', self.Report.position.latitude, self.Report.position.longitude).bearing(self.PlannedStar.route[0])
|
||||
currentHeading = Waypoint('', self.Report.position.latitude, self.Report.position.longitude).bearing(self.PlannedStar.Route[0])
|
||||
distanceToWaypoint = self.Report.distanceToIAF
|
||||
nextWaypointIndex = 0
|
||||
currentPosition = [ self.Report.dynamics.altitude, self.Report.dynamics.groundSpeed ]
|
||||
@@ -105,7 +105,7 @@ class Inbound:
|
||||
distanceToWaypoint -= abs(lastDistance - remainingDistanceNM)
|
||||
if 0 >= distanceToWaypoint:
|
||||
nextWaypointIndex += 1
|
||||
if nextWaypointIndex < len(self.PlannedStar.route):
|
||||
currentHeading = self.PlannedStar.route[nextWaypointIndex - 1].bearing(self.PlannedStar.route[nextWaypointIndex])
|
||||
if nextWaypointIndex < len(self.PlannedStar.Route):
|
||||
currentHeading = self.PlannedStar.Route[nextWaypointIndex - 1].bearing(self.PlannedStar.Route[nextWaypointIndex])
|
||||
|
||||
return timedelta(seconds = flightTimeSeconds)
|
||||
|
||||
@@ -2,27 +2,29 @@
|
||||
|
||||
class PerformanceData:
|
||||
def __init__(self, icao : str):
|
||||
self.icao = icao
|
||||
self.speedAboveFL240 = 0.0
|
||||
self.speedAboveFL100 = 0.0
|
||||
self.speedBelowFL100 = 0.0
|
||||
self.speedApproach = 0.0
|
||||
self.rodAboveFL240 = 0.0
|
||||
self.rodAboveFL100 = 0.0
|
||||
self.rodBelowFL100 = 2000.0
|
||||
self.Icao = icao
|
||||
self.SpeedAboveFL240 = 0.0
|
||||
self.SpeedAboveFL100 = 0.0
|
||||
self.SpeedBelowFL100 = 0.0
|
||||
self.SpeedApproach = 0.0
|
||||
self.RodAboveFL240 = 0.0
|
||||
self.RodAboveFL100 = 0.0
|
||||
self.RodBelowFL100 = 2000.0
|
||||
|
||||
def ias(self, altitude, distance):
|
||||
if 24000 >= altitude:
|
||||
return self.speedAboveFL240
|
||||
return self.SpeedAboveFL240
|
||||
elif 10000 >= altitude:
|
||||
return self.speedAboveFL100
|
||||
return self.SpeedAboveFL100
|
||||
elif 10000 < altitude and 5 < distance:
|
||||
return self.SpeedBelowFL100
|
||||
elif 5 >= distance:
|
||||
return self.speedApproach
|
||||
return self.SpeedApproach
|
||||
else:
|
||||
return 220
|
||||
|
||||
def __str__(self):
|
||||
return 'ICAO: ' + self.icao + ', FL240: ' + str(self.rodAboveFL240) + '@' + str(self.speedAboveFL240) + \
|
||||
', +FL100: ' + str(self.rodAboveFL100) + '@' + str(self.speedAboveFL100) + \
|
||||
', -FL100: ' + str(self.rodBelowFL100) + '@' + str(self.speedBelowFL100) + \
|
||||
', Vapp: ' + str(self.speedApproach)
|
||||
return 'ICAO: ' + self.icao + ', FL240: ' + str(self.RodAboveFL240) + '@' + str(self.SpeedAboveFL240) + \
|
||||
', +FL100: ' + str(self.RodAboveFL100) + '@' + str(self.SpeedAboveFL100) + \
|
||||
', -FL100: ' + str(self.RodBelowFL100) + '@' + str(self.SpeedBelowFL100) + \
|
||||
', Vapp: ' + str(self.SpeedApproach)
|
||||
|
||||
@@ -4,9 +4,9 @@ from aman.types.Waypoint import Waypoint
|
||||
|
||||
class Runway:
|
||||
def __init__(self, start : Waypoint, end : Waypoint):
|
||||
self.name = start.name
|
||||
self.start = start
|
||||
self.end = end
|
||||
self.Name = start.Name
|
||||
self.Start = start
|
||||
self.End = end
|
||||
|
||||
def heading(self):
|
||||
return self.start.bearing(self.end)
|
||||
return self.Start.bearing(self.End)
|
||||
|
||||
@@ -21,18 +21,18 @@ class Waypoint:
|
||||
return dd
|
||||
|
||||
def __init__(self, name : str, latitude : float, longitude : float):
|
||||
self.name = name
|
||||
self.coordinate = np.array([ latitude, longitude ])
|
||||
self.Name = name
|
||||
self.Coordinate = np.array([ latitude, longitude ])
|
||||
|
||||
def __str__(self):
|
||||
return 'Name: ' + self.name + ', Lat: ' + str(self.coordinate[0]) + ', Lon: ' + str(self.coordinate[1])
|
||||
return 'Name: ' + self.Name + ', Lat: ' + str(self.Coordinate[0]) + ', Lon: ' + str(self.Coordinate[1])
|
||||
|
||||
def haversine(self, other):
|
||||
geodesic = pyproj.Geod(ellps='WGS84')
|
||||
forward, backward, distance = geodesic.inv(self.coordinate[1], self.coordinate[0], other.coordinate[1], other.coordinate[0])
|
||||
forward, backward, distance = geodesic.inv(self.Coordinate[1], self.Coordinate[0], other.Coordinate[1], other.Coordinate[0])
|
||||
return distance / 1000.0
|
||||
|
||||
def bearing(self, other):
|
||||
geodesic = pyproj.Geod(ellps='WGS84')
|
||||
forward, backward, distance = geodesic.inv(self.coordinate[1], self.coordinate[0], other.coordinate[1], other.coordinate[0])
|
||||
forward, backward, distance = geodesic.inv(self.Coordinate[1], self.Coordinate[0], other.Coordinate[1], other.Coordinate[0])
|
||||
return forward
|
||||
|
||||
Reference in New Issue
Block a user