Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BlueSky fixes (OpenSky, Performance model, wypt speed, MVP) #386

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
3569f86
Merge branch 'TUDelft-CNS-ATM:master' into master
ninyes Dec 6, 2021
37b35ef
Merge branch 'master' of github.com:ninyes/bluesky
ninyes Dec 6, 2021
3d1f812
Fix indexing in autopilot "potential current climb/descent stop" logic
ninyes Dec 6, 2021
fc9f7f8
Merge branch 'master' of https://github.com/TUDelft-CNS-ATM/bluesky
ninyes Dec 20, 2021
ee75867
Included information on how to manually build setup.py for cbased fun…
ninyes Jan 9, 2022
7fdc88f
Merge branch 'Cdef_statebased'
ninyes Jan 9, 2022
a22cd8e
Merge branch 'TUDelft-CNS-ATM:master' into master
ninyes Apr 21, 2022
100d5c9
Delete unneccesary print statement
ninyes Apr 21, 2022
9e54515
Remove bug from opensky plugin. Number of aircraft is not valid entry…
ninyes Apr 21, 2022
eff3148
The intented Mach number rather than the traf.M should be used to cal…
ninyes Apr 21, 2022
2761b3e
Function to set aircraft mass for performance modelling
ninyes Apr 21, 2022
58fb19f
No conversion to CAS to be able to fly constant Mach to next waypoint…
ninyes Apr 21, 2022
0173994
Implementing a vertical activation switch to avoid aircraft climbing …
ninyes Apr 21, 2022
ebe4145
Capping the performance limits based on the general (BADA/OpenAP) bs.…
ninyes Apr 21, 2022
716e95c
The mvp function returns the TAS, therefore the convertion step is di…
ninyes Apr 21, 2022
81fb027
Merge branch 'master' of https://github.com/ninyes/bluesky
ninyes Apr 29, 2022
d1ec70e
Revert "The mvp function returns the TAS, therefore the convertion st…
ninyes May 3, 2022
dde2e95
Revert "Capping the performance limits based on the general (BADA/Ope…
ninyes May 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bluesky/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from bluesky import settings
# Register settings defaults
settings.set_variable_defaults(prefer_compiled=False)
settings.set_variable_defaults(prefer_compiled=True)
if settings.prefer_compiled:
try:
from . import cgeo as geo
Expand Down
8 changes: 8 additions & 0 deletions bluesky/tools/src_cpp/README.txt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Note, to make use of this you currently still have to manually compile
the extension by running:
python setup.py build_ext --inplace
in bluesky/tools/src_cpp
and copying/moving the resulting cgeo.[so/dll] to bluesky/tools.

In bluesky/tools/__init__, change the variable prefer_compiled
to True if not read correctly from settings.cfg
12 changes: 1 addition & 11 deletions bluesky/traffic/aporasas.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,11 @@ def create(self, n=1):

def update(self):
#--------- Input to Autopilot settings to follow: destination or ASAS ----------
# Convert the ASAS commanded speed from ground speed to TAS
if bs.traf.wind.winddim > 0:
vwn, vwe = bs.traf.wind.getdata(bs.traf.lat, bs.traf.lon, bs.traf.alt)
asastasnorth = bs.traf.cr.tas * np.cos(np.radians(bs.traf.cr.trk)) - vwn
asastaseast = bs.traf.cr.tas * np.sin(np.radians(bs.traf.cr.trk)) - vwe
asastas = np.sqrt(asastasnorth**2 + asastaseast**2)
# no wind, then ground speed = TAS
else:
asastas = bs.traf.cr.tas # TAS [m/s]

# Select asas if there is a conflict AND resolution is on
# Determine desired states per channel whether to use value from ASAS or AP.
# bs.traf.cr.active may be used as well, will set all of these channels
self.trk = np.where(bs.traf.cr.hdgactive, bs.traf.cr.trk, bs.traf.ap.trk)
self.tas = np.where(bs.traf.cr.tasactive, asastas, bs.traf.ap.tas)
self.tas = np.where(bs.traf.cr.tasactive, bs.traf.cr.tas, bs.traf.ap.tas)
self.alt = np.where(bs.traf.cr.altactive, bs.traf.cr.alt, bs.traf.ap.alt)
self.vs = np.where(bs.traf.cr.vsactive, bs.traf.cr.vs, bs.traf.ap.vs)

Expand Down
51 changes: 37 additions & 14 deletions bluesky/traffic/asas/mvp.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
''' Conflict resolution based on the Modified Voltage Potential algorithm. '''
import numpy as np
from bluesky import stack
import bluesky as bs
from bluesky import stack, settings
from bluesky.traffic.asas import ConflictResolution


Expand Down Expand Up @@ -166,19 +167,34 @@ def resolve(self, conf, ownship, intruder):

# Initialize an array to store time needed to resolve vertically
timesolveV = np.ones(ownship.ntraf) * 1e9

# Initialize an array to store if vs action is required irrespective of resolution domain
swvsact = np.zeros(ownship.ntraf, dtype=bool)

# Call MVP function to resolve conflicts-----------------------------------
for ((ac1, ac2), qdr, dist, tcpa, tLOS) in zip(conf.confpairs, conf.qdr, conf.dist, conf.tcpa, conf.tLOS):
idx1 = ownship.id.index(ac1)
idx2 = intruder.id.index(ac2)


# Determine largest RPZ and HPZ of the conflict pair
rpz_m = np.max(conf.rpz[[idx1, idx2]] * self.resofach)
hpz_m = np.max(conf.hpz[[idx1, idx2]] * self.resofacv)

# If A/C indexes are found, then apply MVP on this conflict pair
# Because ADSB is ON, this is done for each aircraft separately
if idx1 >-1 and idx2 > -1:
dv_mvp, tsolV = self.MVP(ownship, intruder, conf, qdr, dist, tcpa, tLOS, idx1, idx2)
dv_mvp, tsolV = self.MVP(ownship, intruder, conf, qdr, dist, tcpa, tLOS, idx1, idx2, rpz_m, hpz_m)
if tsolV < timesolveV[idx1]:
timesolveV[idx1] = tsolV

# Check if AC pair is in rpz and predicted to be in hpz next step
# hpz_m scaled with at least 1.2 resofacv for fast CL/DE traffic
hor_int = dist < rpz_m
ver_int = abs((ownship.alt[idx1] + ownship.vs[idx1]*settings.asas_dt) - \
(intruder.alt[idx2] + intruder.vs[idx2]*settings.asas_dt)) < \
hpz_m / self.resofacv * max(self.resofacv, 1.2)
swvsact[idx1] = np.logical_and(hor_int, ver_int)

# Use priority rules if activated
if self.swprio:
dv[idx1], _ = self.applyprio(dv_mvp, dv[idx1], dv[idx2], ownship.vs[idx1], intruder.vs[idx2])
Expand Down Expand Up @@ -235,11 +251,16 @@ def resolve(self, conf, ownship, intruder):

# Determine ASAS module commands for all aircraft--------------------------

# Cap the velocity
newgscapped = np.maximum(ownship.perf.vmin,np.minimum(ownship.perf.vmax,newgs))

# Cap the vertical speed
vscapped = np.maximum(ownship.perf.vsmin,np.minimum(ownship.perf.vsmax,newvs))
# Compute the true airspeed from newgs (GS=TAS if no wind)
vwn, vwe = bs.traf.wind.getdata(bs.traf.lat, bs.traf.lon, bs.traf.alt)
newtasnorth = newgs * np.cos(np.radians(newtrack)) - vwn
newtaseast = newgs * np.sin(np.radians(newtrack)) - vwe
newtas = np.sqrt(newtasnorth**2 + newtaseast**2)

# Cap the velocity and vertical speed
tascapped, vscapped, altcapped = bs.traf.perf.limits(newtas, newvs, ownship.alt, ownship.ax)
signvs = np.sign(newvs)
vscapped = np.where(np.logical_or(signvs == 0, signvs == np.sign(vscapped)), vscapped, -vscapped)

# Calculate if Autopilot selected altitude should be followed. This avoids ASAS from
# climbing or descending longer than it needs to if the autopilot leveloff
Expand All @@ -261,15 +282,17 @@ def resolve(self, conf, ownship, intruder):
# be equal to auto pilot alt (aalt). This is to prevent a new asasalt being computed
# using the auto pilot vertical speed (ownship.avs) using the code in line 106 (asasalttemp) when only
# horizontal resolutions are allowed.
alt = alt * (1 - self.swresohoriz) + ownship.selalt * self.swresohoriz
return newtrack, newgscapped, vscapped, alt
# Additionally remaining at current alt (ownship.alt) is forced when AC are
# in each others rpz and predicted to enter hpz based on vs and dt ASAS
alt = (alt * (1 - self.swresohoriz) + ownship.selalt * self.swresohoriz) * (1 - swvsact) \
+ ownship.alt * swvsact

return newtrack, tascapped, vscapped, alt

def MVP(self, ownship, intruder, conf, qdr, dist, tcpa, tLOS, idx1, idx2):
def MVP(self, ownship, intruder, conf, qdr, dist, tcpa, tLOS, idx1, idx2, rpz_m, hpz_m):
"""Modified Voltage Potential (MVP) resolution method"""
# Preliminary calculations-------------------------------------------------
# Determine largest RPZ and HPZ of the conflict pair, use lookahead of ownship
rpz_m = np.max(conf.rpz[[idx1, idx2]] * self.resofach)
hpz_m = np.max(conf.hpz[[idx1, idx2]] * self.resofacv)
# Use lookahead of ownship
dtlook = conf.dtlookahead[idx1]
# Convert qdr from degrees to radians
qdr = np.radians(qdr)
Expand Down
5 changes: 5 additions & 0 deletions bluesky/traffic/asas/src_cpp/README.txt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Note, to make use of this you currently still have to manually compile
the extension by running:
python setup.py build_ext --inplace
in bluesky/traffic/asas/src_cpp
and copying/moving the resulting casas.[so/dll] to bluesky/traffic/asas.
4 changes: 2 additions & 2 deletions bluesky/traffic/performance/bada/perfbada.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
""" BlueSky aircraft performance calculations using BADA 3.xx."""
import numpy as np
import bluesky as bs
from bluesky.tools.aero import kts, ft, g0, vtas2cas, vcas2tas
from bluesky.tools.aero import kts, ft, g0, vtas2cas, vcas2tas, vtas2mach
from bluesky.traffic.performance.perfbase import PerfBase
from bluesky.traffic.performance.legacy.performance import esf, phases, calclimits, PHASE
from bluesky import settings
Expand Down Expand Up @@ -585,7 +585,7 @@ def limits(self, intent_v, intent_vs, intent_h, ax):
self.limalt_flag, self.limvs, self.limvs_flag = calclimits(
vtas2cas(intent_v, bs.traf.alt), bs.traf.gs,
self.vmto, self.vmin, self.vmo, self.mmo,
bs.traf.M, bs.traf.alt, self.hmaxact,
vtas2mach(intent_v, bs.traf.alt), bs.traf.alt, self.hmaxact,
intent_h, intent_vs, self.maxthr,
self.thrust, self.D, bs.traf.tas,
self.mass, self.ESF, self.phase)
Expand Down
8 changes: 8 additions & 0 deletions bluesky/traffic/performance/perfbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ def engchange(self, acid: "acid", engine_id: "txt" = ""):
"The currently selected performance model doesn't support engine changes.",
)

@command(name='MASS')
def selacmass(self, acid: 'acid', mass: 'float'):
""" MASS, acid, mass

Select aircraft mass for performance model."""
self.mass[acid] = mass
return True

@command(name="PERFSTATS", aliases=("PERFINFO", "PERFDATA"))
def show_performance(self, acid: "acid"):
"""Show aircraft perfromance parameters for aircraft 'acid'"""
Expand Down
8 changes: 1 addition & 7 deletions bluesky/traffic/route.py
Original file line number Diff line number Diff line change
Expand Up @@ -895,14 +895,8 @@ def direct(acidx: 'acid', wpname: 'wpinroute'):
else:
alt = acrte.wpalt[wpidx]

# Check for valid Mach or CAS
if acrte.wpspd[wpidx] <2.0:
cas = mach2cas(acrte.wpspd[wpidx], alt)
else:
cas = acrte.wpspd[wpidx]

# Save it for next leg
bs.traf.actwp.nextspd[acidx] = cas
bs.traf.actwp.nextspd[acidx] = acrte.wpspd[wpidx]

# No speed specified for next leg
else:
Expand Down
2 changes: 1 addition & 1 deletion plugins/opensky.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def update(self):
if n_new:
actype = [actypes.get(str(i), 'B744') for i in icao24[newac]]
for j in range(n_new):
traf.cre(n_new, acid[newac][j], actype[j], lat[newac][j], lon[newac][j],\
traf.cre(acid[newac][j], actype[j], lat[newac][j], lon[newac][j],\
hdg[newac][j], alt[newac][j], spd[newac][j])
self.my_ac[-n_new:] = True

Expand Down
1 change: 0 additions & 1 deletion plugins/windgfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ def loadwind(self, lat0: 'lat', lon0: 'lon', lat1: 'lat', lon1: 'lon',
pred = 3
elif self.hour == 24:
ymd0 = "%04d%02d%02d" % (self.year, self.month, self.day)
print(ymd0)
ymd1 = (datetime.datetime.strptime(ymd0, '%Y%m%d') +
datetime.timedelta(days=1))
self.year = ymd1.year
Expand Down