47 Commits
rtl ... gr37

Author SHA1 Message Date
Nick Foster
0b6c383506 Merge branch 'master' of github.com:bistromath/gr-air-modes 2017-09-28 13:30:33 -07:00
Nick Foster
f6ba6da8ba Add Dockerfile. 2017-09-28 13:30:18 -07:00
Nick Foster
48c1afc3ba Merge pull request #100 from gnieboer/WinFixes
Win fixes
2017-03-14 12:32:35 -07:00
gnieboer
3b65986159 Updated GrBoost.cmake to include quoted string fix from GR 2017-03-12 15:08:18 -04:00
gnieboer
eb63b0034f Updated FindQwt to include add'l search paths 2017-03-12 15:07:44 -04:00
bistromath
719d52b6dd Merge pull request #96 from kpreid/patch-1
Remove unneeded parameter from rx_path.get_threshold.
2016-11-16 19:03:20 -08:00
Kevin Reid
f96b83cb48 Remove unneeded parameter from rx_path.get_threshold. 2016-11-16 16:32:40 -08:00
bistromath
fd277e7fb0 Merge pull request #93 from kevinluchsinger/master
fixes issue #76 (timestamps did "overflow" after 536 seconds)
2016-10-10 11:10:57 -06:00
Kevin Luchsinger
737c98bf8a fixes issue #76 (timestamps did "overflow" after 536 seconds) 2016-10-08 21:26:10 +02:00
bistromath
8cbcc676c4 Merge pull request #88 from Geoff160/master
Modified gr-air-modes/apps/modes_gui to add correct az_map problem.
2016-09-01 11:01:42 -07:00
Geoffrey Marr
d4d17bc4db Modified gr-air-modes/apps/modes_gui to add correct az_map problem. 2016-09-01 08:45:39 -06:00
Nick Foster
3bad1f5d35 Move az_map import into GUI as it doesn't belong in the module init 2016-08-05 16:19:05 -07:00
Nick Foster
c29eb6030a Fix SWIG on 16.04. Not sure how this didn't get propagated. 2016-06-05 11:38:45 -07:00
bistromath
e8c2a47278 Merge pull request #84 from devnulling/master
Add API Field, update markerwithlabel.js source
2016-05-17 15:59:24 -07:00
devnulling
0599c09198 Add API Key field 2016-05-16 22:18:52 -07:00
devnulling
49b7d87e7a Add API Key field 2016-05-16 22:18:15 -07:00
devnulling
2ffdcf6705 Add API Key field 2016-05-16 22:17:50 -07:00
bistromath
65e5bd1e2e Merge pull request #82 from devnulling/master
Add PyZMQ to required dependencies
2016-05-03 23:39:55 -07:00
devnulling
2b8451cbe2 Add PyZMQ to required dependencies 2016-05-03 21:26:43 -07:00
Nick Foster
bdfcc42b39 Fix for bug 81 (SWIG implicit module naming). Thanks to Maitland Bottoms
(bottoms@debian.org) for the patch.
2016-05-01 19:23:46 -07:00
Nick Foster
514414f6b3 Fix integer truncation issue in tag_to_timestamp(). Thanks to John Ilig
for finding it.
2015-09-03 17:39:53 -07:00
Nick Foster
e82cf9d4de Merge branch 'master' of github.com:bistromath/gr-air-modes 2015-07-10 09:29:15 -07:00
Nick Foster
2c9ef501b8 Correctly use zero offset for devices that don't issue proper timestamp
tags.
2015-07-09 13:08:00 -07:00
Nick Foster
953a7ddded Correctly return a zero timestamp tag instead of a null tag. 2015-07-09 13:03:10 -07:00
Nick Foster
c96dea7fa0 preamble: Check to see if PMT key is actually a symbol before converting
to string.
2015-07-09 12:23:52 -07:00
bistromath
d810ed75a8 Merge pull request #70 from KART35/master
Fix crash with missing gr.udp_source() in gnuradio-companion 3.7.7.1.
2015-05-26 13:31:15 -07:00
Ian Crawford
3d1b95832a Fix crash with missing gr.udp_source() in gnuradio-companion 3.7.7.1. Changed to blocks.udp_source(). 2015-05-26 13:01:58 -07:00
Nick Foster
93078a8cae I'm the worst 2015-05-08 09:08:38 -07:00
Nick Foster
d569e31a68 Fix parse error (shift on float) reported by 'engink1981'. 2015-05-07 20:47:09 -07:00
Nick Foster
f0323160a0 Fix SBS-1 ID list prune issue. Lousy fix, but it'll do for now. The
backend needs a rewrite.
2015-05-07 20:44:28 -07:00
Nick Foster
95ff2cade0 Remove spurious print. 2015-04-17 22:16:55 -07:00
Nick Foster
9bdac2a499 Use whole/fractional timestamps in the whole chain. This prevents loss
of precision when setting time to UTC.
2015-04-17 14:07:28 -07:00
Nick Foster
da15a2daf2 Check for PyZMQ at runtime instead of compile time to support
cross-compiling.
2015-01-21 12:32:39 -08:00
Nick Foster
9891907e97 Spruce up the Javascript map handler so it doesn't just wipe the map
and repopulate. No "flashing icons".
2014-09-28 15:09:07 -07:00
Nick Foster
1536dae56e Fix issue with stop/restart in modes_gui. 2014-09-28 10:43:54 -07:00
Nick Foster
48f9c2a29a Live sample rate changing for RTL devices in modes_gui 2014-09-21 19:28:44 -07:00
Nick Foster
78c5500c80 Fix GUI rate picking logic for <4Msps devices (RTL) 2014-09-21 19:18:21 -07:00
Nick Foster
6f11777724 Fix rate setting for devices with <4Msps rates (RTL). 2014-09-21 19:03:16 -07:00
Nick Foster
cc0fa1801b Fix enumeration in SBS1 list pruning. 2014-02-12 15:15:32 -08:00
Nick Foster
42bf16ffc4 Fix FlightGear and SBS1 outputs. 2014-02-07 16:59:34 -08:00
Tom Rondeau
27e0f87361 CMake fixes (force Python2, use GnuradioConfig.cmake). 2013-12-24 12:51:45 -08:00
Nick Foster
585ecf1ba6 Remove FindZeroMQ as it's causing more trouble than it's worth. Zero_Chaos, this
is my hat I'm eating.
2013-10-26 21:53:49 -07:00
Nick Foster
ca4edd8808 look for zmq.h instead of zmq.hpp, which is apparently missing in some installs. 2013-10-02 22:14:44 -04:00
Nick Foster
aa5fdd17ea Add pkgconfig searching for libzmq 2013-10-02 21:06:51 -04:00
Nick Foster
e47992d800 Actually looking for both ZMQ and PyZMQ now. 2013-09-18 08:57:44 -07:00
Nick Foster
9f522cf082 Add checking for ZMQ/PyZMQ to CMakeLists.txt 2013-09-18 08:53:00 -07:00
Nick Foster
3f86a74132 Fix FindGnuradioRuntime.cmake as per recent gr-modtool fixes. 2013-09-15 11:16:36 -07:00
23 changed files with 299 additions and 184 deletions

View File

@@ -22,7 +22,7 @@
# Project setup
########################################################################
cmake_minimum_required(VERSION 2.6)
project(gr-gr-air-modes CXX)
project(gr-gr-air-modes CXX C)
set(gr-gr-air-modes_VERSION_MAJOR 0)
set(gr-gr-air-modes_VERSION_MINOR 0)
enable_testing()
@@ -49,6 +49,8 @@ endif()
########################################################################
include(GrBoost)
find_package(PythonLibs 2)
########################################################################
# Install directories
########################################################################
@@ -69,12 +71,22 @@ set(GRC_BLOCKS_DIR ${GR_PKG_DATA_DIR}/grc/blocks)
########################################################################
# Find gnuradio build dependencies
########################################################################
find_package(GnuradioRuntime)
set(GR_REQUIRED_COMPONENTS RUNTIME)
find_package(Gnuradio "3.7.2" REQUIRED)
if(NOT GNURADIO_RUNTIME_FOUND)
message(FATAL_ERROR "GnuRadio Runtime required to compile gr-air-modes")
endif()
########################################################################
# Find PyZMQ bindings
########################################################################
include(GrPython)
#GR_PYTHON_CHECK_MODULE("PyZMQ" "zmq" "int(zmq.__version__.split('.')[0]) >= 13" PYZMQ_FOUND)
#if(NOT PYZMQ_FOUND)
# message(FATAL_ERROR "Python ZMQ bindings not found.")
#endif()
########################################################################
# Setup the include and linker paths
########################################################################

10
Dockerfile Normal file
View File

@@ -0,0 +1,10 @@
FROM bistromath/gnuradio:3.7.11
ENV num_threads 10
MAINTAINER bistromath@gmail.com version: 0.1
WORKDIR /opt
RUN mkdir gr-air-modes
COPY . gr-air-modes/
WORKDIR /opt/gr-air-modes
RUN mkdir build && cd build && cmake ../ && make -j${num_threads} && make install && ldconfig

1
README
View File

@@ -97,6 +97,7 @@ gr-air-modes requires:
* Python >= 2.5 (written for Python 2.7, Python 3.0 might work)
** NumPy and SciPy are required for the FlightGear output plugin.
* PyZMQ
* Gnuradio >= 3.5.0
* Ettus UHD >= 3.4.0 for use with USRPs
* osmosdr (any version) for use with RTLSDR dongles

View File

@@ -30,6 +30,7 @@ import air_modes
from air_modes.exceptions import *
from air_modes.modes_rx_ui import Ui_MainWindow
from air_modes.gui_model import *
from air_modes.az_map import *
import sqlite3
import zmq
@@ -87,7 +88,9 @@ class mainwindow(QtGui.QMainWindow):
self.ui.line_my_lat.insert(defaults["latitude"])
if defaults["longitude"] is not None:
self.ui.line_my_lon.insert(defaults["longitude"])
if defaults["apikey"] is not None:
self.ui.line_my_api_key.insert(defaults["apikey"])
#disable by default
self.ui.check_adsbonly.setCheckState(QtCore.Qt.Unchecked)
@@ -104,7 +107,7 @@ 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.az_model = air_modes.az_map.az_map_model(None)
self.ui.azimuth_map.setModel(self.az_model)
#set up dashboard views
@@ -233,7 +236,9 @@ class mainwindow(QtGui.QMainWindow):
try:
import osmosdr
self.src = osmosdr.source("")
self.rates = [rate.start() for rate in self.src.get_sample_rates() if (rate.start() % 2.e6) == 0]
self.rates = [rate.start() for rate in self.src.get_sample_rates()
if ((rate.start() % 2.e6) == 0)
or (rate.start() < 4.e6 and ((rate.start()%0.2e6) == 0))]
self.antennas = ["RX"]
self.src = None
self.ui.combo_ant.setEnabled(False)
@@ -263,10 +268,15 @@ class mainwindow(QtGui.QMainWindow):
#set up recommended sample rate
if len(self.rates) > 1:
recommended_rate = min(x for x in self.rates if x >= 4e6 and
max(self.rates) % x == 0)
if max(self.rates) > 4.e6:
recommended_rate = min(x for x in self.rates if x >= 4e6 and
max(self.rates) % x == 0)
else:
recommended_rate = max(self.rates)
if recommended_rate >= 8.e6:
self.ui.check_pmf.setChecked(True)
else:
self.ui.check_pmf.setChecked(False)
self.ui.combo_rate.setCurrentIndex(self.rates.index(recommended_rate))
################ action handlers ####################
@@ -314,6 +324,11 @@ class mainwindow(QtGui.QMainWindow):
except:
my_position = None
try:
my_apikey = str(self.ui.line_my_api_key.text())
except:
my_apikey = None
self._cpr_dec = air_modes.cpr_decoder(my_position)
self.datamodelout = dashboard_output(self._cpr_dec, self.datamodel, self._publisher)
@@ -336,7 +351,7 @@ class mainwindow(QtGui.QMainWindow):
#add azimuth map output and hook it up
if my_position is not None:
self.az_map_output = air_modes.az_map_output(self._cpr_dec, self.az_model, self._publisher)
self.az_map_output = air_modes.az_map.az_map_output(self._cpr_dec, self.az_model, self._publisher)
#self._relay.subscribe("dl_data", self.az_map_output.output)
#set up map
@@ -352,7 +367,7 @@ class mainwindow(QtGui.QMainWindow):
#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)
htmlstring = air_modes.html_template(my_apikey, my_position, self._jsonfile.name)
self._htmlfile.write(htmlstring)
self._htmlfile.flush()
class WebPage(QtWebKit.QWebPage):
@@ -397,13 +412,17 @@ class mainwindow(QtGui.QMainWindow):
self.prefs["longitude"] = float(self.ui.line_my_lon.text())
except:
pass
try:
self.prefs["apikey"] = self.ui.line_my_api_key.text()
except:
pass
def on_quit(self):
if self.running is True:
self._relay.close()
self._radio.close()
self._relay = None
self._radio = None
self._relay.close()
self._relay = None
self._rps_timer = None
try:
self.kmlgen.done = True
@@ -456,6 +475,7 @@ class mainwindow(QtGui.QMainWindow):
defaults["threshold"] = "5"
defaults["latitude"] = None
defaults["longitude"] = None
defaults["apikey"] = None
prefs = ConfigParser.ConfigParser(defaults)
prefs.optionxform = str

View File

@@ -1,6 +1,36 @@
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(GNURADIO_RUNTIME gnuradio-runtime)
PKG_CHECK_MODULES(PC_GNURADIO_RUNTIME gnuradio-runtime)
if(PC_GNURADIO_RUNTIME_FOUND)
# look for include files
FIND_PATH(
GNURADIO_RUNTIME_INCLUDE_DIRS
NAMES gnuradio/top_block.h
HINTS $ENV{GNURADIO_RUNTIME_DIR}/include
${PC_GNURADIO_RUNTIME_INCLUDE_DIRS}
${CMAKE_INSTALL_PREFIX}/include
PATHS /usr/local/include
/usr/include
)
# look for libs
FIND_LIBRARY(
GNURADIO_RUNTIME_LIBRARIES
NAMES gnuradio-runtime
HINTS $ENV{GNURADIO_RUNTIME_DIR}/lib
${PC_GNURADIO_RUNTIME_LIBDIR}
${CMAKE_INSTALL_PREFIX}/lib/
${CMAKE_INSTALL_PREFIX}/lib64/
PATHS /usr/local/lib
/usr/local/lib64
/usr/lib
/usr/lib64
)
set(GNURADIO_RUNTIME_FOUND ${PC_GNURADIO_RUNTIME_FOUND})
endif(PC_GNURADIO_RUNTIME_FOUND)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_RUNTIME DEFAULT_MSG GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS)
# do not check GNURADIO_RUNTIME_INCLUDE_DIRS, is not set when default include path us used.
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_RUNTIME DEFAULT_MSG GNURADIO_RUNTIME_LIBRARIES)
MARK_AS_ADVANCED(GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS)

View File

@@ -4,23 +4,34 @@
# QWT_FOUND If false, do not try to use Qwt
find_path (QWT_INCLUDE_DIRS
NAMES qwt_plot.h
PATHS
/usr/local/include/qwt-qt4
/usr/local/include/qwt
/usr/include/qwt-qt4
/usr/include/qwt
/opt/local/include/qwt
/sw/include/qwt
NAMES qwt_plot.h
HINTS
${CMAKE_INSTALL_PREFIX}/include/qwt
${CMAKE_PREFIX_PATH}/include/qwt
PATHS
/usr/local/include/qwt-qt4
/usr/local/include/qwt
/usr/include/qwt6
/usr/include/qwt-qt4
/usr/include/qwt
/usr/include/qwt5
/opt/local/include/qwt
/sw/include/qwt
/usr/local/lib/qwt.framework/Headers
)
find_library (QWT_LIBRARIES
NAMES qwt-qt4 qwt
PATHS
/usr/local/lib
/usr/lib
/opt/local/lib
/sw/lib
NAMES qwt6 qwt6-qt4 qwt qwt-qt4 qwt5 qwtd5
HINTS
${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
${CMAKE_PREFIX_PATH}/lib
PATHS
/usr/local/lib
/usr/lib
/opt/local/lib
/sw/lib
/usr/local/lib/qwt.framework
)
# handle the QUIETLY and REQUIRED arguments and set QWT_FOUND to TRUE if

View File

@@ -1,56 +0,0 @@
# - Find zeromq libraries
# This module finds zeromq if it is installed and determines where the
# include files and libraries are. It also determines what the name of
# the library is. This code sets the following variables:
#
# ZEROMQ_FOUND - have the zeromq libs been found
# ZEROMQ_LIBRARIES - path to the zeromq library
# ZEROMQ_INCLUDE_DIRS - path to where zmq.h is found
# ZEROMQ_DEBUG_LIBRARIES - path to the debug library
#INCLUDE(CMakeFindFrameworks)
# Search for the zeromq framework on Apple.
#CMAKE_FIND_FRAMEWORKS(ZeroMQ)
IF(WIN32)
FIND_LIBRARY(ZEROMQ_DEBUG_LIBRARY
NAMES libzmq_d zmq_d
PATHS
${ZEROMQ_LIBRARIES}
)
ENDIF(WIN32)
FIND_LIBRARY(ZEROMQ_LIBRARY
NAMES libzmq zmq
PATHS
${ZEROMQ_LIBRARIES}
${NSCP_LIBRARYDIR}
)
# IF(ZeroMQ_FRAMEWORKS AND NOT ZEROMQ_INCLUDE_DIR)
# FOREACH(dir ${ZeroMQ_FRAMEWORKS})
# SET(ZEROMQ_FRAMEWORK_INCLUDES ${ZEROMQ_FRAMEWORK_INCLUDES}
# ${dir}/Versions/${_CURRENT_VERSION}/include/zeromq${_CURRENT_VERSION})
# ENDFOREACH(dir)
# ENDIF(ZeroMQ_FRAMEWORKS AND NOT ZEROMQ_INCLUDE_DIR)
FIND_PATH(ZEROMQ_INCLUDE_DIR
NAMES zmq.hpp
PATHS
# ${ZEROMQ_FRAMEWORK_INCLUDES}
${ZEROMQ_INCLUDE_DIRS}
${NSCP_INCLUDEDIR}
${ZEROMQ_INCLUDE_DIR}
)
MARK_AS_ADVANCED(
ZEROMQ_DEBUG_LIBRARY
ZEROMQ_LIBRARY
ZEROMQ_INCLUDE_DIR
)
SET(ZEROMQ_INCLUDE_DIRS "${ZEROMQ_INCLUDE_DIR}")
SET(ZEROMQ_LIBRARIES "${ZEROMQ_LIBRARY}")
SET(ZEROMQ_DEBUG_LIBRARIES "${ZEROMQ_DEBUG_LIBRARY}")
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZeroMQ DEFAULT_MSG ZEROMQ_LIBRARIES ZEROMQ_INCLUDE_DIRS)

View File

@@ -87,7 +87,7 @@ set(Boost_NOGO_VERSIONS
)
foreach(ver ${Boost_NOGO_VERSIONS})
if(${Boost_VERSION} EQUAL ${ver})
if("${Boost_VERSION}" STREQUAL "${ver}")
if(NOT ENABLE_BAD_BOOST)
MESSAGE(STATUS "WARNING: Found a known bad version of Boost (v${Boost_VERSION}). Disabling.")
set(Boost_FOUND FALSE)
@@ -95,5 +95,5 @@ foreach(ver ${Boost_NOGO_VERSIONS})
MESSAGE(STATUS "WARNING: Found a known bad version of Boost (v${Boost_VERSION}). Continuing anyway.")
set(Boost_FOUND TRUE)
endif(NOT ENABLE_BAD_BOOST)
endif(${Boost_VERSION} EQUAL ${ver})
endif("${Boost_VERSION}" STREQUAL "${ver}")
endforeach(ver)

View File

@@ -37,11 +37,11 @@ class AIR_MODES_API preamble : virtual public gr::block
{
public:
typedef boost::shared_ptr<preamble> sptr;
static sptr make(int channel_rate, float threshold_db);
static sptr make(float channel_rate, float threshold_db);
virtual void set_rate(int channel_rate) = 0;
virtual void set_rate(float channel_rate) = 0;
virtual void set_threshold(float threshold_db) = 0;
virtual int get_rate(void) = 0;
virtual float get_rate(void) = 0;
virtual float get_threshold(void) = 0;
};

View File

@@ -34,11 +34,11 @@
namespace gr {
air_modes::preamble::sptr air_modes::preamble::make(int channel_rate, float threshold_db) {
air_modes::preamble::sptr air_modes::preamble::make(float channel_rate, float threshold_db) {
return gnuradio::get_initial_sptr(new air_modes::preamble_impl(channel_rate, threshold_db));
}
air_modes::preamble_impl::preamble_impl(int channel_rate, float threshold_db) :
air_modes::preamble_impl::preamble_impl(float channel_rate, float threshold_db) :
gr::block ("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 soft symbols
@@ -53,11 +53,11 @@ air_modes::preamble_impl::preamble_impl(int channel_rate, float threshold_db) :
d_key = pmt::string_to_symbol("preamble_found");
}
void air_modes::preamble_impl::set_rate(int channel_rate) {
void air_modes::preamble_impl::set_rate(float channel_rate) {
d_samples_per_chip = channel_rate / d_chip_rate;
d_samples_per_symbol = d_samples_per_chip * 2;
d_check_width = 120 * d_samples_per_symbol;
d_secs_per_sample = 1.0/channel_rate;
d_sample_rate = channel_rate;
set_output_multiple(1+d_check_width*2);
set_history(d_samples_per_symbol);
}
@@ -71,8 +71,8 @@ float air_modes::preamble_impl::get_threshold(void) {
return d_threshold_db;
}
int air_modes::preamble_impl::get_rate(void) {
return d_samples_per_chip * d_chip_rate;
float air_modes::preamble_impl::get_rate(void) {
return d_sample_rate;
}
static void integrate_and_dump(float *out, const float *in, int chips, int samps_per_chip) {
@@ -97,19 +97,42 @@ static double correlate_preamble(const float *in, int samples_per_chip) {
return corr;
}
//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) {
uint64_t ts_sample, last_whole_stamp;
static pmt::pmt_t tag_to_timestamp(gr::tag_t tstamp, uint64_t abs_sample_cnt, int rate) {
uint64_t last_whole_stamp;
double last_frac_stamp;
pmt::pmt_t tstime = pmt::make_tuple(pmt::from_uint64(0), pmt::from_double(0));
if(tstamp.key == NULL
|| !pmt::is_symbol(tstamp.key)
|| pmt::symbol_to_string(tstamp.key) != "rx_time") {
last_whole_stamp = 0;
last_frac_stamp = 0;
} else {
last_whole_stamp = pmt::to_uint64(pmt::tuple_ref(tstamp.value, 0));
last_frac_stamp = pmt::to_double(pmt::tuple_ref(tstamp.value, 1));
}
if(tstamp.key == NULL || pmt::symbol_to_string(tstamp.key) != "rx_time") return 0;
//the timestamp tag has tstamp.offset, the sample index of the timestamp tag
//also tstamp.value, a pmt pair with (uint64, double) representing int and
//fractional timestamp, respectively.
//this function also gets an abs_sample_cnt which represents the sample count to
//find a timestamp for. sps is obviously samples per second.
//
//so (abs_sample_cnt - tstamp.offset) is the delay we apply to the tag
// int((abs_sample_cnt - tstamp.offset)/sps) is the integer offset
// (abs_sample_cnt - tstamp.offset)/sps is the fractional offset
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;
uint64_t int_offset = (abs_sample_cnt - tstamp.offset)/rate;
double frac_offset = ((abs_sample_cnt - tstamp.offset) % rate) / double(rate);
uint64_t abs_whole = last_whole_stamp + int_offset;
double abs_frac = last_frac_stamp + frac_offset;
if(abs_frac > 1.0f) {
abs_frac -= 1.0f;
abs_whole += 1;
}
tstime = pmt::make_tuple(pmt::from_uint64(abs_whole), pmt::from_double(abs_frac));
double tstime = double(abs_sample_cnt * secs_per_sample) + last_whole_stamp + last_frac_stamp;
if(0) std::cout << "HEY WE GOT A STAMP AT " << tstime << " TICKS AT SAMPLE " << ts_sample << " ABS SAMPLE CNT IS " << abs_sample_cnt << std::endl;
return tstime;
}
@@ -124,7 +147,7 @@ int air_modes::preamble_impl::general_work(int noutput_items,
int mininputs = std::min(ninput_items[0], ninput_items[1]); //they should be matched but let's be safe
//round number of input samples down to nearest d_samples_per_chip
//we also subtract off d_samples_per_chip to allow the bit center finder some leeway
const int ninputs = std::max(mininputs - (mininputs % d_samples_per_chip) - d_samples_per_chip, 0);
const int ninputs = std::max(mininputs - (mininputs % int(d_samples_per_chip)) - int(d_samples_per_chip), 0);
if (ninputs <= 0) { consume_each(0); return 0; }
float *out = (float *) output_items[0];
@@ -194,22 +217,20 @@ int air_modes::preamble_impl::general_work(int noutput_items,
//all right i'm prepared to call this a preamble
for(int j=0; j<240; j++) {
out[j] = in[i+j*d_samples_per_chip] - inavg[i];
out[j] = in[i+int(j*d_samples_per_chip)] - inavg[i];
}
//get the timestamp of the preamble
double tstamp = tag_to_timestamp(d_timestamp, abs_sample_cnt + i, d_secs_per_sample);
pmt::pmt_t tstamp = tag_to_timestamp(d_timestamp, abs_sample_cnt + i, d_sample_rate);
//now tag the preamble
add_item_tag(0, //stream ID
nitems_written(0), //sample
d_key, //frame_info
pmt::from_double(tstamp),
tstamp,
d_me //block src id
);
//std::cout << "PREAMBLE" << std::endl;
//produce only one output per work call -- TODO this should probably change
if(0) std::cout << "Preamble consumed " << i+240*d_samples_per_chip << "with i=" << i << ", returned 240" << std::endl;

View File

@@ -15,26 +15,26 @@ private:
int d_check_width;
int d_chip_rate;
float d_preamble_length_us;
int d_samples_per_chip;
int d_samples_per_symbol;
float d_samples_per_chip;
float d_samples_per_symbol;
float d_threshold_db;
float d_threshold;
pmt::pmt_t d_me, d_key;
gr::tag_t d_timestamp;
double d_secs_per_sample;
pmt::pmt_t d_me, d_key;
int d_sample_rate;
public:
preamble_impl(int channel_rate, float threshold_db);
preamble_impl(float channel_rate, float threshold_db);
int general_work (int noutput_items,
gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
void set_rate(int channel_rate);
void set_rate(float channel_rate);
void set_threshold(float threshold_db);
float get_threshold(void);
int get_rate(void);
float get_rate(void);
};
} //namespace air_modes

View File

@@ -158,8 +158,6 @@ int air_modes::slicer_impl::work(int noutput_items,
}
}
rx_packet.timestamp = pmt::to_double(tag_iter->value);
//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++) {
@@ -183,13 +181,15 @@ int air_modes::slicer_impl::work(int noutput_items,
//crc for the other short packets is usually nonzero, so they can't really be trusted that far
if(rx_packet.crc && (rx_packet.message_type == 11 || rx_packet.message_type == 17)) {continue;}
pmt::pmt_t tstamp = tag_iter->value;
d_payload.str("");
for(int m = 0; m < packet_length/8; m++) {
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
<< " " << std::setprecision(10) << std::setw(10) << rx_packet.timestamp;
<< " " << pmt::to_uint64(pmt::tuple_ref(tstamp, 0)) << " " << std::setprecision(10) << pmt::to_double(pmt::tuple_ref(tstamp, 1));
gr::message::sptr msg = gr::message::make_from_string(std::string(d_payload.str()));
d_queue->handle(msg);
}

View File

@@ -38,6 +38,7 @@ private:
int d_chip_rate;
int d_samples_per_chip;
int d_samples_per_symbol;
gr::tag_t d_timestamp;
gr::msg_queue::sptr d_queue;
std::ostringstream d_payload;

View File

@@ -51,6 +51,12 @@ from air_modes_swig import *
# import any pure python here
#
try:
import zmq
except ImportError:
raise RuntimeError("PyZMQ not found! Please install libzmq and PyZMQ to run gr-air-modes")
from rx_path import rx_path
from zmq_socket import zmq_pubsub_iface
from parse import *
@@ -61,7 +67,6 @@ from kml import output_kml, output_jsonp
from raw_server import raw_server
from radio import modes_radio
from exceptions import *
from az_map import *
from types import *
from altitude import *
from cpr import cpr_decoder

View File

@@ -18,7 +18,6 @@ class output_flightgear:
def __init__(self, cprdec, hostname, port, pub):
self.hostname = hostname
self.port = port
self.localpos = localpos
self.positions = {}
self.velocities = {}
self.callsigns = {}
@@ -26,42 +25,41 @@ class output_flightgear:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.connect((self.hostname, self.port))
pub.subscribe("type17_dl", output)
pub.subscribe("type17_dl", self.output)
def output(self, msg):
try:
msgtype = msg.data["df"]
if msgtype == 17: #ADS-B report
icao24 = msg.data["aa"]
bdsreg = msg.data["me"].get_type()
if bdsreg == 0x08: #ident packet
(ident, actype) = air_modes.parseBDS08(data)
(ident, actype) = air_modes.parseBDS08(msg.data)
#select model based on actype
self.callsigns[icao24] = [ident, actype]
elif bdsreg == 0x06: #BDS0,6 pos
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = air_modes.parseBDS06(data, self._cpr)
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = air_modes.parseBDS06(msg.data, self._cpr)
self.positions[icao24] = [decoded_lat, decoded_lon, 0]
self.update(icao24)
elif bdsreg == 0x05: #BDS0,5 pos
[altitude, decoded_lat, decoded_lon, rnge, bearing] = air_modes.parseBDS05(data, self._cpr)
[altitude, decoded_lat, decoded_lon, rnge, bearing] = air_modes.parseBDS05(msg.data, self._cpr)
self.positions[icao24] = [decoded_lat, decoded_lon, altitude]
self.update(icao24)
elif bdsreg == 0x09: #velocity
subtype = data["bds09"].get_type()
subtype = msg.data["bds09"].get_type()
if subtype == 0:
[velocity, heading, vert_spd, turnrate] = air_modes.parseBDS09_0(data)
[velocity, heading, vert_spd, turnrate] = air_modes.parseBDS09_0(msg.data)
elif subtype == 1:
[velocity, heading, vert_spd] = air_modes.parseBDS09_1(data)
[velocity, heading, vert_spd] = air_modes.parseBDS09_1(msg.data)
turnrate = 0
else:
return
self.velocities[icao24] = [velocity, heading, vert_spd, turnrate]
except ADSBError:
pass

View File

@@ -2,7 +2,7 @@
#HTML template for Mode S map display
#Nick Foster, 2013
def html_template(my_position, json_file):
def html_template(my_apikey, my_position, json_file):
if my_position is None:
my_position = [37, -122]
@@ -25,9 +25,9 @@ def html_template(my_position, json_file):
white-space: nowrap;
}
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false">
<script type="text/javascript" src="http://maps.google.com/maps/api/js?key=%s">
</script>
<script type="text/javascript" src="http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerwithlabel/1.1.9/src/markerwithlabel.js">
<script type="text/javascript" src="https://raw.githubusercontent.com/googlemaps/v3-utility-library/master/markerwithlabel/src/markerwithlabel.js">
</script>
<script type="text/javascript">
var map;
@@ -53,7 +53,6 @@ def html_template(my_position, json_file):
};
function jsonp_callback(results) { // from JSONP
clearMarkers();
airplanes = {};
for (var i = 0; i < results.length; i++) {
airplanes[results[i].icao] = {
@@ -67,10 +66,20 @@ def html_template(my_position, json_file):
highlight: results[i].highlight
};
}
// clearMarkers();
refreshIcons();
}
function refreshIcons() {
//prune the list
for(var i = 0; i < planes.length; i++) {
icao = planes[i].get("icao")
if(!(icao in airplanes)) {
planes[i].setMap(null)
planes.splice(i, 1);
};
};
for (var airplane in airplanes) {
if (airplanes[airplane].highlight != 0) {
icon_file = "http://www.nerdnetworks.org/~bistromath/airplane_sprite_highlight.png";
@@ -86,7 +95,7 @@ def html_template(my_position, json_file):
};
if (airplanes[airplane].ident.length != 8) {
identstr = airplane;
identstr = airplane;
} else {
identstr = airplanes[airplane].ident;
};
@@ -94,14 +103,31 @@ def html_template(my_position, json_file):
var planeOptions = {
map: map,
position: airplanes[airplane].center,
icao: airplane,
icon: plane_icon,
labelContent: identstr,
labelAnchor: new google.maps.Point(35, -32),
labelClass: "labels",
labelStyle: {opacity: 0.75}
};
planeMarker = new MarkerWithLabel(planeOptions);
planes.push(planeMarker);
var i = 0;
for(i; i<planes.length; i++) {
if(planes[i].get("icao") == airplane) {
planes[i].setPosition(airplanes[airplane].center);
if(planes[i].get("icon") != plane_icon) {
planes[i].setIcon(plane_icon); //handles highlight and heading
};
if(planes[i].get("labelContent") != identstr) {
planes[i].set("labelContent", identstr);
};
break;
};
};
if(i == planes.length) {
planeMarker = new MarkerWithLabel(planeOptions);
planes.push(planeMarker);
};
};
};
@@ -126,4 +152,4 @@ def html_template(my_position, json_file):
<div id="map_canvas" style="width:100%%; height:100%%">
</div>
</body>
</html>""" % (my_position[0], my_position[1], json_file)
</html>""" % (my_apikey, my_position[0], my_position[1], json_file)

View File

@@ -335,13 +335,13 @@ def parseBDS09_1(data):
ew = bool(data["dew"])
subtype = data["sub"]
if subtype == 0x02:
ns_vel <<= 2
ew_vel <<= 2
ns_vel *= 4
ew_vel *= 4
velocity = math.hypot(ns_vel, ew_vel)
if ew:
ew_vel = 0 - ew_vel
if ns_vel == 0:
heading = 0
else:
@@ -423,12 +423,12 @@ def parse_TCAS_CRM(data):
def make_parser(pub):
publisher = pub
def publish(message):
[data, ecc, reference, timestamp] = message.split()
[data, ecc, reference, int_timestamp, frac_timestamp] = message.split()
try:
ret = air_modes.modes_report(modes_reply(int(data, 16)),
int(ecc, 16),
10.0*math.log10(max(1e-8,float(reference))),
air_modes.stamp(0, float(timestamp)))
air_modes.stamp(int(int_timestamp), float(frac_timestamp)))
pub["modes_dl"] = ret
pub["type%i_dl" % ret.data.get_type()] = ret
except ADSBError:

View File

@@ -27,13 +27,13 @@ from gnuradio import gr, gru, eng_notation, filter, blocks
from gnuradio.filter import optfir
from gnuradio.eng_option import eng_option
from gnuradio.gr.pubsub import pubsub
from gnuradio.filter import pfb
from optparse import OptionParser, OptionGroup
import air_modes
import zmq
import threading
import time
import re
import fractions
class modes_radio (gr.top_block, pubsub):
def __init__(self, options, context):
@@ -45,12 +45,17 @@ class modes_radio (gr.top_block, pubsub):
self._resample = None
self._setup_source(options)
if self._resample is not None:
self._rate = 4.0e6 #fixed rate we resample to in RTL case
self._rx_path = air_modes.rx_path(self._rate, options.threshold,
if self._rate < 4e6:
self._resample = pfb.arb_resampler_ccf(4.e6/self._rate)
self._rx_rate = 4e6
else:
self._rx_rate = self._rate
self._rx_path = air_modes.rx_path(self._rx_rate, options.threshold,
self._queue, options.pmf, options.dcblock)
#now subscribe to set various options via pubsub
self.subscribe("freq", self.set_freq)
self.subscribe("gain", self.set_gain)
@@ -128,8 +133,20 @@ class modes_radio (gr.top_block, pubsub):
return self.get_gain()
def set_rate(self, rate):
self._rx_path.set_rate(rate)
return self._u.set_rate(rate) if self.live_source() else 0
if(rate < 4e6 and self._rate > 4e6):
raise NotImplementedError("Lowering rate <4e6Msps not currently supported.")
if(rate < 4e6):
self._resample.set_rate(4e6/rate)
self._rx_rate = 4e6
else:
self._rx_rate = rate
self._rx_path.set_rate(self._rx_rate)
if self._options.source in ("osmocom"):
return self._u.set_sample_rate(rate)
if self._options.source in ("uhd"):
return self._u.set_rate(rate)
else:
return 0
def set_threshold(self, threshold):
self._rx_path.set_threshold(threshold)
@@ -175,14 +192,13 @@ class modes_radio (gr.top_block, pubsub):
self._u.set_gain(options.gain)
print "Gain is %i" % self._u.get_gain()
#TODO: detect if you're using an RTLSDR or Jawbreaker
#and set up accordingly.
elif options.source == "osmocom": #RTLSDR dongle or HackRF Jawbreaker
import osmosdr
self._u = osmosdr.source(options.args)
rates = self._u.get_sample_rates()
# self._u.set_sample_rate(3.2e6) #fixed for RTL dongles
self._u.set_sample_rate(options.rate)
actual_rate = int(self._u.get_sample_rate())
if not self._u.set_center_freq(options.freq):
print "Failed to set initial frequency"
@@ -192,14 +208,9 @@ class modes_radio (gr.top_block, pubsub):
self._u.set_gain(options.gain)
print "Gain is %i" % self._u.get_gain()
if actual_rate < 4.0e6:
gcd = fractions.gcd(4.0e6, actual_rate)
interp = 4.0e6 / gcd
decim = actual_rate / gcd
lpfiltcoeffs = filter.firdes.low_pass(1, interp*actual_rate, 1.6e6, 300e3)
self._resample = filter.rational_resampler_ccf(interpolation=interp,
decimation=decim,
taps=lpfiltcoeffs)
#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 = filter.rational_resampler_ccf(interpolation=5, decimation=4, taps=lpfiltcoeffs)
else:
#semantically detect whether it's ip.ip.ip.ip:port or filename
@@ -208,14 +219,16 @@ class modes_radio (gr.top_block, pubsub):
ip, port = re.search("(.*)\:(\d{1,5})", options.source).groups()
except:
raise Exception("Please input UDP source e.g. 192.168.10.1:12345")
self._u = gr.udp_source(gr.sizeof_gr_complex, ip, int(port))
self._u = blocks.udp_source(gr.sizeof_gr_complex, ip, int(port))
print "Using UDP source %s:%s" % (ip, port)
else:
self._u = blocks.file_source(gr.sizeof_gr_complex, options.source)
print "Using file source %s" % options.source
print "Rate is %i" % actual_rate
print "Rate is %i" % (options.rate,)
def close(self):
self.stop()
self.wait()
self._sender.close()
self._u = None

View File

@@ -37,7 +37,7 @@ class rx_path(gr.hier_block2):
# Convert incoming I/Q baseband to amplitude
self._demod = blocks.complex_to_mag_squared()
if use_dcblock:
self._dcblock = filter.dc_blocker_cc(100*self._spc,True)
self._dcblock = filter.dc_blocker_cc(100*self._spc,False)
self.connect(self, self._dcblock, self._demod)
else:
self.connect(self, self._demod)
@@ -65,13 +65,13 @@ class rx_path(gr.hier_block2):
self.connect(self._sync, self._slicer)
def set_rate(self, rate):
self._sync.set_rate(rate)
self._sync.set_rate(int(rate))
self._spc = int(rate/2e6)
self._avg.set_length_and_scale(48*self._spc, 1.0/(48*self._spc))
if self._bb != self._demod:
self._pmf.set_length_and_scale(self._spc, 1.0/self._spc)
if self._dcblock is not None:
self._dcblock.set_length(100*self._spc)
# if self._dcblock is not None:
# self._dcblock.set_length(100*self._spc)
def set_threshold(self, threshold):
self._sync.set_threshold(threshold)
@@ -83,6 +83,6 @@ class rx_path(gr.hier_block2):
def get_pmf(self, pmf):
return not (self._bb == self._demod)
def get_threshold(self, threshold):
def get_threshold(self):
return self._sync.get_threshold()

View File

@@ -23,7 +23,7 @@
import time, os, sys, socket
from string import split, join
import air_modes
from datetime import *
import datetime
from air_modes.exceptions import *
import threading
@@ -63,7 +63,7 @@ class output_sbs1:
#it could be cleaner if there were separate output_* fns
#but this works
for i in (0, 4, 5, 11, 17):
pub.subscribe("type%i_dl" % i, output)
pub.subscribe("type%i_dl" % i, self.output)
#spawn thread to add new connections as they come in
self._runner = dumb_task_runner(self.add_pending_conns, 0.1)
@@ -83,7 +83,7 @@ class output_sbs1:
# dictionary is getting too large.
if len(self._aircraft_id_map) > 1e4:
minimum = min(self._aircraft_id_map.values()) + (len(self._aircraft_id_map) - 1e4)
for icao, _id in self._aircraft_id_map:
for icao, _id in dict(self._aircraft_id_map).iteritems():
if _id < minimum:
del self._aircraft_id_map[icao]
@@ -111,7 +111,7 @@ class output_sbs1:
pass
def current_time(self):
timenow = datetime.now()
timenow = datetime.datetime.now()
return [timenow.strftime("%Y/%m/%d"), timenow.strftime("%H:%M:%S.%f")[0:-3]]
def decode_fs(self, fs):
@@ -154,7 +154,7 @@ class output_sbs1:
[datestr, timestr] = self.current_time()
aircraft_id = self.get_aircraft_id(ecc)
retstr = "MSG,7,0,%i,%06X,%i,%s,%s,%s,%s,,%s,,,,,,,,,," % (aircraft_id, ecc, aircraft_id+100, datestr, timestr, datestr, timestr, air_modes.decode_alt(shortdata["ac"], True))
if vs:
if shortdata["vs"]:
retstr += "1\r\n"
else:
retstr += "0\r\n"
@@ -174,7 +174,7 @@ class output_sbs1:
def pp11(self, shortdata, ecc):
[datestr, timestr] = self.current_time()
aircraft_id = self.get_aircraft_id(icao24)
aircraft_id = self.get_aircraft_id(shortdata["aa"])
return "MSG,8,0,%i,%06X,%i,%s,%s,%s,%s,,,,,,,,,,,,\r\n" % (aircraft_id, shortdata["aa"], aircraft_id+100, datestr, timestr, datestr, timestr)
def pp17(self, data):

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>687</width>
<height>422</height>
<width>719</width>
<height>454</height>
</rect>
</property>
<property name="sizePolicy">
@@ -102,8 +102,8 @@
<rect>
<x>10</x>
<y>20</y>
<width>236</width>
<height>251</height>
<width>241</width>
<height>281</height>
</rect>
</property>
<property name="title">
@@ -307,7 +307,7 @@
<property name="geometry">
<rect>
<x>10</x>
<y>200</y>
<y>190</y>
<width>221</width>
<height>22</height>
</rect>
@@ -320,7 +320,7 @@
<property name="geometry">
<rect>
<x>10</x>
<y>220</y>
<y>210</y>
<width>221</width>
<height>22</height>
</rect>
@@ -329,6 +329,16 @@
<string>Use DC blocking filter</string>
</property>
</widget>
<widget class="QLineEdit" name="line_my_api_key">
<property name="geometry">
<rect>
<x>90</x>
<y>250</y>
<width>121</width>
<height>27</height>
</rect>
</property>
</widget>
</widget>
<widget class="QGroupBox" name="group_output">
<property name="geometry">
@@ -549,6 +559,19 @@
</property>
</widget>
</widget>
<widget class="QLabel" name="label_34">
<property name="geometry">
<rect>
<x>20</x>
<y>270</y>
<width>67</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>API Key</string>
</property>
</widget>
</widget>
<widget class="QWidget" name="dashboard">
<attribute name="title">
@@ -1031,7 +1054,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>687</width>
<width>719</width>
<height>25</height>
</rect>
</property>

View File

@@ -39,7 +39,7 @@ set(GR_SWIG_LIBRARIES air_modes)
#set(GR_SWIG_DOC_FILE ${CMAKE_CURRENT_BINARY_DIR}/gr-air-modes_swig_doc.i)
#set(GR_SWIG_DOC_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../include)
GR_SWIG_MAKE(air_modes_swig air_modes.i)
GR_SWIG_MAKE(air_modes_swig air_modes_swig.i)
########################################################################
# Install the build swig module