3 Commits
rtl ... 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
14 changed files with 503 additions and 58 deletions

View File

@@ -464,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

@@ -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

@@ -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

@@ -172,11 +172,7 @@ 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

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,7 +51,7 @@ from air_modes_swig import *
# import any pure python here
#
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

View File

@@ -33,7 +33,6 @@ import zmq
import threading
import time
import re
import fractions
class modes_radio (gr.top_block, pubsub):
def __init__(self, options, context):
@@ -45,8 +44,6 @@ 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,
self._queue, options.pmf, options.dcblock)
@@ -84,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]")
@@ -175,14 +172,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 +188,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
@@ -214,7 +205,7 @@ class modes_radio (gr.top_block, pubsub):
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._sender.close()

View File

@@ -86,3 +86,46 @@ class rx_path(gr.hier_block2):
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

@@ -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);