From a9c7e4bcca557f7fed6910ad34dc8c4ac5991f13 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Tue, 9 Oct 2012 23:46:02 -0700 Subject: [PATCH 01/12] Add azimuth map widget. Not done, not invoked. --- python/CMakeLists.txt | 1 + python/az_map.py | 161 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100755 python/az_map.py diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 98444b7..54bf701 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -32,6 +32,7 @@ GR_PYTHON_INSTALL( FILES __init__.py altitude.py + az_map.py cpr.py mlat.py exceptions.py diff --git a/python/az_map.py b/python/az_map.py new file mode 100755 index 0000000..8ce0f98 --- /dev/null +++ b/python/az_map.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python +# +# Copyright 2012 Nick Foster +# +# This file is part of gr-air-modes +# +# gr-air-modes is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# gr-air-modes is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with gr-air-modes; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# azimuthal projection widget to plot reception range vs. azimuth + +from PyQt4 import QtCore, QtGui +import threading +import math + + +# model has max range vs. azimuth in n-degree increments +# contains separate max range for a variety of altitudes so +# you can determine your altitude dropouts by bearing +# assumes that if you can hear ac at 1000', you can hear at 5000'+. +class az_map_model(QtCore.QObject): + dataChanged = QtCore.pyqtSignal(name='dataChanged') + npoints = 360/5 + def __init__(self, parent=None): + super(az_map_model, self).__init__(parent) + self._data = [] + self.lock = threading.Lock() + self._altitudes = [0, 1000, 2000, 5000, 10000, 15000, 20000, 25000, 30000] + #initialize everything to 0 + for i in range(0,az_map_model.npoints): + self._data.append([0] * len(self._altitudes)) + + def rowCount(self): + return len(self._data) + + def columnCount(self): + return len(self._altitudes) + + def data(self, row, col): + return self._data[row][col] + + def addRecord(self, bearing, altitude, distance): + with self.lock: + #round up to nearest altitude in altitudes list + #there's probably another way to do it + col = self._altitudes.index(min([alt for alt in self._altitudes if alt >= altitude])) + #find which bearing row we sit in + row = int(bearing+(180/az_map_model.npoints)) / (360/az_map_model.npoints) + #set max range for all alts >= the ac alt + #this expresses the assumption that higher ac can be heard further + for i in range(col, len(self._altitudes)): + if distance > self._data[row][i]: + self._data[row][i] = distance + self.dataChanged.emit() + + def reset(self): + with self.lock: + self._data = [] + for i in range(0,az_map_model.npoints): + self._data.append([0] * len(self._altitudes)) + self.dataChanged.emit() + + +# the azimuth map widget +class az_map(QtGui.QWidget): + maxrange = 450 + ringsize = 100 + bgcolor = QtCore.Qt.black + ringpen = QtGui.QPen(QtGui.QColor(0, 96, 127, 255), 1.3) + rangepen = QtGui.QPen(QtGui.QColor(255, 255, 0, 255), 1.0) + + def __init__(self, parent=None): + super(az_map, self).__init__(parent) + self._model = None + self._path = QtGui.QPainterPath() + + def minimumSizeHint(self): + return QtCore.QSize(50, 50) + + def sizeHint(self): + return QtCore.QSize(300, 300) + + def setModel(self, model): + self._model = model + + def paintEvent(self, event): + painter = QtGui.QPainter(self) + painter.setRenderHint(QtGui.QPainter.Antialiasing) + + #set background + painter.fillRect(event.rect(), QtGui.QBrush(az_map.bgcolor)) + + #draw the range rings + self.drawRangeRings(painter) + self.drawPath() + + painter.setPen(az_map.rangepen) + painter.drawPath(self._path) + + def drawPath(self): + self._path = QtGui.QPainterPath() + if(self._model): + for i in range(az_map_model.npoints-1,-1,-1): + #bearing is to start point of arc (clockwise) + bearing = (i+0.5) * 360./az_map_model.npoints + distance = self._model._data[i][self._model.columnCount()-1] + #convert bearing,distance to x,y + radius = min(self.width(), self.height()) / 2.0 + xpts = (radius * distance / az_map.maxrange) * math.sin(bearing * math.pi / 180) + ypts = (radius * distance / az_map.maxrange) * math.cos(bearing * math.pi / 180) + #get the bounding rectangle of the arc + arcscale = radius * distance / az_map.maxrange + arcrect = QtCore.QRectF(QtCore.QPointF(0-arcscale, 0-arcscale), + QtCore.QPointF(arcscale, arcscale)) + + self._path.lineTo(xpts,0-ypts) + self._path.arcTo(arcrect, 90-bearing, 360./az_map_model.npoints) + + def drawRangeRings(self, painter): + painter.translate(self.width()/2, self.height()/2) + painter.setPen(az_map.ringpen) + for i in range(0, az_map.maxrange, az_map.ringsize): + diameter = (float(i) / az_map.maxrange) * min(self.width(), self.height()) + painter.drawEllipse(QtCore.QRectF(-diameter / 2.0, + -diameter / 2.0, diameter, diameter)) + +import random +class Window(QtGui.QWidget): + def __init__(self): + super(Window, self).__init__() + layout = QtGui.QGridLayout() + model = az_map_model() + for i in range(az_map_model.npoints): + model._data[i][model.columnCount()-1] = random.randint(0,400) + mymap = az_map(None) + mymap.setModel(model) + layout.addWidget(mymap, 0, 1) + self.setLayout(layout) + + +if __name__ == '__main__': + + import sys + + app = QtGui.QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec_()) From 198d46ac999d252cd1e2ffdd94fcf3525d95f55a Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 10 Oct 2012 00:22:13 -0700 Subject: [PATCH 02/12] Event link between model and azimuth map. --- python/az_map.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/python/az_map.py b/python/az_map.py index 8ce0f98..bdb625a 100755 --- a/python/az_map.py +++ b/python/az_map.py @@ -95,6 +95,7 @@ class az_map(QtGui.QWidget): def setModel(self, model): self._model = model + self._model.dataChanged.connect(self.drawPath) def paintEvent(self, event): painter = QtGui.QPainter(self) @@ -105,7 +106,7 @@ class az_map(QtGui.QWidget): #draw the range rings self.drawRangeRings(painter) - self.drawPath() + #self.drawPath() painter.setPen(az_map.rangepen) painter.drawPath(self._path) @@ -128,6 +129,7 @@ class az_map(QtGui.QWidget): self._path.lineTo(xpts,0-ypts) self._path.arcTo(arcrect, 90-bearing, 360./az_map_model.npoints) + #self.paintEvent(QtCore.QEvent()) def drawRangeRings(self, painter): painter.translate(self.width()/2, self.height()/2) @@ -137,19 +139,21 @@ class az_map(QtGui.QWidget): painter.drawEllipse(QtCore.QRectF(-diameter / 2.0, -diameter / 2.0, diameter, diameter)) -import random +import random, time class Window(QtGui.QWidget): def __init__(self): super(Window, self).__init__() layout = QtGui.QGridLayout() - model = az_map_model() - for i in range(az_map_model.npoints): - model._data[i][model.columnCount()-1] = random.randint(0,400) + self.model = az_map_model() mymap = az_map(None) - mymap.setModel(model) + mymap.setModel(self.model) layout.addWidget(mymap, 0, 1) self.setLayout(layout) + def update(self): + for i in range(az_map_model.npoints): + self.model.addRecord(i*360./az_map_model.npoints, 30000, random.randint(0,400)) + if __name__ == '__main__': @@ -158,4 +162,5 @@ if __name__ == '__main__': app = QtGui.QApplication(sys.argv) window = Window() window.show() + window.update() sys.exit(app.exec_()) From dfb48dbe8d4fd11ecc8215198e5352765ccf1a2a Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 10 Oct 2012 00:31:00 -0700 Subject: [PATCH 03/12] Correctly repaint on drawPath (and thus on dataChanged events) --- python/az_map.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/python/az_map.py b/python/az_map.py index bdb625a..dd2baaf 100755 --- a/python/az_map.py +++ b/python/az_map.py @@ -129,7 +129,7 @@ class az_map(QtGui.QWidget): self._path.lineTo(xpts,0-ypts) self._path.arcTo(arcrect, 90-bearing, 360./az_map_model.npoints) - #self.paintEvent(QtCore.QEvent()) + self.repaint() def drawRangeRings(self, painter): painter.translate(self.width()/2, self.height()/2) @@ -140,6 +140,20 @@ class az_map(QtGui.QWidget): -diameter / 2.0, diameter, diameter)) import random, time + +class model_updater(threading.Thread): + def __init__(self, model): + super(model_updater, self).__init__() + self.model = model + self.setDaemon(1) + self.done = False + self.start() + + def run(self): + for i in range(az_map_model.npoints): + self.model.addRecord(i*360./az_map_model.npoints, 30000, random.randint(0,400)) + time.sleep(0.1) + class Window(QtGui.QWidget): def __init__(self): super(Window, self).__init__() @@ -147,14 +161,10 @@ class Window(QtGui.QWidget): self.model = az_map_model() mymap = az_map(None) mymap.setModel(self.model) + self.updater = model_updater(self.model) layout.addWidget(mymap, 0, 1) self.setLayout(layout) - def update(self): - for i in range(az_map_model.npoints): - self.model.addRecord(i*360./az_map_model.npoints, 30000, random.randint(0,400)) - - if __name__ == '__main__': import sys From cd2cfec7303cd63484539036df22198aba160c10 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 10 Oct 2012 08:20:07 -0700 Subject: [PATCH 04/12] Azimuth map working. --- apps/modes_gui | 11 +++++++++-- python/__init__.py | 1 + python/az_map.py | 38 +++++++++++++++++++++++++++++++++++--- res/modes_rx.ui | 27 ++++++++++++++++++++++----- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/apps/modes_gui b/apps/modes_gui index 4a75772..4ef1005 100755 --- a/apps/modes_gui +++ b/apps/modes_gui @@ -85,6 +85,9 @@ class mainwindow(QtGui.QMainWindow): self.ui.list_aircraft.setModel(self.datamodel) self.ui.list_aircraft.setModelColumn(0) + self.az_model = air_modes.az_map_model(None) + self.ui.azimuth_map.setModel(self.az_model) + #set up dashboard views self.icaodelegate = ICAOViewDelegate() self.ui.list_aircraft.setItemDelegate(self.icaodelegate) @@ -276,9 +279,13 @@ class mainwindow(QtGui.QMainWindow): self.outputs.append(rawport.output) self.updates.append(rawport.add_pending_conns) + #add azimuth map output and hook it up + if my_position is not None: + self.az_map_output = air_modes.az_map_output(my_position, self.az_model) + self.outputs.append(self.az_map_output.output) + self.livedata = air_modes.output_print(my_position) #add output for live data box - #TODO: this doesn't handle large volumes of data well; i get segfaults. self.outputs.append(self.output_live_data) #create SQL database for KML and dashboard displays @@ -291,7 +298,7 @@ class mainwindow(QtGui.QMainWindow): #create output handler thread self.output_handler = output_handler(self.outputs, self.updates, self.queue) - + self.ui.button_start.setText("Stop") def on_quit(self): diff --git a/python/__init__.py b/python/__init__.py index 045e4c7..f678ba1 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -58,6 +58,7 @@ from sbs1 import output_sbs1 from kml import output_kml from raw_server import raw_server from exceptions import * +from az_map import * #this is try/excepted in case the user doesn't have numpy installed try: from flightgear import output_flightgear diff --git a/python/az_map.py b/python/az_map.py index dd2baaf..4f84fd5 100755 --- a/python/az_map.py +++ b/python/az_map.py @@ -25,6 +25,7 @@ from PyQt4 import QtCore, QtGui import threading import math +import air_modes # model has max range vs. azimuth in n-degree increments @@ -76,8 +77,8 @@ class az_map_model(QtCore.QObject): # the azimuth map widget class az_map(QtGui.QWidget): - maxrange = 450 - ringsize = 100 + maxrange = 45 + ringsize = 10 bgcolor = QtCore.Qt.black ringpen = QtGui.QPen(QtGui.QColor(0, 96, 127, 255), 1.3) rangepen = QtGui.QPen(QtGui.QColor(255, 255, 0, 255), 1.0) @@ -139,6 +140,34 @@ class az_map(QtGui.QWidget): painter.drawEllipse(QtCore.QRectF(-diameter / 2.0, -diameter / 2.0, diameter, diameter)) +class az_map_output(air_modes.parse): + def __init__(self, mypos, model): + air_modes.parse.__init__(self, mypos) + self.model = model + def output(self, msg): + [data, ecc, reference, timestamp] = msg.split() + data = air_modes.modes_reply(long(data, 16)) + ecc = long(ecc, 16) + rssi = 10.*math.log10(float(reference)) + msgtype = data["df"] + now = time.time() + + if msgtype == 17: + icao = data["aa"] + subtype = data["ftc"] + distance, altitude, bearing = [0,0,0] + if 5 <= subtype <= 8: + (ground_track, decoded_lat, decoded_lon, distance, bearing) = self.parseBDS06(data) + altitude = 0 + elif 9 <= subtype <= 18: + (altitude, decoded_lat, decoded_lon, distance, bearing) = self.parseBDS05(data) + + self.model.addRecord(bearing, altitude, distance) + + +############################## +# Test stuff +############################## import random, time class model_updater(threading.Thread): @@ -151,8 +180,11 @@ class model_updater(threading.Thread): def run(self): for i in range(az_map_model.npoints): - self.model.addRecord(i*360./az_map_model.npoints, 30000, random.randint(0,400)) time.sleep(0.1) + if(self.model): + self.model.addRecord(i*360./az_map_model.npoints, 30000, random.randint(0,400)) + else: + self.stop() class Window(QtGui.QWidget): def __init__(self): diff --git a/res/modes_rx.ui b/res/modes_rx.ui index b54b2d5..849ebd7 100644 --- a/res/modes_rx.ui +++ b/res/modes_rx.ui @@ -24,7 +24,7 @@ - 1 + 3 @@ -234,10 +234,6 @@ Filename - line_inputfile - label_13 - combo_rate - label_27 @@ -847,6 +843,21 @@ Data browser + + + Azimuth map + + + + + 10 + 10 + 245 + 245 + + + + Live data @@ -971,6 +982,12 @@ QWidget
qwt_dial.h
+ + az_map + QWidget +
air_modes/az_map
+ 1 +
From 05de9c603411aa01551b221355eb55987dcf032f Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 10 Oct 2012 08:20:47 -0700 Subject: [PATCH 05/12] Use more realistic value for max az map range. Should make this configurable. --- python/az_map.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/az_map.py b/python/az_map.py index 4f84fd5..a21ed1a 100755 --- a/python/az_map.py +++ b/python/az_map.py @@ -77,8 +77,8 @@ class az_map_model(QtCore.QObject): # the azimuth map widget class az_map(QtGui.QWidget): - maxrange = 45 - ringsize = 10 + maxrange = 450 + ringsize = 100 bgcolor = QtCore.Qt.black ringpen = QtGui.QPen(QtGui.QColor(0, 96, 127, 255), 1.3) rangepen = QtGui.QPen(QtGui.QColor(255, 255, 0, 255), 1.0) From 397451bcbd8a9ec131735d0103ca124ea326e489 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 10 Oct 2012 12:51:44 -0700 Subject: [PATCH 06/12] Azimuth map has multiple rings for different altitudes. --- python/az_map.py | 93 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 30 deletions(-) diff --git a/python/az_map.py b/python/az_map.py index a21ed1a..327cbdb 100755 --- a/python/az_map.py +++ b/python/az_map.py @@ -57,15 +57,22 @@ class az_map_model(QtCore.QObject): with self.lock: #round up to nearest altitude in altitudes list #there's probably another way to do it - col = self._altitudes.index(min([alt for alt in self._altitudes if alt >= altitude])) + if altitude >= max(self._altitudes): + col = self.columnCount()-1 + else: + col = self._altitudes.index(min([alt for alt in self._altitudes if alt >= altitude])) + #find which bearing row we sit in row = int(bearing+(180/az_map_model.npoints)) / (360/az_map_model.npoints) #set max range for all alts >= the ac alt #this expresses the assumption that higher ac can be heard further + update = False for i in range(col, len(self._altitudes)): if distance > self._data[row][i]: self._data[row][i] = distance - self.dataChanged.emit() + update = True + if update: + self.dataChanged.emit() def reset(self): with self.lock: @@ -81,12 +88,14 @@ class az_map(QtGui.QWidget): ringsize = 100 bgcolor = QtCore.Qt.black ringpen = QtGui.QPen(QtGui.QColor(0, 96, 127, 255), 1.3) - rangepen = QtGui.QPen(QtGui.QColor(255, 255, 0, 255), 1.0) + #rangepen = QtGui.QPen(QtGui.QColor(255, 255, 0, 255), 1.0) def __init__(self, parent=None): super(az_map, self).__init__(parent) self._model = None - self._path = QtGui.QPainterPath() + self._paths = [] + self.maxrange = az_map.maxrange + self.ringsize = az_map.ringsize def minimumSizeHint(self): return QtCore.QSize(50, 50) @@ -96,50 +105,72 @@ class az_map(QtGui.QWidget): def setModel(self, model): self._model = model - self._model.dataChanged.connect(self.drawPath) + self._model.dataChanged.connect(self.update) + + def update(self): + self.drawPaths() + self.repaint() def paintEvent(self, event): painter = QtGui.QPainter(self) painter.setRenderHint(QtGui.QPainter.Antialiasing) + #TODO: make it not have to redraw paths EVERY repaint + self.drawPaths() #set background painter.fillRect(event.rect(), QtGui.QBrush(az_map.bgcolor)) #draw the range rings self.drawRangeRings(painter) - #self.drawPath() + #painter.setPen(az_map.rangepen) + for i in range(len(self._paths)): + alpha = 230 * (i+1) / (len(self._paths)) + 25 + painter.setPen(QtGui.QPen(QtGui.QColor(alpha,alpha,0,255), 1.0)) + painter.drawPath(self._paths[i]) - painter.setPen(az_map.rangepen) - painter.drawPath(self._path) - - def drawPath(self): - self._path = QtGui.QPainterPath() + def drawPaths(self): + self._paths = [] if(self._model): - for i in range(az_map_model.npoints-1,-1,-1): - #bearing is to start point of arc (clockwise) - bearing = (i+0.5) * 360./az_map_model.npoints - distance = self._model._data[i][self._model.columnCount()-1] - #convert bearing,distance to x,y - radius = min(self.width(), self.height()) / 2.0 - xpts = (radius * distance / az_map.maxrange) * math.sin(bearing * math.pi / 180) - ypts = (radius * distance / az_map.maxrange) * math.cos(bearing * math.pi / 180) - #get the bounding rectangle of the arc - arcscale = radius * distance / az_map.maxrange - arcrect = QtCore.QRectF(QtCore.QPointF(0-arcscale, 0-arcscale), - QtCore.QPointF(arcscale, arcscale)) - - self._path.lineTo(xpts,0-ypts) - self._path.arcTo(arcrect, 90-bearing, 360./az_map_model.npoints) - self.repaint() + for alt in range(0, self._model.columnCount()): + path = QtGui.QPainterPath() + for i in range(az_map_model.npoints-1,-1,-1): + #bearing is to start point of arc (clockwise) + bearing = (i+0.5) * 360./az_map_model.npoints + distance = self._model._data[i][alt] + radius = min(self.width(), self.height()) / 2.0 + scale = radius * distance / self.maxrange + #convert bearing,distance to x,y + xpts = scale * math.sin(bearing * math.pi / 180) + ypts = scale * math.cos(bearing * math.pi / 180) + #get the bounding rectangle of the arc + + arcrect = QtCore.QRectF(QtCore.QPointF(0-scale, 0-scale), + QtCore.QPointF(scale, scale)) + + if path.isEmpty(): + path.moveTo(xpts, 0-ypts) #so we don't get a line from 0,0 to the first point + else: + path.lineTo(xpts, 0-ypts) + path.arcTo(arcrect, 90-bearing, 360./az_map_model.npoints) + + self._paths.append(path) def drawRangeRings(self, painter): painter.translate(self.width()/2, self.height()/2) painter.setPen(az_map.ringpen) - for i in range(0, az_map.maxrange, az_map.ringsize): + for i in range(0, self.maxrange, self.ringsize): diameter = (float(i) / az_map.maxrange) * min(self.width(), self.height()) painter.drawEllipse(QtCore.QRectF(-diameter / 2.0, -diameter / 2.0, diameter, diameter)) + def setMaxRange(self, maxrange): + self.maxrange = maxrange + self.drawPath() + + def setRingSize(self, ringsize): + self.ringsize = ringsize + self.drawPath() + class az_map_output(air_modes.parse): def __init__(self, mypos, model): air_modes.parse.__init__(self, mypos) @@ -180,9 +211,11 @@ class model_updater(threading.Thread): def run(self): for i in range(az_map_model.npoints): - time.sleep(0.1) + time.sleep(0.05) if(self.model): - self.model.addRecord(i*360./az_map_model.npoints, 30000, random.randint(0,400)) + self.model.addRecord(i*360./az_map_model.npoints, 2000, 80)#random.randint(0,400)/4) + self.model.addRecord(i*360./az_map_model.npoints, 10000, 210) #random.randint(0,400)/2) + self.model.addRecord(i*360./az_map_model.npoints, 30000, 350)#random.randint(0,400)) else: self.stop() From 07b899ac715f4cbfc00ba2672f063737401f1d7e Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 10 Oct 2012 15:49:09 -0700 Subject: [PATCH 07/12] Reinstate random test for az_map.py --- python/az_map.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python/az_map.py b/python/az_map.py index 327cbdb..02bd41a 100755 --- a/python/az_map.py +++ b/python/az_map.py @@ -175,6 +175,7 @@ class az_map_output(air_modes.parse): def __init__(self, mypos, model): air_modes.parse.__init__(self, mypos) self.model = model + def output(self, msg): [data, ecc, reference, timestamp] = msg.split() data = air_modes.modes_reply(long(data, 16)) @@ -213,9 +214,9 @@ class model_updater(threading.Thread): for i in range(az_map_model.npoints): time.sleep(0.05) if(self.model): - self.model.addRecord(i*360./az_map_model.npoints, 2000, 80)#random.randint(0,400)/4) - self.model.addRecord(i*360./az_map_model.npoints, 10000, 210) #random.randint(0,400)/2) - self.model.addRecord(i*360./az_map_model.npoints, 30000, 350)#random.randint(0,400)) + self.model.addRecord(i*360./az_map_model.npoints, 2000, random.randint(0,400)/4) + self.model.addRecord(i*360./az_map_model.npoints, 10000, random.randint(0,400)/2) + self.model.addRecord(i*360./az_map_model.npoints, 30000, random.randint(0,400)) else: self.stop() From a57c932e88190e8f4fd7d8728761aa3b20539ada Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 10 Oct 2012 15:49:55 -0700 Subject: [PATCH 08/12] Modify UI for proper grid layout, now allows expansion --- res/modes_rx.ui | 1925 ++++++++++++++++++++++++----------------------- 1 file changed, 978 insertions(+), 947 deletions(-) diff --git a/res/modes_rx.ui b/res/modes_rx.ui index 849ebd7..3fce593 100644 --- a/res/modes_rx.ui +++ b/res/modes_rx.ui @@ -6,965 +6,996 @@ 0 0 - 686 - 418 + 687 + 422 + + + 0 + 0 + + MainWindow - - - - 130 - 30 - 551 - 301 - - - - 3 - - - - Setup - - - - - 10 - 20 - 236 - 193 - - - - Input - - - - - 11 - 66 - 66 - 17 - - - - Rate - - - - - - 11 - 36 - 66 - 17 - - - - Source - - - - - - 90 - 30 - 121 - 27 - - - - - - - 10 - 95 - 66 - 17 - - - - Threshold - - - - - - 90 - 60 - 91 - 27 - - - - - - - 90 - 90 - 71 - 27 - - - - - - - 164 - 96 - 31 - 17 - - - - dB - - - - - - 184 - 66 - 41 - 17 - - - - Msps - - - - - - -1 - 117 - 221 - 71 - - - - 1 - - - - - - 90 - 40 - 121 - 27 - - - - - - - 90 - 10 - 71 - 27 - - - - - - - 10 - 10 - 66 - 17 - - - - Gain - - - - - - 10 - 40 - 66 - 17 - - - - Antenna - - - - - - 160 - 10 - 31 - 17 - - - - dB - - - - - - - - 91 - 2 - 113 - 27 - - - - - - - 11 - 7 - 66 - 17 - - - - Filename - - - - - - - - - 270 - 20 - 281 - 151 - - - - Output - - - - - 19 - 124 - 61 - 22 - - - - KML - - - - - - 19 - 34 - 71 - 22 - - - - SBS-1 - - - - - - 19 - 64 - 61 - 22 - - - - Raw - - - - - - 160 - 120 - 111 - 27 - - - - - - - 160 - 30 - 71 - 27 - - - - - - - 160 - 60 - 71 - 27 - - - - - - - 160 - 90 - 71 - 27 - - - - - - - 19 - 94 - 101 - 22 - - - - FlightGear - - - - - - 128 - 66 - 31 - 17 - - - - Port - - - - - - 128 - 96 - 31 - 17 - - - - Port - - - - - - 128 - 36 - 31 - 17 - - - - Port - - - - - - 98 - 126 - 66 - 17 - - - - Filename - - - - - - - 280 - 170 - 211 - 111 - - - - RX position - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - 16 - 64 - 71 - 20 - - - - Longitude - - - - - - 90 - 60 - 113 - 27 - - - - - - - 90 - 30 - 113 - 27 - - - - - - - 28 - 34 - 61 - 20 - - - - Latitude - - - - - - - Dashboard - - - - - 193 - 35 - 21 - 18 - - - - nm - - - - - - 483 - 66 - 21 - 17 - - - - kts - - - - - - 483 - 96 - 21 - 17 - - - - ft - - - - - - 250 - 10 - 61 - 20 - - - - Heading - - - - - - 30 - 10 - 61 - 20 - - - - Bearing - - - - - - 120 - 240 - 118 - 23 - - - - 24 - - - false - - - - - - 17 - 243 - 101 - 17 - - - - Signal strength - - - - - - 120 - 10 - 61 - 20 - - - - Range - - - - - - 365 - 64 - 51 - 20 - - - - Speed - - - - - - 352 - 93 - 61 - 20 - - - - Altitude - - - - - - 370 - 154 - 51 - 20 - - - - Ident - - - - - - 120 - 30 - 71 - 27 - - - - true - - - - - - 410 - 60 - 71 - 27 - - - - true - - - - - - 410 - 90 - 71 - 27 - - - - true - - - - - - 410 - 150 - 71 - 27 - - - - true - - - - - - 410 - 180 - 121 - 27 - - - - true - - - - - - 373 - 184 - 51 - 20 - - - - Type - - - - - - 120 - 180 - 121 - 27 - - - - true - - - - - - 120 - 210 - 121 - 27 - - - - true - - - - - - 62 - 183 - 61 - 21 - - - - Latitude - - - - - - 50 - 213 - 71 - 21 - - - - Longitude - - - - - - 410 - 120 - 71 - 27 - - - - true - - - - - - 483 - 125 - 31 - 17 - - - - ft/s - - - - - - 368 - 123 - 41 - 20 - - - - Climb - - - - - - 200 - 30 - 161 - 91 - - - - true - - - 4 - - - - - - 372 - 34 - 51 - 20 - - - - ICAO - - - - - - 410 - 30 - 71 - 27 - - - - - - - true - - - - - - -20 - 30 - 161 - 91 - - - - true - - - 4 - - - - - - Data browser - - - - - Azimuth map - - - - - 10 - 10 - 245 - 245 - - - - - - - Live data - - - - - 5 - 11 - 531 - 251 - - - - - - - - - 19 - 36 - 101 - 17 - - - - Visible aircraft - - - - - - 580 - 340 - 98 - 27 - - - - Start - - - - - false - - - - 4 - 344 - 271 - 22 - - - - Show ADS-B-equipped aircraft only - - - - - - 18 - 59 - 101 - 272 - - - - QAbstractItemView::NoEditTriggers - - - QListView::SinglePass - - - true - - - - - - 520 - 341 - 41 - 27 - - - - - - - 407 - 345 - 111 - 20 - - - - Reports/second - - + + + 0 + 0 + + + + + + + QLayout::SetDefaultConstraint + + + + + + + + + Visible aircraft + + + + + + + + 0 + 0 + + + + + 100 + 100 + + + + + 100 + 16777215 + + + + QAbstractItemView::NoEditTriggers + + + QListView::SinglePass + + + true + + + + + + + + + + 1 + 1 + + + + 3 + + + + + 0 + 0 + + + + Setup + + + + + 10 + 20 + 236 + 193 + + + + Input + + + + + 11 + 66 + 66 + 17 + + + + Rate + + + + + + 11 + 36 + 66 + 17 + + + + Source + + + + + + 90 + 30 + 121 + 27 + + + + + + + 10 + 95 + 66 + 17 + + + + Threshold + + + + + + 90 + 60 + 91 + 27 + + + + + + + 90 + 90 + 71 + 27 + + + + + + + 164 + 96 + 31 + 17 + + + + dB + + + + + + 184 + 66 + 41 + 17 + + + + Msps + + + + + + -1 + 117 + 221 + 71 + + + + 1 + + + + + + 90 + 40 + 121 + 27 + + + + + + + 90 + 10 + 71 + 27 + + + + + + + 10 + 10 + 66 + 17 + + + + Gain + + + + + + 10 + 40 + 66 + 17 + + + + Antenna + + + + + + 160 + 10 + 31 + 17 + + + + dB + + + + + + + + 91 + 2 + 113 + 27 + + + + + + + 11 + 7 + 66 + 17 + + + + Filename + + + + + + + + + 270 + 20 + 281 + 151 + + + + Output + + + + + 19 + 124 + 61 + 22 + + + + KML + + + + + + 19 + 34 + 71 + 22 + + + + SBS-1 + + + + + + 19 + 64 + 61 + 22 + + + + Raw + + + + + + 160 + 120 + 111 + 27 + + + + + + + 160 + 30 + 71 + 27 + + + + + + + 160 + 60 + 71 + 27 + + + + + + + 160 + 90 + 71 + 27 + + + + + + + 19 + 94 + 101 + 22 + + + + FlightGear + + + + + + 128 + 66 + 31 + 17 + + + + Port + + + + + + 128 + 96 + 31 + 17 + + + + Port + + + + + + 128 + 36 + 31 + 17 + + + + Port + + + + + + 98 + 126 + 66 + 17 + + + + Filename + + + + + + + 280 + 170 + 211 + 111 + + + + RX position + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + 16 + 64 + 71 + 20 + + + + Longitude + + + + + + 90 + 60 + 113 + 27 + + + + + + + 90 + 30 + 113 + 27 + + + + + + + 28 + 34 + 61 + 20 + + + + Latitude + + + + + + + Dashboard + + + + + 193 + 35 + 21 + 18 + + + + nm + + + + + + 483 + 66 + 21 + 17 + + + + kts + + + + + + 483 + 96 + 21 + 17 + + + + ft + + + + + + 250 + 10 + 61 + 20 + + + + Heading + + + + + + 30 + 10 + 61 + 20 + + + + Bearing + + + + + + 120 + 240 + 118 + 23 + + + + 24 + + + false + + + + + + 17 + 243 + 101 + 17 + + + + Signal strength + + + + + + 120 + 10 + 61 + 20 + + + + Range + + + + + + 365 + 64 + 51 + 20 + + + + Speed + + + + + + 352 + 93 + 61 + 20 + + + + Altitude + + + + + + 370 + 154 + 51 + 20 + + + + Ident + + + + + + 120 + 30 + 71 + 27 + + + + true + + + + + + 410 + 60 + 71 + 27 + + + + true + + + + + + 410 + 90 + 71 + 27 + + + + true + + + + + + 410 + 150 + 71 + 27 + + + + true + + + + + + 410 + 180 + 121 + 27 + + + + true + + + + + + 373 + 184 + 51 + 20 + + + + Type + + + + + + 120 + 180 + 121 + 27 + + + + true + + + + + + 120 + 210 + 121 + 27 + + + + true + + + + + + 62 + 183 + 61 + 21 + + + + Latitude + + + + + + 50 + 213 + 71 + 21 + + + + Longitude + + + + + + 410 + 120 + 71 + 27 + + + + true + + + + + + 483 + 125 + 31 + 17 + + + + ft/s + + + + + + 368 + 123 + 41 + 20 + + + + Climb + + + + + + 200 + 30 + 161 + 91 + + + + true + + + 4 + + + + + + 372 + 34 + 51 + 20 + + + + ICAO + + + + + + 410 + 30 + 71 + 27 + + + + + + + true + + + + + + -20 + 30 + 161 + 91 + + + + true + + + 4 + + + + + + Azimuth map + + + + + + + + + + + 0 + 0 + + + + Live data + + + + + + + + + + + + + + + + + false + + + Show ADS-B-equipped aircraft only + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Reports/second + + + + + + + + 50 + 16777215 + + + + + + + + Start + + + + + + + + 0 0 - 686 + 687 25 From 2b572a2e05c6c2d15aef41183a3b0970e4b7915f Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 10 Oct 2012 18:14:53 -0700 Subject: [PATCH 09/12] Fix bug in row calculation. There's probably a simpler expression for this. --- python/az_map.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/az_map.py b/python/az_map.py index 02bd41a..62c8c18 100755 --- a/python/az_map.py +++ b/python/az_map.py @@ -63,7 +63,7 @@ class az_map_model(QtCore.QObject): col = self._altitudes.index(min([alt for alt in self._altitudes if alt >= altitude])) #find which bearing row we sit in - row = int(bearing+(180/az_map_model.npoints)) / (360/az_map_model.npoints) + row = int(int(bearing+(180./az_map_model.npoints)) / (360./az_map_model.npoints)) % az_map_model.npoints #set max range for all alts >= the ac alt #this expresses the assumption that higher ac can be heard further update = False From 6cb59af3a547fe55d1f41c9b16b2d12a635fa057 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 10 Oct 2012 18:19:22 -0700 Subject: [PATCH 10/12] Make better test for az map. --- python/az_map.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/python/az_map.py b/python/az_map.py index 62c8c18..f630323 100755 --- a/python/az_map.py +++ b/python/az_map.py @@ -214,11 +214,9 @@ class model_updater(threading.Thread): for i in range(az_map_model.npoints): time.sleep(0.05) if(self.model): - self.model.addRecord(i*360./az_map_model.npoints, 2000, random.randint(0,400)/4) - self.model.addRecord(i*360./az_map_model.npoints, 10000, random.randint(0,400)/2) - self.model.addRecord(i*360./az_map_model.npoints, 30000, random.randint(0,400)) - else: - self.stop() + for alt in self.model._altitudes: + self.model.addRecord(i*360./az_map_model.npoints, alt, random.randint(0,az_map.maxrange)*alt / max(self.model._altitudes)) + self.done = True class Window(QtGui.QWidget): def __init__(self): From 77768ebcc6c1c9611c713be381d8729f4e241d30 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 10 Oct 2012 18:44:28 -0700 Subject: [PATCH 11/12] Small fixes. Need to limit drawPaths() -- it's slow. --- python/az_map.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/python/az_map.py b/python/az_map.py index f630323..d19370e 100755 --- a/python/az_map.py +++ b/python/az_map.py @@ -71,15 +71,15 @@ class az_map_model(QtCore.QObject): if distance > self._data[row][i]: self._data[row][i] = distance update = True - if update: - self.dataChanged.emit() + if update: + self.dataChanged.emit() def reset(self): with self.lock: self._data = [] for i in range(0,az_map_model.npoints): self._data.append([0] * len(self._altitudes)) - self.dataChanged.emit() + self.dataChanged.emit() # the azimuth map widget @@ -105,16 +105,14 @@ class az_map(QtGui.QWidget): def setModel(self, model): self._model = model - self._model.dataChanged.connect(self.update) - - def update(self): - self.drawPaths() - self.repaint() + self._model.dataChanged.connect(self.repaint) def paintEvent(self, event): painter = QtGui.QPainter(self) painter.setRenderHint(QtGui.QPainter.Antialiasing) #TODO: make it not have to redraw paths EVERY repaint + #drawing paths is VERY SLOW + #maybe use a QTimer to limit repaints self.drawPaths() #set background @@ -122,7 +120,6 @@ class az_map(QtGui.QWidget): #draw the range rings self.drawRangeRings(painter) - #painter.setPen(az_map.rangepen) for i in range(len(self._paths)): alpha = 230 * (i+1) / (len(self._paths)) + 25 painter.setPen(QtGui.QPen(QtGui.QColor(alpha,alpha,0,255), 1.0)) @@ -212,7 +209,7 @@ class model_updater(threading.Thread): def run(self): for i in range(az_map_model.npoints): - time.sleep(0.05) + time.sleep(0.005) if(self.model): for alt in self.model._altitudes: self.model.addRecord(i*360./az_map_model.npoints, alt, random.randint(0,az_map.maxrange)*alt / max(self.model._altitudes)) From 6e6150b05185f476019256605a9f2421849a3f3a Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Thu, 11 Oct 2012 23:37:26 -0700 Subject: [PATCH 12/12] Limit scrollback buffer size. --- apps/modes_gui | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/modes_gui b/apps/modes_gui index 0c0de85..d9bbbea 100755 --- a/apps/modes_gui +++ b/apps/modes_gui @@ -324,6 +324,13 @@ class mainwindow(QtGui.QMainWindow): #slot to catch signal emitted by output_live_data (necessary for #thread safety since output_live_data is called by another thread) def on_append_live_data(self, msgstr): + #limit scrollback buffer size -- is there a faster way? + if(self.ui.text_livedata.document().lineCount() > 500): + cursor = self.ui.text_livedata.textCursor() + cursor.movePosition(QtGui.QTextCursor.Start) + cursor.select(QtGui.QTextCursor.LineUnderCursor) + cursor.removeSelectedText() + self.ui.text_livedata.append(msgstr) self.ui.text_livedata.verticalScrollBar().setSliderPosition(self.ui.text_livedata.verticalScrollBar().maximum())