Ejemplo n.º 1
0
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))
Ejemplo n.º 2
0
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))
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
    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)
Ejemplo n.º 14
0
    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))
Ejemplo n.º 15
0
    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)
Ejemplo n.º 16
0
    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))
Ejemplo n.º 17
0
    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
Ejemplo n.º 18
0
    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))
Ejemplo n.º 19
0
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
Ejemplo n.º 20
0
    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()
Ejemplo n.º 21
0
    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))
Ejemplo n.º 22
0
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
Ejemplo n.º 23
0
    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()
Ejemplo n.º 24
0
    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)