3 Commits
gr37 ... uplink

Author SHA1 Message Date
Nick Foster
d0101017e3 Uplink: convert preliminary uplink work to next branch 2013-08-09 21:31:30 -07:00
Nick Foster
adbb7ec64b Uplink: Forgot to add the actual decoder. 2013-08-09 21:10:02 -07:00
Nick Foster
77d695f2ef First stab at interrogation uplink processing. Getting bits, inefficiently. 2013-08-09 21:10:02 -07:00
31 changed files with 664 additions and 334 deletions

View File

@@ -22,7 +22,7 @@
# Project setup
########################################################################
cmake_minimum_required(VERSION 2.6)
project(gr-gr-air-modes CXX C)
project(gr-gr-air-modes CXX)
set(gr-gr-air-modes_VERSION_MAJOR 0)
set(gr-gr-air-modes_VERSION_MINOR 0)
enable_testing()
@@ -49,8 +49,6 @@ endif()
########################################################################
include(GrBoost)
find_package(PythonLibs 2)
########################################################################
# Install directories
########################################################################
@@ -71,22 +69,12 @@ set(GRC_BLOCKS_DIR ${GR_PKG_DATA_DIR}/grc/blocks)
########################################################################
# Find gnuradio build dependencies
########################################################################
set(GR_REQUIRED_COMPONENTS RUNTIME)
find_package(Gnuradio "3.7.2" REQUIRED)
find_package(GnuradioRuntime)
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
########################################################################

View File

@@ -1,10 +0,0 @@
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,7 +97,6 @@ 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,7 +30,6 @@ 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
@@ -88,9 +87,7 @@ 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)
@@ -107,7 +104,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.az_map_model(None)
self.az_model = air_modes.az_map_model(None)
self.ui.azimuth_map.setModel(self.az_model)
#set up dashboard views
@@ -236,9 +233,7 @@ 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)
or (rate.start() < 4.e6 and ((rate.start()%0.2e6) == 0))]
self.rates = [rate.start() for rate in self.src.get_sample_rates() if (rate.start() % 2.e6) == 0]
self.antennas = ["RX"]
self.src = None
self.ui.combo_ant.setEnabled(False)
@@ -268,15 +263,10 @@ class mainwindow(QtGui.QMainWindow):
#set up recommended sample rate
if len(self.rates) > 1:
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)
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)
else:
self.ui.check_pmf.setChecked(False)
self.ui.combo_rate.setCurrentIndex(self.rates.index(recommended_rate))
################ action handlers ####################
@@ -324,11 +314,6 @@ 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)
@@ -351,7 +336,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.az_map_output(self._cpr_dec, self.az_model, self._publisher)
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)
#set up map
@@ -367,7 +352,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_apikey, my_position, self._jsonfile.name)
htmlstring = air_modes.html_template(my_position, self._jsonfile.name)
self._htmlfile.write(htmlstring)
self._htmlfile.flush()
class WebPage(QtWebKit.QWebPage):
@@ -412,17 +397,13 @@ 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._radio.close()
self._radio = None
self._relay.close()
self._radio.close()
self._relay = None
self._radio = None
self._rps_timer = None
try:
self.kmlgen.done = True
@@ -475,7 +456,6 @@ class mainwindow(QtGui.QMainWindow):
defaults["threshold"] = "5"
defaults["latitude"] = None
defaults["longitude"] = None
defaults["apikey"] = None
prefs = ConfigParser.ConfigParser(defaults)
prefs.optionxform = str
@@ -484,7 +464,7 @@ class mainwindow(QtGui.QMainWindow):
prefs.read(os.path.expanduser(self.opt_file))
for item in prefs.items("GUI"):
defaults[item[0]] = item[1]
except (IOError, ConfigParser.NoSectionError):
except IOError:
print "No preferences file %s found, creating..." % os.path.expanduser(self.opt_file)
self.write_defaults(defaults)

View File

@@ -93,7 +93,7 @@ def main():
if options.kml is not None:
kmlgen.close()
if __name__ == '__main__':
main()

View File

@@ -1,36 +1,6 @@
INCLUDE(FindPkgConfig)
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)
PKG_CHECK_MODULES(GNURADIO_RUNTIME gnuradio-runtime)
INCLUDE(FindPackageHandleStandardArgs)
# 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)
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)

View File

@@ -4,34 +4,23 @@
# QWT_FOUND If false, do not try to use Qwt
find_path (QWT_INCLUDE_DIRS
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
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
)
find_library (QWT_LIBRARIES
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
NAMES qwt-qt4 qwt
PATHS
/usr/local/lib
/usr/lib
/opt/local/lib
/sw/lib
)
# handle the QUIETLY and REQUIRED arguments and set QWT_FOUND to TRUE if

View File

@@ -0,0 +1,56 @@
# - 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}" STREQUAL "${ver}")
if(${Boost_VERSION} EQUAL ${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}" STREQUAL "${ver}")
endif(${Boost_VERSION} EQUAL ${ver})
endforeach(ver)

View File

@@ -25,5 +25,6 @@ install(FILES
slicer.h
types.h
api.h
uplink.h
DESTINATION include/gr_air_modes
)

View File

@@ -23,7 +23,7 @@
#ifndef INCLUDED_MODES_CRC_H
#define INCLUDED_MODES_CRC_H
extern const unsigned int modes_crc_table[112];
unsigned int modes_check_crc(unsigned char data[], int length);
int modes_check_crc(unsigned char data[], int length);
bruteResultTypeDef modes_ec_brute(modes_packet &err_packet);
unsigned next_set_of_n_elements(unsigned x);

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(float channel_rate, float threshold_db);
static sptr make(int channel_rate, float threshold_db);
virtual void set_rate(float channel_rate) = 0;
virtual void set_rate(int channel_rate) = 0;
virtual void set_threshold(float threshold_db) = 0;
virtual float get_rate(void) = 0;
virtual int get_rate(void) = 0;
virtual float get_threshold(void) = 0;
};

View File

@@ -0,0 +1,52 @@
/*
# Copyright 2013 Nick Foster
#
# This file is part of gr-air-modes
#
# gr-air-modes is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# gr-air-modes is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gr-air-modes; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
*/
#ifndef INCLUDED_AIR_MODES_UPLINK_H
#define INCLUDED_AIR_MODES_UPLINK_H
#include <gnuradio/block.h>
#include <gr_air_modes/api.h>
#include <gnuradio/msg_queue.h>
namespace gr {
namespace air_modes {
/*!
* \brief mode select preamble detection
* \ingroup block
*/
class AIR_MODES_API uplink : virtual public gr::block
{
public:
typedef boost::shared_ptr<uplink> sptr;
static sptr make(int channel_rate, float threshold_db, gr::msg_queue::sptr queue);
virtual void set_rate(int channel_rate) = 0;
virtual void set_threshold(float threshold_db) = 0;
virtual int get_rate(void) = 0;
virtual float get_threshold(void) = 0;
};
} // namespace air_modes
} // namespace gr
#endif /* INCLUDED_AIR_MODES_UPLINK_H */

View File

@@ -26,6 +26,7 @@ add_library(air_modes SHARED
preamble_impl.cc
slicer_impl.cc
modes_crc.cc
uplink_impl.cc
)
target_link_libraries(air_modes ${Boost_LIBRARIES} ${GNURADIO_RUNTIME_LIBRARIES})
set_target_properties(air_modes PROPERTIES DEFINE_SYMBOL "AIR_MODES_EXPORTS")

View File

@@ -1,24 +1,25 @@
/*
* Copyright 2013 Nick Foster
* Copyright 2007 Free Software Foundation, Inc.
*
* This file is part of gr-air-modes
* This file is part of GNU Radio
*
* gr-air-modes is free software; you can redistribute it and/or modify
* GNU Radio is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* gr-air-modes is distributed in the hope that it will be useful,
* GNU Radio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gr-air-modes; see the file COPYING. If not, write to
* along with GNU Radio; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
//this is copied almost verbatim from Eric Cottrell's gr-air platform.
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -30,34 +31,135 @@
#include <math.h>
#include <stdlib.h>
unsigned int crc_table[256];
const unsigned int POLY=0xFFF409;
//generate a bytewise lookup CRC table
void generate_crc_table(void)
/* Mode S Parity Table
* Index is bit position with bit 0 being the first bit after preamble
* On short frames an offset of 56 is used.
*/
const unsigned int modes_crc_table[112] =
{
unsigned int crc = 0;
for(int n=0; n<256; n++) {
crc = n<<16;
for(int k=0; k<8; k++) {
if(crc & 0x800000) {
crc = ((crc<<1) ^ POLY) & 0xFFFFFF;
} else {
crc = (crc<<1) & 0xFFFFFF;
}
}
crc_table[n] = crc & 0xFFFFFF;
}
}
0x3935ea, // Start of Long Frame CRC
0x1c9af5,
0xf1b77e,
0x78dbbf,
0xc397db,
0x9e31e9,
0xb0e2f0,
0x587178,
0x2c38bc,
0x161c5e,
0x0b0e2f,
0xfa7d13,
0x82c48d,
0xbe9842,
0x5f4c21,
0xd05c14,
0x682e0a,
0x341705,
0xe5f186,
0x72f8c3,
0xc68665,
0x9cb936,
0x4e5c9b,
0xd8d449,
0x939020,
0x49c810,
0x24e408,
0x127204,
0x093902,
0x049c81,
0xfdb444,
0x7eda22,
0x3f6d11, // Extended 56 bit field
0xe04c8c,
0x702646,
0x381323,
0xe3f395,
0x8e03ce,
0x4701e7,
0xdc7af7,
0x91c77f,
0xb719bb,
0xa476d9,
0xadc168,
0x56e0b4,
0x2b705a,
0x15b82d,
0xf52612,
0x7a9309,
0xc2b380,
0x6159c0,
0x30ace0,
0x185670,
0x0c2b38,
0x06159c,
0x030ace,
0x018567,
0xff38b7, // Start of Short Frame CRC
0x80665f,
0xbfc92b,
0xa01e91,
0xaff54c,
0x57faa6,
0x2bfd53,
0xea04ad,
0x8af852,
0x457c29,
0xdd4410,
0x6ea208,
0x375104,
0x1ba882,
0x0dd441,
0xf91024,
0x7c8812,
0x3e4409,
0xe0d800,
0x706c00,
0x383600,
0x1c1b00,
0x0e0d80,
0x0706c0,
0x038360,
0x01c1b0,
0x00e0d8,
0x00706c,
0x003836,
0x001c1b,
0xfff409,
0x800000, // 24 PI or PA bits
0x400000,
0x200000,
0x100000,
0x080000,
0x040000,
0x020000,
0x010000,
0x008000,
0x004000,
0x002000,
0x001000,
0x000800,
0x000400,
0x000200,
0x000100,
0x000080,
0x000040,
0x000020,
0x000010,
0x000008,
0x000004,
0x000002,
0x000001,
};
//Perform a bytewise CRC check
unsigned int modes_check_crc(unsigned char data[], int length)
int modes_check_crc(unsigned char data[], int length)
{
if(crc_table[1] != POLY) generate_crc_table();
unsigned int crc=0;
for(int i=0; i<length; i++) {
crc = crc_table[((crc>>16) ^ data[i]) & 0xff] ^ (crc << 8);
}
return crc & 0xFFFFFF;
int crc=0, i;
for(i = 0; i < length; i++)
{
if(data[i/8] & (1 << (7-(i%8))))
{
crc ^= modes_crc_table[i+(112-length)];
}
}
return crc;
}

View File

@@ -34,11 +34,11 @@
namespace gr {
air_modes::preamble::sptr air_modes::preamble::make(float channel_rate, float threshold_db) {
air_modes::preamble::sptr air_modes::preamble::make(int 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(float channel_rate, float threshold_db) :
air_modes::preamble_impl::preamble_impl(int 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(float channel_rate, float threshold_db)
d_key = pmt::string_to_symbol("preamble_found");
}
void air_modes::preamble_impl::set_rate(float channel_rate) {
void air_modes::preamble_impl::set_rate(int 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_sample_rate = channel_rate;
d_secs_per_sample = 1.0/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;
}
float air_modes::preamble_impl::get_rate(void) {
return d_sample_rate;
int air_modes::preamble_impl::get_rate(void) {
return d_samples_per_chip * d_chip_rate;
}
static void integrate_and_dump(float *out, const float *in, int chips, int samps_per_chip) {
@@ -97,42 +97,19 @@ static double correlate_preamble(const float *in, int samples_per_chip) {
return corr;
}
static pmt::pmt_t tag_to_timestamp(gr::tag_t tstamp, uint64_t abs_sample_cnt, int rate) {
uint64_t last_whole_stamp;
//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;
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));
}
//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
if(tstamp.key == NULL || pmt::symbol_to_string(tstamp.key) != "rx_time") return 0;
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));
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;
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;
}
@@ -147,7 +124,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 % int(d_samples_per_chip)) - int(d_samples_per_chip), 0);
const int ninputs = std::max(mininputs - (mininputs % d_samples_per_chip) - d_samples_per_chip, 0);
if (ninputs <= 0) { consume_each(0); return 0; }
float *out = (float *) output_items[0];
@@ -217,20 +194,22 @@ 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+int(j*d_samples_per_chip)] - inavg[i];
out[j] = in[i+j*d_samples_per_chip] - inavg[i];
}
//get the timestamp of the preamble
pmt::pmt_t tstamp = tag_to_timestamp(d_timestamp, abs_sample_cnt + i, d_sample_rate);
double tstamp = tag_to_timestamp(d_timestamp, abs_sample_cnt + i, d_secs_per_sample);
//now tag the preamble
add_item_tag(0, //stream ID
nitems_written(0), //sample
d_key, //frame_info
tstamp,
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
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;
float d_samples_per_chip;
float d_samples_per_symbol;
int d_samples_per_chip;
int d_samples_per_symbol;
float d_threshold_db;
float d_threshold;
gr::tag_t d_timestamp;
pmt::pmt_t d_me, d_key;
int d_sample_rate;
gr::tag_t d_timestamp;
double d_secs_per_sample;
public:
preamble_impl(float channel_rate, float threshold_db);
preamble_impl(int 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(float channel_rate);
void set_rate(int channel_rate);
void set_threshold(float threshold_db);
float get_threshold(void);
float get_rate(void);
int get_rate(void);
};
} //namespace air_modes

View File

@@ -158,6 +158,8 @@ 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++) {
@@ -170,26 +172,20 @@ int air_modes::slicer_impl::work(int noutput_items,
if(rx_packet.type == Short_Packet && rx_packet.message_type != 11 && rx_packet.numlowconf > 0) {continue;}
if(rx_packet.message_type == 11 && rx_packet.numlowconf >= 10) {continue;}
rx_packet.crc = modes_check_crc(rx_packet.data, (packet_length/8)-3);
unsigned int ap = rx_packet.data[packet_length/8-3] << 16
| rx_packet.data[packet_length/8-2] << 8
| rx_packet.data[packet_length/8-1] << 0;
rx_packet.crc ^= ap;
rx_packet.crc = modes_check_crc(rx_packet.data, packet_length);
//crc for packets that aren't type 11 or type 17 is encoded with the transponder ID, which we don't know
//therefore we toss 'em if there's syndrome
//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
<< " " << pmt::to_uint64(pmt::tuple_ref(tstamp, 0)) << " " << std::setprecision(10) << pmt::to_double(pmt::tuple_ref(tstamp, 1));
<< " " << std::setprecision(10) << std::setw(10) << rx_packet.timestamp;
gr::message::sptr msg = gr::message::make_from_string(std::string(d_payload.str()));
d_queue->handle(msg);
}

View File

@@ -38,7 +38,6 @@ 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;

210
lib/uplink_impl.cc Normal file
View File

@@ -0,0 +1,210 @@
/*
# Copyright 2013 Nick Foster
#
# This file is part of gr-air-modes
#
# gr-air-modes is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# gr-air-modes is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gr-air-modes; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gnuradio/io_signature.h>
#include <string.h>
#include <iostream>
#include <iomanip>
#include <gnuradio/tags.h>
#include "uplink_impl.h"
namespace gr {
air_modes::uplink::sptr air_modes::uplink::make(int channel_rate, float threshold_db, gr::msg_queue::sptr queue) {
return gnuradio::get_initial_sptr(new air_modes::uplink_impl(channel_rate, threshold_db, queue));
}
air_modes::uplink_impl::uplink_impl(int channel_rate, float threshold_db, gr::msg_queue::sptr queue) :
gr::block ("uplink",
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 = 4000000;
set_rate(channel_rate);
set_threshold(threshold_db);
std::stringstream str;
str << name() << unique_id();
d_me = pmt::string_to_symbol(str.str());
d_key = pmt::string_to_symbol("uplink_found");
d_queue = queue;
}
void air_modes::uplink_impl::set_rate(int 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;
set_output_multiple(1+d_check_width*2);
set_history(d_samples_per_symbol);
}
void air_modes::uplink_impl::set_threshold(float threshold_db) {
d_threshold_db = threshold_db;
d_threshold = powf(10., threshold_db/20.);
}
float air_modes::uplink_impl::get_threshold(void) {
return d_threshold_db;
}
int air_modes::uplink_impl::get_rate(void) {
return d_samples_per_chip * d_chip_rate;
}
//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;
double last_frac_stamp;
if(tstamp.key == NULL || pmt::symbol_to_string(tstamp.key) != "rx_time") return 0;
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;
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;
}
//the preamble pattern in bits
//fixme goes in .h
//these are in 0.25us increments
static const int preamble_bits[] = {1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, -1};
static double correlate_preamble(const float *in, int samples_per_chip) {
double corr = 0.0;
for(int i=0; i<20; i++) {
for(int j=0; j<samples_per_chip;j++)
corr += preamble_bits[i]*in[i*samples_per_chip+j];
}
return corr/(20*samples_per_chip);
}
int air_modes::uplink_impl::general_work(int noutput_items,
gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const float *in = (const float *) input_items[0];
const float *inavg = (const float *) input_items[1];
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_symbol) - d_samples_per_symbol, 0);
if (ninputs <= 0) { consume_each(0); return 0; }
float *out = (float *) output_items[0];
if(0) std::cout << "Uplink called with " << ninputs << " samples" << std::endl;
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::string_to_symbol("rx_time"));
//tags.back() is the most recent timestamp, then.
if(tstamp_tags.size() > 0) {
d_timestamp = tstamp_tags.back();
}
for(int i=0; i < ninputs; i++) {
float pulse_threshold = inavg[i] * d_threshold;
//we're looking for negative pulses, since the sync
//phase reversal bit will always be negative.
if(in[i] < (0-fabs(pulse_threshold))) { //hey we got a candidate
if(0) std::cout << "Pulse threshold " << (0-fabs(pulse_threshold)) << " exceeded by sample at " << in[i] << std::endl;
while(in[i+1] < in[i] and (ninputs-112*d_samples_per_symbol) > i) i++;
bool ugly = false;
for(int j=0; j<8*d_samples_per_symbol; j++) {
if(in[i+j+d_samples_per_symbol] < fabs(pulse_threshold)) ugly=true;
}
if(ugly) continue;
if(0) std::cout << "Phase reversal sync found at " << i << " with value " << in[i] << std::endl;
//now we're at the phase reversal sync bit, and we can start pulling bits out
//next bit starts 0.5us later (2 bit periods)
float ref_level = 0;
for(int j=0; j<d_samples_per_symbol*2; j++) {
ref_level += in[i+j];
}
ref_level /= (d_samples_per_symbol*2);
i+=d_samples_per_symbol*2;
//be sure we've got enough room in the input buffer to copy out a whole packet
if(ninputs-i < 112*d_samples_per_symbol) {
consume_each(std::max(i-1,0));
if(0) std::cout << "Uplink consumed " << std::max(i-1,0) << ", returned 0 (no room)" << std::endl;
return 0;
}
for(int j=0; j<ninputs; j++) {
out[j] = in[j];
}
out[i] = 1;
unsigned char bits[14];
memset((void *) bits, 0x00, 14);
for(int j=0; j<56; j++) {
if(in[i+j*d_samples_per_symbol] < 0) bits[j/8] += 1 << (7-(j%8));
}
if(0) {
std::cout << "Data: ";
for(int j=0; j<7; j++) {
std::cout << std::setw(2) << std::setfill('0') << std::hex << int(bits[j]);
}
std::cout << std::dec << std::endl;
}
//get the timestamp of the uplink tag
double tstamp = tag_to_timestamp(d_timestamp, abs_sample_cnt + i, d_secs_per_sample);
d_payload.str("");
for(int m=0; m<7; m++) {
d_payload << std::hex << std::setw(2) << std::setfill('0') << unsigned(bits[m]);
}
d_payload << " " << std::setw(6) << 0 << " " << std::dec << ref_level
<< " " << std::setprecision(10) << std::setw(10) << tstamp;
gr::message::sptr msg = gr::message::make_from_string(std::string(d_payload.str()));
d_queue->handle(msg);
//produce only one output per work call -- TODO this should probably change
if(0) std::cout << "Uplink consumed " << i+112*d_samples_per_symbol << " with i=" << i << ", returned 112" << std::endl;
consume_each(ninputs);
return ninputs;
}
}
//didn't get anything this time
//if(1) std::cout << "Uplink consumed " << ninputs << ", returned 0" << std::endl;
consume_each(ninputs);
return 0;
}
} //namespace gr

46
lib/uplink_impl.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef _AIR_MODES_UPLINK_IMPL_H_
#define _AIR_MODES_UPLINK_IMPL_H_
#include <gnuradio/block.h>
#include <gr_air_modes/api.h>
#include <gr_air_modes/uplink.h>
#include <gnuradio/msg_queue.h>
namespace gr {
namespace air_modes {
class AIR_MODES_API uplink_impl : public uplink
{
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_threshold_db;
float d_threshold;
pmt::pmt_t d_me, d_key;
gr::tag_t d_timestamp;
double d_secs_per_sample;
gr::msg_queue::sptr d_queue;
std::ostringstream d_payload;
public:
uplink_impl(int channel_rate, float threshold_db, gr::msg_queue::sptr queue);
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_threshold(float threshold_db);
float get_threshold(void);
int get_rate(void);
};
} //namespace air_modes
} //namespace gr
#endif //_AIR_MODES_UPLINK_IMPL_H_

View File

@@ -51,13 +51,7 @@ 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 rx_path import rx_path, uplink_rx_path
from zmq_socket import zmq_pubsub_iface
from parse import *
from msprint import output_print
@@ -67,6 +61,7 @@ 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,6 +18,7 @@ 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 = {}
@@ -25,41 +26,42 @@ class output_flightgear:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.connect((self.hostname, self.port))
pub.subscribe("type17_dl", self.output)
pub.subscribe("type17_dl", 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(msg.data)
(ident, actype) = air_modes.parseBDS08(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(msg.data, self._cpr)
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = air_modes.parseBDS06(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(msg.data, self._cpr)
[altitude, decoded_lat, decoded_lon, rnge, bearing] = air_modes.parseBDS05(data, self._cpr)
self.positions[icao24] = [decoded_lat, decoded_lon, altitude]
self.update(icao24)
elif bdsreg == 0x09: #velocity
subtype = msg.data["bds09"].get_type()
subtype = data["bds09"].get_type()
if subtype == 0:
[velocity, heading, vert_spd, turnrate] = air_modes.parseBDS09_0(msg.data)
[velocity, heading, vert_spd, turnrate] = air_modes.parseBDS09_0(data)
elif subtype == 1:
[velocity, heading, vert_spd] = air_modes.parseBDS09_1(msg.data)
[velocity, heading, vert_spd] = air_modes.parseBDS09_1(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_apikey, my_position, json_file):
def html_template(my_position, json_file):
if my_position is None:
my_position = [37, -122]
@@ -25,9 +25,9 @@ def html_template(my_apikey, my_position, json_file):
white-space: nowrap;
}
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?key=%s">
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false">
</script>
<script type="text/javascript" src="https://raw.githubusercontent.com/googlemaps/v3-utility-library/master/markerwithlabel/src/markerwithlabel.js">
<script type="text/javascript" src="http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerwithlabel/1.1.9/src/markerwithlabel.js">
</script>
<script type="text/javascript">
var map;
@@ -53,6 +53,7 @@ def html_template(my_apikey, my_position, json_file):
};
function jsonp_callback(results) { // from JSONP
clearMarkers();
airplanes = {};
for (var i = 0; i < results.length; i++) {
airplanes[results[i].icao] = {
@@ -66,20 +67,10 @@ def html_template(my_apikey, 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";
@@ -95,7 +86,7 @@ def html_template(my_apikey, my_position, json_file):
};
if (airplanes[airplane].ident.length != 8) {
identstr = airplane;
identstr = airplane;
} else {
identstr = airplanes[airplane].ident;
};
@@ -103,31 +94,14 @@ def html_template(my_apikey, 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}
};
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);
};
planeMarker = new MarkerWithLabel(planeOptions);
planes.push(planeMarker);
};
};
@@ -152,4 +126,4 @@ def html_template(my_apikey, my_position, json_file):
<div id="map_canvas" style="width:100%%; height:100%%">
</div>
</body>
</html>""" % (my_apikey, my_position[0], my_position[1], json_file)
</html>""" % (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 *= 4
ew_vel *= 4
ns_vel <<= 2
ew_vel <<= 2
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, int_timestamp, frac_timestamp] = message.split()
[data, ecc, reference, 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(int(int_timestamp), float(frac_timestamp)))
air_modes.stamp(0, float(timestamp)))
pub["modes_dl"] = ret
pub["type%i_dl" % ret.data.get_type()] = ret
except ADSBError:

View File

@@ -27,7 +27,6 @@ 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
@@ -46,16 +45,9 @@ class modes_radio (gr.top_block, pubsub):
self._resample = None
self._setup_source(options)
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._rx_path = air_modes.rx_path(self._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)
@@ -89,7 +81,7 @@ class modes_radio (gr.top_block, pubsub):
@staticmethod
def add_radio_options(parser):
group = OptionGroup(parser, "Receiver setup options")
#Choose source
group.add_option("-s","--source", type="string", default="uhd",
help="Choose source: uhd, osmocom, <filename>, or <ip:port> [default=%default]")
@@ -133,20 +125,8 @@ class modes_radio (gr.top_block, pubsub):
return self.get_gain()
def set_rate(self, rate):
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
self._rx_path.set_rate(rate)
return self._u.set_rate(rate) if self.live_source() else 0
def set_threshold(self, threshold):
self._rx_path.set_threshold(threshold)
@@ -219,7 +199,7 @@ 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 = blocks.udp_source(gr.sizeof_gr_complex, ip, int(port))
self._u = gr.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)
@@ -228,7 +208,5 @@ class modes_radio (gr.top_block, pubsub):
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,False)
self._dcblock = filter.dc_blocker_cc(100*self._spc,True)
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(int(rate))
self._sync.set_rate(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,49 @@ class rx_path(gr.hier_block2):
def get_pmf(self, pmf):
return not (self._bb == self._demod)
def get_threshold(self):
def get_threshold(self, threshold):
return self._sync.get_threshold()
class uplink_rx_path(gr.hier_block2):
def __init__(self, rate, threshold, queue, use_pmf=False, use_dcblock=False):
gr.hier_block2.__init__(self, "modes_uplink_rx_path",
gr.io_signature(1, 1, gr.sizeof_gr_complex),
gr.io_signature(0,0,0))
self._rate = int(rate)
self._threshold = threshold
self._queue = queue
self._spc = int(rate/4e6)
#demodulate DPSK via delay
self._delay = gr.delay(gr.sizeof_gr_complex, self._spc)
self._mult = gr.multiply_cc()
self._conj = gr.conjugate_cc()
self._c2r = gr.complex_to_real()
#self._bb = self._demod
self.filesink = gr.file_sink(gr.sizeof_float, "wat.dat")
# Pulse matched filter for 0.25us pulses
# if use_pmf:
# self._pmf = gr.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
# Synchronize to uplink preamble
self._sync = air_modes_swig.modes_uplink(self._rate, self._threshold)
# Slice Mode-S bits and send to message queue
# self._slicer = air_modes_swig.modes_slicer(self._rate, self._queue)
# Wire up the flowgraph
self.connect(self, (self._mult, 0))
self.connect(self, self._delay, self._conj, (self._mult, 1))
self.connect(self._mult, self._c2r)
self.connect(self._c2r, (self._sync, 0))
self.connect(self._c2r, self._avg, (self._sync, 1))
# self.connect(self._sync, self._slicer)
self.connect(self._sync, self.filesink)

View File

@@ -23,7 +23,7 @@
import time, os, sys, socket
from string import split, join
import air_modes
import datetime
from datetime import *
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, self.output)
pub.subscribe("type%i_dl" % i, 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 dict(self._aircraft_id_map).iteritems():
for icao, _id in self._aircraft_id_map:
if _id < minimum:
del self._aircraft_id_map[icao]
@@ -111,7 +111,7 @@ class output_sbs1:
pass
def current_time(self):
timenow = datetime.datetime.now()
timenow = 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 shortdata["vs"]:
if 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(shortdata["aa"])
aircraft_id = self.get_aircraft_id(icao24)
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>719</width>
<height>454</height>
<width>687</width>
<height>422</height>
</rect>
</property>
<property name="sizePolicy">
@@ -102,8 +102,8 @@
<rect>
<x>10</x>
<y>20</y>
<width>241</width>
<height>281</height>
<width>236</width>
<height>251</height>
</rect>
</property>
<property name="title">
@@ -307,7 +307,7 @@
<property name="geometry">
<rect>
<x>10</x>
<y>190</y>
<y>200</y>
<width>221</width>
<height>22</height>
</rect>
@@ -320,7 +320,7 @@
<property name="geometry">
<rect>
<x>10</x>
<y>210</y>
<y>220</y>
<width>221</width>
<height>22</height>
</rect>
@@ -329,16 +329,6 @@
<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">
@@ -559,19 +549,6 @@
</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">
@@ -1054,7 +1031,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>719</width>
<width>687</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_swig.i)
GR_SWIG_MAKE(air_modes_swig air_modes.i)
########################################################################
# Install the build swig module

View File

@@ -7,11 +7,14 @@
%{
#include "gr_air_modes/preamble.h"
#include "gr_air_modes/slicer.h"
#include "gr_air_modes/uplink.h"
%}
%include "gr_air_modes/preamble.h"
%include "gr_air_modes/slicer.h"
%include "gr_air_modes/uplink.h"
GR_SWIG_BLOCK_MAGIC2(air_modes,preamble);
GR_SWIG_BLOCK_MAGIC2(air_modes,slicer);
GR_SWIG_BLOCK_MAGIC2(air_modes,uplink);