def __init__(self, polygon_path, scope, server_address, display=False): self.assertServerAddressNotUsed(server_address) self.__server_addr_ = server_address self.__usockfd_ = None self.__fplanner_ = FlightPlanner(polygon_path, scope) self.__display_ = display self.__connection_ = None self.__my_nodeId_ = parseNodeId(gs.NODE_ID_PATH) self.__autopilot = Autopilot(speed=gs.AUTOPILOT_SPEED, nodeID=self.__my_nodeId_) # 1 waypoint / sec self.__last_status_node_map_ = dict()
def __init__(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind((safe_get_ip_address(), 49998)) # self.matlab_graphing_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # self.matlab_graphing_sock.bind((safe_get_ip_address(),41862)) self.last_time = datetime.now() self.ekf = Polaris(self, self, self, self, self) # hack for X-Plane self.bxyz = matrix("0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0") self.mxyz = matrix( "25396.8; 2011.7; 38921.5" ) # some sort of magnetic field strength table in nanotesla units try: self.autopilot = Autopilot() except: pass
class FlightServer: def __init__(self, polygon_path, scope, server_address, display=False): self.assertServerAddressNotUsed(server_address) self.__server_addr_ = server_address self.__usockfd_ = None self.__fplanner_ = FlightPlanner(polygon_path, scope) self.__display_ = display self.__connection_ = None self.__my_nodeId_ = parseNodeId(gs.NODE_ID_PATH) self.__autopilot = Autopilot(speed=gs.AUTOPILOT_SPEED, nodeID=self.__my_nodeId_) # 1 waypoint / sec self.__last_status_node_map_ = dict() @staticmethod def assertServerAddressNotUsed(server_address): # Make sure the socket does not already exist try: os.unlink(server_address) except OSError: if os.path.exists(server_address): raise def setup_usocket(self): self.__usockfd_ = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) # Bind the socket to the address print('starting up on {}'.format(self.__server_addr_)) self.__usockfd_.bind(self.__server_addr_) self.__usockfd_.listen(1) # Listen for incoming connections def processReceived(self, received_data): try: status_node_map = json.loads(received_data) except ValueError: print("Ignoring packet") return if not(status_node_map== self.__last_status_node_map_): self.__last_status_node_map_ = status_node_map new_polygon = False if "255" in status_node_map: new_polygon = True self.__fplanner_.notifyNewPolygon() del status_node_map["255"] self.__fplanner_.recompute(status_node_map, new_polygon) if self.__display_: plotAllFlightPlans(self.__fplanner_.flight_plans) for fplan in self.__fplanner_.flight_plans: if fplan.nodeid == self.__my_nodeId_: print("Sending flight-plan to autopilot") print(fplan) self.__autopilot.set_flightplan(fplan) break def receiveData(self, connection, client_address): # Receive the data in small chunks and retransmit it received_chunk = connection.recv(4096) if received_chunk: json_status_nodemap = received_chunk.decode() return json_status_nodemap else: print("Connection closed") return None def start(self): self.setup_usocket() print('Waiting for a connection') # Wait for a connection self.__autopilot.start() while True: self.__connection_, client_address = self.__usockfd_.accept() print('Connection open', client_address) received_chunk = self.__connection_.recv(4096) json_status_nodemap = received_chunk.decode() if received_chunk else None print("Received #", json_status_nodemap, "#") if not json_status_nodemap: print("closing connection") self.__connection_.close() # Clean up the connection break self.processReceived(json_status_nodemap) def stop(self): self.__autopilot.stop() if self.__connection_: self.__connection_.close() # Clean up the connection
def __init__(self, navdb): self.wind = WindSim() # Define the periodic loggers # ToDo: explain what these line sdo in comments (type of logs?) datalog.definePeriodicLogger('SNAPLOG', 'SNAPLOG logfile.', settings.snapdt) datalog.definePeriodicLogger('INSTLOG', 'INSTLOG logfile.', settings.instdt) datalog.definePeriodicLogger('SKYLOG', 'SKYLOG logfile.', settings.skydt) with RegisterElementParameters(self): # Register the following parameters for logging with datalog.registerLogParameters('SNAPLOG', self): # Aircraft Info self.id = [] # identifier (string) self.type = [] # aircaft type (string) # Positions self.lat = np.array([]) # latitude [deg] self.lon = np.array([]) # longitude [deg] self.alt = np.array([]) # altitude [m] self.hdg = np.array([]) # traffic heading [deg] self.trk = np.array([]) # track angle [deg] # Velocities self.tas = np.array([]) # true airspeed [m/s] self.gs = np.array([]) # ground speed [m/s] self.gsnorth = np.array([]) # ground speed [m/s] self.gseast = np.array([]) # ground speed [m/s] self.cas = np.array([]) # calibrated airspeed [m/s] self.M = np.array([]) # mach number self.vs = np.array([]) # vertical speed [m/s] # Atmosphere self.p = np.array([]) # air pressure [N/m2] self.rho = np.array([]) # air density [kg/m3] self.Temp = np.array([]) # air temperature [K] self.dtemp = np.array([]) # delta t for non-ISA conditions # Traffic autopilot settings self.aspd = np.array([]) # selected spd(CAS) [m/s] self.aptas = np.array([]) # just for initializing self.ama = np.array([]) # selected spd above crossover altitude (Mach) [-] self.apalt = np.array([]) # selected alt[m] self.avs = np.array([]) # selected vertical speed [m/s] # Speed offset self.spdoffset = np.array([]) # speed offset [kts] self.spdoffsetdev = np.array([]) # speed offset deviation [kts] self.spd_onoff = np.array([]) # speed offset on/off # Whether to perform LNAV and VNAV self.swlnav = np.array([], dtype=np.bool) self.swvnav = np.array([], dtype=np.bool) # Flight Models self.asas = ASAS(self) self.ap = Autopilot(self) self.pilot = Pilot(self) self.adsb = ADSB(self) self.trails = Trails(self) self.actwp = ActiveWaypoint(self) # Traffic performance data self.avsdef = np.array([]) # [m/s]default vertical speed of autopilot self.aphi = np.array([]) # [rad] bank angle setting of autopilot self.ax = np.array([]) # [m/s2] absolute value of longitudinal accelleration self.bank = np.array([]) # nominal bank angle, [radian] self.hdgsel = np.array([], dtype=np.bool) # determines whether aircraft is turning # Crossover altitude self.abco = np.array([]) self.belco = np.array([]) # limit settings self.limspd = np.array([]) # limit speed self.limspd_flag = np.array([], dtype=np.bool) # flag for limit spd - we have to test for max and min self.limalt = np.array([]) # limit altitude self.limvs = np.array([]) # limit vertical speed due to thrust limitation self.limvs_flag = np.array([]) # Display information on label self.label = [] # Text and bitmap of traffic label # Miscallaneous self.coslat = np.array([]) # Cosine of latitude for computations self.eps = np.array([]) # Small nonzero numbers # Default bank angles per flight phase self.bphase = np.deg2rad(np.array([15, 35, 35, 35, 15, 45])) self.reset(navdb)
class Traffic(DynamicArrays): """ Traffic class definition : Traffic data Methods: Traffic() : constructor reset() : Reset traffic database w.r.t a/c data create(acid,actype,aclat,aclon,achdg,acalt,acspd) : create aircraft delete(acid) : delete an aircraft from traffic data deletall() : delete all traffic update(sim) : do a numerical integration step id2idx(name) : return index in traffic database of given call sign engchange(i,engtype) : change engine type of an aircraft setNoise(A) : Add turbulence Members: see create Created by : Jacco M. Hoekstra """ def __init__(self, navdb): self.wind = WindSim() # Define the periodic loggers # ToDo: explain what these line sdo in comments (type of logs?) datalog.definePeriodicLogger('SNAPLOG', 'SNAPLOG logfile.', settings.snapdt) datalog.definePeriodicLogger('INSTLOG', 'INSTLOG logfile.', settings.instdt) datalog.definePeriodicLogger('SKYLOG', 'SKYLOG logfile.', settings.skydt) with RegisterElementParameters(self): # Register the following parameters for logging with datalog.registerLogParameters('SNAPLOG', self): # Aircraft Info self.id = [] # identifier (string) self.type = [] # aircaft type (string) # Positions self.lat = np.array([]) # latitude [deg] self.lon = np.array([]) # longitude [deg] self.alt = np.array([]) # altitude [m] self.hdg = np.array([]) # traffic heading [deg] self.trk = np.array([]) # track angle [deg] # Velocities self.tas = np.array([]) # true airspeed [m/s] self.gs = np.array([]) # ground speed [m/s] self.gsnorth = np.array([]) # ground speed [m/s] self.gseast = np.array([]) # ground speed [m/s] self.cas = np.array([]) # calibrated airspeed [m/s] self.M = np.array([]) # mach number self.vs = np.array([]) # vertical speed [m/s] # Atmosphere self.p = np.array([]) # air pressure [N/m2] self.rho = np.array([]) # air density [kg/m3] self.Temp = np.array([]) # air temperature [K] self.dtemp = np.array([]) # delta t for non-ISA conditions # Traffic autopilot settings self.aspd = np.array([]) # selected spd(CAS) [m/s] self.aptas = np.array([]) # just for initializing self.ama = np.array([]) # selected spd above crossover altitude (Mach) [-] self.apalt = np.array([]) # selected alt[m] self.avs = np.array([]) # selected vertical speed [m/s] # Speed offset self.spdoffset = np.array([]) # speed offset [kts] self.spdoffsetdev = np.array([]) # speed offset deviation [kts] self.spd_onoff = np.array([]) # speed offset on/off # Whether to perform LNAV and VNAV self.swlnav = np.array([], dtype=np.bool) self.swvnav = np.array([], dtype=np.bool) # Flight Models self.asas = ASAS(self) self.ap = Autopilot(self) self.pilot = Pilot(self) self.adsb = ADSB(self) self.trails = Trails(self) self.actwp = ActiveWaypoint(self) # Traffic performance data self.avsdef = np.array([]) # [m/s]default vertical speed of autopilot self.aphi = np.array([]) # [rad] bank angle setting of autopilot self.ax = np.array([]) # [m/s2] absolute value of longitudinal accelleration self.bank = np.array([]) # nominal bank angle, [radian] self.hdgsel = np.array([], dtype=np.bool) # determines whether aircraft is turning # Crossover altitude self.abco = np.array([]) self.belco = np.array([]) # limit settings self.limspd = np.array([]) # limit speed self.limspd_flag = np.array([], dtype=np.bool) # flag for limit spd - we have to test for max and min self.limalt = np.array([]) # limit altitude self.limvs = np.array([]) # limit vertical speed due to thrust limitation self.limvs_flag = np.array([]) # Display information on label self.label = [] # Text and bitmap of traffic label # Miscallaneous self.coslat = np.array([]) # Cosine of latitude for computations self.eps = np.array([]) # Small nonzero numbers # Default bank angles per flight phase self.bphase = np.deg2rad(np.array([15, 35, 35, 35, 15, 45])) self.reset(navdb) def reset(self, navdb): # This ensures that the traffic arrays (which size is dynamic) # are all reset as well, so all lat,lon,sdp etc but also objects adsb super(Traffic, self).reset() self.ntraf = 0 # Reset models self.wind.clear() # Build new modules for area and turbulence self.area = Area(self) self.Turbulence = Turbulence(self) # Noise (turbulence, ADBS-transmission noise, ADSB-truncated effect) self.setNoise(False) # Import navigation data base self.navdb = navdb # Default: BlueSky internal performance model. # Insert your BADA files to the folder "BlueSky/data/coefficients/BADA" # for working with EUROCONTROL`s Base of Aircraft Data revision 3.12 self.perf = Perf(self) self.AMAN=AMAN(AllFlights,unique_runways) for rwy in unique_runways: self.AMAN.initial_schedule_popupflightsonly(intarrtime_AMAN_runway,rwy,simulation_start,unique_runways) def mcreate(self, count, actype=None, alt=None, spd=None, dest=None, area=None): """ Create multiple random aircraft in a specified area """ idbase = chr(randint(65, 90)) + chr(randint(65, 90)) if actype is None: actype = 'B744' for i in xrange(count): acid = idbase + '%05d' % i aclat = random() * (area[1] - area[0]) + area[0] aclon = random() * (area[3] - area[2]) + area[2] achdg = float(randint(1, 360)) acalt = (randint(2000, 39000) * ft) if alt is None else alt acspd = (randint(250, 450) * kts) if spd is None else spd self.create(acid, actype, aclat, aclon, achdg, acalt, acspd) def create(self, acid=None, actype="B744", aclat=None, aclon=None, achdg=None, acalt=None, casmach=None): """Create an aircraft""" # Check if not already exist if self.id.count(acid.upper()) > 0: return False, acid + " already exists." # already exists do nothing # Catch missing acid, replace by a default if acid == None or acid =="*": acid = "KL204" flno = 204 while self.id.count(acid)>0: flno = flno+1 acid ="KL"+str(flno) # Check for (other) missing arguments if actype == None or aclat == None or aclon == None or achdg == None \ or acalt == None or casmach == None: return False,"CRE: Missing one or more arguments:"\ "acid,actype,aclat,aclon,achdg,acalt,acspd" super(Traffic, self).create() # Increase number of aircraft self.ntraf = self.ntraf + 1 # Aircraft Info self.id[-1] = acid.upper() self.type[-1] = actype # Positions self.lat[-1] = aclat self.lon[-1] = aclon self.alt[-1] = acalt self.hdg[-1] = achdg self.trk[-1] = achdg # Velocities self.tas[-1], self.cas[-1], self.M[-1] = casormach(casmach, acalt) self.gs[-1] = self.tas[-1] self.gsnorth[-1] = self.tas[-1] * cos(radians(self.hdg[-1])) self.gseast[-1] = self.tas[-1] * sin(radians(self.hdg[-1])) # Speed offset self.spd_onoff[-1] = 0. # Speed offset = 0 (off) on default # Atmosphere self.Temp[-1], self.rho[-1], self.p[-1] = vatmos(acalt) # Wind if self.wind.winddim > 0: vnwnd, vewnd = self.wind.getdata(self.lat[-1], self.lon[-1], self.alt[-1]) self.gsnorth[-1] = self.gsnorth[-1] + vnwnd self.gseast[-1] = self.gseast[-1] + vewnd self.trk[-1] = np.degrees(np.arctan2(self.gseast[-1], self.gsnorth[-1])) self.gs[-1] = np.sqrt(self.gsnorth[-1]**2 + self.gseast[-1]**2) # Traffic performance data #(temporarily default values) self.avsdef[-1] = 1500. * fpm # default vertical speed of autopilot self.aphi[-1] = radians(25.) # bank angle setting of autopilot self.ax[-1] = kts # absolute value of longitudinal accelleration self.bank[-1] = radians(25.) # Crossover altitude self.abco[-1] = 0 # not necessary to overwrite 0 to 0, but leave for clarity self.belco[-1] = 1 # Traffic autopilot settings self.aspd[-1] = self.cas[-1] self.aptas[-1] = self.tas[-1] self.apalt[-1] = self.alt[-1] # Display information on label self.label[-1] = ['', '', '', 0] # Miscallaneous self.coslat[-1] = cos(radians(aclat)) # Cosine of latitude for flat-earth aproximations self.eps[-1] = 0.01 # ----- Submodules of Traffic ----- self.ap.create() self.actwp.create() self.pilot.create() self.adsb.create() self.area.create() self.asas.create() self.perf.create() self.trails.create() return True def delete(self, acid): """Delete an aircraft""" # Look up index of aircraft idx = self.id2idx(acid) # Do nothing if not found if idx < 0: return False # Decrease number of aircraft self.ntraf = self.ntraf - 1 # Delete all aircraft parameters super(Traffic, self).delete(idx) # ----- Submodules of Traffic ----- self.perf.delete(idx) self.area.delete(idx) return True def update(self, simt, simdt): # Update only if there is traffic --------------------- if self.ntraf == 0: return #---------- Atmosphere -------------------------------- self.p, self.rho, self.Temp = vatmos(self.alt) #---------- ADSB Update ------------------------------- self.adsb.update(simt) #---------- Fly the Aircraft -------------------------- self.ap.update(simt) self.asas.update(simt) self.pilot.FMSOrAsas() #---------- Limit Speeds ------------------------------ self.pilot.FlightEnvelope() #---------- Kinematics -------------------------------- self.UpdateAirSpeed(simdt, simt) self.UpdateGroundSpeed(simdt) self.UpdatePosition(simdt) #---------- Performance Update ------------------------ self.perf.perf(simt) #---------- Simulate Turbulence ----------------------- self.Turbulence.Woosh(simdt) #---------- Aftermath --------------------------------- self.trails.update(simt) self.area.check(simt) #---------- AMAN -------------------------------------- i=0 while(i<self.ntraf): temp1, temp2 = qdrdist(self.lat[i], self.lon[i], 52.309, 4.764) # Check distance towards EHAM if temp2<2. and self.alt[i]<1.: # If aircraft within 2 nm from airport and below 1 meter, delete it self.delete(self.id[i]) i=i+1 # Calculate energy cost per flight self.AMAN.calculate_energy_cost(self.id,self.vs,self.tas,CD_0,CD_2,self.rho,WingSurface,self.hdg,self.trk,mass_nominal,simdt) # Increase iteration counter self.AMAN.IterationCounter=self.AMAN.IterationCounter+1 # Update trajectory predictor, scheduler and SARA every five seconds if self.AMAN.IterationCounter%2==0: # Check for speed offset self.AMAN.speed_offset_switch(self.id,AMAN_horizon,self.spd_onoff) # Update Trajectory Predictions self.AMAN.update_TP(self.id,self.lat,self.lon,self.tas/kts,self.actwp.lat,self.actwp.lon,simt) # Update schedule per runway for rwy in unique_runways: if '--node' in sys.argv: if var_TP==str('ASAPBASIC'): self.AMAN.scheduler_ASAP_basic(self.id,Take_into_account_schedule_horizon,rwy,intarrtime_AMAN_runway,Freeze_horizon,unique_runways) elif var_TP==str('DYNAMIC'): self.AMAN.scheduler_dynamic(self.id,Take_into_account_schedule_horizon,rwy,intarrtime_AMAN_runway) elif var_TP==str('ASAPUPGRADE'): self.AMAN.scheduler_ASAP_upgrade(self.id,Take_into_account_schedule_horizon,rwy,intarrtime_AMAN_runway,Freeze_horizon,unique_runways) else: self.AMAN.scheduler_ASAP_basic(self.id,Take_into_account_schedule_horizon,rwy,intarrtime_AMAN_runway,Freeze_horizon,unique_runways) # #self.AMAN.scheduler_dynamic(self.id,Take_into_account_schedule_horizon,rwy,intarrtime_AMAN_runway) # #self.AMAN.scheduler_ASAP_upgrade(self.id,Take_into_account_schedule_horizon,rwy,intarrtime_AMAN_runway,Freeze_horizon,unique_runways) # Update SARA advisories self.AMAN.update_SARA(self.id,self.alt,self.ap.route,SARA_horizon,simt,approach_margin) # Save variables in logger self.AMAN.AMAN_LOG_arrtimes_and_energycost(self.id,simulation_start) self.AMAN.AMAN_LOG_STAhistory(self.id,simt) self.AMAN.AMAN_LOG_lowleveldelay(self.id,simt) self.AMAN.AMAN_LOG_seqhistory(self.id) self.AMAN.AMAN_LOG_CBAShistory(self.id,simulation_start) #self.AMAN.AMAN_LOG_ETA_CBAShistory(self.id,simt) self.AMAN.AMAN_LOG_traffic_bunches(self.id,approach_margin,simt) return def UpdateAirSpeed(self, simdt, simt): # Acceleration self.delspd = self.pilot.spd - self.tas swspdsel = np.abs(self.delspd) > 0.4 # <1 kts = 0.514444 m/s ax = self.perf.acceleration(simdt) # Update velocities self.tas = self.tas + swspdsel * ax * np.sign(self.delspd) * simdt self.cas = vtas2cas(self.tas, self.alt) self.M = vtas2mach(self.tas, self.alt) # Turning turnrate = np.degrees(g0 * np.tan(self.bank) / np.maximum(self.tas, self.eps)) delhdg = (self.pilot.hdg - self.hdg + 180.) % 360 - 180. # [deg] self.hdgsel = np.abs(delhdg) > np.abs(2. * simdt * turnrate) # Update heading self.hdg = (self.hdg + simdt * turnrate * self.hdgsel * np.sign(delhdg)) % 360. # Update vertical speed delalt = self.pilot.alt - self.alt self.swaltsel = np.abs(delalt) > np.maximum(10 * ft, np.abs(2 * simdt * np.abs(self.vs))) self.vs = self.swaltsel * np.sign(delalt) * self.pilot.vs def UpdateGroundSpeed(self, simdt): # Compute ground speed and track from heading, airspeed and wind spd_random = self.spdoffset * kts + np.random.randn(self.ntraf) * self.spdoffsetdev * kts #self.spd_onoff = array([0 0 0 0 0 0 1 0 0 0 etc.]) # Array whether speed offset + randomness is on/off per aircraft tasx = self.tas + spd_random * self.spd_onoff #print(tasx) #tasx = self.tas + self.spdoffset * kts # np.random.randn(self.ntraf) * self.spdoffsetdev * kts if self.wind.winddim == 0: # no wind self.gsnorth = tasx * np.cos(np.radians(self.hdg)) self.gseast = tasx * np.sin(np.radians(self.hdg)) self.gs = self.tas self.trk = self.hdg else: windnorth, windeast = self.wind.getdata(self.lat, self.lon, self.alt) self.gsnorth = tasx * np.cos(np.radians(self.hdg)) + windnorth self.gseast = tasx * np.sin(np.radians(self.hdg)) + windeast self.gs = np.sqrt(self.gsnorth**2 + self.gseast**2) self.trk = np.degrees(np.arctan2(self.gseast, self.gsnorth)) % 360. def UpdatePosition(self, simdt): # Update position self.alt = np.where(self.swaltsel, self.alt + self.vs * simdt, self.pilot.alt) self.lat = self.lat + np.degrees(simdt * self.gsnorth / Rearth) self.coslat = np.cos(np.deg2rad(self.lat)) self.lon = self.lon + np.degrees(simdt * self.gseast / self.coslat / Rearth) def id2idx(self, acid): """Find index of aircraft id""" try: return self.id.index(acid.upper()) except: return -1 def setNoise(self, noise=None): """Noise (turbulence, ADBS-transmission noise, ADSB-truncated effect)""" if noise is None: return True, "Noise is currently " + ("on" if self.Turbulence.active else "off") self.Turbulence.SetNoise(noise) self.adsb.SetNoise(noise) return True def engchange(self, acid, engid): """Change of engines""" self.perf.engchange(acid, engid) return def move(self, idx, lat, lon, alt=None, hdg=None, casmach=None, vspd=None): self.lat[idx] = lat self.lon[idx] = lon if alt: self.alt[idx] = alt self.apalt[idx] = alt if hdg: self.hdg[idx] = hdg self.ap.trk[idx] = hdg if casmach: self.tas[idx], self.aspd[-1], dummy = casormach(casmach, alt) if vspd: self.vs[idx] = vspd self.swvnav[idx] = False def nom(self, idx): """ Reset acceleration back to nominal (1 kt/s^2): NOM acid """ self.ax[idx] = kts def setdeltaspeed(self, idx, offset=0.): self.spdoffset[idx] = offset def setdeltaspeeddev(self, idx, offset=0.): self.spdoffsetdev[idx] = offset def poscommand(self, scr, idxorwp):# Show info on aircraft(int) or waypoint or airport (str) """POS command: Show info or an aircraft, airport, waypoint or navaid""" # Aircraft index if type(idxorwp)==int and idxorwp >= 0: idx = idxorwp acid = self.id[idx] actype = self.type[idx] lat, lon = self.lat[idx], self.lon[idx] alt, hdg, trk = self.alt[idx] / ft, self.hdg[idx], round(self.trk[idx]) cas = self.cas[idx] / kts tas = self.tas[idx] / kts route = self.ap.route[idx] # Position report lines = "Info on %s %s index = %d\n" % (acid, actype, idx) \ + "Pos = %.2f, %.2f. Spd: %d kts CAS, %d kts TAS\n" % (lat, lon, cas, tas) \ + "Alt = %d ft, Hdg = %d, Trk = %d\n" % (alt, hdg, trk) # FMS AP modes if self.swlnav[idx] and route.nwp > 0 and route.iactwp >= 0: if self.swvnav[idx]: lines = lines + "VNAV, " lines += "LNAV to " + route.wpname[route.iactwp] + "\n" # Flight info: Destination and origin if self.ap.orig[idx] != "" or self.ap.dest[idx] != "": lines = lines + "Flying" if self.ap.orig[idx] != "": lines = lines + " from " + self.ap.orig[idx] if self.ap.dest[idx] != "": lines = lines + " to " + self.ap.dest[idx] # Show a/c info and highlight route of aircraft in radar window # and pan to a/c (to show route) return scr.showacinfo(acid,lines) # Waypoint: airport, navaid or fix else: wp = idxorwp.upper() # Reference position for finding nearest reflat = scr.ctrlat reflon = scr.ctrlon lines = "Info on "+wp+":\n" # First try airports (most used and shorter, hence faster list) iap = self.navdb.getaptidx(wp) if iap>=0: aptypes = ["large","medium","small"] lines = lines + self.navdb.aptname[iap]+"\n" \ + "is a "+ aptypes[max(-1,self.navdb.aptype[iap]-1)] \ +" airport at:\n" \ + latlon2txt(self.navdb.aptlat[iap], \ self.navdb.aptlon[iap]) + "\n" \ + "Elevation: " \ + str(int(round(self.navdb.aptelev[iap]/ft))) \ + " ft \n" # Show country name try: ico = self.navdb.cocode2.index(self.navdb.aptco[iap].upper()) lines = lines + "in "+self.navdb.coname[ico]+" ("+ \ self.navdb.aptco[iap]+")" except: ico = -1 lines = lines + "Country code: "+self.navdb.aptco[iap] try: rwytxt = str(self.navdb.rwythresholds[self.navdb.aptid[iap]].keys()) lines = lines + "\nRunways: " +rwytxt.strip("[]").replace("'","") except: pass # Not found as airport, try waypoints & navaids else: iwps = self.navdb.getwpindices(wp,reflat,reflon) if iwps[0]>=0: typetxt = "" desctxt = "" lastdesc = "XXXXXXXX" for i in iwps: # One line type text if typetxt == "": typetxt = typetxt+self.navdb.wptype[i] else: typetxt = typetxt+" and "+self.navdb.wptype[i] # Description: multi-line samedesc = self.navdb.wpdesc[i]==lastdesc if desctxt == "": desctxt = desctxt +self.navdb.wpdesc[i] lastdesc = self.navdb.wpdesc[i] elif not samedesc: desctxt = desctxt +"\n"+self.navdb.wpdesc[i] lastdesc = self.navdb.wpdesc[i] # Navaid: frequency if self.navdb.wptype[i] in ["VOR","DME","TACAN"] and not samedesc: desctxt = desctxt + " "+ str(self.navdb.wpfreq[i])+" MHz" elif self.navdb.wptype[i]=="NDB" and not samedesc: desctxt = desctxt+ " " + str(self.navdb.wpfreq[i])+" kHz" iwp = iwps[0] # Basic info lines = lines + wp +" is a "+ typetxt \ + " at\n"\ + latlon2txt(self.navdb.wplat[iwp], \ self.navdb.wplon[iwp]) # Navaids have description if len(desctxt)>0: lines = lines+ "\n" + desctxt # VOR give variation if self.navdb.wptype[iwp]=="VOR": lines = lines + "\nVariation: "+ \ str(self.navdb.wpvar[iwp])+" deg" # How many others? nother = self.navdb.wpid.count(wp)-len(iwps) if nother>0: verb = ["is ","are "][min(1,max(0,nother-1))] lines = lines +"\nThere "+verb + str(nother) +\ " other waypoint(s) also named " + wp else: return False,idxorwp+" not found as a/c, airport, navaid or waypoint" # Show what we found on airport and navaid/waypoint scr.echo(lines) return True
def __init__(self, navdb): self.wind = WindSim() # Define the periodic loggers # ToDo: explain what these line sdo in comments (type of logs?) datalog.definePeriodicLogger('SNAPLOG', 'SNAPLOG logfile.', settings.snapdt) datalog.definePeriodicLogger('INSTLOG', 'INSTLOG logfile.', settings.instdt) datalog.definePeriodicLogger('SKYLOG', 'SKYLOG logfile.', settings.skydt) with RegisterElementParameters(self): # Register the following parameters for logging with datalog.registerLogParameters('SNAPLOG', self): # Aircraft Info self.id = [] # identifier (string) self.type = [] # aircaft type (string) # Positions self.lat = np.array([]) # latitude [deg] self.lon = np.array([]) # longitude [deg] self.alt = np.array([]) # altitude [m] self.hdg = np.array([]) # traffic heading [deg] self.trk = np.array([]) # track angle [deg] # Velocities self.tas = np.array([]) # true airspeed [m/s] self.gs = np.array([]) # ground speed [m/s] self.gsnorth = np.array([]) # ground speed [m/s] self.gseast = np.array([]) # ground speed [m/s] self.cas = np.array([]) # calibrated airspeed [m/s] self.M = np.array([]) # mach number self.vs = np.array([]) # vertical speed [m/s] # Atmosphere self.p = np.array([]) # air pressure [N/m2] self.rho = np.array([]) # air density [kg/m3] self.Temp = np.array([]) # air temperature [K] self.dtemp = np.array([]) # delta t for non-ISA conditions # Traffic autopilot settings self.aspd = np.array([]) # selected spd(CAS) [m/s] self.aptas = np.array([]) # just for initializing self.ama = np.array( []) # selected spd above crossover altitude (Mach) [-] self.apalt = np.array([]) # selected alt[m] self.avs = np.array([]) # selected vertical speed [m/s] # Whether to perform LNAV and VNAV self.swlnav = np.array([], dtype=np.bool) self.swvnav = np.array([], dtype=np.bool) # Flight Models self.asas = ASAS(self) self.ap = Autopilot(self) self.pilot = Pilot(self) self.adsb = ADSB(self) self.trails = Trails(self) self.actwp = ActiveWaypoint(self) # Traffic performance data self.avsdef = np.array( []) # [m/s]default vertical speed of autopilot self.aphi = np.array([]) # [rad] bank angle setting of autopilot self.ax = np.array( []) # [m/s2] absolute value of longitudinal accelleration self.bank = np.array([]) # nominal bank angle, [radian] self.hdgsel = np.array( [], dtype=np.bool) # determines whether aircraft is turning # Crossover altitude self.abco = np.array([]) self.belco = np.array([]) # limit settings self.limspd = np.array([]) # limit speed self.limspd_flag = np.array( [], dtype=np.bool ) # flag for limit spd - we have to test for max and min self.limalt = np.array([]) # limit altitude self.limvs = np.array( []) # limit vertical speed due to thrust limitation self.limvs_flag = np.array([]) # Display information on label self.label = [] # Text and bitmap of traffic label # Miscallaneous self.coslat = np.array([]) # Cosine of latitude for computations self.eps = np.array([]) # Small nonzero numbers # Default bank angles per flight phase self.bphase = np.deg2rad(np.array([15, 35, 35, 35, 15, 45])) self.reset(navdb)
class Traffic(DynamicArrays): """ Traffic class definition : Traffic data Methods: Traffic() : constructor reset() : Reset traffic database w.r.t a/c data create(acid,actype,aclat,aclon,achdg,acalt,acspd) : create aircraft delete(acid) : delete an aircraft from traffic data deletall() : delete all traffic update(sim) : do a numerical integration step id2idx(name) : return index in traffic database of given call sign engchange(i,engtype) : change engine type of an aircraft setNoise(A) : Add turbulence Members: see create Created by : Jacco M. Hoekstra """ def __init__(self, navdb): self.wind = WindSim() # Define the periodic loggers # ToDo: explain what these line sdo in comments (type of logs?) datalog.definePeriodicLogger('SNAPLOG', 'SNAPLOG logfile.', settings.snapdt) datalog.definePeriodicLogger('INSTLOG', 'INSTLOG logfile.', settings.instdt) datalog.definePeriodicLogger('SKYLOG', 'SKYLOG logfile.', settings.skydt) with RegisterElementParameters(self): # Register the following parameters for logging with datalog.registerLogParameters('SNAPLOG', self): # Aircraft Info self.id = [] # identifier (string) self.type = [] # aircaft type (string) # Positions self.lat = np.array([]) # latitude [deg] self.lon = np.array([]) # longitude [deg] self.alt = np.array([]) # altitude [m] self.hdg = np.array([]) # traffic heading [deg] self.trk = np.array([]) # track angle [deg] # Velocities self.tas = np.array([]) # true airspeed [m/s] self.gs = np.array([]) # ground speed [m/s] self.gsnorth = np.array([]) # ground speed [m/s] self.gseast = np.array([]) # ground speed [m/s] self.cas = np.array([]) # calibrated airspeed [m/s] self.M = np.array([]) # mach number self.vs = np.array([]) # vertical speed [m/s] # Atmosphere self.p = np.array([]) # air pressure [N/m2] self.rho = np.array([]) # air density [kg/m3] self.Temp = np.array([]) # air temperature [K] self.dtemp = np.array([]) # delta t for non-ISA conditions # Traffic autopilot settings self.aspd = np.array([]) # selected spd(CAS) [m/s] self.aptas = np.array([]) # just for initializing self.ama = np.array( []) # selected spd above crossover altitude (Mach) [-] self.apalt = np.array([]) # selected alt[m] self.avs = np.array([]) # selected vertical speed [m/s] # Whether to perform LNAV and VNAV self.swlnav = np.array([], dtype=np.bool) self.swvnav = np.array([], dtype=np.bool) # Flight Models self.asas = ASAS(self) self.ap = Autopilot(self) self.pilot = Pilot(self) self.adsb = ADSB(self) self.trails = Trails(self) self.actwp = ActiveWaypoint(self) # Traffic performance data self.avsdef = np.array( []) # [m/s]default vertical speed of autopilot self.aphi = np.array([]) # [rad] bank angle setting of autopilot self.ax = np.array( []) # [m/s2] absolute value of longitudinal accelleration self.bank = np.array([]) # nominal bank angle, [radian] self.hdgsel = np.array( [], dtype=np.bool) # determines whether aircraft is turning # Crossover altitude self.abco = np.array([]) self.belco = np.array([]) # limit settings self.limspd = np.array([]) # limit speed self.limspd_flag = np.array( [], dtype=np.bool ) # flag for limit spd - we have to test for max and min self.limalt = np.array([]) # limit altitude self.limvs = np.array( []) # limit vertical speed due to thrust limitation self.limvs_flag = np.array([]) # Display information on label self.label = [] # Text and bitmap of traffic label # Miscallaneous self.coslat = np.array([]) # Cosine of latitude for computations self.eps = np.array([]) # Small nonzero numbers # Default bank angles per flight phase self.bphase = np.deg2rad(np.array([15, 35, 35, 35, 15, 45])) self.reset(navdb) def reset(self, navdb): # This ensures that the traffic arrays (which size is dynamic) # are all reset as well, so all lat,lon,sdp etc but also objects adsb super(Traffic, self).reset() self.ntraf = 0 # Reset models self.wind.clear() # Build new modules for area and turbulence self.area = Area(self) self.Turbulence = Turbulence(self) # Noise (turbulence, ADBS-transmission noise, ADSB-truncated effect) self.setNoise(False) # Import navigation data base self.navdb = navdb # Default: BlueSky internal performance model. # Insert your BADA files to the folder "BlueSky/data/coefficients/BADA" # for working with EUROCONTROL`s Base of Aircraft Data revision 3.12 self.perf = Perf(self) def mcreate(self, count, actype=None, alt=None, spd=None, dest=None, area=None): """ Create multiple random aircraft in a specified area """ idbase = chr(randint(65, 90)) + chr(randint(65, 90)) if actype is None: actype = 'B744' for i in xrange(count): acid = idbase + '%05d' % i aclat = random() * (area[1] - area[0]) + area[0] aclon = random() * (area[3] - area[2]) + area[2] achdg = float(randint(1, 360)) acalt = (randint(2000, 39000) * ft) if alt is None else alt acspd = (randint(250, 450) * kts) if spd is None else spd self.create(acid, actype, aclat, aclon, achdg, acalt, acspd) def create(self, acid=None, actype="B744", aclat=None, aclon=None, achdg=None, acalt=None, casmach=None): """Create an aircraft""" # Check if not already exist if self.id.count(acid.upper()) > 0: return False, acid + " already exists." # already exists do nothing # Catch missing acid, replace by a default if acid == None or acid == "*": acid = "KL204" flno = 204 while self.id.count(acid) > 0: flno = flno + 1 acid = "KL" + str(flno) # Check for (other) missing arguments if actype == None or aclat == None or aclon == None or achdg == None \ or acalt == None or casmach == None: return False,"CRE: Missing one or more arguments:"\ "acid,actype,aclat,aclon,achdg,acalt,acspd" super(Traffic, self).create() # Increase number of aircraft self.ntraf = self.ntraf + 1 # Aircraft Info self.id[-1] = acid.upper() self.type[-1] = actype # Positions self.lat[-1] = aclat self.lon[-1] = aclon self.alt[-1] = acalt self.hdg[-1] = achdg self.trk[-1] = achdg # Velocities self.tas[-1], self.cas[-1], self.M[-1] = casormach(casmach, acalt) self.gs[-1] = self.tas[-1] self.gsnorth[-1] = self.tas[-1] * cos(radians(self.hdg[-1])) self.gseast[-1] = self.tas[-1] * sin(radians(self.hdg[-1])) # Atmosphere self.Temp[-1], self.rho[-1], self.p[-1] = vatmos(acalt) # Wind if self.wind.winddim > 0: vnwnd, vewnd = self.wind.getdata(self.lat[-1], self.lon[-1], self.alt[-1]) self.gsnorth[-1] = self.gsnorth[-1] + vnwnd self.gseast[-1] = self.gseast[-1] + vewnd self.trk[-1] = np.degrees( np.arctan2(self.gseast[-1], self.gsnorth[-1])) self.gs[-1] = np.sqrt(self.gsnorth[-1]**2 + self.gseast[-1]**2) # Traffic performance data #(temporarily default values) self.avsdef[-1] = 1500. * fpm # default vertical speed of autopilot self.aphi[-1] = radians(25.) # bank angle setting of autopilot self.ax[-1] = kts # absolute value of longitudinal accelleration self.bank[-1] = radians(25.) # Crossover altitude self.abco[ -1] = 0 # not necessary to overwrite 0 to 0, but leave for clarity self.belco[-1] = 1 # Traffic autopilot settings self.aspd[-1] = self.cas[-1] self.aptas[-1] = self.tas[-1] self.apalt[-1] = self.alt[-1] # Display information on label self.label[-1] = ['', '', '', 0] # Miscallaneous self.coslat[-1] = cos( radians(aclat)) # Cosine of latitude for flat-earth aproximations self.eps[-1] = 0.01 # ----- Submodules of Traffic ----- self.ap.create() self.actwp.create() self.pilot.create() self.adsb.create() self.area.create() self.asas.create() self.perf.create() self.trails.create() return True def delete(self, acid): """Delete an aircraft""" # Look up index of aircraft idx = self.id2idx(acid) # Do nothing if not found if idx < 0: return False # Decrease number of aircraft self.ntraf = self.ntraf - 1 # Delete all aircraft parameters super(Traffic, self).delete(idx) # ----- Submodules of Traffic ----- self.perf.delete(idx) self.area.delete(idx) return True def update(self, simt, simdt): # Update only if there is traffic --------------------- if self.ntraf == 0: return #---------- Atmosphere -------------------------------- self.p, self.rho, self.Temp = vatmos(self.alt) #---------- ADSB Update ------------------------------- self.adsb.update(simt) #---------- Fly the Aircraft -------------------------- self.ap.update(simt) self.asas.update(simt) self.pilot.FMSOrAsas() #---------- Limit Speeds ------------------------------ self.pilot.FlightEnvelope() #---------- Kinematics -------------------------------- self.UpdateAirSpeed(simdt, simt) self.UpdateGroundSpeed(simdt) self.UpdatePosition(simdt) #---------- Performance Update ------------------------ self.perf.perf(simt) #---------- Simulate Turbulence ----------------------- self.Turbulence.Woosh(simdt) #---------- Aftermath --------------------------------- self.trails.update(simt) self.area.check(simt) return def UpdateAirSpeed(self, simdt, simt): # Acceleration self.delspd = self.pilot.spd - self.tas swspdsel = np.abs(self.delspd) > 0.4 # <1 kts = 0.514444 m/s ax = self.perf.acceleration(simdt) # Update velocities self.tas = self.tas + swspdsel * ax * np.sign(self.delspd) * simdt self.cas = vtas2cas(self.tas, self.alt) self.M = vtas2mach(self.tas, self.alt) # Turning turnrate = np.degrees(g0 * np.tan(self.bank) / np.maximum(self.tas, self.eps)) delhdg = (self.pilot.hdg - self.hdg + 180.) % 360 - 180. # [deg] self.hdgsel = np.abs(delhdg) > np.abs(2. * simdt * turnrate) # Update heading self.hdg = (self.hdg + simdt * turnrate * self.hdgsel * np.sign(delhdg)) % 360. # Update vertical speed delalt = self.pilot.alt - self.alt self.swaltsel = np.abs(delalt) > np.maximum( 10 * ft, np.abs(2 * simdt * np.abs(self.vs))) self.vs = self.swaltsel * np.sign(delalt) * self.pilot.vs def UpdateGroundSpeed(self, simdt): # Compute ground speed and track from heading, airspeed and wind if self.wind.winddim == 0: # no wind self.gsnorth = self.tas * np.cos(np.radians(self.hdg)) self.gseast = self.tas * np.sin(np.radians(self.hdg)) self.gs = self.tas self.trk = self.hdg else: windnorth, windeast = self.wind.getdata(self.lat, self.lon, self.alt) self.gsnorth = self.tas * np.cos(np.radians(self.hdg)) + windnorth self.gseast = self.tas * np.sin(np.radians(self.hdg)) + windeast self.gs = np.sqrt(self.gsnorth**2 + self.gseast**2) self.trk = np.degrees(np.arctan2(self.gseast, self.gsnorth)) % 360. def UpdatePosition(self, simdt): # Update position self.alt = np.where(self.swaltsel, self.alt + self.vs * simdt, self.pilot.alt) self.lat = self.lat + np.degrees(simdt * self.gsnorth / Rearth) self.coslat = np.cos(np.deg2rad(self.lat)) self.lon = self.lon + np.degrees( simdt * self.gseast / self.coslat / Rearth) def id2idx(self, acid): """Find index of aircraft id""" try: return self.id.index(acid.upper()) except: return -1 def setNoise(self, noise=None): """Noise (turbulence, ADBS-transmission noise, ADSB-truncated effect)""" if noise is None: return True, "Noise is currently " + ( "on" if self.Turbulence.active else "off") self.Turbulence.SetNoise(noise) self.adsb.SetNoise(noise) return True def engchange(self, acid, engid): """Change of engines""" self.perf.engchange(acid, engid) return def move(self, idx, lat, lon, alt=None, hdg=None, casmach=None, vspd=None): self.lat[idx] = lat self.lon[idx] = lon if alt: self.alt[idx] = alt self.apalt[idx] = alt if hdg: self.hdg[idx] = hdg self.ap.trk[idx] = hdg if casmach: self.tas[idx], self.aspd[-1], dummy = casormach(casmach, alt) if vspd: self.vs[idx] = vspd self.swvnav[idx] = False def nom(self, idx): """ Reset acceleration back to nominal (1 kt/s^2): NOM acid """ self.ax[idx] = kts def acinfo(self, idx): acid = self.id[idx] actype = self.type[idx] lat, lon = self.lat[idx], self.lon[idx] alt, hdg, trk = self.alt[idx] / ft, self.hdg[idx], round(self.trk[idx]) cas = self.cas[idx] / kts tas = self.tas[idx] / kts route = self.ap.route[idx] line = "Info on %s %s index = %d\n" % (acid, actype, idx) \ + "Pos = %.2f, %.2f. Spd: %d kts CAS, %d kts TAS\n" % (lat, lon, cas, tas) \ + "Alt = %d ft, Hdg = %d, Trk = %d\n" % (alt, hdg, trk) if self.swlnav[idx] and route.nwp > 0 and route.iactwp >= 0: if self.swvnav[idx]: line += "VNAV, " line += "LNAV to " + route.wpname[route.iactwp] + "\n" if self.ap.orig[idx] != "" or self.ap.dest[idx] != "": line += "Flying" if self.ap.orig[idx] != "": line += " from " + self.ap.orig[idx] if self.ap.dest[idx] != "": line += " to " + self.ap.dest[idx] return acid, line
from bottle import post, get, run, static_file, default_app from capturing import Capturing from autopilot import Autopilot config = { 'photos_directory': 'photos', 'port': 3001 } capturing = Capturing(config['photos_directory']) autopilot = Autopilot() @get('/') @get('/index.html') def index_html(): return static('index.html', 'text/html') @get('/<file_path:re:js/.*>') def js(file_path): return static(file_path, 'application/javascript') @get('/<file_path:re:css/.*>') def css(file_path): return static(file_path, 'text/css') # obsolete @get('/photos-count')
class Traffic(DynamicArrays): """ Traffic class definition : Traffic data Methods: Traffic() : constructor reset() : Reset traffic database w.r.t a/c data create(acid,actype,aclat,aclon,achdg,acalt,acspd) : create aircraft delete(acid) : delete an aircraft from traffic data deletall() : delete all traffic update(sim) : do a numerical integration step id2idx(name) : return index in traffic database of given call sign engchange(i,engtype) : change engine type of an aircraft setNoise(A) : Add turbulence Members: see create Created by : Jacco M. Hoekstra """ def __init__(self): self.wind = WindSim() # Define the periodic loggers # ToDo: explain what these line sdo in comments (type of logs?) datalog.definePeriodicLogger('SNAPLOG', 'SNAPLOG logfile.', settings.snapdt) datalog.definePeriodicLogger('INSTLOG', 'INSTLOG logfile.', settings.instdt) datalog.definePeriodicLogger('SKYLOG', 'SKYLOG logfile.', settings.skydt) with RegisterElementParameters(self): # Register the following parameters for logging with datalog.registerLogParameters('SNAPLOG', self): # Aircraft Info self.id = [] # identifier (string) self.type = [] # aircaft type (string) # Positions self.lat = np.array([]) # latitude [deg] self.lon = np.array([]) # longitude [deg] self.alt = np.array([]) # altitude [m] self.hdg = np.array([]) # traffic heading [deg] self.trk = np.array([]) # track angle [deg] # Velocities self.tas = np.array([]) # true airspeed [m/s] self.gs = np.array([]) # ground speed [m/s] self.gsnorth = np.array([]) # ground speed [m/s] self.gseast = np.array([]) # ground speed [m/s] self.cas = np.array([]) # calibrated airspeed [m/s] self.M = np.array([]) # mach number self.vs = np.array([]) # vertical speed [m/s] # Atmosphere self.p = np.array([]) # air pressure [N/m2] self.rho = np.array([]) # air density [kg/m3] self.Temp = np.array([]) # air temperature [K] self.dtemp = np.array([]) # delta t for non-ISA conditions # Traffic autopilot settings self.aspd = np.array([]) # selected spd(CAS) [m/s] self.aptas = np.array([]) # just for initializing self.ama = np.array([]) # selected spd above crossover altitude (Mach) [-] self.apalt = np.array([]) # selected alt[m] self.avs = np.array([]) # selected vertical speed [m/s] # Whether to perform LNAV and VNAV self.swlnav = np.array([], dtype=np.bool) self.swvnav = np.array([], dtype=np.bool) # Flight Models self.asas = ASAS() self.ap = Autopilot() self.pilot = Pilot() self.adsb = ADSB() self.trails = Trails() self.actwp = ActiveWaypoint() # Traffic performance data self.avsdef = np.array([]) # [m/s]default vertical speed of autopilot self.aphi = np.array([]) # [rad] bank angle setting of autopilot self.ax = np.array([]) # [m/s2] absolute value of longitudinal accelleration self.bank = np.array([]) # nominal bank angle, [radian] self.hdgsel = np.array([], dtype=np.bool) # determines whether aircraft is turning # Crossover altitude self.abco = np.array([]) self.belco = np.array([]) # limit settings self.limspd = np.array([]) # limit speed self.limspd_flag = np.array([], dtype=np.bool) # flag for limit spd - we have to test for max and min self.limalt = np.array([]) # limit altitude self.limvs = np.array([]) # limit vertical speed due to thrust limitation self.limvs_flag = np.array([]) # Display information on label self.label = [] # Text and bitmap of traffic label # Miscallaneous self.coslat = np.array([]) # Cosine of latitude for computations self.eps = np.array([]) # Small nonzero numbers # Default bank angles per flight phase self.bphase = np.deg2rad(np.array([15, 35, 35, 35, 15, 45])) self.reset() def reset(self): # This ensures that the traffic arrays (which size is dynamic) # are all reset as well, so all lat,lon,sdp etc but also objects adsb super(Traffic, self).reset() self.ntraf = 0 # Reset models self.wind.clear() # Build new modules for area and turbulence self.area = Area() self.Turbulence = Turbulence() # Noise (turbulence, ADBS-transmission noise, ADSB-truncated effect) self.setNoise(False) # Default: BlueSky internal performance model. # Insert your BADA files to the folder "BlueSky/data/coefficients/BADA" # for working with EUROCONTROL`s Base of Aircraft Data revision 3.12 self.perf = Perf() self.trails.reset() def mcreate(self, count, actype=None, alt=None, spd=None, dest=None): """ Create multiple random aircraft in a specified area """ area = bs.scr.getviewlatlon() idbase = chr(randint(65, 90)) + chr(randint(65, 90)) if actype is None: actype = 'B744' n = count super(Traffic, self).create(n) # Increase number of aircraft self.ntraf = self.ntraf + count acids = [] aclats = [] aclons = [] achdgs = [] acalts = [] acspds = [] for i in xrange(count): acids.append((idbase + '%05d' % i).upper()) aclats.append(random() * (area[1] - area[0]) + area[0]) aclons.append(random() * (area[3] - area[2]) + area[2]) achdgs.append(float(randint(1, 360))) acalts.append((randint(2000, 39000) * ft) if alt is None else alt) acspds.append((randint(250, 450) * kts) if spd is None else spd) # Aircraft Info self.id[-n:] = acids self.type[-n:] = [actype] * n # Positions self.lat[-n:] = aclats self.lon[-n:] = aclons self.alt[-n:] = acalts self.hdg[-n:] = achdgs self.trk[-n:] = achdgs # Velocities self.tas[-n:], self.cas[-n:], self.M[-n:] = vcasormach(acspds, acalts) self.gs[-n:] = self.tas[-n:] self.gsnorth[-n:] = self.tas[-n:] * np.cos(np.radians(self.hdg[-n:])) self.gseast[-n:] = self.tas[-n:] * np.sin(np.radians(self.hdg[-n:])) # Atmosphere self.p[-n:], self.rho[-n:], self.Temp[-n:] = vatmos(acalts) # Wind if self.wind.winddim > 0: vnwnd, vewnd = self.wind.getdata(self.lat[-n:], self.lon[-n:], self.alt[-n:]) self.gsnorth[-n:] = self.gsnorth[-n:] + vnwnd self.gseast[-n:] = self.gseast[-n:] + vewnd self.trk[-n:] = np.degrees(np.arctan2(self.gseast[-n:], self.gsnorth[-n:])) self.gs[-n:] = np.sqrt(self.gsnorth[-n:]**2 + self.gseast[-n:]**2) # Traffic performance data #(temporarily default values) self.avsdef[-n:] = 1500. * fpm # default vertical speed of autopilot self.aphi[-n:] = np.radians(25.) # bank angle setting of autopilot self.ax[-n:] = kts # absolute value of longitudinal accelleration self.bank[-n:] = np.radians(25.) # Crossover altitude self.abco[-n:] = 0 # not necessary to overwrite 0 to 0, but leave for clarity self.belco[-n:] = 1 # Traffic autopilot settings self.aspd[-n:] = self.cas[-n:] self.aptas[-n:] = self.tas[-n:] self.apalt[-n:] = self.alt[-n:] # Display information on label self.label[-n:] = ['', '', '', 0] # Miscallaneous self.coslat[-n:] = np.cos(np.radians(aclats)) # Cosine of latitude for flat-earth aproximations self.eps[-n:] = 0.01 # ----- Submodules of Traffic ----- self.ap.create(n) self.actwp.create(n) self.pilot.create(n) self.adsb.create(n) self.area.create(n) self.asas.create(n) self.perf.create(n) self.trails.create(n) def create(self, acid=None, actype="B744", aclat=None, aclon=None, achdg=None, acalt=None, casmach=None): """Create an aircraft""" # Check if not already exist if self.id.count(acid.upper()) > 0: return False, acid + " already exists." # already exists do nothing # Catch missing acid, replace by a default if acid is None or acid == "*": acid = "KL204" flno = 204 while self.id.count(acid) > 0: flno = flno + 1 acid = "KL" + str(flno) # Check for (other) missing arguments if actype is None or aclat is None or aclon is None or achdg is None \ or acalt is None or casmach is None: return False, "CRE: Missing one or more arguments:"\ "acid,actype,aclat,aclon,achdg,acalt,acspd" super(Traffic, self).create() # Increase number of aircraft self.ntraf = self.ntraf + 1 # Aircraft Info self.id[-1] = acid.upper() self.type[-1] = actype # Positions self.lat[-1] = aclat self.lon[-1] = aclon self.alt[-1] = acalt self.hdg[-1] = achdg self.trk[-1] = achdg # Velocities self.tas[-1], self.cas[-1], self.M[-1] = casormach(casmach, acalt) self.gs[-1] = self.tas[-1] self.gsnorth[-1] = self.tas[-1] * cos(radians(self.hdg[-1])) self.gseast[-1] = self.tas[-1] * sin(radians(self.hdg[-1])) # Atmosphere self.p[-1], self.rho[-1], self.Temp[-1] = vatmos(acalt) # Wind if self.wind.winddim > 0: vnwnd, vewnd = self.wind.getdata(self.lat[-1], self.lon[-1], self.alt[-1]) self.gsnorth[-1] = self.gsnorth[-1] + vnwnd self.gseast[-1] = self.gseast[-1] + vewnd self.trk[-1] = np.degrees(np.arctan2(self.gseast[-1], self.gsnorth[-1])) self.gs[-1] = np.sqrt(self.gsnorth[-1]**2 + self.gseast[-1]**2) # Traffic performance data #(temporarily default values) self.avsdef[-1] = 1500. * fpm # default vertical speed of autopilot self.aphi[-1] = radians(25.) # bank angle setting of autopilot self.ax[-1] = kts # absolute value of longitudinal accelleration self.bank[-1] = radians(25.) # Crossover altitude self.abco[-1] = 0 # not necessary to overwrite 0 to 0, but leave for clarity self.belco[-1] = 1 # Traffic autopilot settings self.aspd[-1] = self.cas[-1] self.aptas[-1] = self.tas[-1] self.apalt[-1] = self.alt[-1] # Display information on label self.label[-1] = ['', '', '', 0] # Miscallaneous self.coslat[-1] = cos(radians(aclat)) # Cosine of latitude for flat-earth aproximations self.eps[-1] = 0.01 # ----- Submodules of Traffic ----- self.ap.create() self.actwp.create() self.pilot.create() self.adsb.create() self.area.create() self.asas.create() self.perf.create() self.trails.create() return True 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(acid, actype, aclat, aclon, achdg, acalt, acspd) self.ap.selalt(len(self.lat) - 1, altref, acvs) self.vs[-1] = acvs def delete(self, acid): """Delete an aircraft""" # Look up index of aircraft idx = self.id2idx(acid) # Do nothing if not found if idx < 0: return False # Decrease number of aircraft self.ntraf = self.ntraf - 1 # Delete all aircraft parameters super(Traffic, self).delete(idx) # ----- Submodules of Traffic ----- self.perf.delete(idx) self.area.delete(idx) return True def update(self, simt, simdt): # Update only if there is traffic --------------------- if self.ntraf == 0: return #---------- Atmosphere -------------------------------- self.p, self.rho, self.Temp = vatmos(self.alt) #---------- ADSB Update ------------------------------- self.adsb.update(simt) #---------- Fly the Aircraft -------------------------- self.ap.update(simt) self.asas.update(simt) self.pilot.FMSOrAsas() #---------- Limit Speeds ------------------------------ self.pilot.FlightEnvelope() #---------- Kinematics -------------------------------- self.UpdateAirSpeed(simdt, simt) self.UpdateGroundSpeed(simdt) self.UpdatePosition(simdt) #---------- Performance Update ------------------------ self.perf.perf(simt) #---------- Simulate Turbulence ----------------------- self.Turbulence.Woosh(simdt) #---------- Aftermath --------------------------------- self.trails.update(simt) self.area.check(simt) return def UpdateAirSpeed(self, simdt, simt): # Acceleration self.delspd = self.pilot.spd - self.tas swspdsel = np.abs(self.delspd) > 0.4 # <1 kts = 0.514444 m/s ax = self.perf.acceleration(simdt) # Update velocities self.tas = self.tas + swspdsel * ax * np.sign(self.delspd) * simdt self.cas = vtas2cas(self.tas, self.alt) self.M = vtas2mach(self.tas, self.alt) # Turning turnrate = np.degrees(g0 * np.tan(self.bank) / np.maximum(self.tas, self.eps)) delhdg = (self.pilot.hdg - self.hdg + 180.) % 360 - 180. # [deg] self.hdgsel = np.abs(delhdg) > np.abs(2. * simdt * turnrate) # Update heading self.hdg = (self.hdg + simdt * turnrate * self.hdgsel * np.sign(delhdg)) % 360. # Update vertical speed delalt = self.pilot.alt - self.alt self.swaltsel = np.abs(delalt) > np.maximum(10 * ft, np.abs(2. * simdt * np.abs(self.vs))) self.vs = self.swaltsel * np.sign(delalt) * np.abs(self.pilot.vs) def UpdateGroundSpeed(self, simdt): # Compute ground speed and track from heading, airspeed and wind if self.wind.winddim == 0: # no wind self.gsnorth = self.tas * np.cos(np.radians(self.hdg)) self.gseast = self.tas * np.sin(np.radians(self.hdg)) self.gs = self.tas self.trk = self.hdg else: windnorth, windeast = self.wind.getdata(self.lat, self.lon, self.alt) self.gsnorth = self.tas * np.cos(np.radians(self.hdg)) + windnorth self.gseast = self.tas * np.sin(np.radians(self.hdg)) + windeast self.gs = np.sqrt(self.gsnorth**2 + self.gseast**2) self.trk = np.degrees(np.arctan2(self.gseast, self.gsnorth)) % 360. def UpdatePosition(self, simdt): # Update position self.alt = np.where(self.swaltsel, self.alt + self.vs * simdt, self.pilot.alt) self.lat = self.lat + np.degrees(simdt * self.gsnorth / Rearth) self.coslat = np.cos(np.deg2rad(self.lat)) self.lon = self.lon + np.degrees(simdt * self.gseast / self.coslat / Rearth) def id2idx(self, acid): """Find index of aircraft id""" try: return self.id.index(acid.upper()) except: return -1 def setNoise(self, noise=None): """Noise (turbulence, ADBS-transmission noise, ADSB-truncated effect)""" if noise is None: return True, "Noise is currently " + ("on" if self.Turbulence.active else "off") self.Turbulence.SetNoise(noise) self.adsb.SetNoise(noise) return True def engchange(self, acid, engid): """Change of engines""" self.perf.engchange(acid, engid) return def move(self, idx, lat, lon, alt=None, hdg=None, casmach=None, vspd=None): self.lat[idx] = lat self.lon[idx] = lon if alt: self.alt[idx] = alt self.apalt[idx] = alt if hdg: self.hdg[idx] = hdg self.ap.trk[idx] = hdg if casmach: self.tas[idx], self.aspd[-1], dummy = casormach(casmach, alt) if vspd: self.vs[idx] = vspd self.swvnav[idx] = False def nom(self, idx): """ Reset acceleration back to nominal (1 kt/s^2): NOM acid """ self.ax[idx] = kts def poscommand(self, idxorwp):# Show info on aircraft(int) or waypoint or airport (str) """POS command: Show info or an aircraft, airport, waypoint or navaid""" # Aircraft index if type(idxorwp)==int and idxorwp >= 0: idx = idxorwp acid = self.id[idx] actype = self.type[idx] latlon = latlon2txt(self.lat[idx], self.lon[idx]) alt = round(self.alt[idx] / ft) hdg = round(self.hdg[idx]) trk = round(self.trk[idx]) cas = round(self.cas[idx] / kts) tas = round(self.tas[idx] / kts) gs = round(self.gs[idx]/kts) M = self.M[idx] VS = round(self.vs[idx]/ft*60.) route = self.ap.route[idx] # Position report lines = "Info on %s %s index = %d\n" %(acid, actype, idx) \ + "Pos: "+latlon+ "\n" \ + "Hdg: %03d Trk: %03d\n" %(hdg, trk) \ + "Alt: %d ft V/S: %d fpm\n" %(alt,VS) \ + "CAS/TAS/GS: %d/%d/%d kts M: %.3f\n"%(cas,tas,gs,M) # FMS AP modes if self.swlnav[idx] and route.nwp > 0 and route.iactwp >= 0: if self.swvnav[idx]: lines = lines + "VNAV, " lines += "LNAV to " + route.wpname[route.iactwp] + "\n" # Flight info: Destination and origin if self.ap.orig[idx] != "" or self.ap.dest[idx] != "": lines = lines + "Flying" if self.ap.orig[idx] != "": lines = lines + " from " + self.ap.orig[idx] if self.ap.dest[idx] != "": lines = lines + " to " + self.ap.dest[idx] # Show a/c info and highlight route of aircraft in radar window # and pan to a/c (to show route) return bs.scr.showacinfo(acid,lines) # Waypoint: airport, navaid or fix else: wp = idxorwp.upper() # Reference position for finding nearest reflat = bs.scr.ctrlat reflon = bs.scr.ctrlon lines = "Info on "+wp+":\n" # First try airports (most used and shorter, hence faster list) iap = bs.navdb.getaptidx(wp) if iap>=0: aptypes = ["large","medium","small"] lines = lines + bs.navdb.aptname[iap]+"\n" \ + "is a "+ aptypes[max(-1,bs.navdb.aptype[iap]-1)] \ +" airport at:\n" \ + latlon2txt(bs.navdb.aptlat[iap], \ bs.navdb.aptlon[iap]) + "\n" \ + "Elevation: " \ + str(int(round(bs.navdb.aptelev[iap]/ft))) \ + " ft \n" # Show country name try: ico = bs.navdb.cocode2.index(bs.navdb.aptco[iap].upper()) lines = lines + "in "+bs.navdb.coname[ico]+" ("+ \ bs.navdb.aptco[iap]+")" except: ico = -1 lines = lines + "Country code: "+bs.navdb.aptco[iap] try: rwytxt = str(bs.navdb.rwythresholds[bs.navdb.aptid[iap]].keys()) lines = lines + "\nRunways: " +rwytxt.strip("[]").replace("'","") except: pass # Not found as airport, try waypoints & navaids else: iwps = bs.navdb.getwpindices(wp,reflat,reflon) if iwps[0]>=0: typetxt = "" desctxt = "" lastdesc = "XXXXXXXX" for i in iwps: # One line type text if typetxt == "": typetxt = typetxt+bs.navdb.wptype[i] else: typetxt = typetxt+" and "+bs.navdb.wptype[i] # Description: multi-line samedesc = bs.navdb.wpdesc[i]==lastdesc if desctxt == "": desctxt = desctxt +bs.navdb.wpdesc[i] lastdesc = bs.navdb.wpdesc[i] elif not samedesc: desctxt = desctxt +"\n"+bs.navdb.wpdesc[i] lastdesc = bs.navdb.wpdesc[i] # Navaid: frequency if bs.navdb.wptype[i] in ["VOR","DME","TACAN"] and not samedesc: desctxt = desctxt + " "+ str(bs.navdb.wpfreq[i])+" MHz" elif bs.navdb.wptype[i]=="NDB" and not samedesc: desctxt = desctxt+ " " + str(bs.navdb.wpfreq[i])+" kHz" iwp = iwps[0] # Basic info lines = lines + wp +" is a "+ typetxt \ + " at\n"\ + latlon2txt(bs.navdb.wplat[iwp], \ bs.navdb.wplon[iwp]) # Navaids have description if len(desctxt)>0: lines = lines+ "\n" + desctxt # VOR give variation if bs.navdb.wptype[iwp]=="VOR": lines = lines + "\nVariation: "+ \ str(bs.navdb.wpvar[iwp])+" deg" # How many others? nother = bs.navdb.wpid.count(wp)-len(iwps) if nother>0: verb = ["is ","are "][min(1,max(0,nother-1))] lines = lines +"\nThere "+verb + str(nother) +\ " other waypoint(s) also named " + wp # In which airways? connect = bs.navdb.listconnections(wp, \ bs.navdb.wplat[iwp], bs.navdb.wplon[iwp]) if len(connect)>0: awset = set([]) for c in connect: awset.add(c[0]) lines = lines+"\nAirways: "+"-".join(awset) # Try airway id else: # airway awid = wp airway = bs.navdb.listairway(awid) if len(airway)>0: lines = "" for segment in airway: lines = lines+"Airway "+ awid + ": " + \ " - ".join(segment)+"\n" lines = lines[:-1] # cut off final newline else: return False,idxorwp+" not found as a/c, airport, navaid or waypoint" # Show what we found on airport and navaid/waypoint bs.scr.echo(lines) return True def airwaycmd(self,key=""): # Show conections of a waypoint reflat = bs.scr.ctrlat reflon = bs.scr.ctrlon if key=="": return False,'AIRWAY needs waypoint or airway' if bs.navdb.awid.count(key)>0: return self.poscommand(key.upper()) else: # Find connecting airway legs wpid = key.upper() iwp = bs.navdb.getwpidx(wpid,reflat,reflon) if iwp<0: return False,key + " not found." wplat = bs.navdb.wplat[iwp] wplon = bs.navdb.wplon[iwp] connect = bs.navdb.listconnections(key.upper(),wplat,wplon) if len(connect)>0: lines = "" for c in connect: if len(c)>=2: # Add airway, direction, waypoint lines = lines+ c[0]+": to "+c[1]+"\n" return True, lines[:-1] # exclude final newline else: return False,"No airway legs found for ",key