diff --git a/apps/modes_gui b/apps/modes_gui index bbf9af6..789bc01 100755 --- a/apps/modes_gui +++ b/apps/modes_gui @@ -2,6 +2,7 @@ import os, sys, time, threading, datetime from PyQt4 import QtCore,QtGui,QtSql +from PyQt4.Qwt5 import Qwt from gnuradio import gr, gru, optfir, eng_notation, blks2 import gnuradio.gr.gr_threading as _threading import air_modes @@ -30,6 +31,9 @@ class mainwindow(QtGui.QMainWindow): #default to 3dB self.ui.line_threshold.insert("3") + self.ui.prog_rssi.setMinimum(-20) + self.ui.prog_rssi.setMaximum(0) + self.ui.combo_ant.setCurrentIndex(self.ui.combo_ant.findText("RX2")) #check KML by default, leave the rest unchecked. @@ -65,23 +69,53 @@ class mainwindow(QtGui.QMainWindow): self.ui.list_aircraft.setModel(self.datamodel) self.ui.list_aircraft.setModelColumn(0) - + + #set up dashboard views self.icaodelegate = ICAOViewDelegate() self.ui.list_aircraft.setItemDelegate(self.icaodelegate) self.dashboard_mapper = QtGui.QDataWidgetMapper() self.dashboard_mapper.setModel(self.datamodel) - self.dashboard_mapper.addMapping(self.ui.prog_rssi, 2) + #self.dashboard_mapper.addMapping(self.ui.prog_rssi, 2) self.dashboard_mapper.addMapping(self.ui.line_latitude, 3) self.dashboard_mapper.addMapping(self.ui.line_longitude, 4) self.dashboard_mapper.addMapping(self.ui.line_alt, 5) self.dashboard_mapper.addMapping(self.ui.line_speed, 6) - #self.dashboard_mapper.addMapping(self.ui.line_heading, 7) + #self.dashboard_mapper.addMapping(self.ui.compass_heading, 7) self.dashboard_mapper.addMapping(self.ui.line_climb, 8) self.dashboard_mapper.addMapping(self.ui.line_ident, 9) self.dashboard_mapper.addMapping(self.ui.line_type, 10) + compass_palette = QtGui.QPalette() + compass_palette.setColor(QtGui.QPalette.Foreground, QtCore.Qt.white) + self.ui.compass_heading.setPalette(compass_palette) + self.ui.compass_heading.setNeedle(Qwt.QwtDialSimpleNeedle(Qwt.QwtDialSimpleNeedle.Ray, False, QtCore.Qt.black)) + self.ui.compass_heading.setValue(315.0) + #hook up the update signal self.ui.list_aircraft.selectionModel().currentRowChanged.connect(self.dashboard_mapper.setCurrentModelIndex) + self.ui.list_aircraft.selectionModel().currentRowChanged.connect(self.update_heading_widget) + self.ui.list_aircraft.selectionModel().currentRowChanged.connect(self.update_rssi_widget) + + def update_heading_widget(self, index): + heading = index.model().data(index.model().index(index.row(), 7)).toFloat()[0] + self.ui.compass_heading.setValue(heading) + + def update_rssi_widget(self, index): + rssi = index.model().data(index.model().index(index.row(), 2)).toFloat()[0] + self.ui.prog_rssi.setValue(rssi) + + #this is fired off by the main loop each time a packet comes in. + #eventually you might have this set to just QTimer, depending on how fast things go + #this is also updated on EVERY mode S packet, not just valid ADS-B + #the RIGHT way to do this is: + #all SQL is done inside the datamodel, which can be a QSqlTableModel type. + #No fields are editable by the user. + #when an update happens, the SQL model determines the row which has changed (by ICAO) + #the SQL model then invalidates that row, which will selectively update the list and hopefully the mapper. + def update_gui_widgets(self, msg): + #self.datamodel.reset() #this is hella bad + self.datamodel.setQuery("select * from aircraft order by icao") + self.dashboard_mapper.revert() #goes and gets valid antenna, sample rate options from the device and grays out appropriate things def populate_source_options(self): @@ -173,7 +207,7 @@ class mainwindow(QtGui.QMainWindow): except: my_position = None - self.outputs = [] + self.outputs = [self.update_gui_widgets] self.updates = [] #output options to populate outputs, updates @@ -222,7 +256,7 @@ class ICAOViewDelegate(QtGui.QStyledItemDelegate): def paint(self, painter, option, index): paintstr = "%x" % index.model().data(index.model().index(index.row(), 0)).toInt()[0] last_report = str(index.model().data(index.model().index(index.row(), 1)).toString()) - age = (datetime.datetime.now() - datetime.datetime.strptime(last_report, "%Y-%m-%d %H:%M:%S")).total_seconds() + age = (datetime.datetime.utcnow() - datetime.datetime.strptime(last_report, "%Y-%m-%d %H:%M:%S")).total_seconds() #minimum alpha is 0x40 (oldest), max is 0xFF (newest) alpha = 0xFF - (0xBF / 600.) * min(age, 600) painter.setPen(QtGui.QColor(0, 0, 0, alpha)) diff --git a/python/modes_sql.py b/python/modes_sql.py index 58612c6..24cc328 100644 --- a/python/modes_sql.py +++ b/python/modes_sql.py @@ -19,7 +19,7 @@ # Boston, MA 02110-1301, USA. # -import time, os, sys +import time, os, sys, math from string import split, join import modes_parse import sqlite3 @@ -54,6 +54,20 @@ class modes_output_sql(modes_parse.modes_parse): "ident" TEXT NOT NULL );""" c.execute(query) + query = """CREATE TABLE IF NOT EXISTS "aircraft" ( + "icao" integer primary key not null, + "seen" text not null, + "rssi" real, + "latitude" real, + "longitude" real, + "altitude" real, + "speed" real, + "heading" real, + "vertical" real, + "ident" text, + "type" text + );""" + c.execute(query) c.close() #we close the db conn now to reopen it in the output() thread context. self.db.close() @@ -71,12 +85,8 @@ class modes_output_sql(modes_parse.modes_parse): if self.db is None: self.db = sqlite3.connect(self.filename) - query = self.make_insert_query(message) - if query is not None: - c = self.db.cursor() - c.execute(query) - c.close() - self.db.commit() #don't know if this is necessary + self.make_insert_query(message) + self.db.commit() #don't know if this is necessary except ADSBError: pass @@ -87,49 +97,60 @@ class modes_output_sql(modes_parse.modes_parse): data = modes_parse.modes_reply(long(data, 16)) ecc = long(ecc, 16) -# reference = float(reference) + rssi = 10.0*math.log10(float(reference)) query = None msgtype = data["df"] if msgtype == 17: - query = self.sql17(data) + query = self.sql17(data, rssi) return query - def sql17(self, data): + def sql17(self, data, rssi): icao24 = data["aa"] subtype = data["ftc"] - - retstr = None + c = self.db.cursor() if subtype == 4: - (msg, typename) = self.parseBDS08(data) - retstr = "INSERT OR REPLACE INTO ident (icao, ident) VALUES (" + "%i" % icao24 + ", '" + msg + "')" + (ident, typename) = self.parseBDS08(data) + c.execute("insert or ignore into aircraft (icao, seen, rssi, ident, type) values (%i, datetime('now'), %f, '%s', '%s')" % (icao24, rssi, ident, typename)) + c.execute("update aircraft set seen=datetime('now'), rssi=%f, ident='%s', type='%s' where icao=%i" % (rssi, ident, typename, icao24)) elif subtype >= 5 and subtype <= 8: [ground_track, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(data) altitude = 0 if decoded_lat is None: #no unambiguously valid position available - retstr = None + c.execute("insert or ignore into aircraft (icao, seen, rssi) values (%i, datetime('now'), %f)" % (icao24, rssi)) + c.execute("update aircraft set seen=datetime('now'), rssi=%f where icao=%i" % (icao24, rssi)) else: - retstr = "INSERT INTO positions (icao, seen, alt, lat, lon) VALUES (" + "%i" % icao24 + ", datetime('now'), " + str(altitude) + ", " + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ")" + c.execute("insert or ignore into aircraft (icao, seen, rssi, latitude, longitude, altitude) \ + values (%i, datetime('now'), %f, %.6f, %.6f, %i)" % (icao24, rssi, decoded_lat, decoded_lon, altitude)) + c.execute("update aircraft set seen=datetime('now'), rssi=%f, latitude=%.6f, longitude=%.6f, altitude=%i" % (rssi, decoded_lat, decoded_lon, altitude)) elif subtype >= 9 and subtype <= 18 and subtype != 15: #i'm eliminating type 15 records because they don't appear to be valid position reports. [altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS05(data) if decoded_lat is None: #no unambiguously valid position available - retstr = None + c.execute("insert or ignore into aircraft (icao, seen, rssi) values (%i, datetime('now'), %f);" % (icao24, rssi)) + c.execute("update aircraft set seen=datetime('now'), rssi=%f where icao=%i" % (icao24, rssi)) else: - retstr = "INSERT INTO positions (icao, seen, alt, lat, lon) VALUES (" + "%i" % icao24 + ", datetime('now'), " + str(altitude) + ", " + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ")" + c.execute("insert or ignore into aircraft (icao, seen, rssi, latitude, longitude, altitude) \ + values (%i, datetime('now'), %f, %.6f, %.6f, %i)" % (icao24, rssi, decoded_lat, decoded_lon, altitude)) + c.execute("update aircraft set seen=datetime('now'), rssi=%f, latitude=%.6f, longitude=%.6f, altitude=%i where icao=%i" % (rssi, decoded_lat, decoded_lon, altitude, icao24)) elif subtype == 19: subsubtype = data["sub"] if subsubtype == 0: [velocity, heading, vert_spd] = self.parseBDS09_0(data) - retstr = "INSERT INTO vectors (icao, seen, speed, heading, vertical) VALUES (" + "%i" % icao24 + ", datetime('now'), " + "%.0f" % velocity + ", " + "%.0f" % heading + ", " + "%.0f" % vert_spd + ")"; elif 1 <= subsubtype <= 2: [velocity, heading, vert_spd] = self.parseBDS09_1(data) - retstr = "INSERT INTO vectors (icao, seen, speed, heading, vertical) VALUES (" + "%i" % icao24 + ", datetime('now'), " + "%.0f" % velocity + ", " + "%.0f" % heading + ", " + "%.0f" % vert_spd + ")"; + else: + return None + + c.execute("insert or ignore into aircraft (icao, seen, rssi, speed, heading, vertical) \ + values (%i, datetime('now'), %f, %.0f, %.0f, %.0f);" % (icao24, rssi, velocity, heading, vert_spd)) + c.execute("update aircraft set seen=datetime('now'), rssi=%f, speed=%.0f, heading=%.0f, vertical=%.0f where icao=%i" % (rssi, velocity, heading, vert_spd, icao24)) - return retstr + + c.close() diff --git a/res/modes_rx.ui b/res/modes_rx.ui index 50e1112..8d2fc29 100644 --- a/res/modes_rx.ui +++ b/res/modes_rx.ui @@ -776,6 +776,19 @@ Climb + + + + 200 + 30 + 161 + 91 + + + + 4 + + @@ -863,6 +876,18 @@ + + + QwtCompass + QwtDial +
qwt_compass.h
+
+ + QwtDial + QWidget +
qwt_dial.h
+
+