def on_simstream_received(self, streamname, data, sender_id): if streamname == b'SIMINFO': speed, simdt, simt, simtclock, ntraf, state, scenname = data simt = tim2txt(simt)[:-3] simtclock = tim2txt(simtclock)[:-3] self.setNodeInfo(sender_id, simt, scenname) if sender_id == bs.net.actnode(): self.siminfoLabel.setText(u'<b>t:</b> %s, <b>\u0394t:</b> %.2f, <b>Speed:</b> %.1fx, <b>UTC:</b> %s, <b>Mode:</b> %s, <b>Aircraft:</b> %d, <b>Conflicts:</b> %d/%d, <b>LoS:</b> %d/%d' % (simt, simdt, speed, simtclock, self.modes[state], ntraf, self.nconf_cur, self.nconf_tot, self.nlos_cur, self.nlos_tot)) elif streamname == b'ACDATA': self.nconf_cur = data['nconf_cur'] self.nconf_tot = data['nconf_tot'] self.nlos_cur = data['nlos_cur'] self.nlos_tot = data['nlos_tot']
def setclock(self, txt=""): """ Set simulated clock time offset""" if txt == "": pass # avoid error message, just give time elif txt.upper() == "RUN": self.deltclock = 0.0 self.simtclock = self.simt elif txt.upper() == "REAL": tclock = time.localtime() self.simtclock = tclock.tm_hour * 3600. + tclock.tm_min * 60. + tclock.tm_sec self.deltclock = self.simtclock - self.simt elif txt.upper() == "UTC": utclock = time.gmtime() self.simtclock = utclock.tm_hour * 3600. + utclock.tm_min * 60. + utclock.tm_sec self.deltclock = self.simtclock - self.simt elif txt.replace(":", "").replace(".", "").isdigit(): self.simtclock = txt2tim(txt) self.deltclock = self.simtclock - self.simt else: return False, "Time syntax error" return True, "Time is now " + tim2txt(self.simtclock)
def setclock(self,txt=""): """ Set simulated clock time offset""" if txt == "": pass # avoid error message, just give time elif txt.upper()== "RUN": self.deltclock = 0.0 self.simtclock = self.simt elif txt.upper()== "REAL": tclock = time.localtime() self.simtclock = tclock.tm_hour*3600. + tclock.tm_min*60. + tclock.tm_sec self.deltclock = self.simtclock - self.simt elif txt.upper()== "UTC": utclock = time.gmtime() self.simtclock = utclock.tm_hour*3600. + utclock.tm_min*60. + utclock.tm_sec self.deltclock = self.simtclock - self.simt elif txt.replace(":","").replace(".","").isdigit(): self.simtclock = txt2tim(txt) self.deltclock = self.simtclock - self.simt else: return False,"Time syntax error" return True,"Time is now "+tim2txt(self.simtclock)
def savecmd(cmd, line): ''' Save command line to file if SAVEIC is turned on. ''' if savefile is None or cmd in saveexcl: return # Write in format "HH:MM:SS.hh> timtxt = tim2txt(bs.sim.simt - saveict0) savefile.write(f'{timtxt}>{line}\n')
def on_simstream_received(self, streamname, data, sender_id): if streamname == b'SIMINFO': speed, simdt, simt, simutc, ntraf, state, scenname = data simt = tim2txt(simt)[:-3] self.setNodeInfo(sender_id, simt, scenname) if sender_id == bs.net.actnode(): acdata = bs.net.get_nodedata().acdata self.siminfoLabel.setText(u'<b>t:</b> %s, <b>\u0394t:</b> %.2f, <b>Speed:</b> %.1fx, <b>UTC:</b> %s, <b>Mode:</b> %s, <b>Aircraft:</b> %d, <b>Conflicts:</b> %d/%d, <b>LoS:</b> %d/%d' % (simt, simdt, speed, simutc, self.modes[state], ntraf, acdata.nconf_cur, acdata.nconf_tot, acdata.nlos_cur, acdata.nlos_tot))
def stream(self, name, data, sender_id): if name == b'SIMINFO' and ConsoleUI.instance is not None: speed, simdt, simt, simutc, ntraf, state, scenname = data simt = tim2txt(simt)[:-3] self.setNodeInfo(sender_id, simt, scenname) if sender_id == bs.net.actnode(): ConsoleUI.instance.set_infoline( f'[b]t:[/b] {simt} [b]dt:[/b] {simdt} [b]Speed:[/b] {speed:.1f} [b]UTC:[/b] {simutc} [b]Mode:[/b] {self.modes[state]} [b]Aircraft:[/b] {ntraf}' ) # concate times of all nodes node_times = "".join( [self.nodes[key]['time'] for key in self.nodes]) # send to tui ConsoleUI.instance.set_nodes(copy.deepcopy(self.nodes), node_times) if name == b'ACDATA': self.extend_node_data(data, sender_id) if sender_id == bs.net.actnode(): gen_data, table_data = self.get_traffic(data) ConsoleUI.instance.set_traffic(gen_data, table_data, sender_id)
def notify(self, receiver, event): # Keep track of event processing event_processed = False # Events from the simulation threads if receiver is self: if event.type() == PanZoomEventType: if event.zoom is not None: event.origin = (self.radarwidget.width / 2, self.radarwidget.height / 2) if event.pan is not None and not event.absolute: event.pan = ( 2.0 * event.pan[0] / (self.radarwidget.zoom * self.radarwidget.ar), 2.0 * event.pan[1] / (self.radarwidget.zoom * self.radarwidget.flat_earth)) # send the pan/zoom event to the radarwidget self.radarwidget.event(event) elif event.type() == ACDataEventType: self.acdata = event self.radarwidget.update_aircraft_data(event) if self.nd.ac_id in event.id: idx = event.id.index(self.nd.ac_id.upper()) lat = event.lat[idx] lon = event.lon[idx] trk = event.trk[idx] tas = event.tas[idx] self.nd.update_aircraft_data(idx, lat, lon, tas, trk, len(event.lat)) return True elif event.type() == RouteDataEventType: self.routedata = event self.radarwidget.update_route_data(event) return True elif event.type() == DisplayShapeEventType: self.radarwidget.updatePolygon(event.name, event.data) elif event.type() == SimInfoEventType: simt = tim2txt(event.simt)[:-3] simtclock = tim2txt(event.simtclock)[:-3] self.win.setNodeInfo(manager.sender()[0], simt, event.scenname) if manager.sender()[0] == manager.actnode(): self.simt = event.simt self.win.siminfoLabel.setText( u'<b>t:</b> %s, <b>\u0394t:</b> %.2f, <b>Speed:</b> %.1fx, <b>UTC:</b> %s, <b>Mode:</b> %s, <b>Aircraft:</b> %d, <b>Conflicts:</b> %d/%d, <b>LoS:</b> %d/%d' % (simt, event.simdt, event.sys_freq, simtclock, self.modes[event.mode], event.n_ac, self.acdata.nconf_cur, self.acdata.nconf_tot, self.acdata.nlos_cur, self.acdata.nlos_tot)) return True elif event.type() == StackTextEventType: event_processed = True if event.disptext: self.win.console.echo(event.disptext) if event.cmdtext: self.win.console.setCmdline(event.cmdtext) elif event.type() == StackInitEventType: event_processed = True self.win.console.addStackHelp(manager.sender()[0], event.stackdict) elif event.type() == ShowDialogEventType: if event.dialog_type == event.filedialog_type: self.show_file_dialog() elif event.dialog_type == event.docwin_type: self.show_doc_window(event.cmd) return True elif event.type() == DisplayFlagEventType: # Switch/toggle/cycle radar screen features e.g. from SWRAD command if event.switch == 'RESET': self.radarwidget.clearNodeData() # Coastlines elif event.switch == "GEO": self.radarwidget.show_coast = not self.radarwidget.show_coast # FIR boundaries elif event.switch == "FIR": self.radarwidget.showfir = not self.radarwidget.showfir # Airport: 0 = None, 1 = Large, 2= All elif event.switch == "APT": self.radarwidget.show_apt = not self.radarwidget.show_apt # Waypoint: 0 = None, 1 = VOR, 2 = also WPT, 3 = Also terminal area wpts elif event.switch == "VOR" or event.switch == "WPT" or event.switch == "WP" or event.switch == "NAV": self.radarwidget.show_apt = not self.radarwidget.show_apt # Satellite image background on/off elif event.switch == "SAT": self.radarwidget.show_map = not self.radarwidget.show_map # Satellite image background on/off elif event.switch == "TRAF": self.radarwidget.show_traf = not self.radarwidget.show_traf # ND window for selected aircraft elif event.switch == "ND": self.nd.setAircraftID(event.argument) self.nd.setVisible(not self.nd.isVisible()) elif event.switch == "SSD": self.radarwidget.show_ssd(event.argument) elif event.switch == "SYM": # For now only toggle PZ self.radarwidget.show_pz = not self.radarwidget.show_pz elif event.switch == "DEFWPT": self.radarwidget.defwpt(event.argument) elif event.switch == "FILTERALT": # First argument is an on/off flag nact = self.radarwidget.nodedata[manager.sender()[0]] if event.argument[0]: nact.filteralt = event.argument[1:] else: nact.filteralt = False return True elif event.type() == AMANEventType: # self.aman.update(self.simt, event) pass # Mouse/trackpad event handling for the Radar widget elif receiver is self.radarwidget and self.radarwidget.initialized: panzoom = None if event.type() == QEvent.Wheel: # For mice we zoom with control/command and the scrolwheel if event.modifiers() & Qt.ControlModifier: origin = (event.pos().x(), event.pos().y()) zoom = 1.0 try: if event.pixelDelta(): # High resolution scroll zoom *= (1.0 + 0.01 * event.pixelDelta().y()) else: # Low resolution scroll zoom *= (1.0 + 0.001 * event.angleDelta().y()) except: zoom *= (1.0 + 0.001 * event.delta()) panzoom = PanZoomEvent(zoom=zoom, origin=origin) # For touchpad scroll (2D) is used for panning else: try: dlat = 0.01 * event.pixelDelta().y() / ( self.radarwidget.zoom * self.radarwidget.ar) dlon = -0.01 * event.pixelDelta().x() / ( self.radarwidget.zoom * self.radarwidget.flat_earth) panzoom = PanZoomEvent(pan=(dlat, dlon)) except: pass # For touchpad, pinch gesture is used for zoom elif event.type() == QEvent.Gesture: zoom = None pan = None dlat = 0.0 dlon = 0.0 for g in event.gestures(): if g.gestureType() == Qt.PinchGesture: if zoom is None: zoom = 1.0 zoom *= g.scaleFactor() if correct_pinch: zoom /= g.lastScaleFactor() elif g.gestureType() == Qt.PanGesture: if abs(g.delta().y() + g.delta().x()) > 1e-1: dlat += 0.005 * g.delta().y() / ( self.radarwidget.zoom * self.radarwidget.ar) dlon -= 0.005 * g.delta().x() / ( self.radarwidget.zoom * self.radarwidget.flat_earth) pan = (dlat, dlon) if pan is not None or zoom is not None: panzoom = PanZoomEvent(pan, zoom, self.mousepos) elif event.type( ) == QEvent.MouseButtonPress and event.button() & Qt.LeftButton: event_processed = True self.mousedragged = False # For mice we pan with control/command and mouse movement. Mouse button press marks the beginning of a pan self.prevmousepos = (event.x(), event.y()) elif event.type() == QEvent.MouseButtonRelease and event.button( ) & Qt.LeftButton and not self.mousedragged: event_processed = True lat, lon = self.radarwidget.pixelCoordsToLatLon( event.x(), event.y()) tostack, tocmdline = radarclick(self.win.console.command_line, lat, lon, self.acdata, self.routedata) if len(tocmdline) > 0: if '\n' in tocmdline: self.win.console.setCmdline('') # Clear any shape command preview on the radar display self.radarwidget.previewpoly(None) else: self.win.console.appendCmdline(tocmdline) if len(tostack) > 0: self.win.console.stack(tostack) elif event.type() == QEvent.MouseMove: event_processed = True self.mousedragged = True self.mousepos = (event.x(), event.y()) if event.buttons() & Qt.LeftButton: dlat = 0.003 * (event.y() - self.prevmousepos[1]) / ( self.radarwidget.zoom * self.radarwidget.ar) dlon = 0.003 * (self.prevmousepos[0] - event.x()) / ( self.radarwidget.zoom * self.radarwidget.flat_earth) self.prevmousepos = (event.x(), event.y()) panzoom = PanZoomEvent(pan=(dlat, dlon)) # Update pan/zoom to simulation thread only when the pan/zoom gesture is finished elif (event.type() == QEvent.MouseButtonRelease or event.type() == QEvent.TouchEnd) and self.panzoomchanged: self.panzoomchanged = False manager.sendEvent( PanZoomEvent(pan=(self.radarwidget.panlat, self.radarwidget.panlon), zoom=self.radarwidget.zoom, absolute=True)) # If we've just processed a change to pan and/or zoom, send the event to the radarwidget if panzoom is not None: self.panzoomchanged = True return self.radarwidget.event(panzoom) # Send all key presses directly to the main window if event.type() == QEvent.KeyPress: self.win.keyPressEvent(event) return True # If we haven't processed the event: call Base Class Method to Continue Normal Event Processing if not event_processed: return super(Gui, self).notify(receiver, event) if self.win.console.cmd in [ 'AREA', 'BOX', 'POLY', 'POLYALT', 'POLYGON', 'CIRCLE', 'LINE' ]: if self.mousepos != self.prevmousepos and len( self.win.console.args) >= 2: self.prevmousepos = self.mousepos try: # get the largest even number of points start = 0 if self.win.console.cmd == 'AREA' else 3 if self.win.console.cmd == 'POLYALT' else 1 end = ( (len(self.win.console.args) - start) / 2) * 2 + start data = [float(v) for v in self.win.console.args[start:end]] data += self.radarwidget.pixelCoordsToLatLon( *self.mousepos) self.radarwidget.previewpoly(self.win.console.cmd, data) except ValueError: pass event.accept() return True
def update(self): """Draw a new frame""" # First check for keys & mouse self.keyb.update() # Navdisp mode: get center: if self.swnavdisp: i = bs.traf.id2idx(self.ndacid) if i >= 0: self.ndlat = bs.traf.lat[i] self.ndlon = bs.traf.lon[i] self.ndcrs = bs.traf.hdg[i] else: self.swnavdisp = False else: self.ndcrs = 0.0 # Radar window # --------------Background-------------- if self.redrawradbg or self.swnavdisp: if self.swnavdisp or not self.swsat: self.radbmp.fill(darkgrey) else: #--------------Satellite image-------------- navsel = (self.lat0, self.lat1, \ self.lon0, self.lon1) if not self.satsel == navsel: # Map cutting and scaling: normal case if self.lon1 > self.lon0: x0 = max(0, self.lon0 * self.mapax + self.mapbx) x1 = min(self.mapbmp.get_width() - 1, \ self.lon1 * self.mapax + self.mapbx) y1 = min(self.mapbmp.get_height() - 1, \ self.lat0 * self.mapay + self.mapby) y0 = max(0, self.lat1 * self.mapay + self.mapby) selrect = pg.Rect(x0, y0, abs(x1 - x0), abs(y1 - y0)) mapsel = self.mapbmp.subsurface(selrect) self.submap = pg.transform.scale(mapsel, \ (self.width, self.height)) self.radbmp.blit(self.submap, (0, 0)) else: # Wrap around case: clip two segments w0 = int(self.width * (180. - self.lon0) / \ (180.0 - self.lon0 + self.lon1 + 180.)) w1 = int(self.width - w0) # Left part x0 = max(0, self.lon0 * self.mapax + self.mapbx) x1 = self.mapbmp.get_width() - 1 y1 = min(self.mapbmp.get_height() - 1, \ self.lat0 * self.mapay + self.mapby) y0 = max(0, self.lat1 * self.mapay + self.mapby) selrect = pg.Rect(x0, y0, abs(x1 - x0), abs(y1 - y0)) mapsel = self.mapbmp.subsurface(selrect) self.submap = pg.transform.scale(mapsel, \ (w0, self.height)) self.radbmp.blit(self.submap, (0, 0)) # Right half x0 = 0 x1 = min(self.mapbmp.get_width() - 1, \ self.lon1 * self.mapax + self.mapbx) selrect = pg.Rect(x0, y0, abs(x1 - x0), abs(y1 - y0)) mapsel = self.mapbmp.subsurface(selrect) self.submap = pg.transform.scale(mapsel, \ (w1, self.height)) self.radbmp.blit(self.submap, (w0, 0)) self.submap = self.radbmp.copy() self.satsel = navsel else: # Map blit only self.radbmp.blit(self.submap, (0, 0)) if self.swgrid and not self.swnavdisp: # ------Draw lat/lon grid------ ngrad = int(self.lon1 - self.lon0) if ngrad >= 10: step = 10 i0 = step * int(self.lon0 / step) j0 = step * int(self.lat0 / step) else: step = 1 i0 = int(self.lon0) j0 = int(self.lon0) for i in range(i0, int(self.lon1 + 1.), step): x, y = self.ll2xy(self.ctrlat, i) pg.draw.line(self.radbmp, lightgreygreen, \ (x, 0), (x, self.height)) for j in range(j0, int(self.lat1 + 1.), step): x, y = self.ll2xy(j, self.ctrlon) pg.draw.line(self.radbmp, lightgreygreen, \ (0, y), (self.width, y)) #------ Draw coastlines ------ if self.swgeo: # cx,cy = -1,-1 geosel = (self.lat0, self.lon0, self.lat1, self.lon1) if self.geosel != geosel: self.geosel = geosel self.cstsel = np.where( self.onradar(self.coastlat0, self.coastlon0) + \ self.onradar(self.coastlat1, self.coastlon1)) # print len(self.cstsel[0])," coastlines" self.cx0, self.cy0 = self.ll2xy(self.coastlat0, self.coastlon0) self.cx1, self.cy1 = self.ll2xy(self.coastlat1, self.coastlon1) for i in list(self.cstsel[0]): pg.draw.line(self.radbmp, grey, (self.cx0[i], self.cy0[i]), \ (self.cx1[i], self.cy1[i])) #------ Draw FIRs ------ if self.swfir: self.firx0, self.firy0 = self.ll2xy(bs.navdb.firlat0, \ bs.navdb.firlon0) self.firx1, self.firy1 = self.ll2xy(bs.navdb.firlat1, \ bs.navdb.firlon1) for i in range(len(self.firx0)): pg.draw.line(self.radbmp, lightcyan, (self.firx0[i], self.firy0[i]), (self.firx1[i], self.firy1[i])) # -----------------Waypoint & airport symbols----------------- # Check whether we need to reselect waypoint set to be drawn navsel = (self.lat0, self.lat1, \ self.lon0, self.lon1) if self.navsel != navsel: self.navsel = navsel # Make list of indices of waypoints & airports on screen self.wpinside = list(np.where(self.onradar(bs.navdb.wplat, \ bs.navdb.wplon))[0]) self.wptsel = [] for i in self.wpinside: if self.wpsw == 3 or \ (self.wpsw == 1 and len(bs.navdb.wpid[i]) == 3) or \ (self.wpsw == 2 and bs.navdb.wpid[i].isalpha()): self.wptsel.append(i) self.wptx, self.wpty = self.ll2xy(bs.navdb.wplat, bs.navdb.wplon) self.apinside = list(np.where(self.onradar(bs.navdb.aptlat, \ bs.navdb.aptlon))[0]) self.aptsel = [] for i in self.apinside: if self.apsw == 2 or (self.apsw == 1 and \ bs.navdb.aptmaxrwy[i] > 1000.): self.aptsel.append(i) self.aptx, self.apty = self.ll2xy(bs.navdb.aptlat, bs.navdb.aptlon) #------- Draw waypoints ------- if self.wpsw > 0: # print len(self.wptsel)," waypoints" if len(self.wptsel) < self.maxnrwp: wptrect = self.wptsymbol.get_rect() for i in self.wptsel: # wptrect.center = self.ll2xy(bs.navdb.wplat[i], \ # bs.navdb.wplon[i]) wptrect.center = self.wptx[i], self.wpty[i] self.radbmp.blit(self.wptsymbol, wptrect) # If waypoint label bitmap does not yet exist, make it if not self.wpswbmp[i]: self.wplabel[i] = pg.Surface((80, 30), 0, self.win) self.fontnav.printat(self.wplabel[i], 0, 0, \ bs.navdb.wpid[i]) self.wpswbmp[i] = True # In any case, blit it xtxt = wptrect.right + 2 ytxt = wptrect.top self.radbmp.blit(self.wplabel[i], (xtxt, ytxt), \ None, pg.BLEND_ADD) if not self.wpswbmp[i]: xtxt = wptrect.right + 2 ytxt = wptrect.top # self.fontnav.printat(self.radbmp,xtxt,ytxt, \ # bs.navdb.wpid[i]) #------- Draw airports ------- if self.apsw > 0: # if len(self.aptsel)<800: aptrect = self.aptsymbol.get_rect() # print len(self.aptsel)," airports" for i in self.aptsel: # aptrect.center = self.ll2xy(bs.navdb.aptlat[i], \ # bs.navdb.aptlon[i]) aptrect.center = self.aptx[i], self.apty[i] self.radbmp.blit(self.aptsymbol, aptrect) # If airport label bitmap does not yet exist, make it if not self.apswbmp[i]: self.aplabel[i] = pg.Surface((50, 30), 0, self.win) self.fontnav.printat(self.aplabel[i], 0, 0, \ bs.navdb.aptid[i]) self.apswbmp[i] = True # In either case, blit it xtxt = aptrect.right + 2 ytxt = aptrect.top self.radbmp.blit(self.aplabel[i], (xtxt, ytxt), \ None, pg.BLEND_ADD) # self.fontnav.printat(self.radbmp,xtxt,ytxt, \ # bs.navdb.aptid[i]) #---------- Draw background trails ---------- if bs.traf.trails.active: bs.traf.trails.buffer() # move all new trails to background trlsel = list(np.where( self.onradar(bs.traf.trails.bglat0, bs.traf.trails.bglon0) + \ self.onradar(bs.traf.trails.bglat1, bs.traf.trails.bglon1))[0]) x0, y0 = self.ll2xy(bs.traf.trails.bglat0, bs.traf.trails.bglon0) x1, y1 = self.ll2xy(bs.traf.trails.bglat1, bs.traf.trails.bglon1) for i in trlsel: pg.draw.aaline(self.radbmp, bs.traf.trails.bgcol[i], \ (x0[i], y0[i]), (x1[i], y1[i])) #---------- Draw ADSB Coverage Area if self.swAdsbCoverage: # These points are based on the positions of the antennas with range = 200km adsbCoverageLat = [53.7863,53.5362,52.8604,51.9538,51.2285,50.8249,50.7382, 50.9701,51.6096,52.498,53.4047,53.6402] adsbCoverageLon = [4.3757,5.8869,6.9529,7.2913,6.9312,6.251,5.7218,4.2955, 3.2162,2.7701,3.1117,3.4891] for i in range(0,len(adsbCoverageLat)): if i == len(adsbCoverageLat)-1: x0, y0 = self.ll2xy(adsbCoverageLat[i],adsbCoverageLon[i]) x1, y1 = self.ll2xy(adsbCoverageLat[0],adsbCoverageLon[0]) else: x0, y0 = self.ll2xy(adsbCoverageLat[i],adsbCoverageLon[i]) x1, y1 = self.ll2xy(adsbCoverageLat[i+1],adsbCoverageLon[i+1]) pg.draw.line(self.radbmp, red,(x0, y0), (x1, y1)) # User defined objects for i in range(len(self.objtype)): # Draw LINE or POLYGON with objdata = [lat0,lon,lat1,lon1,lat2,lon2,..] if self.objtype[i]=='LINE' or self.objtype[i]=="POLY": npoints = len(self.objdata[i])/2 print(npoints) x0,y0 = self.ll2xy(self.objdata[i][0],self.objdata[i][1]) for j in range(1,npoints): x1,y1 = self.ll2xy(self.objdata[i][j*2],self.objdata[i][j*2+1]) pg.draw.line(self.radbmp,self.objcolor[i],(x0, y0), (x1, y1)) x0,y0 = x1,y1 if self.objtype[i]=="POLY": x1,y1 = self.ll2xy(self.objdata[i][0],self.objdata[i][1]) pg.draw.line(self.radbmp,self.objcolor[i],(x0, y0), (x1, y1)) # Draw bounding box of objdata = [lat0,lon0,lat1,lon1] elif self.objtype[i]=='BOX': lat0 = min(self.objdata[i][0],self.objdata[i][2]) lon0 = min(self.objdata[i][1],self.objdata[i][3]) lat1 = max(self.objdata[i][0],self.objdata[i][2]) lon1 = max(self.objdata[i][1],self.objdata[i][3]) x0,y0 = self.ll2xy(lat1,lon0) x1,y1 = self.ll2xy(lat0,lon1) pg.draw.rect(self.radbmp,self.objcolor[i],pg.Rect(x0, y0, x1-x0, y1-y0),1) # Draw circle with objdata = [latcenter,loncenter,radiusnm] elif self.objtype[i]=='CIRCLE': pass xm,ym = self.ll2xy(self.objdata[i][0],self.objdata[i][1]) xtop,ytop = self.ll2xy(self.objdata[i][0]+self.objdata[i][2]/60.,self.objdata[i][1]) radius = int(round(abs(ytop-ym))) pg.draw.circle(self.radbmp, self.objcolor[i], (int(xm),int(ym)), radius, 1) # Reset background drawing switch self.redrawradbg = False ############################################################################## # END OF BACKGROUND DRAWING # ############################################################################## # Blit background self.win.blit(self.radbmp, (0, 0)) # Decide to redraw radar picture of a/c syst = pg.time.get_ticks() * 0.001 redrawrad = self.redrawradbg or abs(syst - self.radt0) >= self.radardt if redrawrad: self.radt0 = syst # Update lats drawing time of radar # Select which aircraft are within screen area trafsel = np.where((bs.traf.lat > self.lat0) * (bs.traf.lat < self.lat1) * \ (bs.traf.lon > self.lon0) * (bs.traf.lon < self.lon1))[0] # ------------------- Draw aircraft ------------------- # Convert lat,lon to x,y trafx, trafy = self.ll2xy(bs.traf.lat, bs.traf.lon) trafy -= bs.traf.alt*self.isoalt if bs.traf.trails.active: ltx, lty = self.ll2xy(bs.traf.trails.lastlat, bs.traf.trails.lastlon) # Find pixel size of horizontal separation on screen pixelrad=self.dtopix_eq(bs.traf.asas.R/2) # Loop through all traffic indices which we found on screen for i in trafsel: # Get index of ac symbol, based on heading and its rect object isymb = int(round((bs.traf.hdg[i] - self.ndcrs) / 6.)) % 60 pos = self.acsymbol[isymb].get_rect() # Draw aircraft symbol pos.centerx = trafx[i] pos.centery = trafy[i] dy = self.fontrad.linedy * 7 / 6 # Draw aircraft altitude line if self.isoalt>1e-7: pg.draw.line(self.win,white,(int(trafx[i]),int(trafy[i])),(int(trafx[i]),int(trafy[i]+bs.traf.alt[i]*self.isoalt))) # Normal symbol if no conflict else amber toosmall=self.lat1-self.lat0>6 #don't draw circles if zoomed out too much if len(bs.traf.asas.iconf[i]) == 0: self.win.blit(self.acsymbol[isymb], pos) if self.swsep and not toosmall: pg.draw.circle(self.win,green,(int(trafx[i]),int(trafy[i])),pixelrad,1) else: self.win.blit(self.ambacsymbol[isymb], pos) if self.swsep and not toosmall: pg.draw.circle(self.win,amber,(int(trafx[i]),int(trafy[i])),pixelrad,1) # Draw last trail part if bs.traf.trails.active: pg.draw.line(self.win, tuple(bs.traf.trails.accolor[i]), (ltx[i], lty[i]), (trafx[i], trafy[i])) # Label text label = [] if self.swlabel > 0: label.append(bs.traf.id[i]) # Line 1 of label: id else: label.append(" ") if self.swlabel > 1: label.append(str(int(bs.traf.alt[i] / ft))) # Line 2 of label: altitude else: label.append(" ") if self.swlabel > 2: cas = bs.traf.cas[i] / kts label.append(str(int(round(cas)))) # line 3 of label: speed else: label.append(" ") # Check for changes in traffic label text if not (type(bs.traf.label[i])==list) or \ not (type(bs.traf.label[i][3])==str) or \ not (label[:3] == bs.traf.label[i][:3]): bs.traf.label[i] = [] labelbmp = pg.Surface((100, 60), 0, self.win) if len(bs.traf.asas.iconf[i]) == 0: acfont = self.fontrad else: acfont = self.fontamb acfont.printat(labelbmp, 0, 0, label[0]) acfont.printat(labelbmp, 0, dy, label[1]) acfont.printat(labelbmp, 0, 2 * dy, label[2]) bs.traf.label[i].append(label[0]) bs.traf.label[i].append(label[1]) bs.traf.label[i].append(label[2]) bs.traf.label[i].append(labelbmp) # Blit label dest = bs.traf.label[i][3].get_rect() dest.top = trafy[i] - 5 dest.left = trafx[i] + 15 self.win.blit(bs.traf.label[i][3], dest, None, pg.BLEND_ADD) # Draw aircraft speed vectors if self.swspd: # just a nominal length: a speed of 150 kts will be displayed # as an arrow of 30 pixels long on screen nomlength = 30 nomspeed = 150. vectorlength = float(nomlength)*bs.traf.tas[i]/nomspeed spdvcx = trafx[i] + np.sin(np.radians(bs.traf.trk[i])) * vectorlength spdvcy = trafy[i] - np.cos(np.radians(bs.traf.trk[i])) * vectorlength \ - bs.traf.vs[i]/nomspeed*nomlength*self.isoalt / \ self.dtopix_eq(1e5)*1e5 pg.draw.line(self.win,green,(trafx[i],trafy[i]),(spdvcx,spdvcy)) # ---- End of per aircraft i loop # Draw conflicts: line from a/c to closest point of approach if bs.traf.asas.nconf>0: xc,yc = self.ll2xy(bs.traf.asas.latowncpa,bs.traf.asas.lonowncpa) yc = yc - bs.traf.asas.altowncpa*self.isoalt for j in range(bs.traf.asas.nconf): i = bs.traf.id2idx(bs.traf.asas.confpairs[j][0]) if i>=0 and i<bs.traf.ntraf and (i in trafsel): pg.draw.line(self.win,amber,(xc[j],yc[j]),(trafx[i],trafy[i])) # Draw selected route: if self.acidrte != "": i = bs.traf.id2idx(self.acidrte) if i >= 0: for j in range(0,bs.traf.ap.route[i].nwp): if j==0: x1,y1 = self.ll2xy(bs.traf.ap.route[i].wplat[j], \ bs.traf.ap.route[i].wplon[j]) else: x0,y0 = x1,y1 x1,y1 = self.ll2xy(bs.traf.ap.route[i].wplat[j], \ bs.traf.ap.route[i].wplon[j]) pg.draw.line(self.win, magenta,(x0,y0),(x1,y1)) if j>=len(self.rtewpid) or not self.rtewpid[j]== bs.traf.ap.route[i].wpname[j]: # Waypoint name labels # If waypoint label bitmap does not yet exist, make it # Waypoint name and constraint(s), if there are any txt = bs.traf.ap.route[i].wpname[j] alt = bs.traf.ap.route[i].wpalt[j] spd = bs.traf.ap.route[i].wpspd[j] if alt>=0. or spd >=0.: # Altitude if alt < 0: txt = txt + " -----/" elif alt > 4500 * ft: FL = int(round((alt/(100.*ft)))) txt = txt+" FL"+str(FL)+"/" else: txt = txt+" "+str(int(round(alt / ft))) + "/" # Speed if spd < 0: txt = txt+"---" else: txt = txt+str(int(round(spd / kts))) wplabel = pg.Surface((128, 32), 0, self.win) self.fontnav.printat(wplabel, 0, 0, \ txt) if j>=len(self.rtewpid): self.rtewpid.append(txt) self.rtewplabel.append(wplabel) else: self.rtewpid[j] = txt self.rtewplabel[j] = wplabel # In any case, blit the waypoint name xtxt = x1 + 7 ytxt = y1 - 3 self.radbmp.blit(self.rtewplabel[j], (xtxt, ytxt), \ None, pg.BLEND_ADD) # Line from aircraft to active waypoint if bs.traf.ap.route[i].iactwp == j: x0,y0 = self.ll2xy(bs.traf.lat[i],bs.traf.lon[i]) pg.draw.line(self.win, magenta,(x0,y0),(x1,y1)) # Draw aircraft trails which are on screen if bs.traf.trails.active: trlsel = list(np.where( self.onradar(bs.traf.trails.lat0, bs.traf.trails.lon0) + \ self.onradar(bs.traf.trails.lat1, bs.traf.trails.lon1))[0]) x0, y0 = self.ll2xy(bs.traf.trails.lat0, bs.traf.trails.lon0) x1, y1 = self.ll2xy(bs.traf.trails.lat1, bs.traf.trails.lon1) for i in trlsel: pg.draw.line(self.win, bs.traf.trails.col[i], \ (x0[i], y0[i]), (x1[i], y1[i])) # Redraw background => buffer ; if >1500 foreground linepieces on screen if len(trlsel) > 1500: self.redrawradbg = True # Draw edit window self.editwin.update() if self.redrawradbg or redrawrad or self.editwin.redraw: self.win.blit(self.menu.bmps[self.menu.ipage], \ (self.menu.x, self.menu.y)) self.win.blit(self.editwin.bmp, (self.editwin.winx, self.editwin.winy)) # Draw frames pg.draw.rect(self.win, white, self.editwin.rect, 1) pg.draw.rect(self.win, white, pg.Rect(1, 1, self.width - 1, self.height - 1), 1) # Add debug line self.fontsys.printat(self.win, 10, 2, tim2txt(bs.sim.simtclock)) self.fontsys.printat(self.win, 10, 18, tim2txt(bs.sim.simt)) self.fontsys.printat(self.win, 10+80, 2, \ "ntraf = " + str(bs.traf.ntraf)) self.fontsys.printat(self.win, 10+160, 2, \ "Freq=" + str(int(len(bs.sim.dts) / max(0.001, sum(bs.sim.dts))))) self.fontsys.printat(self.win, 10+240, 2, \ "#LOS = " + str(len(bs.traf.asas.LOSlist_now))) self.fontsys.printat(self.win, 10+240, 18, \ "Total LOS = " + str(len(bs.traf.asas.LOSlist_all))) self.fontsys.printat(self.win, 10+240, 34, \ "#Con = " + str(len(bs.traf.asas.conflist_now))) self.fontsys.printat(self.win, 10+240, 50, \ "Total Con = " + str(len(bs.traf.asas.conflist_all))) # Frame ready, flip to screen pg.display.flip() # If needed, take a screenshot if self.screenshot: pg.image.save(self.win,self.screenshotname) self.screenshot=False return
def update(self): """Draw a new frame""" # First check for keys & mouse self.keyb.update() # Navdisp mode: get center: if self.swnavdisp: i = bs.traf.id2idx(self.ndacid) if i >= 0: self.ndlat = bs.traf.lat[i] self.ndlon = bs.traf.lon[i] self.ndcrs = bs.traf.hdg[i] else: self.swnavdisp = False else: self.ndcrs = 0.0 # Radar window # --------------Background-------------- if self.redrawradbg or self.swnavdisp: if self.swnavdisp or not self.swsat: self.radbmp.fill(darkgrey) else: #--------------Satellite image-------------- navsel = (self.lat0, self.lat1, \ self.lon0, self.lon1) if not self.satsel == navsel: # Map cutting and scaling: normal case if self.lon1 > self.lon0: x0 = max(0, self.lon0 * self.mapax + self.mapbx) x1 = min(self.mapbmp.get_width() - 1, \ self.lon1 * self.mapax + self.mapbx) y1 = min(self.mapbmp.get_height() - 1, \ self.lat0 * self.mapay + self.mapby) y0 = max(0, self.lat1 * self.mapay + self.mapby) selrect = pg.Rect(x0, y0, abs(x1 - x0), abs(y1 - y0)) mapsel = self.mapbmp.subsurface(selrect) self.submap = pg.transform.scale(mapsel, \ (self.width, self.height)) self.radbmp.blit(self.submap, (0, 0)) else: # Wrap around case: clip two segments w0 = int(self.width * (180. - self.lon0) / \ (180.0 - self.lon0 + self.lon1 + 180.)) w1 = int(self.width - w0) # Left part x0 = max(0, self.lon0 * self.mapax + self.mapbx) x1 = self.mapbmp.get_width() - 1 y1 = min(self.mapbmp.get_height() - 1, \ self.lat0 * self.mapay + self.mapby) y0 = max(0, self.lat1 * self.mapay + self.mapby) selrect = pg.Rect(x0, y0, abs(x1 - x0), abs(y1 - y0)) mapsel = self.mapbmp.subsurface(selrect) self.submap = pg.transform.scale(mapsel, \ (w0, self.height)) self.radbmp.blit(self.submap, (0, 0)) # Right half x0 = 0 x1 = min(self.mapbmp.get_width() - 1, \ self.lon1 * self.mapax + self.mapbx) selrect = pg.Rect(x0, y0, abs(x1 - x0), abs(y1 - y0)) mapsel = self.mapbmp.subsurface(selrect) self.submap = pg.transform.scale(mapsel, \ (w1, self.height)) self.radbmp.blit(self.submap, (w0, 0)) self.submap = self.radbmp.copy() self.satsel = navsel else: # Map blit only self.radbmp.blit(self.submap, (0, 0)) if self.swgrid and not self.swnavdisp: # ------Draw lat/lon grid------ ngrad = int(self.lon1 - self.lon0) if ngrad >= 10: step = 10 i0 = step * int(self.lon0 / step) j0 = step * int(self.lat0 / step) else: step = 1 i0 = int(self.lon0) j0 = int(self.lon0) for i in range(i0, int(self.lon1 + 1.), step): x, y = self.ll2xy(self.ctrlat, i) pg.draw.line(self.radbmp, lightgreygreen, \ (x, 0), (x, self.height)) for j in range(j0, int(self.lat1 + 1.), step): x, y = self.ll2xy(j, self.ctrlon) pg.draw.line(self.radbmp, lightgreygreen, \ (0, y), (self.width, y)) #------ Draw coastlines ------ if self.swgeo: # cx,cy = -1,-1 geosel = (self.lat0, self.lon0, self.lat1, self.lon1) if self.geosel != geosel: self.geosel = geosel self.cstsel = np.where( self.onradar(self.coastlat0, self.coastlon0) + \ self.onradar(self.coastlat1, self.coastlon1)) # print len(self.cstsel[0])," coastlines" self.cx0, self.cy0 = self.ll2xy(self.coastlat0, self.coastlon0) self.cx1, self.cy1 = self.ll2xy(self.coastlat1, self.coastlon1) for i in list(self.cstsel[0]): pg.draw.line(self.radbmp, grey, (self.cx0[i], self.cy0[i]), \ (self.cx1[i], self.cy1[i])) #------ Draw FIRs ------ if self.swfir: self.firx0, self.firy0 = self.ll2xy(bs.navdb.firlat0, \ bs.navdb.firlon0) self.firx1, self.firy1 = self.ll2xy(bs.navdb.firlat1, \ bs.navdb.firlon1) for i in range(len(self.firx0)): pg.draw.line(self.radbmp, lightcyan, (self.firx0[i], self.firy0[i]), (self.firx1[i], self.firy1[i])) # -----------------Waypoint & airport symbols----------------- # Check whether we need to reselect waypoint set to be drawn navsel = (self.lat0, self.lat1, \ self.lon0, self.lon1) if self.navsel != navsel: self.navsel = navsel # Make list of indices of waypoints & airports on screen self.wpinside = list(np.where(self.onradar(bs.navdb.wplat, \ bs.navdb.wplon))[0]) self.wptsel = [] for i in self.wpinside: if self.wpsw == 3 or \ (self.wpsw == 1 and len(bs.navdb.wpid[i]) == 3) or \ (self.wpsw == 2 and bs.navdb.wpid[i].isalpha()): self.wptsel.append(i) self.wptx, self.wpty = self.ll2xy(bs.navdb.wplat, bs.navdb.wplon) self.apinside = list(np.where(self.onradar(bs.navdb.aptlat, \ bs.navdb.aptlon))[0]) self.aptsel = [] for i in self.apinside: if self.apsw == 2 or (self.apsw == 1 and \ bs.navdb.aptmaxrwy[i] > 1000.): self.aptsel.append(i) self.aptx, self.apty = self.ll2xy(bs.navdb.aptlat, bs.navdb.aptlon) #------- Draw waypoints ------- if self.wpsw > 0: # print len(self.wptsel)," waypoints" if len(self.wptsel) < self.maxnrwp: wptrect = self.wptsymbol.get_rect() for i in self.wptsel: # wptrect.center = self.ll2xy(bs.navdb.wplat[i], \ # bs.navdb.wplon[i]) wptrect.center = self.wptx[i], self.wpty[i] self.radbmp.blit(self.wptsymbol, wptrect) # If waypoint label bitmap does not yet exist, make it if not self.wpswbmp[i]: self.wplabel[i] = pg.Surface((80, 30), 0, self.win) self.fontnav.printat(self.wplabel[i], 0, 0, \ bs.navdb.wpid[i]) self.wpswbmp[i] = True # In any case, blit it xtxt = wptrect.right + 2 ytxt = wptrect.top self.radbmp.blit(self.wplabel[i], (xtxt, ytxt), \ None, pg.BLEND_ADD) if not self.wpswbmp[i]: xtxt = wptrect.right + 2 ytxt = wptrect.top # self.fontnav.printat(self.radbmp,xtxt,ytxt, \ # bs.navdb.wpid[i]) #------- Draw airports ------- if self.apsw > 0: # if len(self.aptsel)<800: aptrect = self.aptsymbol.get_rect() # print len(self.aptsel)," airports" for i in self.aptsel: # aptrect.center = self.ll2xy(bs.navdb.aptlat[i], \ # bs.navdb.aptlon[i]) aptrect.center = self.aptx[i], self.apty[i] self.radbmp.blit(self.aptsymbol, aptrect) # If airport label bitmap does not yet exist, make it if not self.apswbmp[i]: self.aplabel[i] = pg.Surface((50, 30), 0, self.win) self.fontnav.printat(self.aplabel[i], 0, 0, \ bs.navdb.aptid[i]) self.apswbmp[i] = True # In either case, blit it xtxt = aptrect.right + 2 ytxt = aptrect.top self.radbmp.blit(self.aplabel[i], (xtxt, ytxt), \ None, pg.BLEND_ADD) # self.fontnav.printat(self.radbmp,xtxt,ytxt, \ # bs.navdb.aptid[i]) #---------- Draw background trails ---------- if bs.traf.trails.active: bs.traf.trails.buffer() # move all new trails to background trlsel = list(np.where( self.onradar(bs.traf.trails.bglat0, bs.traf.trails.bglon0) + \ self.onradar(bs.traf.trails.bglat1, bs.traf.trails.bglon1))[0]) x0, y0 = self.ll2xy(bs.traf.trails.bglat0, bs.traf.trails.bglon0) x1, y1 = self.ll2xy(bs.traf.trails.bglat1, bs.traf.trails.bglon1) for i in trlsel: pg.draw.aaline(self.radbmp, bs.traf.trails.bgcol[i], \ (x0[i], y0[i]), (x1[i], y1[i])) #---------- Draw ADSB Coverage Area if self.swAdsbCoverage: # These points are based on the positions of the antennas with range = 200km adsbCoverageLat = [ 53.7863, 53.5362, 52.8604, 51.9538, 51.2285, 50.8249, 50.7382, 50.9701, 51.6096, 52.498, 53.4047, 53.6402 ] adsbCoverageLon = [ 4.3757, 5.8869, 6.9529, 7.2913, 6.9312, 6.251, 5.7218, 4.2955, 3.2162, 2.7701, 3.1117, 3.4891 ] for i in range(0, len(adsbCoverageLat)): if i == len(adsbCoverageLat) - 1: x0, y0 = self.ll2xy(adsbCoverageLat[i], adsbCoverageLon[i]) x1, y1 = self.ll2xy(adsbCoverageLat[0], adsbCoverageLon[0]) else: x0, y0 = self.ll2xy(adsbCoverageLat[i], adsbCoverageLon[i]) x1, y1 = self.ll2xy(adsbCoverageLat[i + 1], adsbCoverageLon[i + 1]) pg.draw.line(self.radbmp, red, (x0, y0), (x1, y1)) # User defined objects for i in range(len(self.objtype)): # Draw LINE or POLYGON with objdata = [lat0,lon,lat1,lon1,lat2,lon2,..] if self.objtype[i] == 'LINE' or self.objtype[i] == "POLY": npoints = len(self.objdata[i]) / 2 print(npoints) x0, y0 = self.ll2xy(self.objdata[i][0], self.objdata[i][1]) for j in range(1, npoints): x1, y1 = self.ll2xy(self.objdata[i][j * 2], self.objdata[i][j * 2 + 1]) pg.draw.line(self.radbmp, self.objcolor[i], (x0, y0), (x1, y1)) x0, y0 = x1, y1 if self.objtype[i] == "POLY": x1, y1 = self.ll2xy(self.objdata[i][0], self.objdata[i][1]) pg.draw.line(self.radbmp, self.objcolor[i], (x0, y0), (x1, y1)) # Draw bounding box of objdata = [lat0,lon0,lat1,lon1] elif self.objtype[i] == 'BOX': lat0 = min(self.objdata[i][0], self.objdata[i][2]) lon0 = min(self.objdata[i][1], self.objdata[i][3]) lat1 = max(self.objdata[i][0], self.objdata[i][2]) lon1 = max(self.objdata[i][1], self.objdata[i][3]) x0, y0 = self.ll2xy(lat1, lon0) x1, y1 = self.ll2xy(lat0, lon1) pg.draw.rect(self.radbmp, self.objcolor[i], pg.Rect(x0, y0, x1 - x0, y1 - y0), 1) # Draw circle with objdata = [latcenter,loncenter,radiusnm] elif self.objtype[i] == 'CIRCLE': pass xm, ym = self.ll2xy(self.objdata[i][0], self.objdata[i][1]) xtop, ytop = self.ll2xy( self.objdata[i][0] + self.objdata[i][2] / 60., self.objdata[i][1]) radius = int(round(abs(ytop - ym))) pg.draw.circle(self.radbmp, self.objcolor[i], (int(xm), int(ym)), radius, 1) # Reset background drawing switch self.redrawradbg = False ############################################################################## # END OF BACKGROUND DRAWING # ############################################################################## # Blit background self.win.blit(self.radbmp, (0, 0)) # Decide to redraw radar picture of a/c syst = pg.time.get_ticks() * 0.001 redrawrad = self.redrawradbg or abs(syst - self.radt0) >= self.radardt if redrawrad: self.radt0 = syst # Update lats drawing time of radar # Select which aircraft are within screen area trafsel = np.where((bs.traf.lat > self.lat0) * (bs.traf.lat < self.lat1) * \ (bs.traf.lon > self.lon0) * (bs.traf.lon < self.lon1))[0] # ------------------- Draw aircraft ------------------- # Convert lat,lon to x,y trafx, trafy = self.ll2xy(bs.traf.lat, bs.traf.lon) trafy -= bs.traf.alt * self.isoalt if bs.traf.trails.active: ltx, lty = self.ll2xy(bs.traf.trails.lastlat, bs.traf.trails.lastlon) # Find pixel size of horizontal separation on screen pixelrad = self.dtopix_eq(bs.traf.asas.R / 2) # Loop through all traffic indices which we found on screen for i in trafsel: # Get index of ac symbol, based on heading and its rect object isymb = int(round((bs.traf.hdg[i] - self.ndcrs) / 6.)) % 60 pos = self.acsymbol[isymb].get_rect() # Draw aircraft symbol pos.centerx = trafx[i] pos.centery = trafy[i] dy = self.fontrad.linedy * 7 / 6 # Draw aircraft altitude line if self.isoalt > 1e-7: pg.draw.line( self.win, white, (int(trafx[i]), int(trafy[i])), (int(trafx[i]), int(trafy[i] + bs.traf.alt[i] * self.isoalt))) # Normal symbol if no conflict else amber toosmall = self.lat1 - self.lat0 > 6 #don't draw circles if zoomed out too much if not bs.traf.asas.inconf[i]: self.win.blit(self.acsymbol[isymb], pos) if self.swsep and not toosmall: pg.draw.circle(self.win, green, (int(trafx[i]), int(trafy[i])), pixelrad, 1) else: self.win.blit(self.ambacsymbol[isymb], pos) if self.swsep and not toosmall: pg.draw.circle(self.win, amber, (int(trafx[i]), int(trafy[i])), pixelrad, 1) # Draw last trail part if bs.traf.trails.active: pg.draw.line(self.win, tuple(bs.traf.trails.accolor[i]), (ltx[i], lty[i]), (trafx[i], trafy[i])) # Label text label = [] if self.swlabel > 0: label.append(bs.traf.id[i]) # Line 1 of label: id else: label.append(" ") if self.swlabel > 1: if bs.traf.alt[i] > bs.traf.translvl: label.append( "FL" + str(int(round( bs.traf.alt[i] / (100. * ft))))) # Line 2 of label: altitude else: label.append(str(int( round(bs.traf.alt[i] / ft)))) # Line 2 of label: altitude else: label.append(" ") if self.swlabel > 2: cas = bs.traf.cas[i] / kts label.append(str(int( round(cas)))) # line 3 of label: speed else: label.append(" ") # Check for changes in traffic label text if not (type(bs.traf.label[i])==list) or \ not (type(bs.traf.label[i][3])==str) or \ not (label[:3] == bs.traf.label[i][:3]): bs.traf.label[i] = [] labelbmp = pg.Surface((100, 60), 0, self.win) if not bs.traf.asas.inconf[i]: acfont = self.fontrad else: acfont = self.fontamb acfont.printat(labelbmp, 0, 0, label[0]) acfont.printat(labelbmp, 0, dy, label[1]) acfont.printat(labelbmp, 0, 2 * dy, label[2]) bs.traf.label[i].append(label[0]) bs.traf.label[i].append(label[1]) bs.traf.label[i].append(label[2]) bs.traf.label[i].append(labelbmp) # Blit label dest = bs.traf.label[i][3].get_rect() dest.top = trafy[i] - 5 dest.left = trafx[i] + 15 self.win.blit(bs.traf.label[i][3], dest, None, pg.BLEND_ADD) # Draw aircraft speed vectors if self.swspd: # just a nominal length: a speed of 150 kts will be displayed # as an arrow of 30 pixels long on screen nomlength = 30 nomspeed = 150. vectorlength = float(nomlength) * bs.traf.tas[i] / nomspeed spdvcx = trafx[i] + np.sin(np.radians( bs.traf.trk[i])) * vectorlength spdvcy = trafy[i] - np.cos(np.radians(bs.traf.trk[i])) * vectorlength \ - bs.traf.vs[i]/nomspeed*nomlength*self.isoalt / \ self.dtopix_eq(1e5)*1e5 pg.draw.line(self.win, green, (trafx[i], trafy[i]), (spdvcx, spdvcy)) # ---- End of per aircraft i loop # Draw conflicts: line from a/c to closest point of approach nconf = len(bs.traf.asas.confpairs_unique) n2conf = len(bs.traf.asas.confpairs) if nconf > 0: for j in range(n2conf): i = bs.traf.id2idx(bs.traf.asas.confpairs[j][0]) if i >= 0 and i < bs.traf.ntraf and (i in trafsel): latcpa, loncpa = geo.kwikpos(bs.traf.lat[i], bs.traf.lon[i], \ bs.traf.trk[i], bs.traf.asas.tcpa[j] * bs.traf.gs[i] / nm) altcpa = bs.traf.lat[ i] + bs.traf.vs[i] * bs.traf.asas.tcpa[j] xc, yc = self.ll2xy(latcpa, loncpa) yc = yc - altcpa * self.isoalt pg.draw.line(self.win, amber, (xc, yc), (trafx[i], trafy[i])) # Draw selected route: if self.acidrte != "": i = bs.traf.id2idx(self.acidrte) if i >= 0: for j in range(0, bs.traf.ap.route[i].nwp): if j == 0: x1,y1 = self.ll2xy(bs.traf.ap.route[i].wplat[j], \ bs.traf.ap.route[i].wplon[j]) else: x0, y0 = x1, y1 x1,y1 = self.ll2xy(bs.traf.ap.route[i].wplat[j], \ bs.traf.ap.route[i].wplon[j]) pg.draw.line(self.win, magenta, (x0, y0), (x1, y1)) if j >= len(self.rtewpid) or not self.rtewpid[ j] == bs.traf.ap.route[i].wpname[j]: # Waypoint name labels # If waypoint label bitmap does not yet exist, make it # Waypoint name and constraint(s), if there are any txt = bs.traf.ap.route[i].wpname[j] alt = bs.traf.ap.route[i].wpalt[j] spd = bs.traf.ap.route[i].wpspd[j] if alt >= 0. or spd >= 0.: # Altitude if alt < 0: txt = txt + " -----/" elif alt > 4500 * ft: FL = int(round((alt / (100. * ft)))) txt = txt + " FL" + str(FL) + "/" else: txt = txt + " " + str(int(round( alt / ft))) + "/" # Speed if spd < 0: txt = txt + "---" else: txt = txt + str(int(round(spd / kts))) wplabel = pg.Surface((128, 32), 0, self.win) self.fontnav.printat(wplabel, 0, 0, \ txt) if j >= len(self.rtewpid): self.rtewpid.append(txt) self.rtewplabel.append(wplabel) else: self.rtewpid[j] = txt self.rtewplabel[j] = wplabel # In any case, blit the waypoint name xtxt = x1 + 7 ytxt = y1 - 3 self.radbmp.blit(self.rtewplabel[j], (xtxt, ytxt), \ None, pg.BLEND_ADD) # Line from aircraft to active waypoint if bs.traf.ap.route[i].iactwp == j: x0, y0 = self.ll2xy(bs.traf.lat[i], bs.traf.lon[i]) pg.draw.line(self.win, magenta, (x0, y0), (x1, y1)) # Draw aircraft trails which are on screen if bs.traf.trails.active: trlsel = list(np.where( self.onradar(bs.traf.trails.lat0, bs.traf.trails.lon0) + \ self.onradar(bs.traf.trails.lat1, bs.traf.trails.lon1))[0]) x0, y0 = self.ll2xy(bs.traf.trails.lat0, bs.traf.trails.lon0) x1, y1 = self.ll2xy(bs.traf.trails.lat1, bs.traf.trails.lon1) for i in trlsel: pg.draw.line(self.win, bs.traf.trails.col[i], \ (x0[i], y0[i]), (x1[i], y1[i])) # Redraw background => buffer ; if >1500 foreground linepieces on screen if len(trlsel) > 1500: self.redrawradbg = True # Draw edit window self.editwin.update() if self.redrawradbg or redrawrad or self.editwin.redraw: self.win.blit(self.menu.bmps[self.menu.ipage], \ (self.menu.x, self.menu.y)) self.win.blit(self.editwin.bmp, (self.editwin.winx, self.editwin.winy)) # Draw frames pg.draw.rect(self.win, white, self.editwin.rect, 1) pg.draw.rect(self.win, white, pg.Rect(1, 1, self.width - 1, self.height - 1), 1) # Add debug line self.fontsys.printat(self.win, 10, 2, str(bs.sim.utc.replace(microsecond=0))) self.fontsys.printat(self.win, 10, 18, tim2txt(bs.sim.simt)) self.fontsys.printat(self.win, 10+80, 2, \ "ntraf = " + str(bs.traf.ntraf)) self.fontsys.printat(self.win, 10+160, 2, \ "Freq=" + str(int(len(bs.sim.dts) / max(0.001, sum(bs.sim.dts))))) self.fontsys.printat(self.win, 10+240, 2, \ "#LOS = " + str(len(bs.traf.asas.lospairs_unique))) self.fontsys.printat(self.win, 10+240, 18, \ "Total LOS = " + str(len(bs.traf.asas.lospairs_all))) self.fontsys.printat(self.win, 10+240, 34, \ "#Con = " + str(len(bs.traf.asas.confpairs_unique))) self.fontsys.printat(self.win, 10+240, 50, \ "Total Con = " + str(len(bs.traf.asas.confpairs_all))) # Frame ready, flip to screen pg.display.flip() # If needed, take a screenshot if self.screenshot: pg.image.save(self.win, self.screenshotname) self.screenshot = False return
def notify(self, receiver, event): # Keep track of event processing event_processed = False # Events from the simulation threads if receiver is self: if event.type() == PanZoomEventType: if event.zoom is not None: event.origin = (self.radarwidget.width / 2, self.radarwidget.height / 2) if event.pan is not None and not event.absolute: event.pan = (2.0 * event.pan[0] / (self.radarwidget.zoom * self.radarwidget.ar), 2.0 * event.pan[1] / (self.radarwidget.zoom * self.radarwidget.flat_earth)) # send the pan/zoom event to the radarwidget self.radarwidget.event(event) elif event.type() == ACDataEventType: self.acdata = event self.radarwidget.update_aircraft_data(event) if self.nd.ac_id in event.id: idx = event.id.index(self.nd.ac_id.upper()) lat = event.lat[idx] lon = event.lon[idx] trk = event.trk[idx] tas = event.tas[idx] self.nd.update_aircraft_data(idx, lat, lon, tas, trk, len(event.lat)) return True elif event.type() == RouteDataEventType: self.routedata = event self.radarwidget.update_route_data(event) return True elif event.type() == DisplayShapeEventType: self.radarwidget.updatePolygon(event.name, event.data) elif event.type() == SimInfoEventType: simt = tim2txt(event.simt)[:-3] simtclock = tim2txt(event.simtclock)[:-3] self.win.setNodeInfo(manager.sender()[0], simt, event.scenname) if manager.sender()[0] == manager.actnode(): self.simt = event.simt self.win.siminfoLabel.setText(u'<b>t:</b> %s, <b>\u0394t:</b> %.2f, <b>Speed:</b> %.1fx, <b>UTC:</b> %s, <b>Mode:</b> %s, <b>Aircraft:</b> %d, <b>Conflicts:</b> %d/%d, <b>LoS:</b> %d/%d' % (simt, event.simdt, event.sys_freq, simtclock, self.modes[event.mode], event.n_ac, self.acdata.nconf_cur, self.acdata.nconf_tot, self.acdata.nlos_cur, self.acdata.nlos_tot)) return True elif event.type() == StackTextEventType: event_processed = True if event.disptext: self.win.console.echo(event.disptext) if event.cmdtext: self.win.console.setCmdline(event.cmdtext) elif event.type() == StackInitEventType: event_processed = True self.win.console.addStackHelp(manager.sender()[0], event.stackdict) elif event.type() == ShowDialogEventType: if event.dialog_type == event.filedialog_type: self.show_file_dialog() elif event.dialog_type == event.docwin_type: self.show_doc_window(event.cmd) return True elif event.type() == DisplayFlagEventType: # Switch/toggle/cycle radar screen features e.g. from SWRAD command if event.switch == 'RESET': self.radarwidget.clearNodeData() # Coastlines elif event.switch == "GEO": self.radarwidget.show_coast = not self.radarwidget.show_coast # FIR boundaries elif event.switch == "FIR": self.radarwidget.showfir = not self.radarwidget.showfir # Airport: 0 = None, 1 = Large, 2= All elif event.switch == "APT": self.radarwidget.show_apt = not self.radarwidget.show_apt # Waypoint: 0 = None, 1 = VOR, 2 = also WPT, 3 = Also terminal area wpts elif event.switch == "VOR" or event.switch == "WPT" or event.switch == "WP" or event.switch == "NAV": self.radarwidget.show_apt = not self.radarwidget.show_apt # Satellite image background on/off elif event.switch == "SAT": self.radarwidget.show_map = not self.radarwidget.show_map # Satellite image background on/off elif event.switch == "TRAF": self.radarwidget.show_traf = not self.radarwidget.show_traf # ND window for selected aircraft elif event.switch == "ND": self.nd.setAircraftID(event.argument) self.nd.setVisible(not self.nd.isVisible()) elif event.switch == "SSD": self.radarwidget.show_ssd(event.argument) elif event.switch == "SYM": # For now only toggle PZ self.radarwidget.show_pz = not self.radarwidget.show_pz elif event.switch == "DEFWPT": self.radarwidget.defwpt(event.argument) elif event.switch == "FILTERALT": # First argument is an on/off flag nact = self.radarwidget.nodedata[manager.sender()[0]] if event.argument[0]: nact.filteralt = event.argument[1:] else: nact.filteralt = False return True elif event.type() == AMANEventType: # self.aman.update(self.simt, event) pass # Mouse/trackpad event handling for the Radar widget elif receiver is self.radarwidget and self.radarwidget.initialized: panzoom = None if event.type() == QEvent.Wheel: # For mice we zoom with control/command and the scrolwheel if event.modifiers() & Qt.ControlModifier: origin = (event.pos().x(), event.pos().y()) zoom = 1.0 try: if event.pixelDelta(): # High resolution scroll zoom *= (1.0 + 0.01 * event.pixelDelta().y()) else: # Low resolution scroll zoom *= (1.0 + 0.001 * event.angleDelta().y()) except: zoom *= (1.0 + 0.001 * event.delta()) panzoom = PanZoomEvent(zoom=zoom, origin=origin) # For touchpad scroll (2D) is used for panning else: try: dlat = 0.01 * event.pixelDelta().y() / (self.radarwidget.zoom * self.radarwidget.ar) dlon = -0.01 * event.pixelDelta().x() / (self.radarwidget.zoom * self.radarwidget.flat_earth) panzoom = PanZoomEvent(pan=(dlat, dlon)) except: pass # For touchpad, pinch gesture is used for zoom elif event.type() == QEvent.Gesture: zoom = None pan = None dlat = 0.0 dlon = 0.0 for g in event.gestures(): if g.gestureType() == Qt.PinchGesture: if zoom is None: zoom = 1.0 zoom *= g.scaleFactor() if correct_pinch: zoom /= g.lastScaleFactor() elif g.gestureType() == Qt.PanGesture: if abs(g.delta().y() + g.delta().x()) > 1e-1: dlat += 0.005 * g.delta().y() / (self.radarwidget.zoom * self.radarwidget.ar) dlon -= 0.005 * g.delta().x() / (self.radarwidget.zoom * self.radarwidget.flat_earth) pan = (dlat, dlon) if pan is not None or zoom is not None: panzoom = PanZoomEvent(pan, zoom, self.mousepos) elif event.type() == QEvent.MouseButtonPress and event.button() & Qt.LeftButton: event_processed = True self.mousedragged = False # For mice we pan with control/command and mouse movement. Mouse button press marks the beginning of a pan self.prevmousepos = (event.x(), event.y()) elif event.type() == QEvent.MouseButtonRelease and event.button() & Qt.LeftButton and not self.mousedragged: event_processed = True lat, lon = self.radarwidget.pixelCoordsToLatLon(event.x(), event.y()) tostack, tocmdline = radarclick(self.win.console.command_line, lat, lon, self.acdata, self.routedata) if len(tocmdline) > 0: if '\n' in tocmdline: self.win.console.setCmdline('') # Clear any shape command preview on the radar display self.radarwidget.previewpoly(None) else: self.win.console.appendCmdline(tocmdline) if len(tostack) > 0: self.win.console.stack(tostack) elif event.type() == QEvent.MouseMove: event_processed = True self.mousedragged = True self.mousepos = (event.x(), event.y()) if event.buttons() & Qt.LeftButton: dlat = 0.003 * (event.y() - self.prevmousepos[1]) / (self.radarwidget.zoom * self.radarwidget.ar) dlon = 0.003 * (self.prevmousepos[0] - event.x()) / (self.radarwidget.zoom * self.radarwidget.flat_earth) self.prevmousepos = (event.x(), event.y()) panzoom = PanZoomEvent(pan=(dlat, dlon)) # Update pan/zoom to simulation thread only when the pan/zoom gesture is finished elif (event.type() == QEvent.MouseButtonRelease or event.type() == QEvent.TouchEnd) and self.panzoomchanged: self.panzoomchanged = False manager.sendEvent(PanZoomEvent( pan=(self.radarwidget.panlat, self.radarwidget.panlon), zoom=self.radarwidget.zoom, absolute=True)) # If we've just processed a change to pan and/or zoom, send the event to the radarwidget if panzoom is not None: self.panzoomchanged = True return self.radarwidget.event(panzoom) # Send all key presses directly to the main window if event.type() == QEvent.KeyPress: self.win.keyPressEvent(event) return True # If we haven't processed the event: call Base Class Method to Continue Normal Event Processing if not event_processed: return super(Gui, self).notify(receiver, event) if self.win.console.cmd in ['AREA', 'BOX', 'POLY', 'POLYALT', 'POLYGON', 'CIRCLE', 'LINE']: if self.mousepos != self.prevmousepos and len(self.win.console.args) >= 2: self.prevmousepos = self.mousepos try: # get the largest even number of points start = 0 if self.win.console.cmd == 'AREA' else 3 if self.win.console.cmd == 'POLYALT' else 1 end = ((len(self.win.console.args) - start) // 2) * 2 + start data = [float(v) for v in self.win.console.args[start:end]] data += self.radarwidget.pixelCoordsToLatLon(*self.mousepos) self.radarwidget.previewpoly(self.win.console.cmd, data) except ValueError: pass event.accept() return True