Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2d0ca6051 | ||
|
|
33349efd7f | ||
|
|
dd3e1fe629 | ||
|
|
a09c5add43 | ||
|
|
498cea34b2 | ||
|
|
4174658f0d | ||
|
|
b594fe2799 | ||
|
|
7fef37d34d | ||
|
|
2deefdf310 | ||
|
|
37aa74fbe0 | ||
|
|
d71e6bc1e7 | ||
|
|
0ce6374656 | ||
|
|
2766107a76 | ||
|
|
55cd17de67 |
@@ -69,23 +69,10 @@ set(GRC_BLOCKS_DIR ${GR_PKG_DATA_DIR}/grc/blocks)
|
||||
########################################################################
|
||||
# Find gnuradio build dependencies
|
||||
########################################################################
|
||||
find_package(Gruel)
|
||||
find_package(GnuradioCore)
|
||||
find_package(GnuradioRuntime)
|
||||
|
||||
if(NOT GRUEL_FOUND)
|
||||
message(FATAL_ERROR "Gruel required to compile gr-air-modes")
|
||||
endif()
|
||||
|
||||
if(NOT GNURADIO_CORE_FOUND)
|
||||
message(FATAL_ERROR "GnuRadio Core required to compile gr-air-modes")
|
||||
endif()
|
||||
|
||||
########################################################################
|
||||
# Find 0MQ networking library
|
||||
########################################################################
|
||||
find_package(ZeroMQ)
|
||||
if(NOT ZEROMQ_FOUND)
|
||||
message(FATAL_ERROR "Could not find required library libzmq (ZeroMQ).")
|
||||
if(NOT GNURADIO_RUNTIME_FOUND)
|
||||
message(FATAL_ERROR "GnuRadio Runtime required to compile gr-air-modes")
|
||||
endif()
|
||||
|
||||
########################################################################
|
||||
@@ -94,14 +81,12 @@ endif()
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${GRUEL_INCLUDE_DIRS}
|
||||
${GNURADIO_CORE_INCLUDE_DIRS}
|
||||
${GNURADIO_RUNTIME_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
link_directories(
|
||||
${Boost_LIBRARY_DIRS}
|
||||
${GRUEL_LIBRARY_DIRS}
|
||||
${GNURADIO_CORE_LIBRARY_DIRS}
|
||||
${GNURADIO_RUNTIME_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
# Set component parameters
|
||||
|
||||
112
apps/modes_gui
112
apps/modes_gui
@@ -19,11 +19,11 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
import os, sys, time, threading, datetime, math, csv
|
||||
import os, sys, time, threading, datetime, math, csv, tempfile
|
||||
from optparse import OptionParser
|
||||
from PyQt4 import QtCore,QtGui
|
||||
from PyQt4 import QtCore,QtGui,QtWebKit
|
||||
from PyQt4.Qwt5 import Qwt
|
||||
from gnuradio import gr, gru, optfir, eng_notation, blks2
|
||||
from gnuradio import gr, eng_notation
|
||||
from gnuradio.eng_option import eng_option
|
||||
from gnuradio.gr.pubsub import pubsub
|
||||
import air_modes
|
||||
@@ -73,15 +73,16 @@ class mainwindow(QtGui.QMainWindow):
|
||||
#disable by default
|
||||
self.ui.check_adsbonly.setCheckState(QtCore.Qt.Unchecked)
|
||||
|
||||
#set up the radio stuff
|
||||
self.queue = gr.msg_queue(10)
|
||||
self.running = False
|
||||
self.kmlgen = None #necessary bc we stop its thread in shutdown
|
||||
self.dbname = "air_modes.db"
|
||||
self.dbname = "adsb.db"
|
||||
self.num_reports = 0
|
||||
self.last_report = 0
|
||||
self.context = zmq.Context(1)
|
||||
|
||||
self.datamodel = dashboard_data_model(None)
|
||||
self.datamodel = dashboard_sql_model(None)
|
||||
self.ui.list_aircraft.setModel(self.datamodel)
|
||||
self.ui.list_aircraft.setModelColumn(0)
|
||||
|
||||
@@ -95,15 +96,16 @@ class mainwindow(QtGui.QMainWindow):
|
||||
self.dashboard_mapper.setModel(self.datamodel)
|
||||
self.dashboard_mapper.addMapping(self.ui.line_icao, 0)
|
||||
#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.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)
|
||||
self.dashboard_mapper.addMapping(self.ui.line_range, 11)
|
||||
self.dashboard_mapper.addMapping(self.ui.line_latitude, 2)
|
||||
self.dashboard_mapper.addMapping(self.ui.line_longitude, 3)
|
||||
self.dashboard_mapper.addMapping(self.ui.line_alt, 4)
|
||||
self.dashboard_mapper.addMapping(self.ui.line_speed, 5)
|
||||
#self.dashboard_mapper.addMapping(self.ui.compass_heading, 6)
|
||||
self.dashboard_mapper.addMapping(self.ui.line_climb, 7)
|
||||
self.dashboard_mapper.addMapping(self.ui.line_ident, 8)
|
||||
self.dashboard_mapper.addMapping(self.ui.line_type, 9)
|
||||
#self.dashboard_mapper.addMapping(self.ui.line_range, 11)
|
||||
#self.dashboard_mapper.addMapping(self.ui.compass_bearing, 12)
|
||||
|
||||
compass_palette = QtGui.QPalette()
|
||||
compass_palette.setColor(QtGui.QPalette.Foreground, QtCore.Qt.white)
|
||||
@@ -113,12 +115,14 @@ class mainwindow(QtGui.QMainWindow):
|
||||
self.ui.compass_heading.setNeedle(Qwt.QwtDialSimpleNeedle(Qwt.QwtDialSimpleNeedle.Ray, False, QtCore.Qt.black))
|
||||
self.ui.compass_bearing.setNeedle(Qwt.QwtDialSimpleNeedle(Qwt.QwtDialSimpleNeedle.Ray, False, QtCore.Qt.black))
|
||||
|
||||
#hook up the update signal
|
||||
#hook up the update signals which are explicitly necessary
|
||||
#most of the dashboard_mapper and list_aircraft stuff is implicitly done already
|
||||
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_bearing_widget)
|
||||
self.datamodel.dataChanged.connect(self.unmapped_widgets_dataChanged)
|
||||
self.ui.list_aircraft.selectionModel().currentRowChanged.connect(self.update_rssi_widget)
|
||||
self.ui.list_aircraft.selectionModel().currentRowChanged.connect(self.update_map_highlight)
|
||||
self.datamodel.dataChanged.connect(self.unmapped_widgets_dataChanged)
|
||||
|
||||
#hook up live data text box update signal
|
||||
self.live_data_changed_signal.connect(self.on_append_live_data)
|
||||
@@ -126,29 +130,29 @@ class mainwindow(QtGui.QMainWindow):
|
||||
############ widget update functions for non-mapped widgets ############
|
||||
def update_heading_widget(self, index):
|
||||
if index.model() is not None:
|
||||
heading = index.model().data(index.model().index(index.row(), self.datamodel._colnames.index("heading"))).toDouble()[0]
|
||||
heading = index.model().data(index.model().index(index.row(), 6)).toDouble()[0]
|
||||
self.ui.compass_heading.setValue(heading)
|
||||
|
||||
def update_bearing_widget(self, index):
|
||||
if index.model() is not None:
|
||||
bearing = index.model().data(index.model().index(index.row(), self.datamodel._colnames.index("bearing"))).toDouble()[0]
|
||||
bearing = 0#index.model().data(index.model().index(index.row(), 12)).toDouble()[0]
|
||||
self.ui.compass_bearing.setValue(bearing)
|
||||
|
||||
def unmapped_widgets_dataChanged(self, startIndex, endIndex):
|
||||
index = self.ui.list_aircraft.selectionModel().currentIndex()
|
||||
if index.row() in range(startIndex.row(), endIndex.row()+1): #the current aircraft was affected
|
||||
if self.datamodel._colnames.index("heading") in range(startIndex.column(), endIndex.column()+1):
|
||||
if 6 in range(startIndex.column(), endIndex.column()+1):
|
||||
self.update_heading_widget(index)
|
||||
if self.datamodel._colnames.index("bearing") in range(startIndex.column(), endIndex.column()+1):
|
||||
if 12 in range(startIndex.column(), endIndex.column()+1):
|
||||
self.update_bearing_widget(index)
|
||||
if self.datamodel._colnames.index("rssi") in range(startIndex.column(), endIndex.column()+1):
|
||||
if 2 in range(startIndex.column(), endIndex.column()+1):
|
||||
self.update_rssi_widget(index)
|
||||
|
||||
def update_rssi_widget(self, index):
|
||||
if index.model() is not None:
|
||||
rssi = index.model().data(index.model().index(index.row(), 2)).toDouble()[0]
|
||||
rssi = 0#index.model().data(index.model().index(index.row(), 2)).toDouble()[0]
|
||||
self.ui.prog_rssi.setValue(rssi)
|
||||
|
||||
|
||||
def increment_reportspersec(self, msg):
|
||||
self.num_reports += 1
|
||||
|
||||
@@ -159,6 +163,12 @@ class mainwindow(QtGui.QMainWindow):
|
||||
self.ui.line_reports.setText("%i" % self.num_reports)
|
||||
self.num_reports = 0
|
||||
|
||||
def update_map_highlight(self, index):
|
||||
if index.model() is not None:
|
||||
icaostr = index.model().data(index.model().index(index.row(), self.datamodel._colnames.index("icao"))).toString()
|
||||
icao = int(str(icaostr), 16)
|
||||
self.jsonpgen.set_highlight(icao)
|
||||
|
||||
##################### dynamic option population ########################
|
||||
#goes and gets valid antenna, sample rate options from the device and grays out appropriate things
|
||||
def populate_source_options(self):
|
||||
@@ -166,12 +176,13 @@ class mainwindow(QtGui.QMainWindow):
|
||||
self.rates = []
|
||||
self.ratetext = []
|
||||
self.antennas = []
|
||||
|
||||
|
||||
if sourceid == "UHD":
|
||||
try:
|
||||
from gnuradio import uhd
|
||||
self.src = uhd.single_usrp_source("", uhd.io_type_t.COMPLEX_FLOAT32, 1)
|
||||
self.rates = [rate.start() for rate in self.src.get_samp_rates() if (rate.start() % 2.e6) == 0]
|
||||
self.rates = [rate.start() for rate in self.src.get_samp_rates()
|
||||
if (rate.start() % 2.e6) == 0 and rate >= 4e6]
|
||||
self.antennas = self.src.get_antennas()
|
||||
self.src = None #deconstruct UHD source for now
|
||||
self.ui.combo_ant.setEnabled(True)
|
||||
@@ -183,7 +194,7 @@ class mainwindow(QtGui.QMainWindow):
|
||||
self.ui.combo_ant.setEnabled(False)
|
||||
self.ui.combo_rate.setEnabled(False)
|
||||
self.ui.stack_source.setCurrentIndex(0)
|
||||
|
||||
|
||||
elif sourceid == "Osmocom":
|
||||
try:
|
||||
import osmosdr
|
||||
@@ -216,8 +227,12 @@ class mainwindow(QtGui.QMainWindow):
|
||||
self.ui.combo_ant.clear()
|
||||
self.ui.combo_ant.addItems(self.antennas)
|
||||
|
||||
if 4e6 in self.rates:
|
||||
self.ui.combo_rate.setCurrentIndex(self.rates.index(4e6))
|
||||
#set up recommended sample rate
|
||||
recommended_rate = min(x for x in self.rates if x >= 4e6 and
|
||||
max(self.rates) % x == 0)
|
||||
if recommended_rate >= 8.e6:
|
||||
self.ui.check_pmf.setChecked(True)
|
||||
self.ui.combo_rate.setCurrentIndex(self.rates.index(recommended_rate))
|
||||
|
||||
################ action handlers ####################
|
||||
def on_combo_source_currentIndexChanged(self, index):
|
||||
@@ -264,8 +279,7 @@ class mainwindow(QtGui.QMainWindow):
|
||||
my_position = None
|
||||
|
||||
self._cpr_dec = air_modes.cpr_decoder(my_position)
|
||||
|
||||
self.datamodelout = dashboard_output(self._cpr_dec, self.datamodel, self._publisher)
|
||||
# self.datamodelout = dashboard_output(self._cpr_dec, self.datamodel, self._publisher)
|
||||
|
||||
self.lock = threading.Lock() #grab a lock to ensure sql and kml don't step on each other
|
||||
|
||||
@@ -288,16 +302,35 @@ class mainwindow(QtGui.QMainWindow):
|
||||
self.az_map_output = air_modes.az_map_output(self._cpr_dec, self.az_model, self._publisher)
|
||||
#self._relay.subscribe("dl_data", self.az_map_output.output)
|
||||
|
||||
self.livedata = air_modes.output_print(self._cpr_dec, self._publisher)
|
||||
#add output for live data box
|
||||
#self._relay.subscribe("dl_data", self.output_live_data)
|
||||
#set up map
|
||||
#NOTE this is busted on windows. WebKit requires .htm[l] extensions to render,
|
||||
#so using a temp file doesn't work.
|
||||
self._htmlfile = open("/tmp/mode_s.html", 'wb+')#tempfile.NamedTemporaryFile()
|
||||
self._jsonfile = tempfile.NamedTemporaryFile()
|
||||
|
||||
self.livedata = air_modes.output_print(self._cpr_dec,
|
||||
self._publisher,
|
||||
self.live_data_changed_signal.emit)
|
||||
|
||||
#create SQL database for KML and dashboard displays
|
||||
self.dbwriter = air_modes.output_sql(self._cpr_dec, self.dbname, self.lock, self._publisher)
|
||||
self.jsonpgen = air_modes.output_jsonp(self._jsonfile.name, self.dbname, my_position, self.lock, timeout=1)
|
||||
htmlstring = air_modes.html_template(my_position, self._jsonfile.name)
|
||||
self._htmlfile.write(htmlstring)
|
||||
self._htmlfile.flush()
|
||||
class WebPage(QtWebKit.QWebPage):
|
||||
def javaScriptConsoleMessage(self, msg, line, source):
|
||||
print '%s line %d: %s' % (source, line, msg)
|
||||
page = WebPage()
|
||||
self.ui.mapView.setPage(page)
|
||||
self.ui.mapView.load( QtCore.QUrl( QtCore.QUrl.fromLocalFile("/tmp/mode_s.html") ) )
|
||||
self.ui.mapView.show()
|
||||
|
||||
#output to update reports/sec widget
|
||||
self._relay.subscribe("dl_data", self.increment_reportspersec)
|
||||
#self.updates.append(self.update_reportspersec) #TODO FIXME
|
||||
self._rps_timer = QtCore.QTimer()
|
||||
self._rps_timer.timeout.connect(self.update_reportspersec)
|
||||
self._rps_timer.start(1000)
|
||||
|
||||
#start the flowgraph
|
||||
self._radio.start()
|
||||
@@ -311,6 +344,7 @@ class mainwindow(QtGui.QMainWindow):
|
||||
self._radio.close()
|
||||
self._relay = None
|
||||
self._radio = None
|
||||
self._rps_timer = None
|
||||
try:
|
||||
self.kmlgen.done = True
|
||||
#TODO FIXME need a way to kill kmlgen safely without delay
|
||||
@@ -328,18 +362,10 @@ class mainwindow(QtGui.QMainWindow):
|
||||
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())
|
||||
|
||||
def output_live_data(self, msg):
|
||||
try:
|
||||
msgstr = self.livedata.parse(msg)
|
||||
if msgstr is not None:
|
||||
self.live_data_changed_signal.emit(msgstr)
|
||||
except ADSBError:
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
window = mainwindow()
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
INCLUDE(FindPkgConfig)
|
||||
PKG_CHECK_MODULES(PC_GNURADIO_CORE gnuradio-core)
|
||||
|
||||
FIND_PATH(
|
||||
GNURADIO_CORE_INCLUDE_DIRS
|
||||
NAMES gr_random.h
|
||||
HINTS $ENV{GNURADIO_CORE_DIR}/include/gnuradio
|
||||
${PC_GNURADIO_CORE_INCLUDEDIR}
|
||||
PATHS /usr/local/include/gnuradio
|
||||
/usr/include/gnuradio
|
||||
)
|
||||
|
||||
FIND_LIBRARY(
|
||||
GNURADIO_CORE_LIBRARIES
|
||||
NAMES gnuradio-core
|
||||
HINTS $ENV{GNURADIO_CORE_DIR}/lib
|
||||
${PC_GNURADIO_CORE_LIBDIR}
|
||||
PATHS /usr/local/lib
|
||||
/usr/local/lib64
|
||||
/usr/lib
|
||||
/usr/lib64
|
||||
)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_CORE DEFAULT_MSG GNURADIO_CORE_LIBRARIES GNURADIO_CORE_INCLUDE_DIRS)
|
||||
MARK_AS_ADVANCED(GNURADIO_CORE_LIBRARIES GNURADIO_CORE_INCLUDE_DIRS)
|
||||
6
cmake/Modules/FindGnuradioRuntime.cmake
Normal file
6
cmake/Modules/FindGnuradioRuntime.cmake
Normal file
@@ -0,0 +1,6 @@
|
||||
INCLUDE(FindPkgConfig)
|
||||
PKG_CHECK_MODULES(GNURADIO_RUNTIME gnuradio-runtime)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_RUNTIME DEFAULT_MSG GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS)
|
||||
MARK_AS_ADVANCED(GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS)
|
||||
@@ -1,26 +0,0 @@
|
||||
INCLUDE(FindPkgConfig)
|
||||
PKG_CHECK_MODULES(PC_GRUEL gnuradio-core)
|
||||
|
||||
FIND_PATH(
|
||||
GRUEL_INCLUDE_DIRS
|
||||
NAMES gruel/attributes.h
|
||||
HINTS $ENV{GRUEL_DIR}/include
|
||||
${PC_GRUEL_INCLUDEDIR}
|
||||
PATHS /usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
|
||||
FIND_LIBRARY(
|
||||
GRUEL_LIBRARIES
|
||||
NAMES gruel
|
||||
HINTS $ENV{GRUEL_DIR}/lib
|
||||
${PC_GRUEL_LIBDIR}
|
||||
PATHS /usr/local/lib
|
||||
/usr/local/lib64
|
||||
/usr/lib
|
||||
/usr/lib64
|
||||
)
|
||||
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GRUEL DEFAULT_MSG GRUEL_LIBRARIES GRUEL_INCLUDE_DIRS)
|
||||
MARK_AS_ADVANCED(GRUEL_LIBRARIES GRUEL_INCLUDE_DIRS)
|
||||
@@ -22,7 +22,7 @@
|
||||
#ifndef INCLUDED_AIR_MODES_API_H
|
||||
#define INCLUDED_AIR_MODES_API_H
|
||||
|
||||
#include <gruel/attributes.h>
|
||||
#include <gnuradio/attributes.h>
|
||||
|
||||
#ifdef AIR_MODES_EXPORTS
|
||||
# define AIR_MODES_API __GR_ATTR_EXPORT
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#ifndef INCLUDED_AIR_MODES_PREAMBLE_H
|
||||
#define INCLUDED_AIR_MODES_PREAMBLE_H
|
||||
|
||||
#include <gr_block.h>
|
||||
#include <gnuradio/block.h>
|
||||
#include <air_modes_api.h>
|
||||
|
||||
class air_modes_preamble;
|
||||
@@ -35,7 +35,7 @@ AIR_MODES_API air_modes_preamble_sptr air_make_modes_preamble(int channel_rate,
|
||||
* \brief mode select preamble detection
|
||||
* \ingroup block
|
||||
*/
|
||||
class AIR_MODES_API air_modes_preamble : public gr_block
|
||||
class AIR_MODES_API air_modes_preamble : public gr::block
|
||||
{
|
||||
private:
|
||||
friend air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshold_db);
|
||||
@@ -49,7 +49,7 @@ private:
|
||||
float d_threshold_db;
|
||||
float d_threshold;
|
||||
pmt::pmt_t d_me, d_key;
|
||||
gr_tag_t d_timestamp;
|
||||
gr::tag_t d_timestamp;
|
||||
double d_secs_per_sample;
|
||||
|
||||
public:
|
||||
|
||||
@@ -23,38 +23,36 @@
|
||||
#ifndef INCLUDED_AIR_MODES_slicer_H
|
||||
#define INCLUDED_AIR_MODES_slicer_H
|
||||
|
||||
#include <gr_sync_block.h>
|
||||
#include <gr_msg_queue.h>
|
||||
#include <gnuradio/sync_block.h>
|
||||
#include <gnuradio/msg_queue.h>
|
||||
#include <air_modes_api.h>
|
||||
|
||||
class air_modes_slicer;
|
||||
typedef boost::shared_ptr<air_modes_slicer> air_modes_slicer_sptr;
|
||||
|
||||
AIR_MODES_API air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr_msg_queue_sptr queue);
|
||||
AIR_MODES_API air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr::msg_queue::sptr queue);
|
||||
|
||||
/*!
|
||||
* \brief mode select slicer detection
|
||||
* \ingroup block
|
||||
*/
|
||||
class AIR_MODES_API air_modes_slicer : public gr_sync_block
|
||||
class AIR_MODES_API air_modes_slicer : public gr::sync_block
|
||||
{
|
||||
private:
|
||||
friend air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr_msg_queue_sptr queue);
|
||||
air_modes_slicer(int channel_rate, gr_msg_queue_sptr queue);
|
||||
friend air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr::msg_queue::sptr queue);
|
||||
air_modes_slicer(int channel_rate, gr::msg_queue::sptr queue);
|
||||
|
||||
int d_check_width;
|
||||
int d_chip_rate;
|
||||
int d_samples_per_chip;
|
||||
int d_samples_per_symbol;
|
||||
gr_msg_queue_sptr d_queue;
|
||||
gr::msg_queue::sptr d_queue;
|
||||
std::ostringstream d_payload;
|
||||
|
||||
public:
|
||||
int work (int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items);
|
||||
|
||||
void set_rate(int channel_rate);
|
||||
};
|
||||
|
||||
#endif /* INCLUDED_AIR_MODES_slicer_H */
|
||||
|
||||
@@ -27,7 +27,7 @@ add_library(air_modes SHARED
|
||||
air_modes_slicer.cc
|
||||
modes_crc.cc
|
||||
)
|
||||
target_link_libraries(air_modes ${Boost_LIBRARIES} ${GRUEL_LIBRARIES} ${GNURADIO_CORE_LIBRARIES})
|
||||
target_link_libraries(air_modes ${Boost_LIBRARIES} ${GNURADIO_RUNTIME_LIBRARIES})
|
||||
set_target_properties(air_modes PROPERTIES DEFINE_SYMBOL "AIR_MODES_EXPORTS")
|
||||
set_target_properties(air_modes PROPERTIES SOVERSION "${gr-gr-air-modes_VERSION_MAJOR}")
|
||||
set_target_properties(air_modes PROPERTIES VERSION "${gr-gr-air-modes_VERSION_MAJOR}.${gr-gr-air-modes_VERSION_MINOR}")
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
|
||||
#include <ciso646>
|
||||
#include <air_modes_preamble.h>
|
||||
#include <gr_io_signature.h>
|
||||
#include <gnuradio/io_signature.h>
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <gr_tags.h>
|
||||
#include <gnuradio/tags.h>
|
||||
|
||||
air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshold_db)
|
||||
{
|
||||
@@ -38,39 +38,24 @@ air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshol
|
||||
}
|
||||
|
||||
air_modes_preamble::air_modes_preamble(int channel_rate, float threshold_db) :
|
||||
gr_block ("modes_preamble",
|
||||
gr_make_io_signature2 (2, 2, sizeof(float), sizeof(float)), //stream 0 is received data, stream 1 is moving average for reference
|
||||
gr_make_io_signature (1, 1, sizeof(float))) //the output packets
|
||||
{
|
||||
set_rate(channel_rate);
|
||||
set_threshold(threshold_db);
|
||||
|
||||
std::stringstream str;
|
||||
str << name() << unique_id();
|
||||
d_me = pmt::pmt_string_to_symbol(str.str());
|
||||
d_key = pmt::pmt_string_to_symbol("preamble_found");
|
||||
set_history(d_samples_per_symbol);
|
||||
}
|
||||
|
||||
void air_modes_preamble::set_rate(int channel_rate)
|
||||
gr::block ("modes_preamble",
|
||||
gr::io_signature::make2 (2, 2, sizeof(float), sizeof(float)), //stream 0 is received data, stream 1 is moving average for reference
|
||||
gr::io_signature::make (1, 1, sizeof(float))) //the output packets
|
||||
{
|
||||
d_chip_rate = 2000000; //2Mchips per second
|
||||
d_samples_per_chip = channel_rate / d_chip_rate; //must be integer number of samples per chip to work
|
||||
d_samples_per_symbol = d_samples_per_chip * 2;
|
||||
d_secs_per_sample = 1.0 / channel_rate;
|
||||
d_check_width = 240 * d_samples_per_symbol; //only search to this far from the end of the stream buffer
|
||||
set_output_multiple(1+d_check_width);
|
||||
}
|
||||
|
||||
void air_modes_preamble::set_threshold(float threshold_db)
|
||||
{
|
||||
d_check_width = 120 * d_samples_per_symbol; //only search to this far from the end of the stream buffer
|
||||
d_threshold_db = threshold_db;
|
||||
d_threshold = powf(10., threshold_db/20.); //the level that the sample must be above the moving average in order to qualify as a pulse
|
||||
}
|
||||
|
||||
float air_modes_preamble::get_threshold(void)
|
||||
{
|
||||
return d_threshold_db;
|
||||
d_secs_per_sample = 1.0 / channel_rate;
|
||||
set_output_multiple(1+d_check_width*2);
|
||||
|
||||
std::stringstream str;
|
||||
str << name() << unique_id();
|
||||
d_me = pmt::string_to_symbol(str.str());
|
||||
d_key = pmt::string_to_symbol("preamble_found");
|
||||
set_history(d_samples_per_symbol);
|
||||
}
|
||||
|
||||
static void integrate_and_dump(float *out, const float *in, int chips, int samps_per_chip) {
|
||||
@@ -96,14 +81,14 @@ static double correlate_preamble(const float *in, int samples_per_chip) {
|
||||
}
|
||||
|
||||
//todo: make it return a pair of some kind, otherwise you can lose precision
|
||||
static double tag_to_timestamp(gr_tag_t tstamp, uint64_t abs_sample_cnt, double secs_per_sample) {
|
||||
static double tag_to_timestamp(gr::tag_t tstamp, uint64_t abs_sample_cnt, double secs_per_sample) {
|
||||
uint64_t ts_sample, last_whole_stamp;
|
||||
double last_frac_stamp;
|
||||
|
||||
if(tstamp.key == NULL || pmt::pmt_symbol_to_string(tstamp.key) != "rx_time") return 0;
|
||||
if(tstamp.key == NULL || pmt::symbol_to_string(tstamp.key) != "rx_time") return 0;
|
||||
|
||||
last_whole_stamp = pmt::pmt_to_uint64(pmt::pmt_tuple_ref(tstamp.value, 0));
|
||||
last_frac_stamp = pmt::pmt_to_double(pmt::pmt_tuple_ref(tstamp.value, 1));
|
||||
last_whole_stamp = pmt::to_uint64(pmt::tuple_ref(tstamp.value, 0));
|
||||
last_frac_stamp = pmt::to_double(pmt::tuple_ref(tstamp.value, 1));
|
||||
ts_sample = tstamp.offset;
|
||||
|
||||
double tstime = double(abs_sample_cnt * secs_per_sample) + last_whole_stamp + last_frac_stamp;
|
||||
@@ -137,8 +122,8 @@ int air_modes_preamble::general_work(int noutput_items,
|
||||
};
|
||||
|
||||
uint64_t abs_sample_cnt = nitems_read(0);
|
||||
std::vector<gr_tag_t> tstamp_tags;
|
||||
get_tags_in_range(tstamp_tags, 0, abs_sample_cnt, abs_sample_cnt + ninputs, pmt::pmt_string_to_symbol("rx_time"));
|
||||
std::vector<gr::tag_t> tstamp_tags;
|
||||
get_tags_in_range(tstamp_tags, 0, abs_sample_cnt, abs_sample_cnt + ninputs, pmt::string_to_symbol("rx_time"));
|
||||
//tags.back() is the most recent timestamp, then.
|
||||
if(tstamp_tags.size() > 0) {
|
||||
d_timestamp = tstamp_tags.back();
|
||||
@@ -210,10 +195,10 @@ int air_modes_preamble::general_work(int noutput_items,
|
||||
add_item_tag(0, //stream ID
|
||||
nitems_written(0), //sample
|
||||
d_key, //frame_info
|
||||
pmt::pmt_make_tuple(pmt::pmt_from_double(tstamp), pmt::pmt_from_double(inavg[i])),
|
||||
pmt::from_double(tstamp),
|
||||
d_me //block src id
|
||||
);
|
||||
|
||||
|
||||
//std::cout << "PREAMBLE" << std::endl;
|
||||
|
||||
//produce only one output per work call -- TODO this should probably change
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
|
||||
#include <ciso646>
|
||||
#include <air_modes_slicer.h>
|
||||
#include <gr_io_signature.h>
|
||||
#include <gnuradio/io_signature.h>
|
||||
#include <air_modes_types.h>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <modes_crc.h>
|
||||
#include <iostream>
|
||||
#include <gr_tags.h>
|
||||
#include <gnuradio/tags.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -41,26 +41,23 @@ extern "C"
|
||||
#include <string.h>
|
||||
}
|
||||
|
||||
air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr_msg_queue_sptr queue)
|
||||
air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr::msg_queue::sptr queue)
|
||||
{
|
||||
return air_modes_slicer_sptr (new air_modes_slicer(channel_rate, queue));
|
||||
}
|
||||
|
||||
air_modes_slicer::air_modes_slicer(int channel_rate, gr_msg_queue_sptr queue) :
|
||||
gr_sync_block ("modes_slicer",
|
||||
gr_make_io_signature (1, 1, sizeof(float)), //stream 0 is received data, stream 1 is binary preamble detector output
|
||||
gr_make_io_signature (0, 0, 0) )
|
||||
{
|
||||
set_rate(channel_rate);
|
||||
d_queue = queue;
|
||||
}
|
||||
|
||||
void air_modes_slicer::set_rate(int channel_rate)
|
||||
air_modes_slicer::air_modes_slicer(int channel_rate, gr::msg_queue::sptr queue) :
|
||||
gr::sync_block ("modes_slicer",
|
||||
gr::io_signature::make (1, 1, sizeof(float)), //stream 0 is received data, stream 1 is binary preamble detector output
|
||||
gr::io_signature::make (0, 0, 0) )
|
||||
{
|
||||
//initialize private data here
|
||||
d_chip_rate = 2000000; //2Mchips per second
|
||||
d_samples_per_chip = 2;//FIXME this is constant now channel_rate / d_chip_rate;
|
||||
d_samples_per_symbol = d_samples_per_chip * 2;
|
||||
d_check_width = 120 * d_samples_per_symbol; //how far you will have to look ahead
|
||||
d_queue = queue;
|
||||
|
||||
set_output_multiple(d_check_width*2); //how do you specify buffer size for sinks?
|
||||
}
|
||||
|
||||
@@ -110,10 +107,10 @@ int air_modes_slicer::work(int noutput_items,
|
||||
|
||||
if(0) std::cout << "Slicer called with " << size << " samples" << std::endl;
|
||||
|
||||
std::vector<gr_tag_t> tags;
|
||||
std::vector<gr::tag_t> tags;
|
||||
uint64_t abs_sample_cnt = nitems_read(0);
|
||||
get_tags_in_range(tags, 0, abs_sample_cnt, abs_sample_cnt + size, pmt::pmt_string_to_symbol("preamble_found"));
|
||||
std::vector<gr_tag_t>::iterator tag_iter;
|
||||
get_tags_in_range(tags, 0, abs_sample_cnt, abs_sample_cnt + size, pmt::string_to_symbol("preamble_found"));
|
||||
std::vector<gr::tag_t>::iterator tag_iter;
|
||||
|
||||
for(tag_iter = tags.begin(); tag_iter != tags.end(); tag_iter++) {
|
||||
uint64_t i = tag_iter->offset - abs_sample_cnt;
|
||||
@@ -159,14 +156,17 @@ int air_modes_slicer::work(int noutput_items,
|
||||
if(rx_packet.numlowconf < 24) rx_packet.lowconfbits[rx_packet.numlowconf++] = j;
|
||||
}
|
||||
}
|
||||
|
||||
rx_packet.timestamp = pmt_to_double(pmt_tuple_ref(tag_iter->value, 0));
|
||||
double ref = pmt_to_double(pmt_tuple_ref(tag_iter->value, 1));
|
||||
|
||||
//traverse the whole packet and if you find all 0's, just toss it. don't know why these packets turn up, but they pass ECC.
|
||||
/******************** BEGIN TIMESTAMP BS ******************/
|
||||
rx_packet.timestamp = pmt::to_double(tag_iter->value);
|
||||
/******************* END TIMESTAMP BS *********************/
|
||||
|
||||
//increment for the next round
|
||||
|
||||
//here you might want to traverse the whole packet and if you find all 0's, just toss it. don't know why these packets turn up, but they pass ECC.
|
||||
bool zeroes = 1;
|
||||
for(int m = 0; m < 14; m++) {
|
||||
if(rx_packet.data[m]) { zeroes = 0; break; }
|
||||
if(rx_packet.data[m]) zeroes = 0;
|
||||
}
|
||||
if(zeroes) {continue;} //toss it
|
||||
|
||||
@@ -187,9 +187,9 @@ int air_modes_slicer::work(int noutput_items,
|
||||
d_payload << std::hex << std::setw(2) << std::setfill('0') << unsigned(rx_packet.data[m]);
|
||||
}
|
||||
|
||||
d_payload << " " << std::setw(6) << rx_packet.crc << " " << std::dec << rx_packet.reference_level / ref
|
||||
d_payload << " " << std::setw(6) << rx_packet.crc << " " << std::dec << rx_packet.reference_level
|
||||
<< " " << std::setprecision(10) << std::setw(10) << rx_packet.timestamp;
|
||||
gr_message_sptr msg = gr_make_message_from_string(std::string(d_payload.str()));
|
||||
gr::message::sptr msg = gr::message::make_from_string(std::string(d_payload.str()));
|
||||
d_queue->handle(msg);
|
||||
}
|
||||
if(0) std::cout << "Slicer consumed " << size << ", returned " << size << std::endl;
|
||||
|
||||
@@ -34,6 +34,7 @@ GR_PYTHON_INSTALL(
|
||||
altitude.py
|
||||
az_map.py
|
||||
cpr.py
|
||||
html_template.py
|
||||
mlat.py
|
||||
exceptions.py
|
||||
flightgear.py
|
||||
|
||||
@@ -57,7 +57,7 @@ from parse import *
|
||||
from msprint import output_print
|
||||
from sql import output_sql
|
||||
from sbs1 import output_sbs1
|
||||
from kml import output_kml
|
||||
from kml import output_kml, output_jsonp
|
||||
from raw_server import raw_server
|
||||
from radio import modes_radio
|
||||
from exceptions import *
|
||||
@@ -65,6 +65,7 @@ from az_map import *
|
||||
from types import *
|
||||
from altitude import *
|
||||
from cpr import cpr_decoder
|
||||
from html_template import html_template
|
||||
#this is try/excepted in case the user doesn't have numpy installed
|
||||
try:
|
||||
from flightgear import output_flightgear
|
||||
|
||||
@@ -22,10 +22,11 @@
|
||||
# This file contains data models, view delegates, and associated classes
|
||||
# for handling the GUI back end data model.
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4 import QtCore, QtGui, QtSql
|
||||
import air_modes
|
||||
import threading, math, time
|
||||
from air_modes.exceptions import *
|
||||
from gnuradio.gr.pubsub import pubsub
|
||||
|
||||
#fades the ICAOs out as their last report gets older,
|
||||
#and display ident if available, ICAO otherwise
|
||||
@@ -37,12 +38,16 @@ class ICAOViewDelegate(QtGui.QStyledItemDelegate):
|
||||
painter.drawRect(option.rect)
|
||||
|
||||
#if there's an ident available, use it. otherwise print the ICAO
|
||||
if index.model().data(index.model().index(index.row(), 9)) != QtCore.QVariant():
|
||||
paintstr = index.model().data(index.model().index(index.row(), 9)).toString()
|
||||
if index.model().data(index.model().index(index.row(), 8)) != QtCore.QVariant():
|
||||
paintstr = index.model().data(index.model().index(index.row(), 8)).toString()
|
||||
else:
|
||||
paintstr = index.model().data(index.model().index(index.row(), 0)).toString()
|
||||
last_report = index.model().data(index.model().index(index.row(), 1)).toDouble()[0]
|
||||
age = (time.time() - last_report)
|
||||
|
||||
#FIXME this is kind of heinous, find out how you got int data out of it last time
|
||||
last_report = time.strptime(str(index.model().data(index.model().index(index.row(), 1)).toString()), "%Y-%m-%d %H:%M:%S")
|
||||
age = (time.mktime(time.gmtime()) - time.mktime(last_report)) - 3600.*time.daylight
|
||||
print age
|
||||
|
||||
max_age = 60. #age at which it grays out
|
||||
#minimum alpha is 0x40 (oldest), max is 0xFF (newest)
|
||||
age = min(age, max_age)
|
||||
@@ -50,152 +55,36 @@ class ICAOViewDelegate(QtGui.QStyledItemDelegate):
|
||||
painter.setPen(QtGui.QColor(0, 0, 0, alpha))
|
||||
painter.drawText(option.rect.left()+3, option.rect.top(), option.rect.width(), option.rect.height(), option.displayAlignment, paintstr)
|
||||
|
||||
#the data model used to display dashboard data.
|
||||
class dashboard_data_model(QtCore.QAbstractTableModel):
|
||||
#class dashboard_sql_model(QtCore.QAbstractTableModel):
|
||||
# def __init__(self, parent):
|
||||
# QtCore.QAbstractTableModel.__init__(self, parent)
|
||||
|
||||
# def update(self, icao):
|
||||
|
||||
#TODO must add libqt4-sql, libqt4-sql-sqlite, python-qt4-sql to dependencies
|
||||
#TODO looks like you're going to have to either integrate this into sql.py (ugh!) or find a way to keep it in sync
|
||||
#seems like it wants to have control over maintaining data currency
|
||||
#worst case is you make your own damn SQL query model based on abstracttablemodel.
|
||||
class dashboard_sql_model(QtSql.QSqlQueryModel):
|
||||
def __init__(self, parent):
|
||||
QtCore.QAbstractTableModel.__init__(self, parent)
|
||||
self._data = []
|
||||
self.lock = threading.Lock()
|
||||
self._colnames = ["icao", "seen", "rssi", "latitude", "longitude", "altitude", "speed", "heading", "vertical", "ident", "type", "range", "bearing"]
|
||||
#custom precision limits for display
|
||||
self._precisions = [None, None, None, 6, 6, 0, 0, 0, 0, None, None, 2, 0]
|
||||
for field in self._colnames:
|
||||
self.setHeaderData(self._colnames.index(field), QtCore.Qt.Horizontal, field)
|
||||
def rowCount(self, parent=QtCore.QVariant()):
|
||||
return len(self._data)
|
||||
def columnCount(self, parent=QtCore.QVariant()):
|
||||
return len(self._colnames)
|
||||
def data(self, index, role=QtCore.Qt.DisplayRole):
|
||||
if not index.isValid():
|
||||
return QtCore.QVariant()
|
||||
if index.row() >= self.rowCount():
|
||||
return QtCore.QVariant()
|
||||
if index.column() >= self.columnCount():
|
||||
return QtCore.QVariant()
|
||||
if (role != QtCore.Qt.DisplayRole) and (role != QtCore.Qt.EditRole):
|
||||
return QtCore.QVariant()
|
||||
if self._data[index.row()][index.column()] is None:
|
||||
return QtCore.QVariant()
|
||||
else:
|
||||
#if there's a dedicated precision for that column, print it out with the specified precision.
|
||||
#this only works well if you DON'T have other views/widgets that depend on numeric data coming out.
|
||||
#i don't like this, but it works for now. unfortunately it seems like Qt doesn't give you a
|
||||
#good alternative.
|
||||
if self._precisions[index.column()] is not None:
|
||||
return QtCore.QVariant("%.*f" % (self._precisions[index.column()], self._data[index.row()][index.column()]))
|
||||
else:
|
||||
if self._colnames[index.column()] == "icao":
|
||||
return QtCore.QVariant("%06x" % self._data[index.row()][index.column()]) #return as hex string
|
||||
else:
|
||||
return QtCore.QVariant(self._data[index.row()][index.column()])
|
||||
|
||||
def setData(self, index, value, role=QtCore.Qt.EditRole):
|
||||
self.lock.acquire()
|
||||
if not index.isValid():
|
||||
return False
|
||||
if index.row() >= self.rowCount():
|
||||
return False
|
||||
if index.column >= self.columnCount():
|
||||
return False
|
||||
if role != QtCore.Qt.EditRole:
|
||||
return False
|
||||
self._data[index.row()][index.column()] = value
|
||||
self.lock.release()
|
||||
|
||||
#addRecord implements an upsert on self._data; that is,
|
||||
#it updates the row if the ICAO exists, or else it creates a new row.
|
||||
def addRecord(self, record):
|
||||
self.lock.acquire()
|
||||
icaos = [x[0] for x in self._data]
|
||||
if record["icao"] in icaos:
|
||||
row = icaos.index(record["icao"])
|
||||
for column in record:
|
||||
self._data[row][self._colnames.index(column)] = record[column]
|
||||
#create index to existing row and tell the model everything's changed in this row
|
||||
#or inside the for loop, use dataChanged on each changed field (might be better)
|
||||
self.dataChanged.emit(self.createIndex(row, 0), self.createIndex(row, len(self._colnames)-1))
|
||||
|
||||
#only create records for ICAOs with ADS-B reports
|
||||
elif ("latitude" or "speed" or "ident") in record:
|
||||
#find new inserted row number
|
||||
icaos.append(record["icao"])
|
||||
newrowoffset = sorted(icaos).index(record["icao"])
|
||||
self.beginInsertRows(QtCore.QModelIndex(), newrowoffset, newrowoffset)
|
||||
newrecord = [None for x in xrange(len(self._colnames))]
|
||||
for col in xrange(0, len(self._colnames)):
|
||||
if self._colnames[col] in record:
|
||||
newrecord[col] = record[self._colnames[col]]
|
||||
self._data.append(newrecord)
|
||||
self._data = sorted(self._data, key = lambda x: x[0]) #sort by icao
|
||||
self.endInsertRows()
|
||||
self.lock.release()
|
||||
self.prune()
|
||||
|
||||
#weeds out ICAOs older than 5 minutes
|
||||
def prune(self):
|
||||
self.lock.acquire()
|
||||
for (index,row) in enumerate(self._data):
|
||||
if time.time() - row[1] >= 60:
|
||||
self.beginRemoveRows(QtCore.QModelIndex(), index, index)
|
||||
self._data.pop(index)
|
||||
self.endRemoveRows()
|
||||
self.lock.release()
|
||||
|
||||
class dashboard_output:
|
||||
def __init__(self, cprdec, model, pub):
|
||||
self.model = model
|
||||
self._cpr = cprdec
|
||||
pub.subscribe("modes_dl", self.output)
|
||||
def output(self, msg):
|
||||
try:
|
||||
msgtype = msg.data["df"]
|
||||
now = time.time()
|
||||
newrow = {"rssi": msg.rssi, "seen": now}
|
||||
if msgtype in [0, 4, 20]:
|
||||
newrow["altitude"] = air_modes.altitude.decode_alt(msg.data["ac"], True)
|
||||
newrow["icao"] = msg.ecc
|
||||
self.model.addRecord(newrow)
|
||||
|
||||
elif msgtype == 17:
|
||||
icao = msg.data["aa"]
|
||||
newrow["icao"] = icao
|
||||
subtype = msg.data["ftc"]
|
||||
if subtype == 4:
|
||||
(ident, actype) = air_modes.parseBDS08(msg.data)
|
||||
newrow["ident"] = ident
|
||||
newrow["type"] = actype
|
||||
elif 5 <= subtype <= 8:
|
||||
(ground_track, decoded_lat, decoded_lon, rnge, bearing) = air_modes.parseBDS06(msg.data, self._cpr)
|
||||
newrow["heading"] = ground_track
|
||||
newrow["latitude"] = decoded_lat
|
||||
newrow["longitude"] = decoded_lon
|
||||
newrow["altitude"] = 0
|
||||
if rnge is not None:
|
||||
newrow["range"] = rnge
|
||||
newrow["bearing"] = bearing
|
||||
elif 9 <= subtype <= 18:
|
||||
(altitude, decoded_lat, decoded_lon, rnge, bearing) = air_modes.parseBDS05(msg.data, self._cpr)
|
||||
newrow["altitude"] = altitude
|
||||
newrow["latitude"] = decoded_lat
|
||||
newrow["longitude"] = decoded_lon
|
||||
if rnge is not None:
|
||||
newrow["range"] = rnge
|
||||
newrow["bearing"] = bearing
|
||||
elif subtype == 19:
|
||||
subsubtype = msg.data["sub"]
|
||||
velocity = None
|
||||
heading = None
|
||||
vert_spd = None
|
||||
if subsubtype == 0:
|
||||
(velocity, heading, vert_spd) = air_modes.parseBDS09_0(msg.data)
|
||||
elif 1 <= subsubtype <= 2:
|
||||
(velocity, heading, vert_spd) = air_modes.parseBDS09_1(msg.data)
|
||||
newrow["speed"] = velocity
|
||||
newrow["heading"] = heading
|
||||
newrow["vertical"] = vert_spd
|
||||
|
||||
self.model.addRecord(newrow)
|
||||
|
||||
except ADSBError:
|
||||
return
|
||||
QtSql.QSqlQueryModel.__init__(self, parent)
|
||||
self._query = """select tab1.icao, tab1.seen, tab1.lat, tab1.lon, tab1.alt, speed, heading, vertical, ident, type
|
||||
from (select * from (select * from positions order by seen desc) group by icao) tab1
|
||||
left join (select * from (select * from vectors order by seen desc) group by icao) tab2
|
||||
on tab1.icao=tab2.icao
|
||||
left join (select * from (select * from ident)) tab3
|
||||
on tab1.icao=tab3.icao
|
||||
where tab1.seen > datetime('now', '-1 minute')"""
|
||||
self._sql = None
|
||||
self._db = QtSql.QSqlDatabase("QSQLITE")
|
||||
self._db.setDatabaseName("adsb.db") #TODO specify this elsewhere
|
||||
self._db.open()
|
||||
#what is this i don't even
|
||||
#fetches the combined data of all three tables for all ICAOs seen in the last minute.
|
||||
#FIXME PyQt's SQLite gives you different results than the SQLite browser
|
||||
self.setQuery(self._query, self._db)
|
||||
|
||||
#the big club
|
||||
def update_all(self, icao):
|
||||
self.setQuery(self._query, self._db)
|
||||
#self.dataChanged.emit(self.createIndex(0, 0), self.createIndex(self.rowCount(), self.columnCount()))
|
||||
|
||||
127
python/html_template.py
Normal file
127
python/html_template.py
Normal file
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python
|
||||
#HTML template for Mode S map display
|
||||
#Nick Foster, 2013
|
||||
|
||||
def html_template(my_position, json_file):
|
||||
if my_position is None:
|
||||
my_position = [37, -122]
|
||||
|
||||
return """
|
||||
<html>
|
||||
<head>
|
||||
<title>ADS-B Aircraft Map</title>
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
|
||||
<style type="text/css">
|
||||
.labels {
|
||||
color: red;
|
||||
background-color: white;
|
||||
font-family: "Lucida Grande", "Arial", sans-serif;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
border: 2px solid black;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false">
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
var map;
|
||||
var markers = [];
|
||||
var defaultLocation = new google.maps.LatLng(%f, %f);
|
||||
var defaultZoomLevel = 9;
|
||||
|
||||
function requestJSONP() {
|
||||
var script = document.createElement("script");
|
||||
script.src = "%s?" + Math.random();
|
||||
script.params = Math.random();
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
};
|
||||
|
||||
var planeMarker;
|
||||
var planes = [];
|
||||
|
||||
function clearMarkers() {
|
||||
for (var i = 0; i < planes.length; i++) {
|
||||
planes[i].setMap(null);
|
||||
}
|
||||
planes = [];
|
||||
};
|
||||
|
||||
function jsonp_callback(results) { // from JSONP
|
||||
clearMarkers();
|
||||
airplanes = {};
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
airplanes[results[i].icao] = {
|
||||
center: new google.maps.LatLng(results[i].lat, results[i].lon),
|
||||
heading: results[i].hdg,
|
||||
altitude: results[i].alt,
|
||||
type: results[i].type,
|
||||
ident: results[i].ident,
|
||||
speed: results[i].speed,
|
||||
vertical: results[i].vertical,
|
||||
highlight: results[i].highlight
|
||||
};
|
||||
}
|
||||
refreshIcons();
|
||||
}
|
||||
|
||||
function refreshIcons() {
|
||||
for (var airplane in airplanes) {
|
||||
if (airplanes[airplane].highlight != 0) {
|
||||
icon_file = "http://www.nerdnetworks.org/~bistromath/airplane_sprite_highlight.png";
|
||||
} else {
|
||||
icon_file = "http://www.nerdnetworks.org/~bistromath/airplane_sprite.png";
|
||||
};
|
||||
var plane_icon = {
|
||||
url: icon_file,
|
||||
size: new google.maps.Size(128,128),
|
||||
origin: new google.maps.Point(parseInt(airplanes[airplane].heading/10)*128,0),
|
||||
anchor: new google.maps.Point(64,64),
|
||||
//scaledSize: new google.maps.Size(4608,126)
|
||||
};
|
||||
|
||||
identstr = airplanes[airplane].ident;
|
||||
if (identstr === "" || !identstr) {
|
||||
identstr = airplanes[airplane].icao;
|
||||
};
|
||||
|
||||
var planeOptions = {
|
||||
map: map,
|
||||
position: airplanes[airplane].center,
|
||||
icon: plane_icon,
|
||||
//label content meaningless unless you use the MarkerWithLabel class from the Maps Utility Library
|
||||
labelContent: identstr,
|
||||
labelAnchor: new google.maps.Point(64, 0),
|
||||
labelClass: "labels",
|
||||
labelStyle: {opacity: 0.75}
|
||||
};
|
||||
planeMarker = new google.maps.Marker(planeOptions);
|
||||
planes.push(planeMarker);
|
||||
};
|
||||
};
|
||||
|
||||
function initialize()
|
||||
{
|
||||
var myOptions =
|
||||
{
|
||||
zoom: defaultZoomLevel,
|
||||
center: defaultLocation,
|
||||
disableDefaultUI: true,
|
||||
mapTypeId: google.maps.MapTypeId.TERRAIN
|
||||
};
|
||||
|
||||
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
|
||||
|
||||
requestJSONP();
|
||||
setInterval("requestJSONP()", 1000);
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body onload="initialize()">
|
||||
<div id="map_canvas" style="width:100%%; height:100%%">
|
||||
</div>
|
||||
</body>
|
||||
</html>""" % (my_position[0], my_position[1], json_file)
|
||||
@@ -30,7 +30,7 @@ class output_kml(threading.Thread):
|
||||
self.my_coords = localpos
|
||||
self._timeout = timeout
|
||||
self._lock = lock
|
||||
|
||||
|
||||
self.shutdown = threading.Event()
|
||||
self.finished = threading.Event()
|
||||
self.setDaemon(1)
|
||||
@@ -41,7 +41,7 @@ class output_kml(threading.Thread):
|
||||
while self.shutdown.is_set() is False:
|
||||
self.writekml()
|
||||
time.sleep(self._timeout)
|
||||
|
||||
|
||||
self._db.close()
|
||||
self._db = None
|
||||
self.finished.set()
|
||||
@@ -52,8 +52,8 @@ class output_kml(threading.Thread):
|
||||
#there's a bug here where self._timeout is long and close() has
|
||||
#to wait for the sleep to expire before closing. we just bail
|
||||
#instead with the 0.2 param above.
|
||||
|
||||
|
||||
|
||||
|
||||
def writekml(self):
|
||||
kmlstr = self.genkml()
|
||||
if kmlstr is not None:
|
||||
@@ -64,7 +64,7 @@ class output_kml(threading.Thread):
|
||||
def locked_execute(self, c, query):
|
||||
with self._lock:
|
||||
c.execute(query)
|
||||
|
||||
|
||||
def draw_circle(self, center, rng):
|
||||
retstr = ""
|
||||
steps=30
|
||||
@@ -91,7 +91,7 @@ class output_kml(threading.Thread):
|
||||
|
||||
retstr = string.lstrip(retstr)
|
||||
return retstr
|
||||
|
||||
|
||||
def genkml(self):
|
||||
#first let's draw the static content
|
||||
retstr="""<?xml version="1.0" encoding="UTF-8"?>\n<kml xmlns="http://www.opengis.net/kml/2.2">\n<Document>\n\t<Style id="airplane">\n\t\t<IconStyle>\n\t\t\t<Icon><href>airports.png</href></Icon>\n\t\t</IconStyle>\n\t</Style>\n\t<Style id="rangering">\n\t<LineStyle>\n\t\t<color>9f4f4faf</color>\n\t\t<width>2</width>\n\t</LineStyle>\n\t</Style>\n\t<Style id="track">\n\t<LineStyle>\n\t\t<color>5fff8f8f</color>\n\t\t<width>4</width>\n\t</LineStyle>\n\t</Style>"""
|
||||
@@ -101,7 +101,7 @@ class output_kml(threading.Thread):
|
||||
for rng in [100, 200, 300]:
|
||||
retstr += """\n\t\t<Placemark>\n\t\t\t<name>%inm</name>\n\t\t\t<styleUrl>#rangering</styleUrl>\n\t\t\t<LinearRing>\n\t\t\t\t<coordinates>%s</coordinates>\n\t\t\t</LinearRing>\n\t\t</Placemark>""" % (rng, self.draw_circle(self.my_coords, rng),)
|
||||
retstr += """\t</Folder>\n"""
|
||||
|
||||
|
||||
retstr += """\t<Folder>\n\t\t<name>Aircraft locations</name>\n\t\t<open>0</open>"""
|
||||
|
||||
#read the database and add KML
|
||||
@@ -124,11 +124,11 @@ class output_kml(threading.Thread):
|
||||
if lon is None: lon = 0
|
||||
alt = track[0][2]
|
||||
if alt is None: alt = 0
|
||||
|
||||
|
||||
metric_alt = alt * 0.3048 #google earth takes meters, the commie bastards
|
||||
|
||||
|
||||
trackstr = ""
|
||||
|
||||
|
||||
for pos in track:
|
||||
trackstr += " %f,%f,%f" % (pos[4], pos[3], pos[2]*0.3048)
|
||||
|
||||
@@ -162,7 +162,7 @@ class output_kml(threading.Thread):
|
||||
seen = 0
|
||||
speed = 0
|
||||
heading = 0
|
||||
vertical = 0
|
||||
vertical = 0
|
||||
#now generate some KML
|
||||
retstr+= "\n\t\t<Placemark>\n\t\t\t<name>%s</name>\n\t\t\t<Style><IconStyle><heading>%i</heading></IconStyle></Style>\n\t\t\t<styleUrl>#airplane</styleUrl>\n\t\t\t<description>\n\t\t\t\t<![CDATA[Altitude: %s<br/>Heading: %i<br/>Speed: %i<br/>Vertical speed: %i<br/>ICAO: %x<br/>Last seen: %s]]>\n\t\t\t</description>\n\t\t\t<Point>\n\t\t\t\t<altitudeMode>absolute</altitudeMode>\n\t\t\t\t<extrude>1</extrude>\n\t\t\t\t<coordinates>%s,%s,%i</coordinates>\n\t\t\t</Point>\n\t\t</Placemark>" % (ident, heading, alt, heading, speed, vertical, icao[0], seen, lon, lat, metric_alt, )
|
||||
|
||||
@@ -170,3 +170,79 @@ class output_kml(threading.Thread):
|
||||
|
||||
retstr+= '\n\t</Folder>\n</Document>\n</kml>'
|
||||
return retstr
|
||||
|
||||
#we just inherit from output_kml because we're doing the same thing, only in a different format.
|
||||
class output_jsonp(output_kml):
|
||||
def set_highlight(self, icao):
|
||||
self.highlight = icao
|
||||
|
||||
def genkml(self):
|
||||
retstr="""jsonp_callback(["""
|
||||
|
||||
# if self.my_coords is not None:
|
||||
# retstr += """\n\t<Folder>\n\t\t<name>Range rings</name>\n\t\t<open>0</open>"""
|
||||
# for rng in [100, 200, 300]:
|
||||
# retstr += """\n\t\t<Placemark>\n\t\t\t<name>%inm</name>\n\t\t\t<styleUrl>#rangering</styleUrl>\n\t\t\t<LinearRing>\n\t\t\t\t<coordinates>%s</coordinates>\n\t\t\t</LinearRing>\n\t\t</Placemark>""" % (rng, self.draw_circle(self.my_coords, rng),)
|
||||
# retstr += """\t</Folder>\n"""
|
||||
|
||||
# retstr += """\t<Folder>\n\t\t<name>Aircraft locations</name>\n\t\t<open>0</open>"""
|
||||
|
||||
#read the database and add KML
|
||||
q = "select distinct icao from positions where seen > datetime('now', '-5 minute')"
|
||||
c = self._db.cursor()
|
||||
self.locked_execute(c, q)
|
||||
icaolist = c.fetchall()
|
||||
#now we have a list icaolist of all ICAOs seen in the last 5 minutes
|
||||
|
||||
for icao in icaolist:
|
||||
icao = icao[0]
|
||||
|
||||
#now get metadata
|
||||
q = "select ident, type from ident where icao=%i" % icao
|
||||
self.locked_execute(c, q)
|
||||
r = c.fetchall()
|
||||
if len(r) != 0:
|
||||
ident = r[0][0]
|
||||
actype = r[0][1]
|
||||
else:
|
||||
ident=""
|
||||
actype = ""
|
||||
if ident is None: ident = ""
|
||||
#get most recent speed/heading/vertical
|
||||
q = "select seen, speed, heading, vertical from vectors where icao=%i order by seen desc limit 1" % icao
|
||||
self.locked_execute(c, q)
|
||||
r = c.fetchall()
|
||||
if len(r) != 0:
|
||||
seen = r[0][0]
|
||||
speed = r[0][1]
|
||||
heading = r[0][2]
|
||||
vertical = r[0][3]
|
||||
|
||||
else:
|
||||
seen = 0
|
||||
speed = 0
|
||||
heading = 0
|
||||
vertical = 0
|
||||
|
||||
q = "select lat, lon, alt from positions where icao=%i order by seen desc limit 1" % icao
|
||||
self.locked_execute(c, q)
|
||||
r = c.fetchall()
|
||||
if len(r) != 0:
|
||||
lat = r[0][0]
|
||||
lon = r[0][1]
|
||||
alt = r[0][2]
|
||||
else:
|
||||
lat = 0
|
||||
lon = 0
|
||||
alt = 0
|
||||
|
||||
highlight = 0
|
||||
if hasattr(self, 'highlight'):
|
||||
if self.highlight == icao:
|
||||
highlight = 1
|
||||
|
||||
#now generate some JSONP
|
||||
retstr+= """{"icao": "%.6x", "lat": %f, "lon": %f, "alt": %i, "hdg": %i, "speed": %i, "vertical": %i, "ident": "%s", "type": "%s", "highlight": %i},""" % (icao, lat, lon, alt, heading, speed, vertical, ident, actype, highlight)
|
||||
|
||||
retstr+= """]);"""
|
||||
return retstr
|
||||
|
||||
@@ -28,9 +28,10 @@ import math
|
||||
#TODO get rid of class and convert to functions
|
||||
#no need for class here
|
||||
class output_print:
|
||||
def __init__(self, cpr, publisher):
|
||||
def __init__(self, cpr, publisher, callback=None):
|
||||
self._cpr = cpr
|
||||
#sub to every function that starts with "print"
|
||||
self._callback = callback
|
||||
#sub to every function that starts with "handle"
|
||||
self._fns = [int(l[6:]) for l in dir(self) if l.startswith("handle")]
|
||||
for i in self._fns:
|
||||
publisher.subscribe("type%i_dl" % i, getattr(self, "handle%i" % i))
|
||||
@@ -41,6 +42,12 @@ class output_print:
|
||||
def prefix(msg):
|
||||
return "(%i %.8f) " % (msg.rssi, msg.timestamp)
|
||||
|
||||
def _print(self, msg):
|
||||
if self._callback is None:
|
||||
print msg
|
||||
else:
|
||||
self._callback(msg)
|
||||
|
||||
def catch_nohandler(self, msg):
|
||||
if msg.data.get_type() not in self._fns:
|
||||
retstr = output_print.prefix(msg)
|
||||
@@ -49,7 +56,7 @@ class output_print:
|
||||
retstr += " from %.6x" % msg.ecc
|
||||
else:
|
||||
retstr += " from %.6x" % msg.data["aa"]
|
||||
print retstr
|
||||
self._print(retstr)
|
||||
|
||||
def handle0(self, msg):
|
||||
try:
|
||||
@@ -77,7 +84,7 @@ class output_print:
|
||||
if msg.data["vs"] is 1:
|
||||
retstr += " (aircraft is on the ground)"
|
||||
|
||||
print retstr
|
||||
self._print(retstr)
|
||||
|
||||
@staticmethod
|
||||
def fs_text(fs):
|
||||
@@ -101,7 +108,7 @@ class output_print:
|
||||
retstr += output_print.fs_text(msg.data["fs"])
|
||||
except ADSBError:
|
||||
return
|
||||
print retstr
|
||||
self._print(retstr)
|
||||
|
||||
def handle5(self, msg):
|
||||
try:
|
||||
@@ -110,7 +117,7 @@ class output_print:
|
||||
retstr += output_print.fs_text(msg.data["fs"])
|
||||
except ADSBError:
|
||||
return
|
||||
print retstr
|
||||
self._print(retstr)
|
||||
|
||||
def handle11(self, msg):
|
||||
try:
|
||||
@@ -118,7 +125,7 @@ class output_print:
|
||||
retstr += "Type 11 (all call reply) from %x in reply to interrogator %i with capability level %i" % (msg.data["aa"], msg.ecc & 0xF, msg.data["ca"]+1)
|
||||
except ADSBError:
|
||||
return
|
||||
print retstr
|
||||
self._print(retstr)
|
||||
|
||||
#the only one which requires state
|
||||
def handle17(self, msg):
|
||||
@@ -157,20 +164,20 @@ class output_print:
|
||||
[mag_hdg, vel_src, vel, vert_spd, geo_diff] = air_modes.parseBDS09_3(msg.data)
|
||||
retstr += "Type 17 BDS0,9-%i (air course report) from %x with %s %.0fkt magnetic heading %.0f VS %.0f geo. diff. from baro. alt. %.0fft" \
|
||||
% (subtype, icao24, vel_src, vel, mag_hdg, vert_spd, geo_diff)
|
||||
|
||||
|
||||
else:
|
||||
retstr += "Type 17 BDS0,9-%i from %x not implemented" % (subtype, icao24)
|
||||
|
||||
elif bdsreg == 0x62:
|
||||
emerg_str = air_modes.parseBDS62(data)
|
||||
retstr += "Type 17 BDS6,2 (emergency) from %x type %s" % (icao24, emerg_str)
|
||||
|
||||
|
||||
else:
|
||||
retstr += "Type 17 with FTC=%i from %x not implemented" % (msg.data["ftc"], icao24)
|
||||
except ADSBError:
|
||||
return
|
||||
|
||||
print retstr
|
||||
self._print(retstr)
|
||||
|
||||
def printTCAS(self, msg):
|
||||
msgtype = msg.data["df"]
|
||||
@@ -222,8 +229,8 @@ class output_print:
|
||||
retstr += " at %ift" % air_modes.decode_alt(msg.data["ac"], True)
|
||||
else:
|
||||
retstr += " ident %x" % air_modes.decode_id(msg.data["id"])
|
||||
|
||||
print retstr
|
||||
|
||||
self._print(retstr)
|
||||
|
||||
handle16 = printTCAS
|
||||
handle20 = printTCAS
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
# You pass it options, it gives you data.
|
||||
# It uses the pubsub interface to allow clients to subscribe to its data feeds.
|
||||
|
||||
from gnuradio import gr, gru, optfir, eng_notation, blks2
|
||||
from gnuradio import gr, gru, eng_notation, filter
|
||||
from gnuradio.filter import optfir
|
||||
from gnuradio.eng_option import eng_option
|
||||
from gnuradio.gr.pubsub import pubsub
|
||||
from optparse import OptionParser, OptionGroup
|
||||
@@ -186,7 +187,7 @@ class modes_radio (gr.top_block, pubsub):
|
||||
|
||||
#Note: this should only come into play if using an RTLSDR.
|
||||
# lpfiltcoeffs = gr.firdes.low_pass(1, 5*3.2e6, 1.6e6, 300e3)
|
||||
# self._resample = blks2.rational_resampler_ccf(interpolation=5, decimation=4, taps=lpfiltcoeffs)
|
||||
# self._resample = filter.rational_resampler_ccf(interpolation=5, decimation=4, taps=lpfiltcoeffs)
|
||||
|
||||
else:
|
||||
#semantically detect whether it's ip.ip.ip.ip:port or filename
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
from gnuradio import gr
|
||||
from gnuradio import gr, blocks
|
||||
import air_modes_swig
|
||||
|
||||
class rx_path(gr.hier_block2):
|
||||
@@ -35,17 +35,17 @@ class rx_path(gr.hier_block2):
|
||||
self._spc = int(rate/2e6)
|
||||
|
||||
# Convert incoming I/Q baseband to amplitude
|
||||
self._demod = gr.complex_to_mag()
|
||||
self._demod = blocks.complex_to_mag()
|
||||
self._bb = self._demod
|
||||
|
||||
# Pulse matched filter for 0.5us pulses
|
||||
if use_pmf:
|
||||
self._pmf = gr.moving_average_ff(self._spc, 1.0/self._spc)#, self._rate)
|
||||
self._pmf = blocks.moving_average_ff(self._spc, 1.0/self._spc)#, self._rate)
|
||||
self.connect(self._demod, self._pmf)
|
||||
self._bb = self._pmf
|
||||
|
||||
# Establish baseline amplitude (noise, interference)
|
||||
self._avg = gr.moving_average_ff(48*self._spc, 1.0/(48*self._spc))#, self._rate) # 3 preambles
|
||||
self._avg = blocks.moving_average_ff(48*self._spc, 1.0/(48*self._spc))#, self._rate) # 3 preambles
|
||||
|
||||
# Synchronize to Mode-S preamble
|
||||
self._sync = air_modes_swig.modes_preamble(self._rate, self._threshold)
|
||||
|
||||
@@ -38,7 +38,7 @@ class output_sql:
|
||||
c = self._db.cursor()
|
||||
query = """CREATE TABLE IF NOT EXISTS "positions" (
|
||||
"icao" INTEGER KEY NOT NULL,
|
||||
"seen" TEXT NOT NULL,
|
||||
"seen" DATETIME NOT NULL,
|
||||
"alt" INTEGER,
|
||||
"lat" REAL,
|
||||
"lon" REAL
|
||||
@@ -46,7 +46,7 @@ class output_sql:
|
||||
c.execute(query)
|
||||
query = """CREATE TABLE IF NOT EXISTS "vectors" (
|
||||
"icao" INTEGER KEY NOT NULL,
|
||||
"seen" TEXT NOT NULL,
|
||||
"seen" DATETIME NOT NULL,
|
||||
"speed" REAL,
|
||||
"heading" REAL,
|
||||
"vertical" REAL
|
||||
@@ -74,13 +74,13 @@ class output_sql:
|
||||
#constructor.
|
||||
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()
|
||||
|
||||
except ADSBError:
|
||||
pass
|
||||
@@ -96,42 +96,34 @@ class output_sql:
|
||||
|
||||
return query
|
||||
|
||||
#TODO: if there's a way to publish selective reports on upsert to distinguish,
|
||||
#for instance, between a new ICAO that's just been heard, and a refresh of an
|
||||
#existing ICAO, both of those would be useful publishers for the GUI model.
|
||||
#otherwise, worst-case you can just refresh everything every time a report
|
||||
#comes in, but it's going to use more CPU. Not likely a problem if you're only
|
||||
#looking at ADS-B (no mode S) data.
|
||||
#It's probably time to look back at the Qt SQL table model and see if it can be
|
||||
#bent into shape for you.
|
||||
def sql17(self, data):
|
||||
icao24 = data["aa"]
|
||||
bdsreg = data["me"].get_type()
|
||||
#self["bds%.2i" % bdsreg] = icao24 #publish under "bds08", "bds06", etc.
|
||||
|
||||
if bdsreg == 0x08:
|
||||
(msg, typename) = air_modes.parseBDS08(data)
|
||||
return "INSERT OR REPLACE INTO ident (icao, ident, type) VALUES (" + "%i" % icao24 + ", '" + msg + "', '" + typename + "')"
|
||||
(msg, typename) = self.parseBDS08(data)
|
||||
return "INSERT OR REPLACE INTO ident (icao, ident, type) VALUES (%i, '%s', '%s')" % (icao24, msg, typename)
|
||||
elif bdsreg == 0x06:
|
||||
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = air_modes.parseBDS06(data, self._cpr)
|
||||
altitude = 0
|
||||
if decoded_lat is None: #no unambiguously valid position available
|
||||
raise CPRNoPositionError
|
||||
else:
|
||||
return "INSERT INTO positions (icao, seen, alt, lat, lon) VALUES (" + "%i" % icao24 + ", datetime('now'), " + str(altitude) + ", " + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ")"
|
||||
return "INSERT INTO positions (icao, seen, alt, lat, lon) VALUES (%i, datetime('now'), %i, %.6f, %.6f)" % (icao24, int(altitude), decoded_lat, decoded_lon)
|
||||
elif bdsreg == 0x05:
|
||||
[altitude, decoded_lat, decoded_lon, rnge, bearing] = air_modes.parseBDS05(data, self._cpr)
|
||||
if decoded_lat is None: #no unambiguously valid position available
|
||||
raise CPRNoPositionError
|
||||
else:
|
||||
return "INSERT INTO positions (icao, seen, alt, lat, lon) VALUES (" + "%i" % icao24 + ", datetime('now'), " + str(altitude) + ", " + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ")"
|
||||
return "INSERT INTO positions (icao, seen, alt, lat, lon) VALUES (%i, datetime('now'), %i, %.6f, %.6f)" % (icao24, int(altitude), decoded_lat, decoded_lon)
|
||||
elif bdsreg == 0x09:
|
||||
subtype = data["bds09"].get_type()
|
||||
if subtype == 0:
|
||||
[velocity, heading, vert_spd, turnrate] = air_modes.parseBDS09_0(data)
|
||||
return "INSERT INTO vectors (icao, seen, speed, heading, vertical) VALUES (" + "%i" % icao24 + ", datetime('now'), " + "%.0f" % velocity + ", " + "%.0f" % heading + ", " + "%.0f" % vert_spd + ")"
|
||||
[velocity, heading, vert_spd, turnrate] = self.parseBDS09_0(data)
|
||||
return "INSERT INTO vectors (icao, seen, speed, heading, vertical) VALUES (%i, datetime('now'), %.0f, %.0f, %.0f)" % (icao24, velocity, heading, vert_spd)
|
||||
elif subtype == 1:
|
||||
[velocity, heading, vert_spd] = air_modes.parseBDS09_1(data)
|
||||
return "INSERT INTO vectors (icao, seen, speed, heading, vertical) VALUES (" + "%i" % icao24 + ", datetime('now'), " + "%.0f" % velocity + ", " + "%.0f" % heading + ", " + "%.0f" % vert_spd + ")"
|
||||
[velocity, heading, vert_spd] = self.parseBDS09_1(data)
|
||||
return "INSERT INTO vectors (icao, seen, speed, heading, vertical) VALUES (%i, datetime('now'), %.0f, %.0f, %.0f)" % (icao24, velocity, heading, vert_spd)
|
||||
else:
|
||||
raise NoHandlerError
|
||||
|
||||
@@ -856,7 +856,7 @@
|
||||
<string>Climb</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QwtCompass" name="compass_heading">
|
||||
<widget class="QwtCompass" name="compass_heading" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>200</x>
|
||||
@@ -865,10 +865,10 @@
|
||||
<height>91</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<property name="readOnly" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<property name="lineWidth" stdset="0">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -901,7 +901,7 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QwtCompass" name="compass_bearing">
|
||||
<widget class="QwtCompass" name="compass_bearing" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>-20</x>
|
||||
@@ -910,10 +910,10 @@
|
||||
<height>91</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<property name="readOnly" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<property name="lineWidth" stdset="0">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -928,6 +928,16 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="map_tab">
|
||||
<attribute name="title">
|
||||
<string>Map</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_8">
|
||||
<item row="0" column="0">
|
||||
<widget class="QWebView" name="mapView" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="livedatatab">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
@@ -1016,11 +1026,6 @@
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QwtCompass</class>
|
||||
<extends>QwtDial</extends>
|
||||
<header>qwt_compass.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QwtDial</class>
|
||||
<extends>QWidget</extends>
|
||||
@@ -1032,6 +1037,17 @@
|
||||
<header location="global">air_modes/az_map</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QwtCompass</class>
|
||||
<extends>QwtDial</extends>
|
||||
<header>qwt_compass.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QWebView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header location="global">QtWebKit/qwebview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
@@ -31,11 +31,8 @@ include(GrPython)
|
||||
########################################################################
|
||||
# Setup swig generation
|
||||
########################################################################
|
||||
foreach(incdir ${GNURADIO_CORE_INCLUDE_DIRS})
|
||||
list(APPEND GR_SWIG_INCLUDE_DIRS ${incdir}/swig)
|
||||
endforeach(incdir)
|
||||
foreach(incdir ${GRUEL_INCLUDE_DIRS})
|
||||
list(APPEND GR_SWIG_INCLUDE_DIRS ${incdir}/gruel/swig)
|
||||
foreach(incdir ${GNURADIO_RUNTIME_INCLUDE_DIRS})
|
||||
list(APPEND GR_SWIG_INCLUDE_DIRS ${incdir}/gnuradio/swig)
|
||||
endforeach(incdir)
|
||||
|
||||
set(GR_SWIG_LIBRARIES air_modes)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
%{
|
||||
#include "air_modes_preamble.h"
|
||||
#include "air_modes_slicer.h"
|
||||
#include <gr_msg_queue.h>
|
||||
#include <gnuradio/msg_queue.h>
|
||||
%}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
@@ -21,7 +21,7 @@ GR_SWIG_BLOCK_MAGIC(air,modes_preamble);
|
||||
|
||||
air_modes_preamble_sptr air_make_modes_preamble (int channel_rate, float threshold_db);
|
||||
|
||||
class air_modes_preamble : public gr_sync_block
|
||||
class air_modes_preamble : public gr::sync_block
|
||||
{
|
||||
set_rate(int channel_rate);
|
||||
set_threshold(float threshold_db);
|
||||
@@ -32,13 +32,13 @@ private:
|
||||
|
||||
GR_SWIG_BLOCK_MAGIC(air,modes_slicer);
|
||||
|
||||
air_modes_slicer_sptr air_make_modes_slicer (int channel_rate, gr_msg_queue_sptr queue);
|
||||
air_modes_slicer_sptr air_make_modes_slicer (int channel_rate, gr::msg_queue::sptr queue);
|
||||
|
||||
class air_modes_slicer : public gr_block
|
||||
class air_modes_slicer : public gr::block
|
||||
{
|
||||
set_rate(int channel_rate);
|
||||
private:
|
||||
air_modes_slicer (int channel_rate, gr_msg_queue_sptr queue);
|
||||
air_modes_slicer (int channel_rate, gr::msg_queue::sptr queue);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user