Exemple #1
0
    def act5(self):
        # Compute 15 degree turn left waypoint.
        # Use current speed to compute waypoint.
        dqdr = 15
        latA = traf.lat[self.acidx]
        lonA = traf.lon[self.acidx]
        turnrad = traf.tas[self.acidx]**2 / (np.maximum(0.01, np.tan(traf.bank[self.acidx])) * g0) # [m]

        #Turn right so add bearing
#        qdr = traf.qdr[self.acidx] + 90

        latR, lonR = qdrpos(latA, lonA, traf.hdg[self.acidx] - 90, turnrad/nm) # [deg, deg]
        # Rotate vector
        latB, lonB = qdrpos(latR, lonR, traf.hdg[self.acidx] + 90 - dqdr, turnrad/nm) # [deg, deg]
        cmd = "{} BEFORE {} ADDWPT '{},{}'".format(traf.id[self.acidx], traf.ap.route[0].wpname[-2], latB, lonB)
        stack.stack(cmd)
Exemple #2
0
    def arguments(numargs, cmdargs):
        syntaxerror = False
        # tunables:
        acalt = float(10000)  # default
        acspd = float(300)  # default
        actype = "B747"  # default
        startdistance = 1  # default

        ang = float(cmdargs[1]) / 2

        if numargs > 2:  #process optional arguments
            for i in range(
                    2, numargs
            ):  # loop over arguments (TODO: put arguments in np array)
                if cmdargs[i].upper().startswith("-R"):  #radius
                    startdistance = geo.qdrpos(0, 0, 90, float(
                        cmdargs[i][3:]))[2]  #input in nm
                elif cmdargs[i].upper().startswith("-A"):  #altitude
                    acalt = txt2alt(cmdargs[i][3:]) * ft
                elif cmdargs[i].upper().startswith("-S"):  #speed
                    acspd = txt2spd(cmdargs[i][3:], acalt)
                elif cmdargs[i].upper().startswith("-T"):  #ac type
                    actype = cmdargs[i][3:].upper()
                else:
                    syntaxerror = True
        return syntaxerror, acalt, acspd, actype, startdistance, ang
Exemple #3
0
    def creconfs(self,
                 acid,
                 actype,
                 targetidx,
                 dpsi,
                 cpa,
                 tlosh,
                 dH=None,
                 tlosv=None,
                 spd=None):
        latref = self.lat[targetidx]  # deg
        lonref = self.lon[targetidx]  # deg
        altref = self.alt[targetidx]  # m
        trkref = radians(self.trk[targetidx])
        gsref = self.gs[targetidx]  # m/s
        vsref = self.vs[targetidx]  # m/s
        cpa = cpa * nm
        pzr = settings.asas_pzr * nm
        pzh = settings.asas_pzh * ft

        trk = trkref + radians(dpsi)
        gs = gsref if spd is None else spd
        if dH is None:
            acalt = altref
            acvs = 0.0
        else:
            acalt = altref + dH
            tlosv = tlosh if tlosv is None else tlosv
            acvs = vsref - np.sign(dH) * (abs(dH) - pzh) / tlosv

        # Horizontal relative velocity vector
        gsn, gse = gs * cos(trk), gs * sin(trk)
        vreln, vrele = gsref * cos(trkref) - gsn, gsref * sin(trkref) - gse
        # Relative velocity magnitude
        vrel = sqrt(vreln * vreln + vrele * vrele)
        # Relative travel distance to closest point of approach
        drelcpa = tlosh * vrel + (0 if cpa > pzr else sqrt(pzr * pzr -
                                                           cpa * cpa))
        # Initial intruder distance
        dist = sqrt(drelcpa * drelcpa + cpa * cpa)
        # Rotation matrix diagonal and cross elements for distance vector
        rd = drelcpa / dist
        rx = cpa / dist
        # Rotate relative velocity vector to obtain intruder bearing
        brn = degrees(atan2(-rx * vreln + rd * vrele, rd * vreln + rx * vrele))

        # Calculate intruder lat/lon
        aclat, aclon = geo.qdrpos(latref, lonref, brn, dist / nm)

        # convert groundspeed to CAS, and track to heading
        wn, we = self.wind.getdata(aclat, aclon, acalt)
        tasn, tase = gsn - wn, gse - we
        acspd = vtas2cas(sqrt(tasn * tasn + tase * tase), acalt)
        achdg = degrees(atan2(tase, tasn))

        # Create and, when necessary, set vertical speed
        self.create(1, actype, acalt, acspd, None, aclat, aclon, achdg, acid)
        self.ap.selaltcmd(len(self.lat) - 1, altref, acvs)
        self.vs[-1] = acvs
Exemple #4
0
    def act4(self):
        # Compute 15 degree turn left waypoint.
        # Use current speed to compute waypoint.

        latA = traf.lat[self.acidx]
        lonA = traf.lon[self.acidx]

        latB, lonB = qdrpos(latA, lonA, traf.hdg[self.acidx], 0.25)
        cmd = "{} BEFORE {} ADDWPT '{},{}'".format(traf.id[self.acidx], traf.ap.route[0].wpname[-2], latB, lonB)
        stack.stack(cmd)
Exemple #5
0
def ilsgate(rwyname):
    if '/' not in rwyname:
        return False, 'Argument is not a runway ' + rwyname
    apt, rwy = rwyname.split('/RW')
    rwy = rwy.lstrip('Y')
    apt_thresholds = navdb.rwythresholds.get(apt)
    if not apt_thresholds:
        return False, 'Argument is not a runway (airport not found) ' + apt
    rwy_threshold = apt_thresholds.get(rwy)
    if not rwy_threshold:
        return False, 'Argument is not a runway (runway not found) ' + rwy
    # Extract runway threshold lat/lon, and runway heading
    lat, lon, hdg = rwy_threshold

    # The ILS gate is defined as a triangular area pointed away from the runway
    # First calculate the two far corners in cartesian coordinates
    cone_length = 50 # nautical miles
    cone_angle  = 20.0 # degrees
    lat1, lon1 = geo.qdrpos(lat, lon, hdg - 180.0 + cone_angle, cone_length)
    lat2, lon2 = geo.qdrpos(lat, lon, hdg - 180.0 - cone_angle, cone_length)
    coordinates = np.array([lat, lon, lat1, lon1, lat2, lon2])
    areafilter.defineArea('ILS' + rwyname, 'POLYALT', coordinates, top=4000*ft)
Exemple #6
0
    def creconfs(self, acid, actype, targetidx, dpsi, cpa, tlosh, dH=None, tlosv=None, spd=None):
        latref  = self.lat[targetidx]  # deg
        lonref  = self.lon[targetidx]  # deg
        altref  = self.alt[targetidx]  # m
        trkref  = radians(self.trk[targetidx])
        gsref   = self.gs[targetidx]   # m/s
        vsref   = self.vs[targetidx]   # m/s
        cpa     = cpa * nm
        pzr     = settings.asas_pzr * nm
        pzh     = settings.asas_pzh * ft

        trk     = trkref + radians(dpsi)
        gs      = gsref if spd is None else spd
        if dH is None:
            acalt = altref
            acvs  = 0.0
        else:
            acalt = altref + dH
            tlosv = tlosh if tlosv is None else tlosv
            acvs  = vsref - np.sign(dH) * (abs(dH) - pzh) / tlosv

        # Horizontal relative velocity vector
        gsn, gse     = gs    * cos(trk),          gs    * sin(trk)
        vreln, vrele = gsref * cos(trkref) - gsn, gsref * sin(trkref) - gse
        # Relative velocity magnitude
        vrel    = sqrt(vreln * vreln + vrele * vrele)
        # Relative travel distance to closest point of approach
        drelcpa = tlosh * vrel + (0 if cpa > pzr else sqrt(pzr * pzr - cpa * cpa))
        # Initial intruder distance
        dist    = sqrt(drelcpa * drelcpa + cpa * cpa)
        # Rotation matrix diagonal and cross elements for distance vector
        rd      = drelcpa / dist
        rx      = cpa / dist
        # Rotate relative velocity vector to obtain intruder bearing
        brn     = degrees(atan2(-rx * vreln + rd * vrele,
                                 rd * vreln + rx * vrele))

        # Calculate intruder lat/lon
        aclat, aclon = geo.qdrpos(latref, lonref, brn, dist / nm)

        # convert groundspeed to CAS, and track to heading
        wn, we     = self.wind.getdata(aclat, aclon, acalt)
        tasn, tase = gsn - wn, gse - we
        acspd      = vtas2cas(sqrt(tasn * tasn + tase * tase), acalt)
        achdg      = degrees(atan2(tase, tasn))

        # Create and, when necessary, set vertical speed
        self.create(1, actype, acalt, acspd, None, aclat, aclon, achdg, acid)
        self.ap.selaltcmd(len(self.lat) - 1, altref, acvs)
        self.vs[-1] = acvs
Exemple #7
0
def calc_turn_wp(delta_qdr, acidx):
    latA = traf.lat[acidx]
    lonA = traf.lon[acidx]
    turnrad = traf.tas[acidx]**2 / (
        np.maximum(0.01, np.tan(traf.bank[acidx])) * g0)  # [m]

    # Turn left
    if delta_qdr > 0:
        # Compute centre of turning circle
        latR, lonR = qdrpos(latA, lonA, traf.hdg[acidx] + 90,
                            turnrad / nm)  # [deg, deg]
        # Rotate vector position vector along turning circle
        latB, lonB = qdrpos(latR, lonR, traf.hdg[acidx] - 90 + abs(delta_qdr),
                            turnrad / nm)  # [deg, deg]

    else:
        # Compute centre of turning circle
        latR, lonR = qdrpos(latA, lonA, traf.hdg[acidx] - 90,
                            turnrad / nm)  # [deg, deg]
        # Rotate vector
        latB, lonB = qdrpos(latR, lonR, traf.hdg[acidx] + 90 - abs(delta_qdr),
                            turnrad / nm)  # [deg, deg]
    return latB, lonB
Exemple #8
0
    def arguments(numargs, cmdargs):
        syntaxerror = False
        # tunables:
        acalt = float(10000) # default
        acspd = float(300) # default
        actype = "B747" # default
        startdistance = 1 # default

        ang = float(cmdargs[1])/2

        if numargs>2:   #process optional arguments
            for i in range(2 ,numargs): # loop over arguments (TODO: put arguments in np array)
                if cmdargs[i].upper().startswith("-R"): #radius
                    startdistance = geo.qdrpos(0,0,90,float(cmdargs[i][3:]))[2] #input in nm
                elif cmdargs[i].upper().startswith("-A"): #altitude
                    acalt = txt2alt(cmdargs[i][3:])*ft
                elif cmdargs[i].upper().startswith("-S"): #speed
                    acspd = txt2spd(cmdargs[i][3:],acalt)
                elif cmdargs[i].upper().startswith("-T"): #ac type
                    actype = cmdargs[i][3:].upper()
                else:
                    syntaxerror = True
        return syntaxerror,acalt,acspd,actype,startdistance,ang
Exemple #9
0
    def addwptStack(self, idx, *args):  # args: all arguments of addwpt
        """ADDWPT acid, (wpname/lat,lon),[alt],[spd],[afterwp],[beforewp]"""

        #        print "addwptStack:",args
        # Check FLYBY or FLYOVER switch, instead of adding a waypoint

        if len(args) == 1:
            swwpmode = args[0].replace('-', '')

            if swwpmode == "FLYBY":
                self.swflyby = True
                self.swflyturn = False
                return True

            elif swwpmode == "FLYOVER":

                self.swflyby = False
                self.swflyturn = False
                return True

            elif swwpmode == "FLYTURN":

                self.swflyby = False
                self.swflyturn = True
                return True

        elif len(args) == 2:

            swwpmode = args[0].replace('-', '')

            if swwpmode == "TURNRAD" or swwpmode == "TURNRADIUS":

                try:
                    self.turnrad = float(
                        args[1]) / ft  # arg was originally parsed as wpalt
                except:
                    return False, "Error in processing value of turn radius"
                return True

            elif swwpmode == "TURNSPD" or swwpmode == "TURNSPEED":

                try:
                    self.turnspd = args[
                        1] * kts / ft  # [m/s] Arg was wpalt Keep it as IAS/CAS orig in kts, now in m/s
                except:
                    return False, "Error in processing value of turn speed"

                return True

        # Convert to positions
        name = args[0].upper().strip()

        # Choose reference position ot look up VOR and waypoints
        # First waypoint: own position
        if self.nwp == 0:
            reflat = bs.traf.lat[idx]
            reflon = bs.traf.lon[idx]

        # Or last waypoint before destination
        else:
            if self.wptype[-1] != Route.dest or self.nwp == 1:
                reflat = self.wplat[-1]
                reflon = self.wplon[-1]
            else:
                reflat = self.wplat[-2]
                reflon = self.wplon[-2]

        # Default altitude, speed and afterwp
        alt = -999.
        spd = -999.
        afterwp = ""
        beforewp = ""

        # Is it aspecial take-off waypoint?
        takeoffwpt = name.replace('-', '') == "TAKEOFF"

        # Normal waypoint (no take-off waypoint => see else)
        if not takeoffwpt:

            # Get waypoint position
            success, posobj = txt2pos(name, reflat, reflon)
            if success:
                lat = posobj.lat
                lon = posobj.lon

                if posobj.type == "nav" or posobj.type == "apt":
                    wptype = Route.wpnav

                elif posobj.type == "rwy":
                    wptype = Route.runway

                else:  # treat as lat/lon
                    name = bs.traf.id[idx]
                    wptype = Route.wplatlon

                if len(args) > 1 and args[1]:
                    alt = args[1]

                if len(args) > 2 and args[2]:
                    spd = args[2]

                if len(args) > 3 and args[3]:
                    afterwp = args[3]

                if len(args) > 4 and args[4]:
                    beforewp = args[4]

            else:
                return False, "Waypoint " + name + " not found."

        # Take off waypoint: positioned 20% of the runway length after the runway
        else:

            # Look up runway in route
            rwyrteidx = -1
            i = 0
            while i < self.nwp and rwyrteidx < 0:
                if self.wpname[i].count("/") > 0:
                    #                   print (self.wpname[i])
                    rwyrteidx = i
                i += 1

            # Only TAKEOFF is specified wihtou a waypoint/runway
            if len(args) == 1 or not args[1]:
                # No runway given: use first in route or current position

                # print ("rwyrteidx =",rwyrteidx)
                # We find a runway in the route, so use it
                if rwyrteidx > 0:
                    rwylat = self.wplat[rwyrteidx]
                    rwylon = self.wplon[rwyrteidx]
                    aptidx = bs.navdb.getapinear(rwylat, rwylon)
                    aptname = bs.navdb.aptname[aptidx]

                    rwyname = self.wpname[rwyrteidx].split("/")[1]
                    rwyid = rwyname.replace("RWY", "").replace("RW", "")
                    rwyhdg = bs.navdb.rwythresholds[aptname][rwyid][2]

                else:
                    rwylat = bs.traf.lat[idx]
                    rwylon = bs.traf.lon[idx]
                    rwyhdg = bs.traf.trk[idx]

            elif args[1].count(
                    "/") > 0 or len(args) > 2 and args[2]:  # we need apt,rwy
                # Take care of both EHAM/RW06 as well as EHAM,RWY18L (so /&, and RW/RWY)
                if args[1].count("/") > 0:
                    aptid, rwyname = args[1].split("/")
                else:
                    # Runway specified
                    aptid = args[1]
                    rwyname = args[2]

                rwyid = rwyname.replace("RWY",
                                        "").replace("RW",
                                                    "")  # take away RW or RWY
                #                    print ("apt,rwy=",aptid,rwyid)
                # TODO: Add finding the runway heading with rwyrteidx>0 and navdb!!!
                # Try to get it from the database
                try:
                    rwyhdg = bs.navdb.rwythresholds[aptid][rwyid][2]
                except:
                    rwydir = rwyid.replace("L",
                                           "").replace("R",
                                                       "").replace("C", "")
                    try:
                        rwyhdg = float(rwydir) * 10.
                    except:
                        return False, name + " not found."

                success, posobj = txt2pos(aptid + "/RW" + rwyid, reflat,
                                          reflon)
                if success:
                    rwylat, rwylon = posobj.lat, posobj.lon
                else:
                    rwylat = bs.traf.lat[idx]
                    rwylon = bs.traf.lon[idx]

            else:
                return False, "Use ADDWPT TAKEOFF,AIRPORTID,RWYNAME"

            # Create a waypoint 2 nm away from current point
            rwydist = 2.0  # [nm] use default distance away from threshold
            lat, lon = geo.qdrpos(rwylat, rwylon, rwyhdg, rwydist)  #[deg,deg
            wptype = Route.wplatlon

            # Add after the runwy in the route
            if rwyrteidx > 0:
                afterwp = self.wpname[rwyrteidx]

            elif self.wptype and self.wptype[0] == Route.orig:
                afterwp = self.wpname[0]

            else:
                # Assume we're called before other waypoints are added
                afterwp = ""

            name = "T/O-" + bs.traf.id[idx]  # Use lat/lon naming convention
        # Add waypoint
        wpidx = self.addwpt(idx, name, wptype, lat, lon, alt, spd, afterwp,
                            beforewp)

        # Recalculate flight plan
        self.calcfp()

        # Check for success by checking insetred locaiton in flight plan >= 0
        if wpidx < 0:
            return False, "Waypoint " + name + " not added."

        # chekc for presence of orig/dest
        norig = int(bs.traf.ap.orig[idx] != "")
        ndest = int(bs.traf.ap.dest[idx] != "")

        # Check whether this is first 'real' waypoint (not orig & dest),
        # And if so, make active
        if self.nwp - norig - ndest == 1:  # first waypoint: make active
            self.direct(idx, self.wpname[norig])  # 0 if no orig
            bs.traf.swlnav[idx] = True

        if afterwp and self.wpname.count(afterwp) == 0:
            return True, "Waypoint " + afterwp + " not found" + \
                "waypoint added at end of route"
        else:
            return True
Exemple #10
0
    def update_aircraft_data(self, data):
        ''' Update GPU buffers with new aircraft simulation data. '''
        if not self.initialized:
            return

        self.glsurface.makeCurrent()
        actdata = bs.net.get_nodedata()
        if actdata.filteralt:
            idx = np.where(
                (data.alt >= actdata.filteralt[0]) * (data.alt <= actdata.filteralt[1]))
            data.lat = data.lat[idx]
            data.lon = data.lon[idx]
            data.trk = data.trk[idx]
            data.alt = data.alt[idx]
            data.tas = data.tas[idx]
            data.vs = data.vs[idx]
            data.rpz = data.rpz[idx]
        naircraft = len(data.lat)
        actdata.translvl = data.translvl
        # self.asas_vmin = data.vmin # TODO: array should be attribute not uniform
        # self.asas_vmax = data.vmax

        if naircraft == 0:
            self.cpalines.set_vertex_count(0)
        else:
            # Update data in GPU buffers
            self.lat.update(np.array(data.lat, dtype=np.float32))
            self.lon.update(np.array(data.lon, dtype=np.float32))
            self.hdg.update(np.array(data.trk, dtype=np.float32))
            self.alt.update(np.array(data.alt, dtype=np.float32))
            self.tas.update(np.array(data.tas, dtype=np.float32))
            self.rpz.update(np.array(data.rpz, dtype=np.float32))
            if hasattr(data, 'asasn') and hasattr(data, 'asase'):
                self.asasn.update(np.array(data.asasn, dtype=np.float32))
                self.asase.update(np.array(data.asase, dtype=np.float32))

            # CPA lines to indicate conflicts
            ncpalines = np.count_nonzero(data.inconf)

            cpalines = np.zeros(4 * ncpalines, dtype=np.float32)
            self.cpalines.set_vertex_count(2 * ncpalines)

            # Labels and colors
            rawlabel = ''
            color = np.empty(
                (min(naircraft, MAX_NAIRCRAFT), 4), dtype=np.uint8)
            selssd = np.zeros(naircraft, dtype=np.uint8)
            confidx = 0

            zdata = zip(data.id, data.ingroup, data.inconf, data.tcpamax, data.trk, data.gs,
                        data.cas, data.vs, data.alt, data.lat, data.lon)
            for i, (acid, ingroup, inconf, tcpa,
                    trk, gs, cas, vs, alt, lat, lon) in enumerate(zdata):
                if i >= MAX_NAIRCRAFT:
                    break

                # Make label: 3 lines of 8 characters per aircraft
                if actdata.show_lbl >= 1:
                    rawlabel += '%-8s' % acid[:8]
                    if actdata.show_lbl == 2:
                        if alt <= data.translvl:
                            rawlabel += '%-5d' % int(alt / ft + 0.5)
                        else:
                            rawlabel += 'FL%03d' % int(alt / ft / 100. + 0.5)
                        vsarrow = 30 if vs > 0.25 else 31 if vs < -0.25 else 32
                        rawlabel += '%1s  %-8d' % (chr(vsarrow),
                                                   int(cas / kts + 0.5))
                    else:
                        rawlabel += 16 * ' '

                if inconf:
                    if actdata.ssd_conflicts:
                        selssd[i] = 255
                    color[i, :] = palette.conflict + (255,)
                    lat1, lon1 = geo.qdrpos(lat, lon, trk, tcpa * gs / nm)
                    cpalines[4 * confidx: 4 * confidx +
                             4] = [lat, lon, lat1, lon1]
                    confidx += 1
                else:
                    # Get custom color if available, else default
                    rgb = palette.aircraft
                    if ingroup:
                        for groupmask, groupcolor in actdata.custgrclr.items():
                            if ingroup & groupmask:
                                rgb = groupcolor
                                break
                    rgb = actdata.custacclr.get(acid, rgb)
                    color[i, :] = tuple(rgb) + (255,)

                #  Check if aircraft is selected to show SSD
                if actdata.ssd_all or acid in actdata.ssd_ownship:
                    selssd[i] = 255

            if len(actdata.ssd_ownship) > 0 or actdata.ssd_conflicts or actdata.ssd_all:
                self.ssd.update(selssd=selssd)

            self.cpalines.update(vertex=cpalines)
            self.color.update(color)
            self.lbl.update(np.array(rawlabel.encode('utf8'), dtype=np.string_))
            
            # If there is a visible route, update the start position
            if self.route_acid in data.id:
                idx = data.id.index(self.route_acid)
                self.route.vertex.update(np.array([data.lat[idx], data.lon[idx]], dtype=np.float32))
Exemple #11
0
    def update_aircraft_data(self, data):
        if not self.initialized:
            return

        self.makeCurrent()
        actdata = bs.net.get_nodedata()
        if actdata.filteralt:
            idx = np.where((data.alt >= actdata.filteralt[0]) *
                           (data.alt <= actdata.filteralt[1]))
            data.lat = data.lat[idx]
            data.lon = data.lon[idx]
            data.trk = data.trk[idx]
            data.alt = data.alt[idx]
            data.tas = data.tas[idx]
            data.vs = data.vs[idx]
        self.naircraft = len(data.lat)
        actdata.translvl = data.translvl
        self.asas_vmin = data.vmin
        self.asas_vmax = data.vmax
        if self.naircraft == 0:
            self.cpalines.set_vertex_count(0)
        else:
            # Update data in GPU buffers
            self.aclatbuf.update(np.array(data.lat, dtype=np.float32))
            self.aclonbuf.update(np.array(data.lon, dtype=np.float32))
            self.achdgbuf.update(np.array(data.trk, dtype=np.float32))
            self.acaltbuf.update(np.array(data.alt, dtype=np.float32))
            self.actasbuf.update(np.array(data.tas, dtype=np.float32))
            self.asasnbuf.update(np.array(data.asasn, dtype=np.float32))
            self.asasebuf.update(np.array(data.asase, dtype=np.float32))

            # CPA lines to indicate conflicts
            ncpalines = np.count_nonzero(data.inconf)

            cpalines = np.zeros(4 * ncpalines, dtype=np.float32)
            self.cpalines.set_vertex_count(2 * ncpalines)

            # Labels and colors
            rawlabel = ''
            color = np.empty((min(self.naircraft, MAX_NAIRCRAFT), 4),
                             dtype=np.uint8)
            selssd = np.zeros(self.naircraft, dtype=np.uint8)
            confidx = 0

            zdata = zip(data.id, data.ingroup, data.inconf, data.tcpamax,
                        data.trk, data.gs, data.cas, data.vs, data.alt,
                        data.lat, data.lon)
            for i, (acid, ingroup, inconf, tcpa, trk, gs, cas, vs, alt, lat,
                    lon) in enumerate(zdata):
                if i >= MAX_NAIRCRAFT:
                    break

                # Make label: 3 lines of 8 characters per aircraft
                if actdata.show_lbl >= 1:
                    rawlabel += '%-8s' % acid[:8]
                    if actdata.show_lbl == 2:
                        if alt <= data.translvl:
                            rawlabel += '%-5d' % int(alt / ft + 0.5)
                        else:
                            rawlabel += 'FL%03d' % int(alt / ft / 100. + 0.5)
                        vsarrow = 30 if vs > 0.25 else 31 if vs < -0.25 else 32
                        rawlabel += '%1s  %-8d' % (chr(vsarrow),
                                                   int(cas / kts + 0.5))
                    else:
                        rawlabel += 16 * ' '

                if inconf:
                    if actdata.ssd_conflicts:
                        selssd[i] = 255
                    color[i, :] = palette.conflict + (255, )
                    lat1, lon1 = geo.qdrpos(lat, lon, trk, tcpa * gs / nm)
                    cpalines[4 * confidx:4 * confidx +
                             4] = [lat, lon, lat1, lon1]
                    confidx += 1
                else:
                    # Get custom color if available, else default
                    rgb = palette.aircraft
                    if ingroup:
                        for groupmask, groupcolor in actdata.custgrclr.items():
                            if ingroup & groupmask:
                                rgb = groupcolor
                                break
                    rgb = actdata.custacclr.get(acid, rgb)
                    color[i, :] = tuple(rgb) + (255, )

                #  Check if aircraft is selected to show SSD
                if actdata.ssd_all or acid in actdata.ssd_ownship:
                    selssd[i] = 255

            if len(actdata.ssd_ownship
                   ) > 0 or actdata.ssd_conflicts or actdata.ssd_all:
                self.ssd.selssd.buf.update(selssd)

            self.confcpabuf.update(cpalines)
            self.accolorbuf.update(color)
            self.aclblbuf.update(
                np.array(rawlabel.encode('utf8'), dtype=np.string_))

            # If there is a visible route, update the start position
            if self.route_acid != "":
                if self.route_acid in data.id:
                    idx = data.id.index(self.route_acid)
                    self.route.vertex.update(
                        np.array([data.lat[idx], data.lon[idx]],
                                 dtype=np.float32))

            # Update trails database with new lines
            if data.swtrails:
                actdata.traillat0.extend(data.traillat0)
                actdata.traillon0.extend(data.traillon0)
                actdata.traillat1.extend(data.traillat1)
                actdata.traillon1.extend(data.traillon1)
                self.traillines.update(vertex=np.array(list(
                    zip(actdata.traillat0, actdata.traillon0,
                        actdata.traillat1, actdata.traillon1)) + list(
                            zip(data.traillastlat, data.traillastlon,
                                list(data.lat), list(data.lon))),
                                                       dtype=np.float32))

            else:
                actdata.traillat0 = []
                actdata.traillon0 = []
                actdata.traillat1 = []
                actdata.traillon1 = []

                self.traillines.set_vertex_count(0)
Exemple #12
0
    def addwptStack(self, idx, *args):  # args: all arguments of addwpt
        """ADDWPT acid, (wpname/lat,lon),[alt],[spd],[afterwp],[beforewp]"""

#        print "addwptStack:",args
        # Check FLYBY or FLYOVER switch, instead of adding a waypoint

        if len(args) == 1:
            isflyby = args[0].replace('-', '')

            if isflyby == "FLYBY":
                self.swflyby = True
                return True

            elif isflyby == "FLYOVER":
                self.swflyby = False
                return True

        # Convert to positions
        name = args[0].upper().strip()

        # Choose reference position ot look up VOR and waypoints
        # First waypoint: own position
        if self.nwp == 0:
            reflat = bs.traf.lat[idx]
            reflon = bs.traf.lon[idx]

        # Or last waypoint before destination
        else:
            if self.wptype[-1] != Route.dest or self.nwp == 1:
                reflat = self.wplat[-1]
                reflon = self.wplon[-1]
            else:
                reflat = self.wplat[-2]
                reflon = self.wplon[-2]

        # Default altitude, speed and afterwp
        alt     = -999.
        spd     = -999.
        afterwp = ""
        beforewp = ""

        # Is it aspecial take-off waypoint?
        takeoffwpt = name.replace('-', '') == "TAKEOFF"

        # Normal waypoint (no take-off waypoint => see else)
        if not takeoffwpt:

            # Get waypoint position
            success, posobj = txt2pos(name, reflat, reflon)
            if success:
                lat      = posobj.lat
                lon      = posobj.lon

                if posobj.type == "nav" or posobj.type == "apt":
                    wptype = Route.wpnav

                elif posobj.type == "rwy":
                    wptype  = Route.runway

                else:  # treat as lat/lon
                    name    = bs.traf.id[idx]
                    wptype  = Route.wplatlon

                if len(args) > 1 and args[1]:
                    alt = args[1]

                if len(args) > 2 and args[2]:
                    spd = args[2]

                if len(args) > 3 and args[3]:
                    afterwp = args[3]

                if len(args) > 4 and args[4]:
                    beforewp = args[4]

            else:
                return False, "Waypoint " + name + " not found."

        # Take off waypoint: positioned 20% of the runway length after the runway
        else:

            # Look up runway in route
            rwyrteidx = -1
            i      = 0
            while i<self.nwp and rwyrteidx<0:
                if self.wpname[i].count("/") >0:
#                    print (self.wpname[i])
                    rwyrteidx = i
                i += 1

            # Only TAKEOFF is specified wihtou a waypoint/runway
            if len(args) == 1 or not args[1]:
                # No runway given: use first in route or current position

                # print ("rwyrteidx =",rwyrteidx)
                # We find a runway in the route, so use it
                if rwyrteidx>0:
                    rwylat   = self.wplat[rwyrteidx]
                    rwylon   = self.wplon[rwyrteidx]
                    aptidx  = bs.navdb.getapinear(rwylat,rwylon)
                    aptname = bs.navdb.aptname[aptidx]

                    rwyname = self.wpname[rwyrteidx].split("/")[1]
                    rwyid = rwyname.replace("RWY","").replace("RW","")
                    rwyhdg = bs.navdb.rwythresholds[aptname][rwyid][2]

                else:
                    rwylat  = bs.traf.lat[idx]
                    rwylon  = bs.traf.lon[idx]
                    rwyhdg = bs.traf.trk[idx]

            elif args[1].count("/") > 0 or len(args) > 2 and args[2]: # we need apt,rwy
                # Take care of both EHAM/RW06 as well as EHAM,RWY18L (so /&, and RW/RWY)
                if args[1].count("/")>0:
                    aptid,rwyname = args[1].split("/")
                else:
                # Runway specified
                    aptid = args[1]
                    rwyname = args[2]

                rwyid = rwyname.replace("RWY", "").replace("RW", "")  # take away RW or RWY
                #                    print ("apt,rwy=",aptid,rwyid)
                # TDO: Add fingind the runway heading with rwyrteidx>0 and navdb!!!
                # Try to get it from the database
                try:
                    rwyhdg = bs.navdb.rwythresholds[aptid][rwyid][2]
                except:
                    rwydir = rwyid.replace("L","").replace("R","").replace("C","")
                    try:
                        rwyhdg = float(rwydir)*10.
                    except:
                        return False,name+" not found."

                success, posobj = txt2pos(aptid+"/RW"+rwyid, reflat, reflon)
                if success:
                    rwylat,rwylon = posobj.lat,posobj.lon
                else:
                    rwylat = bs.traf.lat[idx]
                    rwylon = bs.traf.lon[idx]

            else:
                return False,"Use ADDWPT TAKEOFF,AIRPORTID,RWYNAME"

            # Create a waypoint 2 nm away from current point
            rwydist = 2.0 # [nm] use default distance away from threshold
            lat,lon = geo.qdrpos(rwylat, rwylon, rwyhdg, rwydist) #[deg,deg
            wptype  = Route.wplatlon

            # Add after the runwy in the route
            if rwyrteidx > 0:
                afterwp = self.wpname[rwyrteidx]

            elif self.wptype and self.wptype[0] == Route.orig:
                afterwp = self.wpname[0]

            else:
                # Assume we're called before other waypoints are added
                afterwp = ""

            name = "T/O-" + bs.traf.id[idx] # Use lat/lon naming convention
        # Add waypoint
        wpidx = self.addwpt(idx, name, wptype, lat, lon, alt, spd, afterwp, beforewp)

        # Check for success by checking insetred locaiton in flight plan >= 0
        if wpidx < 0:
            return False, "Waypoint " + name + " not added."

        # chekc for presence of orig/dest
        norig = int(bs.traf.ap.orig[idx] != "")
        ndest = int(bs.traf.ap.dest[idx] != "")

        # Check whether this is first 'real' wayppint (not orig & dest),
        # And if so, make active
        if self.nwp - norig - ndest == 1:  # first waypoint: make active
            self.direct(idx, self.wpname[norig])  # 0 if no orig
            bs.traf.swlnav[idx] = True

        if afterwp and self.wpname.count(afterwp) == 0:
            return True, "Waypoint " + afterwp + " not found" + \
                "waypoint added at end of route"
        else:
            return True
Exemple #13
0
def detect(asas, traf, simt):
    if not asas.swasas:
        return

    # Reset lists before new CD
    asas.iconf        = [[] for ac in range(traf.ntraf)]
    asas.nconf        = 0
    asas.confpairs    = []
    asas.latowncpa    = []
    asas.lonowncpa    = []
    asas.altowncpa    = []

    asas.LOSlist_now  = []
    asas.conflist_now = []

    # Horizontal conflict ---------------------------------------------------------

    # qdlst is for [i,j] qdr from i to j, from perception of ADSB and own coordinates
    qdlst = geo.qdrdist_matrix(np.mat(traf.lat), np.mat(traf.lon),
                               np.mat(traf.adsb.lat), np.mat(traf.adsb.lon))

    # Convert results from mat-> array
    asas.qdr  = np.array(qdlst[0])  # degrees
    I           = np.eye(traf.ntraf)  # Identity matric of order ntraf
    asas.dist = np.array(qdlst[1]) * nm + 1e9 * I  # meters i to j

    # Transmission noise
    if traf.adsb.transnoise:
        # error in the determined bearing between two a/c
        bearingerror = np.random.normal(0, traf.adsb.transerror[0], asas.qdr.shape)  # degrees
        asas.qdr += bearingerror
        # error in the perceived distance between two a/c
        disterror = np.random.normal(0, traf.adsb.transerror[1], asas.dist.shape)  # meters
        asas.dist += disterror

    # Calculate horizontal closest point of approach (CPA)
    qdrrad    = np.radians(asas.qdr)
    asas.dx = asas.dist * np.sin(qdrrad)  # is pos j rel to i
    asas.dy = asas.dist * np.cos(qdrrad)  # is pos j rel to i

    trkrad   = np.radians(traf.trk)
    asas.u = traf.gs * np.sin(trkrad).reshape((1, len(trkrad)))  # m/s
    asas.v = traf.gs * np.cos(trkrad).reshape((1, len(trkrad)))  # m/s

    # parameters received through ADSB
    adsbtrkrad = np.radians(traf.adsb.trk)
    adsbu = traf.adsb.gs * np.sin(adsbtrkrad).reshape((1, len(adsbtrkrad)))  # m/s
    adsbv = traf.adsb.gs * np.cos(adsbtrkrad).reshape((1, len(adsbtrkrad)))  # m/s

    du = asas.u - adsbu.T  # Speed du[i,j] is perceived eastern speed of i to j
    dv = asas.v - adsbv.T  # Speed dv[i,j] is perceived northern speed of i to j

    dv2 = du * du + dv * dv
    dv2 = np.where(np.abs(dv2) < 1e-6, 1e-6, dv2)  # limit lower absolute value

    vrel = np.sqrt(dv2)

    asas.tcpa = -(du * asas.dx + dv * asas.dy) / dv2 + 1e9 * I

    # Calculate CPA positions
    # xcpa = asas.tcpa * du
    # ycpa = asas.tcpa * dv

    # Calculate distance^2 at CPA (minimum distance^2)
    dcpa2 = asas.dist * asas.dist - asas.tcpa * asas.tcpa * dv2

    # Check for horizontal conflict
    R2 = asas.R * asas.R
    swhorconf = dcpa2 < R2  # conflict or not

    # Calculate times of entering and leaving horizontal conflict
    dxinhor = np.sqrt(np.maximum(0., R2 - dcpa2))  # half the distance travelled inzide zone
    dtinhor = dxinhor / vrel

    tinhor = np.where(swhorconf, asas.tcpa - dtinhor, 1e8)  # Set very large if no conf

    touthor = np.where(swhorconf, asas.tcpa + dtinhor, -1e8)  # set very large if no conf
    # swhorconf = swhorconf*(touthor>0)*(tinhor<asas.dtlook)

    # Vertical conflict -----------------------------------------------------------

    # Vertical crossing of disk (-dh,+dh)
    alt = traf.alt.reshape((1, traf.ntraf))
    adsbalt = traf.adsb.alt.reshape((1, traf.ntraf))
    if traf.adsb.transnoise:
        # error in the determined altitude of other a/c
        alterror = np.random.normal(0, traf.adsb.transerror[2], traf.alt.shape)  # degrees
        adsbalt += alterror

    asas.dalt = alt - adsbalt.T


    vs = traf.vs.reshape(1, len(traf.vs))


    avs = traf.adsb.vs.reshape(1, len(traf.adsb.vs))

    dvs = vs - avs.T

    # Check for passing through each others zone
    dvs = np.where(np.abs(dvs) < 1e-6, 1e-6, dvs)  # prevent division by zero
    tcrosshi = (asas.dalt + asas.dh) / -dvs
    tcrosslo = (asas.dalt - asas.dh) / -dvs

    tinver = np.minimum(tcrosshi, tcrosslo)
    toutver = np.maximum(tcrosshi, tcrosslo)

    # Combine vertical and horizontal conflict-------------------------------------
    asas.tinconf = np.maximum(tinver, tinhor)

    asas.toutconf = np.minimum(toutver, touthor)

    swconfl = swhorconf * (asas.tinconf <= asas.toutconf) * \
        (asas.toutconf > 0.) * (asas.tinconf < asas.dtlookahead) \
        * (1. - I)

    # ----------------------------------------------------------------------
    # Update conflict lists
    # ----------------------------------------------------------------------
    if len(swconfl) == 0:
        return
    # Calculate CPA positions of traffic in lat/lon?

    # Select conflicting pairs: each a/c gets their own record
    confidxs            = np.where(swconfl)
    iown                = confidxs[0]
    ioth                = confidxs[1]

    # Store result
    asas.nconf        = len(confidxs[0])

    for idx in range(asas.nconf):
        i = iown[idx]
        j = ioth[idx]
        if i == j:
            continue

        asas.iconf[i].append(idx)
        asas.confpairs.append((traf.id[i], traf.id[j]))

        rng        = asas.tcpa[i, j] * traf.gs[i] / nm
        lato, lono = geo.qdrpos(traf.lat[i], traf.lon[i], traf.trk[i], rng)
        alto       = traf.alt[i] + asas.tcpa[i, j] * traf.vs[i]

        asas.latowncpa.append(lato)
        asas.lonowncpa.append(lono)
        asas.altowncpa.append(alto)

        dx = (traf.lat[i] - traf.lat[j]) * 111319.
        dy = (traf.lon[i] - traf.lon[j]) * 111319.

        hdist2 = dx**2 + dy**2
        hLOS   = hdist2 < asas.R**2
        vdist  = abs(traf.alt[i] - traf.alt[j])
        vLOS   = vdist < asas.dh
        LOS    = (hLOS & vLOS)

        # Add to Conflict and LOSlist, to count total conflicts and LOS

        # NB: if only one A/C detects a conflict, it is also added to these lists
        combi = str(traf.id[i]) + " " + str(traf.id[j])
        combi2 = str(traf.id[j]) + " " + str(traf.id[i])

        experimenttime = simt > 2100 and simt < 5700  # These parameters may be
        # changed to count only conflicts within a given expirement time window

        if combi not in asas.conflist_all and combi2 not in asas.conflist_all:
            asas.conflist_all.append(combi)

        if combi not in asas.conflist_exp and combi2 not in asas.conflist_exp and experimenttime:
            asas.conflist_exp.append(combi)

        if combi not in asas.conflist_now and combi2 not in asas.conflist_now:
            asas.conflist_now.append(combi)

        if LOS:
            if combi not in asas.LOSlist_all and combi2 not in asas.LOSlist_all:
                asas.LOSlist_all.append(combi)
                asas.LOSmaxsev.append(0.)
                asas.LOShmaxsev.append(0.)
                asas.LOSvmaxsev.append(0.)

            if combi not in asas.LOSlist_exp and combi2 not in asas.LOSlist_exp and experimenttime:
                asas.LOSlist_exp.append(combi)

            if combi not in asas.LOSlist_now and combi2 not in asas.LOSlist_now:
                asas.LOSlist_now.append(combi)

            # Now, we measure intrusion and store it if it is the most severe
            Ih = 1.0 - np.sqrt(hdist2) / asas.R
            Iv = 1.0 - vdist / asas.dh
            severity = min(Ih, Iv)

            try:  # Only continue if combi is found in LOSlist (and not combi2)
                idx = asas.LOSlist_all.index(combi)
            except:
                idx = -1

            if idx >= 0:
                if severity > asas.LOSmaxsev[idx]:
                    asas.LOSmaxsev[idx]  = severity
                    asas.LOShmaxsev[idx] = Ih
                    asas.LOSvmaxsev[idx] = Iv

    # Convert to numpy arrays for vectorisation
    asas.latowncpa = np.array(asas.latowncpa)
    asas.lonowncpa = np.array(asas.lonowncpa)
    asas.altowncpa = np.array(asas.altowncpa)

    # Calculate whether ASAS or A/P commands should be followed
    ResumeNav(asas, traf)
Exemple #14
0
    def update_aircraft_data(self, data):
        if not self.initialized:
            return

        self.makeCurrent()
        actdata = bs.net.get_nodedata()
        if actdata.filteralt:
            idx = np.where((data.alt >= actdata.filteralt[0]) * (data.alt <= actdata.filteralt[1]))
            data.lat = data.lat[idx]
            data.lon = data.lon[idx]
            data.trk = data.trk[idx]
            data.alt = data.alt[idx]
            data.tas = data.tas[idx]
            data.vs  = data.vs[idx]
        self.naircraft = len(data.lat)
        actdata.translvl = data.translvl
        self.asas_vmin = data.vmin
        self.asas_vmax = data.vmax
        if self.naircraft == 0:
            self.cpalines.set_vertex_count(0)
        else:
            # Update data in GPU buffers
            update_buffer(self.aclatbuf, np.array(data.lat[:MAX_NAIRCRAFT], dtype=np.float32))
            update_buffer(self.aclonbuf, np.array(data.lon[:MAX_NAIRCRAFT], dtype=np.float32))
            update_buffer(self.achdgbuf, np.array(data.trk[:MAX_NAIRCRAFT], dtype=np.float32))
            update_buffer(self.acaltbuf, np.array(data.alt[:MAX_NAIRCRAFT], dtype=np.float32))
            update_buffer(self.actasbuf, np.array(data.tas[:MAX_NAIRCRAFT], dtype=np.float32))
            update_buffer(self.asasnbuf, np.array(data.asasn[:MAX_NAIRCRAFT], dtype=np.float32))
            update_buffer(self.asasebuf, np.array(data.asase[:MAX_NAIRCRAFT], dtype=np.float32))

            # CPA lines to indicate conflicts
            ncpalines = np.count_nonzero(data.inconf)

            cpalines  = np.zeros(4 * ncpalines, dtype=np.float32)
            self.cpalines.set_vertex_count(2 * ncpalines)

            # Labels and colors
            rawlabel = ''
            color = np.empty((min(self.naircraft, MAX_NAIRCRAFT), 4), dtype=np.uint8)
            selssd = np.zeros(self.naircraft, dtype=np.uint8)
            confidx = 0

            zdata = zip(data.id, data.inconf, data.tcpamax, data.trk, data.gs,
                        data.cas, data.vs, data.alt, data.lat, data.lon)
            for i, (acid, inconf, tcpa, trk, gs, cas, vs, alt, lat, lon) in enumerate(zdata):
                if i >= MAX_NAIRCRAFT:
                    break

                # Make label: 3 lines of 8 characters per aircraft
                if actdata.show_lbl >= 1:
                    rawlabel += '%-8s' % acid[:8]
                    if actdata.show_lbl == 2:
                        if alt <= data.translvl:
                            rawlabel += '%-5d' % int(alt / ft  + 0.5)
                        else:
                            rawlabel += 'FL%03d' % int(alt / ft / 100. + 0.5)
                        vsarrow = 30 if vs > 0.25 else 31 if vs < -0.25 else 32
                        rawlabel += '%1s  %-8d' % (chr(vsarrow), int(cas / kts + 0.5))
                    else:
                        rawlabel += 16 * ' '

                if inconf:
                    if actdata.ssd_conflicts:
                        selssd[i] = 255
                    color[i, :] = palette.conflict + (255,)
                    lat1, lon1 = geo.qdrpos(lat, lon, trk, tcpa * gs / nm)
                    cpalines[4 * confidx : 4 * confidx + 4] = [lat, lon, lat1, lon1]
                    confidx += 1
                else:
                    color[i, :] = palette.aircraft + (255,)

                #  Check if aircraft is selected to show SSD
                if actdata.ssd_all or acid in actdata.ssd_ownship:
                    selssd[i] = 255

            if len(actdata.ssd_ownship) > 0 or actdata.ssd_conflicts or actdata.ssd_all:
                update_buffer(self.ssd.selssdbuf, selssd[:MAX_NAIRCRAFT])

            update_buffer(self.confcpabuf, cpalines[:MAX_NCONFLICTS * 4])
            update_buffer(self.accolorbuf, color)
            update_buffer(self.aclblbuf, np.array(rawlabel.encode('utf8'), dtype=np.string_))

            # If there is a visible route, update the start position
            if self.route_acid != "":
                if self.route_acid in data.id:
                    idx = data.id.index(self.route_acid)
                    update_buffer(self.routebuf,
                                  np.array([data.lat[idx], data.lon[idx]], dtype=np.float32))

            # Update trails database with new lines
            if data.swtrails:
                actdata.traillat0.extend(data.traillat0)
                actdata.traillon0.extend(data.traillon0)
                actdata.traillat1.extend(data.traillat1)
                actdata.traillon1.extend(data.traillon1)
                update_buffer(self.trailbuf, np.array(
                    list(zip(actdata.traillat0, actdata.traillon0,
                             actdata.traillat1, actdata.traillon1)) +
                    list(zip(data.traillastlat, data.traillastlon,
                             list(data.lat), list(data.lon))),
                    dtype=np.float32))

                self.traillines.set_vertex_count(2 * len(actdata.traillat0) +
                                                 2 * len(data.lat))

            else:
                actdata.traillat0 = []
                actdata.traillon0 = []
                actdata.traillat1 = []
                actdata.traillon1 = []

                self.traillines.set_vertex_count(0)
Exemple #15
0
def detect(asas, traf, simt):
    if not asas.swasas:
        return

    # Reset lists before new CD
    asas.iconf = [[] for ac in range(traf.ntraf)]
    asas.nconf = 0
    asas.confpairs = []
    asas.latowncpa = []
    asas.lonowncpa = []
    asas.altowncpa = []

    asas.LOSlist_now = []
    asas.conflist_now = []

    # Horizontal conflict ---------------------------------------------------------

    # qdlst is for [i,j] qdr from i to j, from perception of ADSB and own coordinates
    qdlst = geo.qdrdist_matrix(np.mat(traf.lat), np.mat(traf.lon),
                               np.mat(traf.adsb.lat), np.mat(traf.adsb.lon))

    # Convert results from mat-> array
    asas.qdr = np.array(qdlst[0])  # degrees
    I = np.eye(traf.ntraf)  # Identity matric of order ntraf
    asas.dist = np.array(qdlst[1]) * nm + 1e9 * I  # meters i to j

    # Transmission noise
    if traf.adsb.transnoise:
        # error in the determined bearing between two a/c
        bearingerror = np.random.normal(0, traf.adsb.transerror[0],
                                        asas.qdr.shape)  # degrees
        asas.qdr += bearingerror
        # error in the perceived distance between two a/c
        disterror = np.random.normal(0, traf.adsb.transerror[1],
                                     asas.dist.shape)  # meters
        asas.dist += disterror

    # Calculate horizontal closest point of approach (CPA)
    qdrrad = np.radians(asas.qdr)
    asas.dx = asas.dist * np.sin(qdrrad)  # is pos j rel to i
    asas.dy = asas.dist * np.cos(qdrrad)  # is pos j rel to i

    trkrad = np.radians(traf.trk)
    asas.u = traf.gs * np.sin(trkrad).reshape((1, len(trkrad)))  # m/s
    asas.v = traf.gs * np.cos(trkrad).reshape((1, len(trkrad)))  # m/s

    # parameters received through ADSB
    adsbtrkrad = np.radians(traf.adsb.trk)
    adsbu = traf.adsb.gs * np.sin(adsbtrkrad).reshape(
        (1, len(adsbtrkrad)))  # m/s
    adsbv = traf.adsb.gs * np.cos(adsbtrkrad).reshape(
        (1, len(adsbtrkrad)))  # m/s

    du = asas.u - adsbu.T  # Speed du[i,j] is perceived eastern speed of i to j
    dv = asas.v - adsbv.T  # Speed dv[i,j] is perceived northern speed of i to j

    dv2 = du * du + dv * dv
    dv2 = np.where(np.abs(dv2) < 1e-6, 1e-6, dv2)  # limit lower absolute value

    vrel = np.sqrt(dv2)

    asas.tcpa = -(du * asas.dx + dv * asas.dy) / dv2 + 1e9 * I

    # Calculate CPA positions
    # xcpa = asas.tcpa * du
    # ycpa = asas.tcpa * dv

    # Calculate distance^2 at CPA (minimum distance^2)
    dcpa2 = asas.dist * asas.dist - asas.tcpa * asas.tcpa * dv2

    # Check for horizontal conflict
    R2 = asas.R * asas.R
    swhorconf = dcpa2 < R2  # conflict or not

    # Calculate times of entering and leaving horizontal conflict
    dxinhor = np.sqrt(np.maximum(
        0., R2 - dcpa2))  # half the distance travelled inzide zone
    dtinhor = dxinhor / vrel

    tinhor = np.where(swhorconf, asas.tcpa - dtinhor,
                      1e8)  # Set very large if no conf

    touthor = np.where(swhorconf, asas.tcpa + dtinhor,
                       -1e8)  # set very large if no conf
    # swhorconf = swhorconf*(touthor>0)*(tinhor<asas.dtlook)

    # Vertical conflict -----------------------------------------------------------

    # Vertical crossing of disk (-dh,+dh)
    alt = traf.alt.reshape((1, traf.ntraf))
    adsbalt = traf.adsb.alt.reshape((1, traf.ntraf))
    if traf.adsb.transnoise:
        # error in the determined altitude of other a/c
        alterror = np.random.normal(0, traf.adsb.transerror[2],
                                    traf.alt.shape)  # degrees
        adsbalt += alterror

    asas.dalt = alt - adsbalt.T

    vs = traf.vs.reshape(1, len(traf.vs))

    avs = traf.adsb.vs.reshape(1, len(traf.adsb.vs))

    dvs = vs - avs.T

    # Check for passing through each others zone
    dvs = np.where(np.abs(dvs) < 1e-6, 1e-6, dvs)  # prevent division by zero
    tcrosshi = (asas.dalt + asas.dh) / -dvs
    tcrosslo = (asas.dalt - asas.dh) / -dvs

    tinver = np.minimum(tcrosshi, tcrosslo)
    toutver = np.maximum(tcrosshi, tcrosslo)

    # Combine vertical and horizontal conflict-------------------------------------
    asas.tinconf = np.maximum(tinver, tinhor)

    asas.toutconf = np.minimum(toutver, touthor)

    swconfl = swhorconf * (asas.tinconf <= asas.toutconf) * \
        (asas.toutconf > 0.) * (asas.tinconf < asas.dtlookahead) \
        * (1. - I)

    # ----------------------------------------------------------------------
    # Update conflict lists
    # ----------------------------------------------------------------------
    if len(swconfl) == 0:
        return
    # Calculate CPA positions of traffic in lat/lon?

    # Select conflicting pairs: each a/c gets their own record
    confidxs = np.where(swconfl)
    iown = confidxs[0]
    ioth = confidxs[1]

    # Store result
    asas.nconf = len(confidxs[0])

    for idx in range(asas.nconf):
        i = iown[idx]
        j = ioth[idx]
        if i == j:
            continue

        asas.iconf[i].append(idx)
        asas.confpairs.append((traf.id[i], traf.id[j]))

        rng = asas.tcpa[i, j] * traf.gs[i] / nm
        lato, lono = geo.qdrpos(traf.lat[i], traf.lon[i], traf.trk[i], rng)
        alto = traf.alt[i] + asas.tcpa[i, j] * traf.vs[i]

        asas.latowncpa.append(lato)
        asas.lonowncpa.append(lono)
        asas.altowncpa.append(alto)

        dx = (traf.lat[i] - traf.lat[j]) * 111319.
        dy = (traf.lon[i] - traf.lon[j]) * 111319.

        hdist2 = dx**2 + dy**2
        hLOS = hdist2 < asas.R**2
        vdist = abs(traf.alt[i] - traf.alt[j])
        vLOS = vdist < asas.dh
        LOS = (hLOS & vLOS)

        # Add to Conflict and LOSlist, to count total conflicts and LOS

        # NB: if only one A/C detects a conflict, it is also added to these lists
        combi = str(traf.id[i]) + " " + str(traf.id[j])
        combi2 = str(traf.id[j]) + " " + str(traf.id[i])

        experimenttime = simt > 2100 and simt < 5700  # These parameters may be
        # changed to count only conflicts within a given expirement time window

        if combi not in asas.conflist_all and combi2 not in asas.conflist_all:
            asas.conflist_all.append(combi)

        if combi not in asas.conflist_exp and combi2 not in asas.conflist_exp and experimenttime:
            asas.conflist_exp.append(combi)

        if combi not in asas.conflist_now and combi2 not in asas.conflist_now:
            asas.conflist_now.append(combi)

        if LOS:
            if combi not in asas.LOSlist_all and combi2 not in asas.LOSlist_all:
                asas.LOSlist_all.append(combi)
                asas.LOSmaxsev.append(0.)
                asas.LOShmaxsev.append(0.)
                asas.LOSvmaxsev.append(0.)

            if combi not in asas.LOSlist_exp and combi2 not in asas.LOSlist_exp and experimenttime:
                asas.LOSlist_exp.append(combi)

            if combi not in asas.LOSlist_now and combi2 not in asas.LOSlist_now:
                asas.LOSlist_now.append(combi)

            # Now, we measure intrusion and store it if it is the most severe
            Ih = 1.0 - np.sqrt(hdist2) / asas.R
            Iv = 1.0 - vdist / asas.dh
            severity = min(Ih, Iv)

            try:  # Only continue if combi is found in LOSlist (and not combi2)
                idx = asas.LOSlist_all.index(combi)
            except:
                idx = -1

            if idx >= 0:
                if severity > asas.LOSmaxsev[idx]:
                    asas.LOSmaxsev[idx] = severity
                    asas.LOShmaxsev[idx] = Ih
                    asas.LOSvmaxsev[idx] = Iv

    # Convert to numpy arrays for vectorisation
    asas.latowncpa = np.array(asas.latowncpa)
    asas.lonowncpa = np.array(asas.lonowncpa)
    asas.altowncpa = np.array(asas.altowncpa)

    # Calculate whether ASAS or A/P commands should be followed
    ResumeNav(asas, traf)