diff --git a/apps/modes_gui b/apps/modes_gui index 8017176..8f7de29 100755 --- a/apps/modes_gui +++ b/apps/modes_gui @@ -8,13 +8,13 @@ import air_modes from air_modes.modes_exceptions import * from air_modes.modes_rx_ui import Ui_MainWindow import csv +import sqlite3 class mainwindow(QtGui.QMainWindow): def __init__(self): QtGui.QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) - self.setObjectName("gr-air-modes Mode S receiver") #set defaults #add file, RTL, UHD sources @@ -53,6 +53,13 @@ class mainwindow(QtGui.QMainWindow): self.updates = [] self.output_handler = None self.kmlgen = None #necessary bc we stop its thread in shutdown + self.dbinput = None + + self.dbname = 'air_modes.db' + + self.datamodel = modes_datamodel(self.dbname) + self.ui.list_aircraft.setModel(self.datamodel) + self.ui.list_aircraft.show() #TODO remove #goes and gets valid antenna, sample rate options from the device and grays out appropriate things def populate_source_options(self): @@ -111,6 +118,8 @@ class mainwindow(QtGui.QMainWindow): if self.runner is not None: self.output_handler.done = True self.output_handler = None + self.outputs = [] + self.updates = [] self.fg.stop() self.runner = None self.fg = None @@ -120,6 +129,9 @@ class mainwindow(QtGui.QMainWindow): #self.kmlgen.join() #self.kmlgen = None + if self.dbinput is not None: self.dbinput.close() + self.dbinput = None + self.ui.button_start.setText("Start") else: #we aren't already running, let's get this party started @@ -145,8 +157,7 @@ class mainwindow(QtGui.QMainWindow): #output options to populate outputs, updates if self.ui.check_kml.checkState(): #we spawn a thread to run every 30 seconds (or whatever) to generate KML - self.kmlgen = air_modes.modes_kml(self.ui.line_kmlfilename.text(), my_position) #create a KML generating thread - self.outputs.append(self.kmlgen.output) + self.kmlgen = air_modes.modes_kml(self.ui.line_kmlfilename.text(), self.dbname, my_position) #create a KML generating thread if self.ui.check_sbs1.checkState(): sbs1port = int(self.ui.line_sbs1port.text()) @@ -166,19 +177,57 @@ class mainwindow(QtGui.QMainWindow): self.updates.append(rawport.add_pending_conns) self.livedata = air_modes.modes_output_print(my_position) - #add output for live data box self.outputs.append(self.output_live_data) - #create output handler + #create SQL database for KML and dashboard displays + self.dbwriter = air_modes.modes_output_sql(my_position, self.dbname) + self.outputs.append(self.dbwriter.output) #now the db will update itself + + #create output handler thread self.output_handler = output_handler(self.outputs, self.updates, self.queue) - self.ui.button_start.setText("Stop") #modify button text + + self.ui.button_start.setText("Stop") def output_live_data(self, msg): msgstr = self.livedata.parse(msg) if msgstr is not None: self.ui.text_livedata.append(msgstr) + self.ui.text_livedata.verticalScrollBar().setSliderPosition(self.ui.text_livedata.verticalScrollBar().maximum()) + def on_list_aircraft_clicked(self, index): + icao = long(str(index.data().toString()), 16) + #ok now let's fetch the info for this icao and update the display + print icao + +#eventually: set up a QTimer or whatever and have it self-update and emit dataChanged() +#better yet just have the SQL interface yell and say hey that line changed. +#on selected aircraft or on update for visible aircraft, emit signal to update current dashboard display. +class modes_datamodel(QtCore.QAbstractListModel): + def __init__(self, dbname): + QtCore.QAbstractListModel.__init__(self) + self.db = sqlite3.connect(dbname) + def rowCount(self, parent=QtCore.QModelIndex()): + icaoquery = "select count(distinct icao) from positions" + cursor = self.db.cursor() + cursor.execute(icaoquery) + icaolist = cursor.fetchall() + cursor.close() + return icaolist[0][0] + def data(self, index, role=QtCore.Qt.DisplayRole): + if not index.isValid(): + return QtCore.QVariant() + if index.row() >= self.rowCount(): + return QtCore.QVariant() + if role != QtCore.Qt.DisplayRole: + return QtCore.QVariant() + + icaoquery = "select distinct icao from positions order by icao" + cursor = self.db.cursor() + cursor.execute(icaoquery) + icaolist = cursor.fetchall() + cursor.close() + return "%06x" % icaolist[index.row()][0] class output_handler(threading.Thread): def __init__(self, outputs, updates, queue): @@ -205,6 +254,9 @@ class output_handler(threading.Thread): time.sleep(0.1) self.done = True + self.outputs = None + self.updates = None + self.queue = None class top_block_runner(_threading.Thread): @@ -274,17 +326,11 @@ class adsb_rx_block (gr.top_block): self.connect(self.avg, (self.preamble, 1)) self.connect(self.preamble, self.slicer) -class wat_block(gr.top_block): - def __init__(self, options, queue): - gr.top_block.__init__(self) - - self.src = gr.file_source(gr.sizeof_gr_complex, options["filename"]) - self.sink = gr.null_sink(gr.sizeof_gr_complex) - self.connect(self.src, self.sink) if __name__ == '__main__': app = QtGui.QApplication(sys.argv) window = mainwindow() + window.setWindowTitle("Mode S/ADS-B receiver") window.show() sys.exit(app.exec_()) diff --git a/apps/modes_rx b/apps/modes_rx index c7ec4dd..bd3899e 100755 --- a/apps/modes_rx +++ b/apps/modes_rx @@ -190,8 +190,10 @@ if __name__ == '__main__': if options.kml is not None: #we spawn a thread to run every 30 seconds (or whatever) to generate KML - kmlgen = air_modes.modes_kml(options.kml, my_position) #create a KML generating thread - outputs.append(kmlgen.output) + dbname = 'adsb.db' + sqldb = air_modes.modes_output_sql(my_position, dbname) #input into the db + kmlgen = air_modes.modes_kml(options.kml, dbname, my_position) #create a KML generating thread to read from the db + outputs.append(sqldb.output) if options.sbs1 is True: sbs1port = air_modes.modes_output_sbs1(my_position, 30003) diff --git a/python/modes_kml.py b/python/modes_kml.py index 046ee42..b6d103f 100644 --- a/python/modes_kml.py +++ b/python/modes_kml.py @@ -21,13 +21,11 @@ import sqlite3 import string, math, threading, time -from air_modes.modes_sql import modes_output_sql -class modes_kml(threading.Thread, modes_output_sql): - def __init__(self, filename, localpos, timeout=5): +class modes_kml(threading.Thread): + def __init__(self, filename, dbname, localpos, timeout=5): threading.Thread.__init__(self) - self._dbname = 'adsb.db' - modes_output_sql.__init__(self, localpos, self._dbname) #write to the db + self._dbname = dbname self._filename = filename self.my_coords = localpos self._timeout = timeout @@ -43,6 +41,8 @@ class modes_kml(threading.Thread, modes_output_sql): time.sleep(self._timeout) self.done = True + self._db.close() + self._db = None def writekml(self): kmlstr = self.genkml() diff --git a/python/modes_sql.py b/python/modes_sql.py index 944bcbf..58612c6 100644 --- a/python/modes_sql.py +++ b/python/modes_sql.py @@ -29,6 +29,7 @@ class modes_output_sql(modes_parse.modes_parse): def __init__(self, mypos, filename): modes_parse.modes_parse.__init__(self, mypos) #create the database + self.filename = filename self.db = sqlite3.connect(filename) #now execute a schema to create the tables you need c = self.db.cursor() @@ -54,18 +55,28 @@ class modes_output_sql(modes_parse.modes_parse): );""" c.execute(query) c.close() + #we close the db conn now to reopen it in the output() thread context. + self.db.close() + self.db = None def __del__(self): - self.db.close() + self.db = None def output(self, message): try: + #we're checking to see if the db is empty, and creating the db object + #if it is. the reason for this is so that the db writing is done within + #the thread context of output(), rather than the thread context of the + #constructor. that way you can spawn a thread to do output(). + 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() + self.db.commit() #don't know if this is necessary except ADSBError: pass diff --git a/res/modes_rx.ui b/res/modes_rx.ui index 7c30c38..9da5252 100644 --- a/res/modes_rx.ui +++ b/res/modes_rx.ui @@ -6,195 +6,116 @@ 0 0 - 877 - 618 + 686 + 413 MainWindow - + - 10 - 240 - 91 - 281 + 130 + 30 + 551 + 301 - - - - - 10 - 10 - 221 - 191 - + + 0 - - Input - - - - - 10 - 60 - 66 - 17 - - - - Rate - - - - - - 10 - 30 - 66 - 17 - - - - Source - - - - - - 90 - 30 - 121 - 27 - - - - - - - 10 - 90 - 66 - 17 - - - - Threshold - - - - - - 90 - 60 - 91 - 27 - - - - - - - 90 - 90 - 71 - 27 - - - - - - - 160 - 90 - 31 - 17 - - - - dB - - - - - - 180 - 60 - 41 - 17 - - - - Msps - - - - - - 0 - 120 - 221 - 71 - - - - 1 - - - + + + Setup + + + + + 10 + 20 + 221 + 191 + + + + Input + + + + + 10 + 60 + 66 + 17 + + + + Rate + + + + + + 10 + 30 + 66 + 17 + + + + Source + + + 90 - 40 + 30 121 27 - + + + + 10 + 90 + 66 + 17 + + + + Threshold + + + 90 - 10 + 60 + 91 + 27 + + + + + + + 90 + 90 71 27 - - - - 10 - 10 - 66 - 17 - - - - Gain - - - - - - 10 - 40 - 66 - 17 - - - - Antenna - - - + 160 - 10 + 90 31 17 @@ -203,23 +124,254 @@ dB - - - + - 90 - 10 - 113 + 180 + 60 + 41 + 17 + + + + Msps + + + + + + 0 + 120 + 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 + + + + + + + + 90 + 10 + 113 + 27 + + + + + + + 10 + 10 + 66 + 17 + + + + Filename + + + + + + + + + 270 + 20 + 281 + 151 + + + + Output + + + + + 10 + 120 + 61 + 22 + + + + KML + + + + + + 130 + 30 + 31 + 17 + + + + Port + + + + + + 130 + 60 + 31 + 17 + + + + Port + + + + + + 10 + 30 + 71 + 22 + + + + SBS-1 + + + + + + 130 + 90 + 31 + 17 + + + + Port + + + + + + 10 + 60 + 61 + 22 + + + + Raw + + + + + + 160 + 120 + 111 27 - + - 10 - 10 + 160 + 30 + 71 + 27 + + + + + + + 160 + 60 + 71 + 27 + + + + + + + 160 + 90 + 71 + 27 + + + + + + + 100 + 120 66 17 @@ -228,204 +380,87 @@ Filename + + + + 10 + 90 + 101 + 22 + + + + FlightGear + + + + + + 280 + 170 + 211 + 111 + + + + RX position + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + 20 + 60 + 71 + 20 + + + + Longitude + + + + + + 90 + 60 + 113 + 27 + + + + + + + 90 + 30 + 113 + 27 + + + + + + + 30 + 40 + 61 + 20 + + + + Latitude + + + + group_input + group_output + groupBox + groupBox - - - - - 260 - 10 - 281 - 151 - - - - Output - - - - - 10 - 120 - 61 - 22 - - - - KML - - - - - - 130 - 30 - 31 - 17 - - - - Port - - - - - - 130 - 60 - 31 - 17 - - - - Port - - - - - - 10 - 30 - 71 - 22 - - - - SBS-1 - - - - - - 130 - 90 - 31 - 17 - - - - Port - - - - - - 10 - 60 - 61 - 22 - - - - Raw - - - - - - 160 - 120 - 111 - 27 - - - - - - - 160 - 30 - 71 - 27 - - - - - - - 160 - 60 - 71 - 27 - - - - - - - 160 - 90 - 71 - 27 - - - - - - - 100 - 120 - 66 - 17 - - - - Filename - - - - - - 10 - 90 - 101 - 22 - - - - FlightGear - - - check_kml - check_kml - label_6 - label_7 - check_sbs1 - label_8 - check_raw - line_kmlfilename - line_sbs1port - line_rawport - line_fgfsport - label_9 - check_fgfs - - - - - 100 - 240 - 16 - 281 - - - - Qt::Vertical - - - - - - 130 - 220 - 741 - 301 - - - - 2 - Dashboard @@ -716,12 +751,12 @@ Live data - + 5 11 - 721 + 531 251 @@ -732,7 +767,7 @@ 10 - 220 + 39 101 17 @@ -744,8 +779,8 @@ - 670 - 30 + 580 + 340 98 27 @@ -758,7 +793,7 @@ 10 - 540 + 340 271 22 @@ -767,67 +802,18 @@ Show ADS-B-equipped aircraft only - + - 570 - 90 - 211 - 111 + 15 + 60 + 101 + 271 - - RX position + + QListView::SinglePass - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - 20 - 60 - 71 - 20 - - - - Longitude - - - - - - 90 - 60 - 113 - 27 - - - - - - - 90 - 30 - 113 - 27 - - - - - - - 30 - 40 - 61 - 20 - - - - Latitude - - @@ -835,7 +821,7 @@ 0 0 - 877 + 686 25