def update(): mylog = list() for idx, name in enumerate(sectors): # Perform inside count, and check entering and leaving aircraft inside = areafilter.checkInside(name, traf.lat, traf.lon, traf.alt) ids = set(np.array(traf.id)[inside]) previds = previnside[idx] arrived = str.join(', ', ids - previds) left = str.join(', ', previds - ids) n_tot = len(ids) n_arrived = len(arrived) n_left = len(left) # Add log string to list mylog.append('%s, %d' % (name, n_tot)) # Print to console if n_left > 0: scr.echo('%s aircraft that have left: %s' % (name, left)) if n_arrived > 0: scr.echo('%s aircraft that have arrived: %s' % (name, arrived)) if n_left + n_arrived > 0: scr.echo('%s occupancy count: %d' % (name, n_tot)) previnside[idx] = ids # Log data if enabled logger.log(str.join(', ', mylog))
def update(): mylog = list() for idx, name in enumerate(sectors): # Perform inside count, and check entering and leaving aircraft inside = areafilter.checkInside(name, traf.lat, traf.lon, traf.alt) ids = set(np.array(traf.id)[inside]) previds = previnside[idx] arrived = str.join(', ', ids - previds) left = str.join(', ', previds - ids) n_tot = len(ids) n_arrived = len(arrived) n_left = len(left) # Add log string to list mylog.append('%s, %d' % (name, n_tot)) # Print to console if n_left > 0: scr.echo('%s aircraft that have left: %s' % (name, left)) if n_arrived > 0: scr.echo('%s aircraft that have arrived: %s' % (name, arrived)) if n_left + n_arrived > 0: scr.echo('%s occupancy count: %d' % (name, n_tot)) previnside[idx] = ids # Log data if enabled logger.log(str.join(', ', mylog))
def sectorcount(sw, name=''): if sw == 'LIST': if len(sectors) == 0: return True, 'No registered sectors available' else: return True, 'Registered sectors:', str.join(', ', sectors) elif sw == 'ADD': # Add new sector to list. if name in sectors: return True, 'Sector %s already registered.' % name elif areafilter.hasArea(name): # Add new area to the sector list, and add an initial inside count of traffic sectors.append(name) inside = areafilter.checkInside(name, traf.lat, traf.lon, traf.alt) previnside.append(set(np.array(traf.id)[inside])) return True, 'Added %s to sector list.' % name else: return False, "No area found with name '%s', create it first with one of the shape commands" % name else: # Remove area from sector list if name in sectors: idx = sectors.index(name) sectors.pop(idx) previnside.pop(idx) return True, 'Removed %s from sector list.' % name else: return False, "No sector registered with name '%s'." % name
def sectorcount(sw, name=''): if sw == 'LIST': if len(sectors) == 0: return True, 'No registered sectors available' else: return True, 'Registered sectors:', str.join(', ', sectors) elif sw == 'ADD': # Add new sector to list. if name in sectors: return True, 'Sector %s already registered.' % name elif areafilter.hasArea(name): # Add new area to the sector list, and add an initial inside count of traffic sectors.append(name) inside = areafilter.checkInside(name, traf.lat, traf.lon, traf.alt) previnside.append(set(np.array(traf.id)[inside])) return True, 'Added %s to sector list.' % name else: return False, "No area found with name '%s', create it first with one of the shape commands" % name else: # Remove area from sector list if name in sectors: idx = sectors.index(name) sectors.pop(idx) previnside.pop(idx) return True, 'Removed %s from sector list.' % name else: return False, "No sector registered with name '%s'." % name
def update_simdt(self): if self.active: ''' Update eacht simulation timestep ''' # Determine which aircraft are currently inside logging area self.currinside = areafilter.checkInside(self.areaname, traf.lat, traf.lon, traf.alt) # Check if loginterval is active and write to .log files if self.interval is not None: # If current time lies on interval: update loggers if self.interval[0] <= sim.simt < self.interval[1]: self.logging = True self.update_geolog() self.update_fstlog() # Update conflict/resolution/LoS pairs each simulation timestep self.update_pairs() # If current timestep is first outside interval: log unfinished parameters elif self.logging: self.logging = False self.reset_geolog() self.reset_fstlog() self.reset_conlog() # If loginterval is not active, always update loggers else: self.logging = True self.update_geolog() self.update_fstlog() # Update conflict/resolution/LoS pairs each simulation timestep self.update_pairs() # Update previously stored insids self.previnside = np.array(self.currinside)
def get_sectors(self, sectors): self.traf_in_sectors = [] for sector in sectors: self.traf_in_sectors.append(areafilter.checkInside( sector, traf.lat, traf.lon, traf.alt)) self.traf_in_sectors = np.array(self.traf_in_sectors)
def applygeovec(): # Apply each geovector for vec in geovecs: areaname = vec[0] if areafilter.hasArea(areaname): swinside = areafilter.checkInside(areaname, traf.lat, traf.lon, traf.alt) gsmin, gsmax, trkmin, trkmax, vsmin, vsmax = vec[1:] # -----Ground speed limiting # For now assume no wind: so use tas as gs if gsmin: casmin = vtas2cas(np.ones(traf.ntraf) * gsmin, traf.alt) usemin = traf.selspd < casmin traf.selspd[swinside & usemin] = casmin[swinside & usemin] if gsmax: casmax = vtas2cas(np.ones(traf.ntraf) * gsmax, traf.alt) usemax = traf.selspd > casmax traf.selspd[swinside & usemax] = casmax[swinside & usemax] #------ Limit Track(so hdg) # Max track interval is 180 degrees to avoid ambiguity of what is inside the interval if trkmin and trkmax: # Use degto180 to avodi problems for e.g interval [350,30] usemin = swinside & (degto180(traf.trk - trkmin) < 0 ) # Left of minimum usemax = swinside & (degto180(traf.trk - trkmax) > 0 ) # Right of maximum #print(usemin,usemax) traf.ap.trk[swinside & usemin] = trkmin traf.ap.trk[swinside & usemax] = trkmax # -----Ground speed limiting # For now assume no wind: so use tas as gs if vsmin: traf.selvs[swinside & (traf.vs < vsmin)] = vsmin # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs < vsmin)] = traf.alt[swinside & (traf.vs < vsmin)] + \ np.sign(vsmin)*200.*ft if vsmax: traf.selvs[swinside & (traf.vs > vsmax)] = vsmax # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs > vsmax)] = traf.alt[swinside & (traf.vs > vsmax)] + \ np.sign(vsmax)*200.*ft return
def update(self): ''' Update flight efficiency metrics 2D and 3D distance [m], and work done (force*distance) [J] ''' if not self.active: return #self.data_collection_count += np.around(self.dt, decimals = 0) #internal timestamp in minutes self.data_collection_count += 5 #resultantspd = np.sqrt(traf.gs * traf.gs + traf.vs * traf.vs) self.distance2D += self.dt * traf.gs / nm #in nautical miles #self.distance3D += self.dt * resultantspd if traf.asas.inconf.any(): in_conf = np.where(traf.asas.inconf == True) traf.timeinconf[in_conf] += self.dt if settings.performance_model == 'openap': self.work += (traf.perf.thrust * self.dt * traf.gs) else: self.work += (traf.perf.Thr * self.dt * traf.gs) # ToDo: Add autodelete for descending with swTaxi: if self.swtaxi: pass # To be added!!! if len(traf.asas.confpairs) > 0 and traf.asas.priocode == "SRS1": self.count_relevancy += 1 if self.count_relevancy == 5: #takes relevancy data each five iterations self.update_relevancy() self.count_relevancy = 0 self.update_asasdata() # Find out which aircraft are currently inside the experiment area, and # determine which aircraft need to be deleted. inside = areafilter.checkInside(self.name, traf.lat, traf.lon, traf.alt) #gets all aircraft that have left the experiment area since the last update #delidx = np.intersect1d(np.where(np.array(self.inside)==True), np.where(np.array(inside)==False)) delidx = np.where(np.array(inside) == False) #self.inside = inside # Log flight statistics when for deleted aircraft if delidx[0].any(): self.logger.log( np.array(traf.id)[delidx], traf.cretime[delidx], sim.simt - traf.cretime[delidx], traf.timeinconf[delidx], self.distance2D[delidx], self.work[delidx], traf.alt[delidx], traf.tas[delidx]) self.inconf = traf.asas.inconf #self.update_asasdata(deletion=True) # delete all aicraft in self.delidx for idx in delidx: #print("Aircraft %s was/were deleted" % (np.array(traf.id)[idx])) traf.delete(idx)
def update(self): ''' Update flight efficiency metrics 2D and 3D distance [m], and work done (force*distance) [J] ''' if not self.active: return resultantspd = np.sqrt(traf.gs * traf.gs + traf.vs * traf.vs) self.distance2D += self.dt * traf.gs self.distance3D += self.dt * resultantspd if settings.performance_model == 'nap': self.work += (traf.perf.thrust * self.dt * resultantspd) else: self.work += (traf.perf.Thr * self.dt * resultantspd) # ToDo: Add autodelete for descending with swTaxi: if self.swtaxi: pass # To be added!!! # Find out which aircraft are currently inside the experiment area, and # determine which aircraft need to be deleted. inside = areafilter.checkInside(self.name, traf.lat, traf.lon, traf.alt) delidx = np.intersect1d(np.where(np.array(self.inside) == True), np.where(np.array(inside) == False)) self.inside = inside # Log flight statistics when for deleted aircraft if len(delidx) > 0: self.logger.log( np.array(traf.id)[delidx], self.create_time[delidx], sim.simt - self.create_time[delidx], self.distance2D[delidx], self.distance3D[delidx], self.work[delidx], traf.lat[delidx], traf.lon[delidx], traf.alt[delidx], traf.tas[delidx], traf.vs[delidx], traf.hdg[delidx], # traf.ap.origlat[delidx], # traf.ap.origlon[delidx], # traf.ap.destlat[delidx], # traf.ap.destlon[delidx], traf.asas.active[delidx], traf.pilot.alt[delidx], traf.pilot.tas[delidx], traf.pilot.vs[delidx], traf.pilot.hdg[delidx]) # delete all aicraft in self.delidx for idx in delidx: traf.delete(idx)
def update(self): ''' Update flight efficiency metrics 2D and 3D distance [m], and work done (force*distance) [J] ''' if not self.active: return resultantspd = np.sqrt(traf.gs * traf.gs + traf.vs * traf.vs) self.distance2D += self.dt * traf.gs self.distance3D += self.dt * resultantspd if settings.performance_model == 'nap': self.work += (traf.perf.thrust * self.dt * resultantspd) else: self.work += (traf.perf.Thr * self.dt * resultantspd) # ToDo: Add autodelete for descending with swTaxi: if self.swtaxi: pass # To be added!!! # Find out which aircraft are currently inside the experiment area, and # determine which aircraft need to be deleted. inside = areafilter.checkInside(self.name, traf.lat, traf.lon, traf.alt) delidx = np.intersect1d(np.where(np.array(self.inside)==True), np.where(np.array(inside)==False)) self.inside = inside # Log flight statistics when for deleted aircraft if len(delidx) > 0: self.logger.log( np.array(traf.id)[delidx], self.create_time[delidx], sim.simt - self.create_time[delidx], self.distance2D[delidx], self.distance3D[delidx], self.work[delidx], traf.lat[delidx], traf.lon[delidx], traf.alt[delidx], traf.tas[delidx], traf.vs[delidx], traf.hdg[delidx], # traf.ap.origlat[delidx], # traf.ap.origlon[delidx], # traf.ap.destlat[delidx], # traf.ap.destlon[delidx], traf.asas.active[delidx], traf.pilot.alt[delidx], traf.pilot.tas[delidx], traf.pilot.vs[delidx], traf.pilot.hdg[delidx] ) # delete all aicraft in self.delidx for idx in delidx: traf.delete(idx)
def applygeovec(): # Apply each geovector for vec in geovecs: areaname = vec[0] if areafilter.hasArea(areaname): swinside = areafilter.checkInside(areaname, traf.lat, traf.lon, traf.alt) gsmin,gsmax,trkmin,trkmax,vsmin,vsmax = vec[1:] # -----Ground speed limiting # For now assume no wind: so use tas as gs if gsmin: casmin = vtas2cas(np.ones(traf.ntraf)*gsmin,traf.alt) usemin = traf.selspd<casmin traf.selspd[swinside & usemin] = casmin[swinside & usemin] if gsmax: casmax = vtas2cas(np.ones(traf.ntraf)*gsmax,traf.alt) usemax = traf.selspd > casmax traf.selspd[swinside & usemax] = casmax[swinside & usemax] #------ Limit Track(so hdg) # Max track interval is 180 degrees to avoid ambiguity of what is inside the interval if trkmin and trkmax: # Use degto180 to avodi problems for e.g interval [350,30] usemin = swinside & (degto180(traf.trk - trkmin)<0) # Left of minimum usemax = swinside & (degto180(traf.trk - trkmax)>0) # Right of maximum #print(usemin,usemax) traf.ap.trk[swinside & usemin] = trkmin traf.ap.trk[swinside & usemax] = trkmax # -----Ground speed limiting # For now assume no wind: so use tas as gs if vsmin: traf.selvs[swinside & (traf.vs<vsmin)] = vsmin # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs < vsmin)] = traf.alt[swinside & (traf.vs < vsmin)] + \ np.sign(vsmin)*200.*ft if vsmax: traf.selvs[swinside & (traf.vs > vsmax)] = vsmax # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs > vsmax)] = traf.alt[swinside & (traf.vs > vsmax)] + \ np.sign(vsmax)*200.*ft return
def get_limits(): ''' Get active geovector limits per aircraft (vectorized) ''' # Initialize arrays for active limit values per aircraft traf_gsmin = np.full(traf.ntraf, np.nan) traf_gsmax = np.full(traf.ntraf, np.nan) traf_trkmin = np.full(traf.ntraf, np.nan) traf_trkmax = np.full(traf.ntraf, np.nan) traf_vsmin = np.full(traf.ntraf, np.nan) traf_vsmax = np.full(traf.ntraf, np.nan) # Determine active limits for each aircraft (if any) for geoname, geovec in geovector.geovecs.items(): # Create arrays with limit values gsmin = np.full(traf.ntraf, geovec.gsmin if geovec.gsmin is not None else np.nan) gsmax = np.full(traf.ntraf, geovec.gsmax if geovec.gsmax is not None else np.nan) trkmin = np.full(traf.ntraf, geovec.trkmin if geovec.trkmin is not None else np.nan) trkmax = np.full(traf.ntraf, geovec.trkmax if geovec.trkmax is not None else np.nan) vsmin = np.full(traf.ntraf, geovec.vsmin if geovec.vsmin is not None else np.nan) vsmax = np.full(traf.ntraf, geovec.vsmax if geovec.vsmax is not None else np.nan) # Determine which aircraft are inside the current geovector swinside = checkInside(geoname, traf.lat, traf.lon, traf.alt) # Set limits for each aircraft # The limits depend on the location of each aircraft # If an aircraft is subject to multiple geovectors: the intersection remains traf_gsmin[swinside] = np.fmax(traf_gsmin[swinside], gsmin[swinside]) traf_gsmax[swinside] = np.fmin(traf_gsmax[swinside], gsmax[swinside]) traf_trkmin[swinside] = np.where(np.invert(degto180(trkmin[swinside] - traf_trkmin[swinside]) < 0),\ trkmin[swinside], traf_trkmin[swinside]) traf_trkmax[swinside] = np.where(np.invert(degto180(trkmax[swinside] - traf_trkmax[swinside]) > 0),\ trkmax[swinside], traf_trkmax[swinside]) traf_vsmin[swinside] = np.fmax(traf_vsmin[swinside], vsmin[swinside]) traf_vsmax[swinside] = np.fmin(traf_vsmax[swinside], vsmax[swinside]) # Check if the intersection of geovectors is empty (set of allowed velocity vectors is empty) empt = np.logical_or(traf_gsmin > traf_gsmax, traf_vsmin > traf_vsmax) # check if min spd limit > max spd limit empt = np.logical_or(empt, degto180(traf_trkmin - traf_trkmax) > 0) # check if min trk limit > max trk limit # For aircraft with empty intersection of geovectors, set limits to nan (hence ignore all limits) # In this case it is not clear which limits to adhere to traf_gsmin[empt] = np.nan traf_gsmax[empt] = np.nan traf_trkmin[empt] = np.nan traf_trkmax[empt] = np.nan traf_vsmin[empt] = np.nan traf_vsmax[empt] = np.nan return traf_gsmin, traf_gsmax, traf_trkmin, traf_trkmax, traf_vsmin, traf_vsmax
def update(self): if self.active: ''' Update inside log for delarea ''' # Determine which aircraft are currently inside the deletion area self.currinside = areafilter.checkInside(self.areaname, traf.lat, traf.lon, traf.alt) # Check which aircraft have left the deletion area exits = np.logical_and(self.previnside, np.logical_not(self.currinside)) # Delete aircraft which have left the deletion area if np.any(exits): traf.delete(np.arange(traf.ntraf)[exits]) # Update previously stored insids self.previnside = np.array(self.currinside)
def group(self, groupname='', *args): '''Add aircraft to group, list aircraft in group, or list existing groups.''' # Return list of groups if no groupname is given if not groupname: if not self.groups: return True, 'There are currently no traffic groups defined.' else: return True, 'Defined traffic groups:\n' + ', '.join( self.groups) if len(self.groups) >= 64: return False, 'Maximum number of 64 groups reached' if groupname not in self.groups: if not args: return False, 'Group {} doesn\'t exist'.format(groupname) # Get first unused group mask for i in range(64): groupmask = (1 << i) if not self.allmasks & groupmask: self.allmasks |= groupmask self.groups[groupname] = groupmask break elif not args: acnames = np.array(bs.traf.id)[self.listgroup(groupname)] return True, 'Aircraft in group {}:\n{}'.format( groupname, ', '.join(acnames)) # Add aircraft to group if areafilter.hasArea(args[0]): inside = areafilter.checkInside(args[0], bs.traf.lat, bs.traf.lon, bs.traf.alt) self.ingroup[inside] |= self.groups[groupname] acnames = np.array(bs.traf.id)[inside] else: idx = list(args) self.ingroup[idx] |= self.groups[groupname] acnames = np.array(bs.traf.id)[idx] return True, 'Aircraft added to group {}:\n{}'.format( groupname, ', '.join(acnames))
def check(self, t): # ToDo: Add autodelete for descending with swTaxi: if self.swtaxi: pass # To be added!!! # Update area once per areadt seconds: if self.active and abs(t - self.t0) > self.dt: self.t0 = t # Find out which aircraft are inside the experiment area inside = areafilter.checkInside(self.name, bs.traf.lat, bs.traf.lon, bs.traf.alt) # Determine the aircraft indexes that should be deleted delAircraftidx = np.intersect1d( np.where(np.array(self.inside) == True), np.where(np.array(inside) == False)) # Update self.inside with the new inside self.inside = inside # delete all aicraft in delAircraftidx and log their flight statistics for acid in [bs.traf.id[idx] for idx in delAircraftidx]: bs.traf.delete(acid)
def group(self, groupname='', *args): '''Add aircraft to group, list aircraft in group, or list existing groups.''' # Return list of groups if no groupname is given if not groupname: if not self.groups: return True, 'There are currently no traffic groups defined.' else: return True, 'Defined traffic groups:\n' + ', '.join(self.groups) if len(self.groups) >= 64: return False, 'Maximum number of 64 groups reached' if groupname not in self.groups: if not args: return False, 'Group {} doesn\'t exist'.format(groupname) # Get first unused group mask for i in range(64): groupmask = (1 << i) if not self.allmasks & groupmask: self.allmasks |= groupmask self.groups[groupname] = groupmask break elif not args: acnames = np.array(bs.traf.id)[self.listgroup(groupname)] return True, 'Aircraft in group {}:\n{}'.format(groupname, ', '.join(acnames)) # Add aircraft to group if areafilter.hasArea(args[0]): inside = areafilter.checkInside( args[0], bs.traf.lat, bs.traf.lon, bs.traf.alt) self.ingroup[inside] |= self.groups[groupname] acnames = np.array(bs.traf.id)[inside] else: idx = list(args) self.ingroup[idx] |= self.groups[groupname] acnames = np.array(bs.traf.id)[idx] return True, 'Aircraft added to group {}:\n{}'.format(groupname, ', '.join(acnames))
def update_geolog(self): ''' Write to geovector log ''' # Loop over all geovectors for geoname, geovec in geovector.geovecs.items(): try: self.previds[geoname + lims[0]] # If the geovector is newly created: except KeyError: # Create new previous ids item in dict for limname in lims: key = geoname + limname self.previds[key] = set() # Create new geovector item in dict self.geovecs[geoname] = np.array([geovec.gsmin, \ geovec.gsmax, geovec.trkmin, geovec.trkmax, geovec.vsmin, geovec.vsmax]) # Check aircraft inside geovector area geoinside = areafilter.checkInside(geoname, traf.lat, traf.lon, traf.alt) # Get gs breaches if geovec.gsmin is not None: gsl = geovec.gsmin - 1e-10 #prevent floating point errors swgsmin = traf.gs < gsl else: swgsmin = np.zeros(traf.ntraf, dtype=bool) if geovec.gsmax is not None: gsr = geovec.gsmax + 1e-10 #prevent floating point errors swgsmax = traf.gs > gsr else: swgsmax = np.zeros(traf.ntraf, dtype=bool) #Compute course breach values if None not in (geovec.trkmin, geovec.trkmax): swtrkmin = degto180(traf.trk - geovec.trkmin) < 0. swtrkmax = degto180(traf.trk - geovec.trkmax) > 0. else: swtrkmin = np.zeros(traf.ntraf, dtype=bool) swtrkmax = np.zeros(traf.ntraf, dtype=bool) # Get vs breaches if geovec.vsmin is not None: vsl = geovec.vsmin - 1e-10 #prevent floating point errors swvsmin = traf.vs < vsl else: swvsmin = np.zeros(traf.ntraf, dtype=bool) if geovec.vsmax is not None: vsr = geovec.vsmax + 1e-10 #prevent floating point errors swvsmax = traf.vs > vsr else: swvsmax = np.zeros(traf.ntraf, dtype=bool) # Update self.tcrb here to avoid having to check geovector breaches twice swbreaches = (swgsmin | swgsmax | swtrkmin | swtrkmax | swvsmin | swvsmax) & geoinside self.tcrb[self.currinside & traf.cr.active & swbreaches] += settings.simdt # Get id of all aircraft currently breaching the geovector inside the log area for lim in lims: if lim == "gsmin": curids = set( np.array(traf.id)[swgsmin & geoinside & self.currinside]) elif lim == "gsmax": curids = set( np.array(traf.id)[swgsmax & geoinside & self.currinside]) elif lim == "trkmin": curids = set( np.array(traf.id)[swtrkmin & geoinside & self.currinside]) elif lim == "trkmax": curids = set( np.array(traf.id)[swtrkmax & geoinside & self.currinside]) elif lim == "vsmin": curids = set( np.array(traf.id)[swvsmin & geoinside & self.currinside]) elif lim == "vsmax": curids = set( np.array(traf.id)[swvsmax & geoinside & self.currinside]) # Determine new and del ids newids = curids - self.previds[geoname + lim] delids = self.previds[geoname + lim] - curids # For new ids: create new breach object for id in newids: name = geoname + lim + id self.breaches[name] = self.Breach(id, geoname, lim, \ self.geovecs[geoname][lims.index(lim)]) # For del ids: send data and pop item for id in delids: name = geoname + lim + id val, avg, dur = self.breaches[name].send_data() self.geolog.log(id, geoname, lim, val, avg, dur) self.breaches.pop(name) # For curids: update value for id in curids: name = geoname + lim + id self.breaches[name].update_data() # Update previds self.previds[geoname + lim] = curids
def update(self, dt): ''' Update flight efficiency metrics 2D and 3D distance [m], and work done (force*distance) [J] ''' if self.active: resultantspd = np.sqrt(traf.gs * traf.gs + traf.vs * traf.vs) self.distance2D += dt * traf.gs self.distance3D += dt * resultantspd # Find out which aircraft are currently inside the experiment area, and # determine which aircraft need to be deleted. insdel = areafilter.checkInside(self.delarea, traf.lat, traf.lon, traf.alt) insexp = insdel if not self.exparea else \ areafilter.checkInside(self.exparea, traf.lat, traf.lon, traf.alt) # Find all aircraft that were inside in the previous timestep, but no # longer are in the current timestep delidx = np.where( np.array(self.insdel) * (np.array(insdel) == False))[0] self.insdel = insdel # Count new conflicts where at least one of the aircraft is inside # the experiment area # Store statistics for all new conflict pairs # Conflict pairs detected in the current timestep that were not yet present in the previous timestep confpairs_new = list(set(traf.cd.confpairs) - self.prevconfpairs) if confpairs_new: # If necessary: select conflict geometry parameters for new conflicts # idxdict = dict((v, i) for i, v in enumerate(traf.cd.confpairs)) # idxnew = [idxdict.get(i) for i in confpairs_new] # dcpa_new = np.asarray(traf.cd.dcpa)[idxnew] # tcpa_new = np.asarray(traf.cd.tcpa)[idxnew] # tLOS_new = np.asarray(traf.cd.tLOS)[idxnew] # qdr_new = np.asarray(traf.cd.qdr)[idxnew] # dist_new = np.asarray(traf.cd.dist)[idxnew] newconf_unique = {frozenset(pair) for pair in confpairs_new} ac1, ac2 = zip(*newconf_unique) idx1 = traf.id2idx(ac1) idx2 = traf.id2idx(ac2) newconf_inside = np.logical_or(insexp[idx1], insexp[idx2]) nnewconf_exp = np.count_nonzero(newconf_inside) if nnewconf_exp: self.confinside_all += nnewconf_exp self.conflog.log(self.confinside_all) self.prevconfpairs = set(traf.cd.confpairs) # Register distance values upon entry of experiment area newentries = np.logical_not(self.insexp) * insexp self.dstart2D[newentries] = self.distance2D[newentries] self.dstart3D[newentries] = self.distance3D[newentries] self.workstart[newentries] = traf.work[newentries] self.entrytime[newentries] = sim.simt # Log flight statistics when exiting experiment area exits = np.logical_and(self.insexp, np.logical_not(insexp)) # Update insexp self.insexp = insexp if np.any(exits): self.flst.log( np.array(traf.id)[exits], self.create_time[exits], sim.simt - self.entrytime[exits], (self.distance2D[exits] - self.dstart2D[exits]) / nm, (self.distance3D[exits] - self.dstart3D[exits]) / nm, (traf.work[exits] - self.workstart[exits]) * 1e-6, traf.lat[exits], traf.lon[exits], traf.alt[exits] / ft, traf.tas[exits] / kts, traf.vs[exits] / fpm, traf.hdg[exits], traf.cr.active[exits], traf.aporasas.alt[exits] / ft, traf.aporasas.tas[exits] / kts, traf.aporasas.vs[exits] / fpm, traf.aporasas.hdg[exits]) # delete all aicraft in self.delidx if len(delidx) > 0: traf.delete(delidx) # Autodelete for descending with swTaxi: if not self.swtaxi: delidxalt = np.where((self.oldalt >= self.swtaxialt) * (traf.alt < self.swtaxialt))[0] self.oldalt = traf.alt if len(delidxalt) > 0: traf.delete(list(delidxalt))
def calc_reward(n_ac_neighbours): global done, obs, crash_count # multi agents obs: lat, long, hdg, dist_wpt, hdg_wpt, dist_plane1, hdg_plane1, dist_plane2, hdg_plane2 (nm/deg) # This function calculates the "intrensic" reward as well as additional reward shaping # Constants to determine faulty states: # settings.los = 1.05 # nm # settings.wpt_reached = 5 # nm # settings.gamma = 0.99 # Match with trainer/implement in settings # Create reward dict depending on obs id's. reward = dict.fromkeys(obs.keys()) for agent_id in reward.keys(): # Initialize reward to 0. reward[agent_id] = 0 done[agent_id] = False # First check if goal area is reached if settings.destination_hdg: if obs[agent_id][4] <= settings.wpt_almost_reached: if areafilter.checkInside( area_list[np.int32(-obs[agent_id][0])], obs[agent_id][1], obs[agent_id][2], 10000 * ft): if check_hdg_constraint(-obs[agent_id][0], obs[agent_id][3]): reward[agent_id] += 1 done[agent_id] = True # else: # done[agent_id] = True # reward[agent_id] += -.49 # if settings.destination_hdg: # Determine approach hdg for destinations if wanted. # 1. EHAM: [1: 340-20] # [2: 70-110] # [3: 210-250] # # 2. EHGR/EHEH: [1: 260-300] # [2: 190-230] # # 3. EHDL: [1: 190-230] # [2: 10-40 ] # correct_hdg = check_hdg_constraint(obs[agent_id][0], obs[agent_id][1], obs[agent_id][4]) # if correct_hdg: # reward[agent_id] += 2 # done[agent_id] = True # # else: # reward[agent_id] += 1 # done[agent_id] = True elif not settings.destination_hdg: if obs[agent_id][4] <= settings.wpt_almost_reached: if areafilter.checkInside( area_list[np.int32(-obs[agent_id][0])], obs[agent_id][1], obs[agent_id][2], 10000 * ft): reward[agent_id] += 1 done[agent_id] = True # Check if there are any collisions dist_idx = np.arange(6, 6 + n_ac_neighbours * 2 - 1, 2) #len(obs[agent_id]) if any(obs[agent_id][dist_idx] <= settings.los): reward[agent_id] += -1 done[agent_id] = True crash_count += 1 if obs[agent_id][4] >= 180: reward[agent_id] += -0.5 done[agent_id] = True # Implement reward shaping: # F = settings.gamma * (100 / obs[agent_id][3]) - (100 / prev_obs[agent_id][3]) # Final reward # reward[agent_id] += F # temp = info[agent_id]['sequence'] # info[agent_id] = {'sequence': temp, 'metrics': [landed_bool, semi_landed_bool, crashed_bool]} return reward, done
def update(self): self.sectorsd = np.zeros(len(self.sectors)) self.sectorconv = np.zeros(len(self.sectors)) self.sectoreff = [] if not traf.ntraf or not self.sectors: return # Check convergence using CD with large RPZ and tlook confpairs, lospairs, inconf, tcpamax, qdr, dist, dcpa, tcpa, tLOS = \ traf.asas.cd.detect(traf, traf, 20 * nm, traf.asas.dh, 3600) if confpairs: own, intr = zip(*confpairs) ownidx = traf.id2idx(own) mask = traf.alt[ownidx] > 70 * ft ownidx = np.array(ownidx)[mask] dcpa = np.array(dcpa)[mask] tcpa = np.array(tcpa)[mask] else: ownidx = np.array([]) sendeff = False for idx, (sector, previnside) in enumerate(zip(self.sectors, self.acinside)): inside = areafilter.checkInside(sector, traf.lat, traf.lon, traf.alt) sectoreff = [] # Detect aircraft leaving and entering the sector previds = set(previnside.acid) ids = set(np.array(traf.id)[inside]) arrived = list(ids - previds) left = previds - ids # Split left aircraft in deleted and not deleted left_intraf = left.intersection(traf.id) left_del = list(left - left_intraf) left_intraf = list(left_intraf) arridx = traf.id2idx(arrived) leftidx = traf.id2idx(left_intraf) # Retrieve the current distance flown for arriving and leaving aircraft arrdist = traf.distflown[arridx] arrlat = traf.lat[arridx] arrlon = traf.lon[arridx] leftlat, leftlon, leftdist = self.delac.get(left_del) leftlat = np.append(leftlat, traf.lat[leftidx]) leftlon = np.append(leftlon, traf.lon[leftidx]) leftdist = np.append(leftdist, traf.distflown[leftidx]) leftlat0, leftlon0, leftdist0 = previnside.get(left_del + left_intraf) self.delac.delete(left_del) if len(left) > 0: q, d = geo.qdrdist(leftlat0, leftlon0, leftlat, leftlon) # Exclude aircraft where origin = destination mask = d > 10 sectoreff = list((leftdist[mask] - leftdist0[mask]) / d[mask] / nm) names = np.array(left_del + left_intraf)[mask] for name, eff in zip(names, sectoreff): self.feff.write('{}, {}, {}\n'.format(sim.simt, name, eff)) sendeff = True # print('{} aircraft left sector {}, distance flown (acid:dist):'.format(len(left), sector)) # for a, d0, d1, e in zip(left, leftdist0, leftdist, sectoreff): # print('Aircraft {} flew {} meters (eff = {})'.format(a, round(d1-d0), e)) # Update inside data for this sector previnside.delete(left) previnside.extend(arrived, arrlat, arrlon, arrdist) self.sectoreff.append(sectoreff) self.sectorsd[idx] = np.count_nonzero(inside) insidx = np.where(np.logical_and(inside, inconf)) pairsinside = np.isin(ownidx, insidx) if len(pairsinside): tnorm = np.array(tcpa)[pairsinside] / 300.0 dcpanorm = np.array(dcpa)[pairsinside] / (5.0 * nm) self.sectorconv[idx] = np.sum( np.sqrt(2.0 / tnorm * tnorm + dcpanorm * dcpanorm)) else: self.sectorconv[idx] = 0 self.fconv.write('{}, {}\n'.format(sim.simt, self.sectorconv[idx])) self.fsd.write('{}, {}\n'.format(sim.simt, self.sectorsd[idx])) if sendeff: self.effplot.send()
def update(self, dt): ''' Update flight efficiency metrics 2D and 3D distance [m], and work done (force*distance) [J] ''' if self.active: resultantspd = np.sqrt(traf.gs * traf.gs + traf.vs * traf.vs) self.distance2D += dt * traf.gs self.distance3D += dt * resultantspd self.work += (traf.perf.thrust * dt * resultantspd) # Find out which aircraft are currently inside the experiment area, and # determine which aircraft need to be deleted. insdel = areafilter.checkInside(self.delarea, traf.lat, traf.lon, traf.alt) insexp = insdel if not self.exparea else \ areafilter.checkInside(self.exparea, traf.lat, traf.lon, traf.alt) # Find all aircraft that were inside in the previous timestep, but no # longer are in the current timestep delidx = np.where( np.array(self.insdel) * (np.array(insdel) == False))[0] self.insdel = insdel # Count new conflicts where at least one of the aircraft is inside # the experiment area if traf.asas.confpairs_new: newconf_unique = { frozenset(pair) for pair in traf.asas.confpairs_new } ac1, ac2 = zip(*newconf_unique) idx1 = traf.id2idx(ac1) idx2 = traf.id2idx(ac2) newconf_inside = np.logical_or(insexp[idx1], insexp[idx2]) nnewconf_exp = np.count_nonzero(newconf_inside) if nnewconf_exp: self.confinside_all += nnewconf_exp self.conflog.log(self.confinside_all) # Register distance values upon entry of experiment area newentries = np.logical_not(self.insexp) * insexp self.dstart2D[newentries] = self.distance2D[newentries] self.dstart3D[newentries] = self.distance3D[newentries] self.workstart[newentries] = self.work[newentries] self.entrytime[newentries] = sim.simt # Log flight statistics when exiting experiment area exits = self.insexp * np.logical_not(insexp) if np.any(exits): self.flst.log( np.array(traf.id)[exits], self.create_time[exits], sim.simt - self.entrytime[exits], self.dstart2D[exits] - self.distance2D[exits], self.dstart3D[exits] - self.distance3D[exits], self.workstart[exits] - self.work[exits], traf.lat[exits], traf.lon[exits], traf.alt[exits], traf.tas[exits], traf.vs[exits], traf.hdg[exits], traf.asas.active[exits], traf.pilot.alt[exits], traf.pilot.tas[exits], traf.pilot.vs[exits], traf.pilot.hdg[exits]) # delete all aicraft in self.delidx if len(delidx) > 0: traf.delete(delidx) # Autodelete for descending with swTaxi: if not self.swtaxi: delidxalt = np.where((self.oldalt >= self.swtaxialt) * (traf.alt < self.swtaxialt))[0] self.oldalt = traf.alt if len(delidxalt) > 0: traf.delete(list(delidxalt))
def applygeovec(): # Apply each geovector for areaname, vec in geovecs.items(): if areafilter.hasArea(areaname): swinside = areafilter.checkInside(areaname, traf.lat, traf.lon, traf.alt) insids = set(np.array(traf.id)[swinside]) newids = insids - vec.previnside delids = vec.previnside - insids # Store LNAV/VNAV status of new aircraft for acid in newids: idx = traf.id2idx(acid) vec.prevstatus[acid] = [traf.swlnav[idx], traf.swvnav[idx]] # Revert aircraft who have exited the geovectored area to their original status for acid in delids: idx = traf.id2idx(acid) if idx >= 0: traf.swlnav[idx], traf.swvnav[idx] = vec.prevstatus.pop( acid) vec.previnside = insids # -----Ground speed limiting # For now assume no wind: so use tas as gs if vec.gsmin is not None: casmin = vtas2cas(np.ones(traf.ntraf) * vec.gsmin, traf.alt) usemin = traf.selspd < casmin traf.selspd[swinside & usemin] = casmin[swinside & usemin] traf.swvnav[swinside & usemin] = False if vec.gsmax is not None: casmax = vtas2cas(np.ones(traf.ntraf) * vec.gsmax, traf.alt) usemax = traf.selspd > casmax traf.selspd[swinside & usemax] = casmax[swinside & usemax] traf.swvnav[swinside & usemax] = False #------ Limit Track(so hdg) # Max track interval is 180 degrees to avoid ambiguity of what is inside the interval if None not in [vec.trkmin, vec.trkmax]: # Use degto180 to avodi problems for e.g interval [350,30] usemin = swinside & (degto180(traf.trk - vec.trkmin) < 0 ) # Left of minimum usemax = swinside & (degto180(traf.trk - vec.trkmax) > 0 ) # Right of maximum traf.swlnav[swinside & (usemin | usemax)] = False traf.ap.trk[swinside & usemin] = vec.trkmin traf.ap.trk[swinside & usemax] = vec.trkmax # -----Ground speed limiting # For now assume no wind: so use tas as gs if vec.vsmin is not None: traf.selvs[swinside & (traf.vs < vec.vsmin)] = vec.vsmin traf.swvnav[swinside & (traf.vs < vec.vsmin)] = False # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs < vec.vsmin)] = traf.alt[swinside & (traf.vs < vec.vsmin)] + \ np.sign(vec.vsmin)*200.*ft if vec.vsmax is not None: traf.selvs[swinside & (traf.vs > vec.vsmax)] = vec.vsmax traf.swvnav[swinside & (traf.vs < vec.vsmax)] = False # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs > vec.vsmax)] = traf.alt[swinside & (traf.vs > vec.vsmax)] + \ np.sign(vec.vsmax)*200.*ft return
def update(self): self.sectorsd = np.zeros(len(self.sectors)) self.sectorconv = np.zeros(len(self.sectors)) self.sectoreff = [] if not traf.ntraf or not self.sectors: return # Check convergence using CD with large RPZ and tlook confpairs, lospairs, inconf, tcpamax, qdr, dist, dcpa, tcpa, tLOS = \ traf.asas.cd.detect(traf, traf, 20 * nm, traf.asas.dh, 3600) if confpairs: own, intr = zip(*confpairs) ownidx = traf.id2idx(own) mask = traf.alt[ownidx] > 70 * ft ownidx = np.array(ownidx)[mask] dcpa = np.array(dcpa)[mask] tcpa = np.array(tcpa)[mask] else: ownidx = np.array([]) sendeff = False for idx, (sector, previnside) in enumerate(zip(self.sectors, self.acinside)): inside = areafilter.checkInside(sector, traf.lat, traf.lon, traf.alt) sectoreff = [] # Detect aircraft leaving and entering the sector previds = set(previnside.acid) ids = set(np.array(traf.id)[inside]) arrived = list(ids - previds) left = previds - ids # Split left aircraft in deleted and not deleted left_intraf = left.intersection(traf.id) left_del = list(left - left_intraf) left_intraf = list(left_intraf) arridx = traf.id2idx(arrived) leftidx = traf.id2idx(left_intraf) # Retrieve the current distance flown for arriving and leaving aircraft arrdist = traf.distflown[arridx] arrlat = traf.lat[arridx] arrlon = traf.lon[arridx] leftlat, leftlon, leftdist = self.delac.get(left_del) leftlat = np.append(leftlat, traf.lat[leftidx]) leftlon = np.append(leftlon, traf.lon[leftidx]) leftdist = np.append(leftdist, traf.distflown[leftidx]) leftlat0, leftlon0, leftdist0 = previnside.get(left_del + left_intraf) self.delac.delete(left_del) if len(left) > 0: q, d = geo.qdrdist(leftlat0, leftlon0, leftlat, leftlon) # Exclude aircraft where origin = destination mask = d > 10 sectoreff = list( (leftdist[mask] - leftdist0[mask]) / d[mask] / nm) names = np.array(left_del + left_intraf)[mask] for name, eff in zip(names, sectoreff): self.feff.write('{}, {}, {}\n'.format(sim.simt, name, eff)) sendeff = True # print('{} aircraft left sector {}, distance flown (acid:dist):'.format(len(left), sector)) # for a, d0, d1, e in zip(left, leftdist0, leftdist, sectoreff): # print('Aircraft {} flew {} meters (eff = {})'.format(a, round(d1-d0), e)) # Update inside data for this sector previnside.delete(left) previnside.extend(arrived, arrlat, arrlon, arrdist) self.sectoreff.append(sectoreff) self.sectorsd[idx] = np.count_nonzero(inside) insidx = np.where(np.logical_and(inside, inconf)) pairsinside = np.isin(ownidx, insidx) if len(pairsinside): tnorm = np.array(tcpa)[pairsinside] / 300.0 dcpanorm = np.array(dcpa)[pairsinside] / (5.0 * nm) self.sectorconv[idx] = np.sum( np.sqrt(2.0 / tnorm * tnorm + dcpanorm * dcpanorm)) else: self.sectorconv[idx] = 0 self.fconv.write('{}, {}\n'.format(sim.simt, self.sectorconv[idx])) self.fsd.write('{}, {}\n'.format(sim.simt, self.sectorsd[idx])) if sendeff: self.effplot.send()
def update(self, dt): """ Log all desired data if AREA is active. """ if self.active: total_spd = np.sqrt(traf.gs * traf.gs + traf.vs * traf.vs) self.distance2D += dt * traf.gs self.distance3D += dt * total_spd # Check whether all aircraft are currently inside the experiment area. inside_exp = areafilter.checkInside(self.exp_area, traf.lat, traf.lon, traf.alt) if not np.all(inside_exp): raise RuntimeError('An aircraft escaped the experiment area!') # Check if mean speed in scenario below threshold. if self.stable and traf.ntraf >= 5 and np.mean(traf.gs) < 2.: # Turn reso OFF and unset stable flag, to let this scenario be able to finish. print( f'WARNING! Simulation stuck.\n' f'Mean speed of simulation reached {np.mean(traf.gs):.2f}m/s\n' f'Setting stable flag to False and Turning RESO OFF for the rest of this scenario' ) stack.stack('RESO OFF') self.stable = False # Check for arrived flights. # Upon reaching destination, autopilot switches off the LNAV. arrived = ~traf.swlnav # Count new conflicts and losses of separation. # Each conflict pair is only counted once in the entire simulation. # Store statistics for all new conflict pairs. confpairs_new = traf.cd.confpairs_unique - self.prevconfpairs lospairs_new = traf.cd.lospairs_unique - self.prevlospairs # Ignore conflicts and losses for descending or arrived aircraft. ignore_confpairs = set() ignore_lospairs = set() for pair in traf.cd.confpairs_unique: for ac in pair: try: idx = traf.id.index(ac) if traf.vs[idx] < -1E-4 or arrived[idx]: ignore_confpairs.add(pair) except ValueError: # Aircraft is already deleted. ignore_confpairs.add(pair) for pair in traf.cd.lospairs_unique: # Do not count LoS'ses as conflicts. ignore_confpairs.add(pair) for ac in pair: try: idx = traf.id.index(ac) if traf.vs[idx] < -1E-4 or arrived[idx]: ignore_lospairs.add(pair) except ValueError: # Aircraft is already deleted. ignore_lospairs.add(pair) confpairs_new -= ignore_confpairs lospairs_new -= ignore_lospairs # if lospairs_new: # # Use this for debugging reso algo. # print('LoS detected:', lospairs_new) # print() # # if confpairs_new: # # Use this for debugging reso algo. # print('Conflict detected:', confpairs_new) # print() self.ntotal_conf += len(confpairs_new) self.ntotal_los += len(lospairs_new) self.conf_log.log( traf.ntraf, len(traf.cd.confpairs_unique) - len(ignore_confpairs), len(traf.cd.lospairs_unique) - len(ignore_lospairs), self.ntotal_ac, self.ntotal_conf, self.ntotal_los, bool(traf.cr.do_cr), self.stable) # Log distance values upon entry of experiment area (includes spawning aircraft). newentries = np.logical_not(self.inside_exp) * inside_exp self.dstart2D[newentries] = self.distance2D[newentries] self.dstart3D[newentries] = self.distance3D[newentries] self.workstart[newentries] = traf.work[newentries] self.entrytime[newentries] = sim.simt # Update values for next loop. self.inside_exp = inside_exp self.prevconfpairs.update(confpairs_new) self.prevlospairs.update(lospairs_new) # Log flight statistics when reaching destination and delete aircraft. del_idx = np.flatnonzero(arrived) self.flst_log.log( np.array(traf.id)[del_idx], self.create_time[del_idx], sim.simt - self.entrytime[del_idx], (self.distance2D[del_idx] - self.dstart2D[del_idx]), (self.distance3D[del_idx] - self.dstart3D[del_idx]), (traf.work[del_idx] - self.workstart[del_idx]) * 1e-6, traf.lat[del_idx], traf.lon[del_idx], traf.alt[del_idx] / ft, traf.tas[del_idx], traf.vs[del_idx] / fpm, traf.hdg[del_idx], traf.cr.active[del_idx], traf.aporasas.alt[del_idx] / ft, traf.aporasas.tas[del_idx], traf.aporasas.hdg[del_idx], traf.aporasas.vs[del_idx] / fpm, traf.cr.do_cr, self.stable) traf.delete(del_idx)