define member variables with capital letters

This commit is contained in:
Sven Czarnian
2021-10-13 12:52:29 +02:00
parent 9d69a60396
commit 1e043e2765
15 changed files with 229 additions and 223 deletions

View File

@@ -27,12 +27,11 @@ class AMAN:
def __init__(self): def __init__(self):
# default initialization of members # default initialization of members
self.systemConfig = None self.SystemConfig = None
self.aircraftPerformance = None self.AircraftPerformance = None
self.receiver = None self.Receiver = None
self.weather = None self.Weather = None
self.workers = [] self.Workers = []
self.inbounds = {}
def __del__(self): def __del__(self):
self.release() self.release()
@@ -41,19 +40,19 @@ class AMAN:
configPath = AMAN.findConfigPath() configPath = AMAN.findConfigPath()
# read all system relevant configuration files # 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') print('Parsed System.ini')
# read the aircraft performance data # read the aircraft performance data
self.aircraftPerformance = AircraftPerformance(os.path.join(configPath, 'PerformanceData.ini')) self.AircraftPerformance = AircraftPerformance(os.path.join(configPath, 'PerformanceData.ini'))
if None == self.aircraftPerformance: if None == self.AircraftPerformance:
sys.stderr.write('No aircraft performance data found!') sys.stderr.write('No aircraft performance data found!')
sys.exit(-1) sys.exit(-1)
else: 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 = Weather()
self.weather.acquire(self.systemConfig.Weather) self.Weather.acquire(self.SystemConfig.Weather)
# find the airport configurations and create the workers # find the airport configurations and create the workers
airportsPath = os.path.join(os.path.join(configPath, 'airports'), '*.ini') airportsPath = os.path.join(os.path.join(configPath, 'airports'), '*.ini')
@@ -65,33 +64,33 @@ class AMAN:
# initialize the worker thread # initialize the worker thread
worker = Worker() worker = Worker()
worker.acquire(icao, airportConfig, self.weather, self.aircraftPerformance) worker.acquire(icao, airportConfig, self.Weather, self.AircraftPerformance)
self.workers.append(worker) self.Workers.append(worker)
print('Started worker for ' + icao) print('Started worker for ' + icao)
# create the EuroScope receiver # create the EuroScope receiver
self.receiver = Euroscope() self.Receiver = Euroscope()
self.receiver.acquire(configPath, self.systemConfig.Server, self) self.Receiver.acquire(configPath, self.SystemConfig.Server, self)
def release(self): def release(self):
if None != self.receiver: if None != self.Receiver:
self.receiver.release() self.Receiver.release()
self.receiver = None self.Receiver = None
if None != self.weather: if None != self.Weather:
self.weather.release() self.Weather.release()
self.weather = None self.Weather = None
if None != self.workers: if None != self.Workers:
for worker in self.workers: for worker in self.Workers:
worker.release() worker.release()
self.workers = None self.Workers = None
def updateAircraftReport(self, report : AircraftReport_pb2.AircraftReport): def updateAircraftReport(self, report : AircraftReport_pb2.AircraftReport):
# find the correct worker for the inbound # find the correct worker for the inbound
for worker in self.workers: for worker in self.Workers:
if worker.icao == report.destination: if worker.Icao == report.destination:
worker.acquireLock() worker.acquireLock()
worker.reportQueue[report.aircraft.callsign] = report worker.ReportQueue[report.aircraft.callsign] = report
worker.releaseLock() worker.releaseLock()
break break

View File

@@ -19,8 +19,8 @@ from datetime import datetime as dt
# - third element of wind data tuple: wind speed (KT) # - third element of wind data tuple: wind speed (KT)
class DwdCrawler(): class DwdCrawler():
def __init__(self): def __init__(self):
self.updateTime = None self.UpdateTime = None
self.windData = None self.WindData = None
def parseGaforAreas(areas : str): def parseGaforAreas(areas : str):
areas = areas.replace(':', '') areas = areas.replace(':', '')
@@ -125,8 +125,8 @@ class DwdCrawler():
return nextUpdate, windInformation return nextUpdate, windInformation
def receiveWindData(self): def receiveWindData(self):
self.updateTime = None self.UpdateTime = None
self.windData = None self.WindData = None
with urllib.request.urlopen('https://www.dwd.de/DE/fachnutzer/luftfahrt/teaser/luftsportberichte/luftsportberichte_node.html') as site: with urllib.request.urlopen('https://www.dwd.de/DE/fachnutzer/luftfahrt/teaser/luftsportberichte/luftsportberichte_node.html') as site:
data = site.read().decode('utf-8') data = site.read().decode('utf-8')
@@ -141,17 +141,17 @@ class DwdCrawler():
pages.append('https://www.dwd.de/' + link['href'].split(';')[0]) pages.append('https://www.dwd.de/' + link['href'].split(';')[0])
# receive the wind data # receive the wind data
self.updateTime = None self.UpdateTime = None
self.windData = [] self.WindData = []
for page in pages: for page in pages:
next, wind = self.parseGaforPage(page) next, wind = self.parseGaforPage(page)
if None != next: if None != next:
if None == self.updateTime or self.updateTime > next: if None == self.UpdateTime or self.UpdateTime > next:
self.updateTime = next self.UpdateTime = next
self.windData.extend(wind) self.WindData.extend(wind)
# indicate that new wind data is available # indicate that new wind data is available
if None != self.updateTime: if None != self.UpdateTime:
return True return True
else: else:
return False return False

View File

@@ -16,20 +16,20 @@ from threading import Thread, _active
class ReceiverThread(Thread): class ReceiverThread(Thread):
def __init__(self, socket, aman): def __init__(self, socket, aman):
Thread.__init__(self) Thread.__init__(self)
self.socket = socket self.Socket = socket
self.aman = aman self.AMAN = aman
def run(self): def run(self):
while True: while True:
try: try:
msg = self.socket.recv(zmq.NOBLOCK) msg = self.Socket.recv(zmq.NOBLOCK)
# parse the received message # parse the received message
report = AircraftReport_pb2.AircraftReport() report = AircraftReport_pb2.AircraftReport()
report.ParseFromString(msg) report.ParseFromString(msg)
# try to associate the received aircraft to an airport # try to associate the received aircraft to an airport
self.aman.updateAircraftReport(report) self.AMAN.updateAircraftReport(report)
except zmq.ZMQError as error: except zmq.ZMQError as error:
if zmq.EAGAIN == error.errno: if zmq.EAGAIN == error.errno:
@@ -54,10 +54,10 @@ class ReceiverThread(Thread):
# @brief Receives and sends messages to EuroScope plugins # @brief Receives and sends messages to EuroScope plugins
class Euroscope: class Euroscope:
def __init__(self): def __init__(self):
self.context = None self.Context = None
self.receiverSocket = None self.ReceiverSocket = None
self.receiverThread = None self.ReceiverThread = None
self.notificationSocket = None self.NotificationSocket = None
def __del__(self): def __del__(self):
self.release() self.release()
@@ -65,7 +65,7 @@ class Euroscope:
# @brief Initializes the ZMQ socket # @brief Initializes the ZMQ socket
# @param[in] config The server configuration # @param[in] config The server configuration
def acquire(self, configPath : str, config : Server, aman): def acquire(self, configPath : str, config : Server, aman):
self.context = zmq.Context() self.Context = zmq.Context()
# find the key directories # find the key directories
serverKeyPath = os.path.join(os.path.join(configPath, 'keys'), 'server') serverKeyPath = os.path.join(os.path.join(configPath, 'keys'), 'server')
@@ -88,34 +88,36 @@ class Euroscope:
keyPair = zmq.auth.load_certificate(keyPairPath[0]) keyPair = zmq.auth.load_certificate(keyPairPath[0])
# initialize the receiver # initialize the receiver
self.receiverSocket = zmq.Socket(self.context, zmq.SUB) self.ReceiverSocket = zmq.Socket(self.Context, zmq.SUB)
self.receiverSocket.setsockopt(zmq.CURVE_PUBLICKEY, keyPair[0]) self.ReceiverSocket.setsockopt(zmq.CURVE_PUBLICKEY, keyPair[0])
self.receiverSocket.setsockopt(zmq.CURVE_SECRETKEY, keyPair[1]) self.ReceiverSocket.setsockopt(zmq.CURVE_SECRETKEY, keyPair[1])
self.receiverSocket.setsockopt(zmq.CURVE_SERVER, True) self.ReceiverSocket.setsockopt(zmq.CURVE_SERVER, True)
self.receiverSocket.bind('tcp://' + config.Address + ':' + str(config.PortReceiver)) self.ReceiverSocket.bind('tcp://' + config.Address + ':' + str(config.PortReceiver))
self.receiverSocket.setsockopt(zmq.SUBSCRIBE, b'') self.ReceiverSocket.setsockopt(zmq.SUBSCRIBE, b'')
self.receiverThread = ReceiverThread(self.receiverSocket, aman) self.ReceiverThread = ReceiverThread(self.ReceiverSocket, aman)
self.receiverThread.start() self.ReceiverThread.start()
print('Listening to tcp://' + config.Address + ':' + str(config.PortReceiver)) print('Listening to tcp://' + config.Address + ':' + str(config.PortReceiver))
# initialize the notification # initialize the notification
self.notificationSocket = zmq.Socket(self.context, zmq.PUB) self.NotificationSocket = zmq.Socket(self.Context, zmq.PUB)
self.notificationSocket.setsockopt(zmq.CURVE_PUBLICKEY, keyPair[0]) self.NotificationSocket.setsockopt(zmq.CURVE_PUBLICKEY, keyPair[0])
self.notificationSocket.setsockopt(zmq.CURVE_SECRETKEY, keyPair[1]) self.NotificationSocket.setsockopt(zmq.CURVE_SECRETKEY, keyPair[1])
self.notificationSocket.setsockopt(zmq.CURVE_SERVER, True) self.NotificationSocket.setsockopt(zmq.CURVE_SERVER, True)
self.notificationSocket.bind('tcp://' + config.Address + ':' + str(config.PortNotification)) self.NotificationSocket.bind('tcp://' + config.Address + ':' + str(config.PortNotification))
print('Publishing to tcp://' + config.Address + ':' + str(config.PortNotification)) print('Publishing to tcp://' + config.Address + ':' + str(config.PortNotification))
def release(self): def release(self):
if None != self.receiverThread: if None != self.ReceiverThread:
self.receiverThread.stopThread() self.ReceiverThread.stopThread()
self.receiverThread.join() self.ReceiverThread.join()
self.receiverThread = None self.ReceiverThread = None
if None != self.receiverSocket: if None != self.ReceiverSocket:
self.receiverSocket.close() self.ReceiverSocket.close()
self.receiverSocket = None self.ReceiverSocket = None
if None != self.notificationSocket: if None != self.NotificationSocket:
self.notificationSocket.close() self.NotificationSocket.close()
self.notificationSocket = None self.NotificationSocket = None
self.Context = None

View File

@@ -14,19 +14,19 @@ class Weather(Thread):
def __init__(self): def __init__(self):
Thread.__init__(self) Thread.__init__(self)
self.nextUpdate = None self.NextUpdate = None
self.lastUpdateTried = None self.LastUpdateTried = None
self.stopThread = False self.StopThread = False
self.provider = None self.Provider = None
def acquire(self, config : aman.config.Weather.Weather): def acquire(self, config : aman.config.Weather.Weather):
self.nextUpdate = dt.utcfromtimestamp(int(time.time())) self.NextUpdate = dt.utcfromtimestamp(int(time.time()))
self.lastUpdateTried = None self.LastUpdateTried = None
self.stopThread = False self.StopThread = False
self.provider = None self.Provider = None
if 'DWD' == config.Provider.upper(): if 'DWD' == config.Provider.upper():
self.provider = DwdCrawler() self.Provider = DwdCrawler()
else: else:
sys.stderr.write('Invalid or unknown weather-provider defined') sys.stderr.write('Invalid or unknown weather-provider defined')
sys.exit(-1) sys.exit(-1)
@@ -34,7 +34,7 @@ class Weather(Thread):
self.start() self.start()
def release(self): def release(self):
self.stopThread = True self.StopThread = True
self.join() self.join()
def currentClock(): def currentClock():
@@ -42,15 +42,15 @@ class Weather(Thread):
return clock return clock
def run(self): def run(self):
while False == self.stopThread and None != self.provider: while False == self.StopThread and None != self.Provider:
now = Weather.currentClock() now = Weather.currentClock()
# check if an update is required # 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) time.sleep(1)
continue continue
if None == self.lastUpdateTried or self.lastUpdateTried <= now: if None == self.LastUpdateTried or self.LastUpdateTried <= now:
if True == self.provider.receiveWindData(): if True == self.Provider.receiveWindData():
self.nextUpdate = self.provider.updateTime self.NextUpdate = self.Provider.UpdateTime
print('Received new wind data') print('Received new wind data')

View File

@@ -17,12 +17,12 @@ class AircraftPerformance:
aircraft = PerformanceData(key) aircraft = PerformanceData(key)
aircraft.speedAboveFL240 = config[key]['speedabovefl240'] aircraft.SpeedAboveFL240 = config[key]['speedabovefl240']
aircraft.rodAboveFL240 = config[key]['rodabovefl240'] aircraft.RodAboveFL240 = config[key]['rodabovefl240']
aircraft.speedAboveFL100 = config[key]['speedabovefl100'] aircraft.SpeedAboveFL100 = config[key]['speedabovefl100']
aircraft.rodAboveFL100 = config[key]['rodabovefl100'] aircraft.RodAboveFL100 = config[key]['rodabovefl100']
aircraft.speedBelowFL100 = config[key]['speedbelowfl100'] aircraft.SpeedBelowFL100 = config[key]['speedbelowfl100']
aircraft.rodBelowFL100 = config[key]['rodbelowfl100'] aircraft.RodBelowFL100 = config[key]['rodbelowfl100']
aircraft.speedApproach = config[key]['speedapproach'] aircraft.SpeedApproach = config[key]['speedapproach']
self.Aircrafts[aircraft.icao] = aircraft self.Aircrafts[aircraft.Icao] = aircraft

View File

@@ -43,7 +43,7 @@ class Airport:
if None == planning.get('arrivalspacingdefault'): if None == planning.get('arrivalspacingdefault'):
sys.stderr.write('No "arrivalspacingdefault" entry found!') sys.stderr.write('No "arrivalspacingdefault" entry found!')
sys.exit(-1) 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.stderr.write('Unable to find' + icao + 'in the SCT data!')
sys.exit(-1) sys.exit(-1)
@@ -77,8 +77,8 @@ class Airport:
sys.exit(-1) sys.exit(-1)
found = False found = False
for runway in self.GngData.runways[icao]: for runway in self.GngData.Runways[icao]:
if ident == runway.name: if ident == runway.Name:
sequence = RunwaySequencing(runway) sequence = RunwaySequencing(runway)
sequence.Spacing = spacings[ident] sequence.Spacing = spacings[ident]
self.DefaultSequencing.activateRunway(sequence) self.DefaultSequencing.activateRunway(sequence)
@@ -103,9 +103,13 @@ class Airport:
for i in range(0, len(dependencies), 2): for i in range(0, len(dependencies), 2):
self.DefaultSequencing.addDependency(dependencies[i], dependencies[i + 1]) self.DefaultSequencing.addDependency(dependencies[i], dependencies[i + 1])
def __init__(self, filepath : str, icao : str): def parseConstraints(self, icao : str, planning):
self.arrivalRoutes = {} 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 = configparser.ConfigParser()
config.read(filepath) config.read(filepath)
@@ -153,5 +157,6 @@ class Airport:
sys.exit(-1) sys.exit(-1)
self.GaforId = dataConfig['gaforid'] self.GaforId = dataConfig['gaforid']
# get the default sequencing data # get the default sequencing data and constraints
self.parseDefaultSequencingConfiguration(icao, planningConfig) self.parseDefaultSequencingConfiguration(icao, planningConfig)
self.parseConstraints(icao, planningConfig)

View File

@@ -13,14 +13,14 @@ class AirportSequencing:
def activateRunway(self, runway : RunwaySequencing): def activateRunway(self, runway : RunwaySequencing):
for active in self.ActiveArrivalRunways: for active in self.ActiveArrivalRunways:
if active.Runway.name == runway.Runway.name: if active.Runway.Name == runway.Runway.Name:
self.ActiveArrivalRunways[runway.Runway.name] = runway self.ActiveArrivalRunways[runway.Runway.Name] = runway
return return
self.ActiveArrivalRunways.append(runway) self.ActiveArrivalRunways.append(runway)
def runwayIndex(self, identifier : str): def runwayIndex(self, identifier : str):
for i in range(0, len(self.ActiveArrivalRunways)): 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 i
return -1 return -1
@@ -70,7 +70,7 @@ class AirportSequencing:
def findRunway(self, identifier : str): def findRunway(self, identifier : str):
for runway in self.ActiveArrivalRunways: for runway in self.ActiveArrivalRunways:
if runway.Runway.name == identifier: if runway.Runway.Name == identifier:
return runway return runway
return None return None

View File

@@ -82,32 +82,32 @@ class SctEseFormat:
# extract all waypoints # extract all waypoints
for waypoint in config['VOR']: for waypoint in config['VOR']:
waypoint = SctEseFormat.parseWaypoint(waypoint, 0, 2, 3) 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']: for waypoint in config['NDB']:
waypoint = SctEseFormat.parseWaypoint(waypoint, 0, 1, 2) 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']: for waypoint in config['FIXES']:
waypoint = SctEseFormat.parseWaypoint(waypoint, 0, 1, 2) waypoint = SctEseFormat.parseWaypoint(waypoint, 0, 1, 2)
self.waypoints.setdefault(waypoint.name, []).append(waypoint) self.Waypoints.setdefault(waypoint.Name, []).append(waypoint)
# extract the airports # extract the airports
for airport in config['AIRPORT']: for airport in config['AIRPORT']:
airport = SctEseFormat.parseWaypoint(airport, 0, 2, 3) airport = SctEseFormat.parseWaypoint(airport, 0, 2, 3)
self.airports.setdefault(airport.name, []).append(airport) self.Airports.setdefault(airport.Name, []).append(airport)
# extract the runways # extract the runways
for runway in config['RUNWAY']: for runway in config['RUNWAY']:
airport, runway0, runway1 = SctEseFormat.parseRunway(runway) airport, runway0, runway1 = SctEseFormat.parseRunway(runway)
if None != airport: if None != airport:
if not airport in self.runways: if not airport in self.Runways:
self.runways.setdefault(airport, []) self.Runways.setdefault(airport, [])
self.runways[airport].append(runway0) self.Runways[airport].append(runway0)
self.runways[airport].append(runway1) self.Runways[airport].append(runway1)
def parseArrivalRoute(self, route : str, airport : Waypoint): def parseArrivalRoute(self, route : str, airport : Waypoint):
# split the route and validate that it is a STAR for the airport # split the route and validate that it is a STAR for the airport
split = route.split(':') 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 return None
# find all waypoints # find all waypoints
@@ -115,7 +115,7 @@ class SctEseFormat:
route = list(filter(None, split[4].split(' '))) route = list(filter(None, split[4].split(' ')))
for waypoint in route: for waypoint in route:
# find the waypoint in the route # find the waypoint in the route
coordinates = self.waypoints[waypoint] coordinates = self.Waypoints[waypoint]
# no waypoint with this name defined # no waypoint with this name defined
if None == coordinates: if None == coordinates:
sys.stderr.write('Unable to find waypoint ' + waypoint) sys.stderr.write('Unable to find waypoint ' + waypoint)
@@ -150,10 +150,10 @@ class SctEseFormat:
foundSidsStars = False foundSidsStars = False
# search the airport in the extracted list # search the airport in the extracted list
if not airport in self.airports: if not airport in self.Airports:
sys.stderr.write(airport + 'in self.airports', 'Unable to find the requested airport') sys.stderr.write('Unable to find the requested airport')
sys.exit(-1) sys.exit(-1)
airport = self.airports[airport][0] airport = self.Airports[airport][0]
for key in config: for key in config:
if 'SIDSSTARS' == key: if 'SIDSSTARS' == key:
@@ -166,14 +166,14 @@ class SctEseFormat:
# parse all arrival routes # parse all arrival routes
for line in config['SIDSSTARS']: for line in config['SIDSSTARS']:
route = self.parseArrivalRoute(line, airport) route = self.parseArrivalRoute(line, airport)
if None != route and route.name in allowedRoutes: if None != route and route.Name in allowedRoutes:
self.arrivalRoutes.setdefault(route.runway, []).append(route) self.ArrivalRoutes.setdefault(route.Runway, []).append(route)
def __init__(self, sctFilepath : str, eseFilepath : str, airport : str, allowedRoutes : list): def __init__(self, sctFilepath : str, eseFilepath : str, airport : str, allowedRoutes : list):
self.arrivalRoutes = {} self.ArrivalRoutes = {}
self.waypoints = {} self.Waypoints = {}
self.airports = {} self.Airports = {}
self.runways = {} self.Runways = {}
self.extractSctInformation(sctFilepath) self.extractSctInformation(sctFilepath)
self.extractArrivalRoutes(eseFilepath, airport, allowedRoutes) self.extractArrivalRoutes(eseFilepath, airport, allowedRoutes)

View File

@@ -7,13 +7,13 @@ import scipy.interpolate
class WeatherModel: class WeatherModel:
def __init__(self, gaforId, weather : Weather): def __init__(self, gaforId, weather : Weather):
self.gafor = gaforId self.Gafor = gaforId
self.weather = weather self.Weather = weather
self.windDirectionModel = None self.WindDirectionModel = None
self.windSpeedModel = None self.WindSpeedModel = None
self.lastWeatherUpdate = None self.LastWeatherUpdate = None
self.minimumAltitude = 1000000 self.MinimumAltitude = 1000000
self.maximumAltitude = -1 self.MaximumAltitude = -1
# create the density interpolation model # create the density interpolation model
# the density model is based on https://aerotoolbox.com/atmcalc/ # 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()) return ias * math.sqrt(1.225 / self.densityModel(altitude).item())
def updateWindModel(self): def updateWindModel(self):
if None == self.lastWeatherUpdate or self.lastWeatherUpdate != self.weather.provider.updateTime: if None == self.LastWeatherUpdate or self.LastWeatherUpdate != self.Weather.Provider.UpdateTime:
self.lastWeatherUpdate = self.weather.provider.updateTime self.LastWeatherUpdate = self.Weather.Provider.UpdateTime
self.minimumAltitude = 1000000 self.MinimumAltitude = 1000000
self.maximumAltitude = -1 self.MaximumAltitude = -1
self.windDirectionModel = None self.WindDirectionModel = None
self.windSpeedModel = 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 = [] altitudes = []
directions = [] directions = []
speeds = [] speeds = []
# collect the data for the wind model # 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]) altitudes.append(level[0])
directions.append(level[1]) directions.append(level[1])
speeds.append(level[2]) speeds.append(level[2])
# define the thresholds for later boundary checks # define the thresholds for later boundary checks
if self.minimumAltitude > level[0]: if self.MinimumAltitude > level[0]:
self.minimumAltitude = level[0] self.MinimumAltitude = level[0]
if self.maximumAltitude < level[0]: if self.MaximumAltitude < level[0]:
self.maximumAltitude = level[0] self.MaximumAltitude = level[0]
# calculate the models # calculate the models
if 1 < len(altitudes): if 1 < len(altitudes):
self.windDirectionModel = scipy.interpolate.interp1d(altitudes, directions) self.WindDirectionModel = scipy.interpolate.interp1d(altitudes, directions)
self.windSpeedModel = scipy.interpolate.interp1d(altitudes, speeds) self.WindSpeedModel = scipy.interpolate.interp1d(altitudes, speeds)
def calculateGS(self, altitude : int, ias : int, heading : int): def calculateGS(self, altitude : int, ias : int, heading : int):
self.updateWindModel() self.updateWindModel()
tas = self.calculateTAS(altitude, ias) tas = self.calculateTAS(altitude, ias)
# initialize the wind data # initialize the wind data
if None != self.windDirectionModel and None != self.windSpeedModel: if None != self.WindDirectionModel and None != self.WindSpeedModel:
direction = 0.0 direction = 0.0
speed = 0.0 speed = 0.0
if None != self.windSpeedModel and None != self.windDirectionModel: if None != self.WindSpeedModel and None != self.WindDirectionModel:
if self.maximumAltitude <= altitude: if self.MaximumAltitude <= altitude:
altitude = self.maximumAltitude - 1 altitude = self.MaximumAltitude - 1
if self.minimumAltitude >= altitude: if self.MinimumAltitude >= altitude:
altitude = self.minimumAltitude + 1 altitude = self.MinimumAltitude + 1
direction = self.windDirectionModel(altitude).item() direction = self.WindDirectionModel(altitude).item()
speed = self.windSpeedModel(altitude).item() speed = self.WindSpeedModel(altitude).item()
else: else:
speed = 0 speed = 0
direction = 0 direction = 0

View File

@@ -14,48 +14,46 @@ from aman.types.PerformanceData import PerformanceData
class Worker(Thread): class Worker(Thread):
def __init__(self): def __init__(self):
Thread.__init__(self) Thread.__init__(self)
self.stopThread = None self.StopThread = None
self.icao = None self.Icao = None
self.configuration = None self.Configuration = None
self.arrivalRoutes = None self.PerformanceData = None
self.performanceData = None self.UpdateLock = None
self.updateLock = None self.ReportQueue = {}
self.reportQueue = {} self.WeatherModel = None
self.weatherModel = None
self.RecedingHorizonControl = None self.RecedingHorizonControl = None
def __del__(self): def __del__(self):
self.release() self.release()
def acquire(self, icao : str, configuration : Airport, weather : Weather, performance : PerformanceData): def acquire(self, icao : str, configuration : Airport, weather : Weather, performance : PerformanceData):
self.stopThread = None self.StopThread = None
self.icao = icao self.Icao = icao
self.configuration = configuration self.Configuration = configuration
self.sequencingConfiguration = self.configuration.DefaultSequencing self.sequencingConfiguration = configuration.DefaultSequencing
self.performanceData = performance self.PerformanceData = performance
self.arrivalRoutes = configuration.GngData.arrivalRoutes self.UpdateLock = Lock()
self.updateLock = Lock() self.ReportQueue = {}
self.reportQueue = {} self.WeatherModel = WeatherModel(configuration.GaforId, weather)
self.weatherModel = WeatherModel(self.configuration.GaforId, weather) self.RecedingHorizonControl = RecedingHorizonControl(configuration.RecedingHorizonControl)
self.RecedingHorizonControl = RecedingHorizonControl(self.configuration.RecedingHorizonControl)
self.start() self.start()
def acquireLock(self): def acquireLock(self):
if None != self.updateLock: if None != self.UpdateLock:
self.updateLock.acquire() self.UpdateLock.acquire()
def release(self): def release(self):
self.stopThread = True self.StopThread = True
self.join() self.join()
def releaseLock(self): def releaseLock(self):
if None != self.updateLock: if None != self.UpdateLock:
self.updateLock.release() self.UpdateLock.release()
def run(self): def run(self):
counter = 0 counter = 0
while None == self.stopThread: while None == self.StopThread:
time.sleep(1) time.sleep(1)
counter += 1 counter += 1
if 0 != (counter % 10): if 0 != (counter % 10):
@@ -67,17 +65,17 @@ class Worker(Thread):
self.RecedingHorizonControl.cleanupWindows() self.RecedingHorizonControl.cleanupWindows()
# update the aircraft information in RHC # update the aircraft information in RHC
for callsign in self.reportQueue: for callsign in self.ReportQueue:
report = self.reportQueue[callsign] report = self.ReportQueue[callsign]
if 0 != report.distanceToIAF and '' != report.initialApproachFix: 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: if None != inbound.PlannedRunway and None != inbound.PlannedStar:
self.RecedingHorizonControl.update(inbound) self.RecedingHorizonControl.update(inbound)
else: else:
print('Unable to find all data of ' + report.aircraft.callsign) print('Unable to find all data of ' + report.aircraft.callsign)
self.reportQueue.clear() self.ReportQueue.clear()
if 0 != len(self.RecedingHorizonControl.Windows): if 0 != len(self.RecedingHorizonControl.Windows):
print('FCFS run:') print('FCFS run:')
@@ -89,9 +87,9 @@ class Worker(Thread):
# this is required to handle the overlap between windows # this is required to handle the overlap between windows
preceedingInbounds = {} preceedingInbounds = {}
for runway in self.sequencingConfiguration.ActiveArrivalRunways: for runway in self.sequencingConfiguration.ActiveArrivalRunways:
inbound = self.RecedingHorizonControl.lastFixedInboundOnRunway(runway.Runway.name) inbound = self.RecedingHorizonControl.lastFixedInboundOnRunway(runway.Runway.Name)
if None != inbound: if None != inbound:
preceedingInbounds[runway.Runway.name] = inbound preceedingInbounds[runway.Runway.Name] = inbound
# search the ACO relevant aircrafts # search the ACO relevant aircrafts
relevantInbounds = self.RecedingHorizonControl.optimizationRelevantInbounds() relevantInbounds = self.RecedingHorizonControl.optimizationRelevantInbounds()
@@ -106,6 +104,6 @@ class Worker(Thread):
# TODO perform the ACO run # TODO perform the ACO run
# TODO update the RHC stages based on the ACO run result # TODO update the RHC stages based on the ACO run result
else: 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() self.releaseLock()

View File

@@ -2,10 +2,10 @@
class ArrivalRoute: class ArrivalRoute:
def __init__(self, name : str, runway : str, waypoints : list): def __init__(self, name : str, runway : str, waypoints : list):
self.name = name self.Name = name
self.runway = runway self.Runway = runway
self.iaf = waypoints[0] self.Iaf = waypoints[0]
self.route = waypoints self.Route = waypoints
def __str__(self): 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

View File

@@ -39,7 +39,7 @@ class Inbound:
distance = 100000.0 distance = 100000.0
currentPosition = Waypoint('', self.Report.position.latitude, self.Report.position.longitude) currentPosition = Waypoint('', self.Report.position.latitude, self.Report.position.longitude)
for runway in sequencingConfig.ActiveArrivalRunways: for runway in sequencingConfig.ActiveArrivalRunways:
candidateDistance = runway.Runway.start.haversine(currentPosition) candidateDistance = runway.Runway.Start.haversine(currentPosition)
if distance > candidateDistance: if distance > candidateDistance:
self.PlannedRunway = runway self.PlannedRunway = runway
distance = candidateDistance distance = candidateDistance
@@ -49,11 +49,11 @@ class Inbound:
if None == self.PlannedRunway: if None == self.PlannedRunway:
return return
for arrivalRunway in navData.arrivalRoutes: for arrivalRunway in navData.ArrivalRoutes:
if arrivalRunway == self.PlannedRunway.Runway.name: if arrivalRunway == self.PlannedRunway.Runway.Name:
stars = navData.arrivalRoutes[arrivalRunway] stars = navData.ArrivalRoutes[arrivalRunway]
for star in stars: 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 self.PlannedStar = star
return return
@@ -63,14 +63,14 @@ class Inbound:
# calculate remaining trackmiles # calculate remaining trackmiles
remainingDistanceNM = self.Report.distanceToIAF remainingDistanceNM = self.Report.distanceToIAF
start = self.PlannedStar.route[0] start = self.PlannedStar.Route[0]
for i in range(1, len(self.PlannedStar.route)): for i in range(1, len(self.PlannedStar.Route)):
remainingDistanceNM += start.haversine(self.PlannedStar.route[i]) * 0.539957 remainingDistanceNM += start.haversine(self.PlannedStar.Route[i]) * 0.539957
start = self.PlannedStar.route[i] start = self.PlannedStar.Route[i]
# calculate descend profile # calculate descend profile
flightTimeSeconds = 0 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 distanceToWaypoint = self.Report.distanceToIAF
nextWaypointIndex = 0 nextWaypointIndex = 0
currentPosition = [ self.Report.dynamics.altitude, self.Report.dynamics.groundSpeed ] currentPosition = [ self.Report.dynamics.altitude, self.Report.dynamics.groundSpeed ]
@@ -105,7 +105,7 @@ class Inbound:
distanceToWaypoint -= abs(lastDistance - remainingDistanceNM) distanceToWaypoint -= abs(lastDistance - remainingDistanceNM)
if 0 >= distanceToWaypoint: if 0 >= distanceToWaypoint:
nextWaypointIndex += 1 nextWaypointIndex += 1
if nextWaypointIndex < len(self.PlannedStar.route): if nextWaypointIndex < len(self.PlannedStar.Route):
currentHeading = self.PlannedStar.route[nextWaypointIndex - 1].bearing(self.PlannedStar.route[nextWaypointIndex]) currentHeading = self.PlannedStar.Route[nextWaypointIndex - 1].bearing(self.PlannedStar.Route[nextWaypointIndex])
return timedelta(seconds = flightTimeSeconds) return timedelta(seconds = flightTimeSeconds)

View File

@@ -2,27 +2,29 @@
class PerformanceData: class PerformanceData:
def __init__(self, icao : str): def __init__(self, icao : str):
self.icao = icao self.Icao = icao
self.speedAboveFL240 = 0.0 self.SpeedAboveFL240 = 0.0
self.speedAboveFL100 = 0.0 self.SpeedAboveFL100 = 0.0
self.speedBelowFL100 = 0.0 self.SpeedBelowFL100 = 0.0
self.speedApproach = 0.0 self.SpeedApproach = 0.0
self.rodAboveFL240 = 0.0 self.RodAboveFL240 = 0.0
self.rodAboveFL100 = 0.0 self.RodAboveFL100 = 0.0
self.rodBelowFL100 = 2000.0 self.RodBelowFL100 = 2000.0
def ias(self, altitude, distance): def ias(self, altitude, distance):
if 24000 >= altitude: if 24000 >= altitude:
return self.speedAboveFL240 return self.SpeedAboveFL240
elif 10000 >= altitude: elif 10000 >= altitude:
return self.speedAboveFL100 return self.SpeedAboveFL100
elif 10000 < altitude and 5 < distance:
return self.SpeedBelowFL100
elif 5 >= distance: elif 5 >= distance:
return self.speedApproach return self.SpeedApproach
else: else:
return 220 return 220
def __str__(self): def __str__(self):
return 'ICAO: ' + self.icao + ', FL240: ' + str(self.rodAboveFL240) + '@' + str(self.speedAboveFL240) + \ return 'ICAO: ' + self.icao + ', FL240: ' + str(self.RodAboveFL240) + '@' + str(self.SpeedAboveFL240) + \
', +FL100: ' + str(self.rodAboveFL100) + '@' + str(self.speedAboveFL100) + \ ', +FL100: ' + str(self.RodAboveFL100) + '@' + str(self.SpeedAboveFL100) + \
', -FL100: ' + str(self.rodBelowFL100) + '@' + str(self.speedBelowFL100) + \ ', -FL100: ' + str(self.RodBelowFL100) + '@' + str(self.SpeedBelowFL100) + \
', Vapp: ' + str(self.speedApproach) ', Vapp: ' + str(self.SpeedApproach)

View File

@@ -4,9 +4,9 @@ from aman.types.Waypoint import Waypoint
class Runway: class Runway:
def __init__(self, start : Waypoint, end : Waypoint): def __init__(self, start : Waypoint, end : Waypoint):
self.name = start.name self.Name = start.Name
self.start = start self.Start = start
self.end = end self.End = end
def heading(self): def heading(self):
return self.start.bearing(self.end) return self.Start.bearing(self.End)

View File

@@ -21,18 +21,18 @@ class Waypoint:
return dd return dd
def __init__(self, name : str, latitude : float, longitude : float): def __init__(self, name : str, latitude : float, longitude : float):
self.name = name self.Name = name
self.coordinate = np.array([ latitude, longitude ]) self.Coordinate = np.array([ latitude, longitude ])
def __str__(self): 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): def haversine(self, other):
geodesic = pyproj.Geod(ellps='WGS84') 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 return distance / 1000.0
def bearing(self, other): def bearing(self, other):
geodesic = pyproj.Geod(ellps='WGS84') 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 return forward