Initial commit
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
#
|
||||
# Copyright 2004 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU Radio
|
||||
#
|
||||
# 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 3, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# 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 GNU Radio; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
SUBDIRS = lib python
|
||||
@@ -0,0 +1,84 @@
|
||||
#
|
||||
# Copyright 2004,2005,2006,2008,2009 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU Radio
|
||||
#
|
||||
# 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 3, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# 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 GNU Radio; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
# C/C++ headers get installed in ${prefix}/include/gnuradio
|
||||
grinclude_HEADERS = \
|
||||
air_modes_preamble.h \
|
||||
air_modes_framer.h \
|
||||
air_modes_slicer.h \
|
||||
air_modes_types.h \
|
||||
modes_energy.h \
|
||||
modes_parity.h
|
||||
|
||||
###################################
|
||||
# SWIG Python interface and library
|
||||
|
||||
TOP_SWIG_IFILES = \
|
||||
air.i
|
||||
|
||||
# Install so that they end up available as:
|
||||
# import gnuradio.air
|
||||
# This ends up at:
|
||||
# ${prefix}/lib/python${python_version}/site-packages/gnuradio
|
||||
air_pythondir_category = \
|
||||
gnuradio
|
||||
|
||||
# additional arguments to the SWIG command
|
||||
#air_la_swig_args = \
|
||||
# -g
|
||||
|
||||
# additional sources for the SWIG-generated library
|
||||
air_la_swig_sources = \
|
||||
air_modes_preamble.cc \
|
||||
air_modes_framer.cc \
|
||||
air_modes_slicer.cc \
|
||||
modes_energy.cc \
|
||||
modes_parity.cc
|
||||
|
||||
# additional arguments to the SWIG command
|
||||
#air_la_swig_args = \
|
||||
# $(SWIG_PYTHON_ARGS)
|
||||
|
||||
# additional libraries for linking with the SWIG-generated library
|
||||
#air_la_swig_libadd = \
|
||||
# libm.dylib
|
||||
|
||||
# additional LD flags for linking the SWIG-generated library
|
||||
#air_la_swig_ldflags = \
|
||||
# -L/opt/local/bin
|
||||
|
||||
# additional Python files to be installed along with the SWIG-generated one
|
||||
#air_python = \
|
||||
# __init__.py
|
||||
|
||||
# additional SWIG files to be installed
|
||||
#air_swiginclude_headers = \
|
||||
# nothing.i
|
||||
|
||||
include $(top_srcdir)/Makefile.swig
|
||||
|
||||
# add some of the variables generated inside the Makefile.swig.gen
|
||||
BUILT_SOURCES = $(swig_built_sources)
|
||||
|
||||
# Do not distribute the output of SWIG
|
||||
no_dist_files = $(swig_built_sources)
|
||||
@@ -0,0 +1,52 @@
|
||||
/* -*- c++ -*- */
|
||||
|
||||
%include "gnuradio.i" // the common stuff
|
||||
|
||||
%{
|
||||
#include "air_modes_preamble.h"
|
||||
#include "air_modes_framer.h"
|
||||
#include "air_modes_slicer.h"
|
||||
#include <gr_msg_queue.h>
|
||||
%}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* First arg is the package prefix.
|
||||
* Second arg is the name of the class minus the prefix.
|
||||
*
|
||||
* This does some behind-the-scenes magic so we can
|
||||
* access howto_square_ff from python as howto.square_ff
|
||||
*/
|
||||
GR_SWIG_BLOCK_MAGIC(air,modes_preamble);
|
||||
|
||||
air_modes_preamble_sptr air_make_modes_preamble (int channel_rate, float threshold_db);
|
||||
|
||||
class air_modes_preamble : public gr_sync_block
|
||||
{
|
||||
private:
|
||||
air_modes_preamble (int channel_rate, float threshold_db);
|
||||
};
|
||||
|
||||
GR_SWIG_BLOCK_MAGIC(air,modes_framer);
|
||||
|
||||
air_modes_framer_sptr air_make_modes_framer (int channel_rate);
|
||||
|
||||
class air_modes_framer : public gr_sync_block
|
||||
{
|
||||
private:
|
||||
air_modes_framer (int channel_rate);
|
||||
};
|
||||
|
||||
GR_SWIG_BLOCK_MAGIC(air,modes_slicer);
|
||||
|
||||
air_modes_slicer_sptr air_make_modes_slicer (int channel_rate, gr_msg_queue_sptr queue);
|
||||
|
||||
class air_modes_slicer : public gr_block
|
||||
{
|
||||
private:
|
||||
air_modes_slicer (int channel_rate, gr_msg_queue_sptr queue);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <air_modes_framer.h>
|
||||
#include <gr_io_signature.h>
|
||||
#include <air_modes_types.h>
|
||||
#include <modes_energy.h>
|
||||
|
||||
air_modes_framer_sptr air_make_modes_framer(int channel_rate)
|
||||
{
|
||||
return air_modes_framer_sptr (new air_modes_framer(channel_rate));
|
||||
}
|
||||
|
||||
air_modes_framer::air_modes_framer(int channel_rate) :
|
||||
gr_sync_block ("modes_framer",
|
||||
gr_make_io_signature2 (2, 2, sizeof(float), sizeof(unsigned char)), //stream 0 is received data, stream 1 is binary preamble detector output
|
||||
gr_make_io_signature (1, 1, sizeof(unsigned char))) //output is [0, 1, 2]: [no frame, short frame, long frame]
|
||||
{
|
||||
//initialize private data here
|
||||
d_chip_rate = 2000000; //2Mchips per second
|
||||
d_samples_per_chip = channel_rate / d_chip_rate; //must be integer number of samples per chip to work
|
||||
d_samples_per_symbol = d_samples_per_chip * 2;
|
||||
d_check_width = 120 * d_samples_per_symbol; //gotta be able to look at two long frame lengths at a time in the event that FRUIT occurs near the end of the first frame
|
||||
|
||||
set_output_multiple(1+d_check_width*2);
|
||||
}
|
||||
|
||||
int air_modes_framer::work(int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items)
|
||||
{
|
||||
//do things!
|
||||
const float *inraw = (const float *) input_items[0];
|
||||
const unsigned char *inattrib = (const unsigned char *) input_items[1];
|
||||
|
||||
//float *outraw = (float *) output_items[0];
|
||||
unsigned char *outattrib = (unsigned char *) output_items[0];
|
||||
|
||||
int size = noutput_items - d_check_width; //need to be able to look ahead a full frame
|
||||
|
||||
int reference_level = 0;
|
||||
framer_packet_type packet_attrib;
|
||||
|
||||
for(int i = 0; i < size; i++) {
|
||||
|
||||
packet_attrib = No_Packet;
|
||||
|
||||
if(!inattrib[i]) {
|
||||
outattrib[i] = packet_attrib;
|
||||
continue; //if there's no preamble marker, forget it, move on
|
||||
}
|
||||
|
||||
//first, assume we have a long packet
|
||||
packet_attrib = Long_Packet;
|
||||
|
||||
//let's use the preamble marker to get a reference level for the packet
|
||||
reference_level = (bit_energy(&inraw[i], d_samples_per_chip)
|
||||
+ bit_energy(&inraw[i+int(1.0*d_samples_per_symbol)], d_samples_per_chip)
|
||||
+ bit_energy(&inraw[i+int(3.5*d_samples_per_symbol)], d_samples_per_chip)
|
||||
+ bit_energy(&inraw[i+int(4.5*d_samples_per_symbol)], d_samples_per_chip)) / 4;
|
||||
|
||||
//armed with our reference level, let's look for marks within 3dB of the reference level in bits 57-62 (65-70, see above)
|
||||
//if bits 57-62 have marks in either chip, we've got a long packet
|
||||
//otherwise we have a short packet
|
||||
|
||||
//NOTE: you can change the default here to be short packet, and then check for a long packet. don't know which way is better.
|
||||
|
||||
for(int j = (65 * d_samples_per_symbol); j < (70 * d_samples_per_symbol); j += d_samples_per_symbol) {
|
||||
int t_max = (bit_energy(&inraw[i+j], d_samples_per_chip) > bit_energy(&inraw[i+j+d_samples_per_chip], d_samples_per_chip)) ? bit_energy(&inraw[i+j], d_samples_per_chip) : bit_energy(&inraw[i+j+d_samples_per_chip], d_samples_per_chip);
|
||||
if(t_max < (reference_level / 2)) packet_attrib = Short_Packet;
|
||||
}
|
||||
|
||||
//BUT: we must also loop through the entire packet to make sure it is clear of additional preamble markers! if it has another preamble marker, it's been FRUITed, and we must only
|
||||
//mark the new packet (i.e., just continue).
|
||||
|
||||
int lookahead;
|
||||
if(packet_attrib == Long_Packet) lookahead = 112;
|
||||
else lookahead = 56;
|
||||
|
||||
for(int j = i+1; j < i+(lookahead * d_samples_per_symbol); j++) {
|
||||
if(inattrib[j]) packet_attrib = Fruited_Packet; //FRUITed by mode S! in this case, we drop this first packet
|
||||
//if(inraw[j] > (reference_level * 2)) packet_attrib = Fruited_Packet; //catches strong Mode A/C fruit inside the packet
|
||||
//but good error correction should cope with that
|
||||
}
|
||||
|
||||
outattrib[i] = packet_attrib;
|
||||
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef INCLUDED_AIR_MODES_FRAMER_H
|
||||
#define INCLUDED_AIR_MODES_FRAMER_H
|
||||
|
||||
#include <gr_sync_block.h>
|
||||
|
||||
class air_modes_framer;
|
||||
typedef boost::shared_ptr<air_modes_framer> air_modes_framer_sptr;
|
||||
|
||||
air_modes_framer_sptr air_make_modes_framer(int channel_rate);
|
||||
|
||||
/*!
|
||||
* \brief mode select framer detection
|
||||
* \ingroup block
|
||||
*/
|
||||
class air_modes_framer : public gr_sync_block
|
||||
{
|
||||
private:
|
||||
friend air_modes_framer_sptr air_make_modes_framer(int channel_rate);
|
||||
air_modes_framer(int channel_rate);
|
||||
|
||||
int d_check_width;
|
||||
int d_chip_rate;
|
||||
int d_samples_per_chip;
|
||||
int d_samples_per_symbol;
|
||||
|
||||
public:
|
||||
int work (int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items);
|
||||
};
|
||||
|
||||
|
||||
#endif /* INCLUDED_AIR_MODES_framer_H */
|
||||
@@ -0,0 +1,147 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <air_modes_preamble.h>
|
||||
#include <gr_io_signature.h>
|
||||
#include <modes_energy.h>
|
||||
|
||||
air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshold_db)
|
||||
{
|
||||
return air_modes_preamble_sptr (new air_modes_preamble(channel_rate, threshold_db));
|
||||
}
|
||||
|
||||
air_modes_preamble::air_modes_preamble(int channel_rate, float threshold_db) :
|
||||
gr_sync_block ("modes_preamble",
|
||||
gr_make_io_signature2 (2, 2, sizeof(float), sizeof(float)), //stream 0 is received data, stream 1 is moving average for reference
|
||||
gr_make_io_signature (1, 1, sizeof(unsigned char)))
|
||||
{
|
||||
//initialize private data here
|
||||
d_chip_rate = 2000000; //2Mchips per second
|
||||
d_samples_per_chip = channel_rate / d_chip_rate; //must be integer number of samples per chip to work
|
||||
d_samples_per_symbol = d_samples_per_chip * 2;
|
||||
d_check_width = 7.5 * d_samples_per_symbol; //only search to this far from the end of the stream buffer
|
||||
d_threshold_db = threshold_db;
|
||||
d_threshold = powf(10., threshold_db/10.); //the level that the sample must be above the moving average in order to qualify as a pulse
|
||||
set_output_multiple(1+d_check_width*2);
|
||||
}
|
||||
|
||||
int air_modes_preamble::work(int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items)
|
||||
{
|
||||
//do things!
|
||||
const float *inraw = (const float *) input_items[0];
|
||||
const float *inavg = (const float *) input_items[1];
|
||||
|
||||
//float *outraw = (float *) output_items[0];
|
||||
unsigned char *outattrib = (unsigned char *) output_items[0];
|
||||
|
||||
int size = noutput_items - d_check_width;
|
||||
int pulse_offsets[4];
|
||||
int bit_energies[4];
|
||||
|
||||
for(int i = d_samples_per_chip; i < size; i++) {
|
||||
float pulse_threshold = bit_energy(&inavg[i], d_samples_per_chip) * d_threshold;
|
||||
bool valid_preamble = false;
|
||||
float gate_sum_now = 0, gate_sum_early = 0, gate_sum_late = 0;
|
||||
|
||||
if(bit_energy(&inraw[i], d_samples_per_chip) > pulse_threshold) { //if the sample is greater than the reference level by the specified amount
|
||||
//while(inraw[i+1] > inraw[i]) i++;
|
||||
|
||||
//if(inraw[i+1] > inraw[i]) continue; //we're still coming up on the pulse peak, so let's just fall out and look at it next time around
|
||||
//if(inraw[i-1] > inraw[i]) continue; //we're past the peak, so it's no longer a valid pulse
|
||||
|
||||
//a note on the above. this simple early/late gate system works for decim = 16, but doesn't work so great for decim = 8. the extra samples, subject to noise,
|
||||
//mean the peak is not necessarily the center of the bit, and you get fooled into sampling at strange bit edges. the solution is an area integral, a real early/late gate
|
||||
//system, computing the area of a bit and maximizing so you sample at the center of the bit shape for any decimation. for decim = 16 it won't really matter since you only have
|
||||
//one possible bit center.
|
||||
|
||||
int gate_sum = early_late(&inraw[i], d_samples_per_chip); //see modes_energy.cc
|
||||
if(gate_sum != 0) continue; //if either the early gate or the late gate had greater energy, keep moving.
|
||||
// if(gate_sum_late > gate_sum_now) continue;
|
||||
|
||||
//the packets are so short we choose not to do any sort of closed-loop synchronization after this simple gating. if we get a good center sample, the drift should be negligible.
|
||||
|
||||
pulse_offsets[0] = i;
|
||||
pulse_offsets[1] = i+int(1.0 * d_samples_per_symbol);
|
||||
pulse_offsets[2] = i+int(3.5 * d_samples_per_symbol);
|
||||
pulse_offsets[3] = i+int(4.5 * d_samples_per_symbol);
|
||||
|
||||
bit_energies[0] = bit_energy(&inraw[pulse_offsets[0]], d_samples_per_chip);
|
||||
bit_energies[1] = bit_energy(&inraw[pulse_offsets[1]], d_samples_per_chip);
|
||||
bit_energies[2] = bit_energy(&inraw[pulse_offsets[2]], d_samples_per_chip);
|
||||
bit_energies[3] = bit_energy(&inraw[pulse_offsets[3]], d_samples_per_chip);
|
||||
|
||||
//search for the rest of the pulses at their expected positions
|
||||
if( bit_energies[1] < pulse_threshold) continue;
|
||||
if( bit_energies[2] < pulse_threshold) continue;
|
||||
if( bit_energies[3] < pulse_threshold) continue;
|
||||
|
||||
valid_preamble = true; //this gets falsified by the following statements to disqualify a preamble
|
||||
|
||||
float avgpeak = (bit_energies[0] + bit_energies[1] + bit_energies[2] + bit_energies[3]) / 4;
|
||||
|
||||
float space_threshold = bit_energies[0] / d_threshold; //set the threshold requirement for spaces (0 chips) to threshold dB below the current peak
|
||||
//search between pulses and all the way out to 8.0us to make sure there are no pulses inside the "0" chips. make sure all the samples are <= (inraw[peak] * d_threshold).
|
||||
//so 0.5us has to be < space_threshold, as does (1.5-3), 4, (5-7.5) in order to qualify.
|
||||
for(int j = 1.5 * d_samples_per_symbol; j <= 3 * d_samples_per_symbol; j+=d_samples_per_chip)
|
||||
if(bit_energy(&inraw[i+j], d_samples_per_chip) > space_threshold) valid_preamble = false;
|
||||
for(int j = 5 * d_samples_per_symbol; j <= 7.5 * d_samples_per_symbol; j+=d_samples_per_chip)
|
||||
if(bit_energy(&inraw[i+j], d_samples_per_chip) > space_threshold) valid_preamble = false;
|
||||
|
||||
//make sure all four peaks are within 2dB of each other
|
||||
|
||||
|
||||
float minpeak = avgpeak * 0.631; //-2db
|
||||
float maxpeak = avgpeak * 1.585; //2db
|
||||
|
||||
if(bit_energies[0] < minpeak || bit_energies[0] > maxpeak) continue;
|
||||
if(bit_energies[1] < minpeak || bit_energies[1] > maxpeak) continue;
|
||||
if(bit_energies[2] < minpeak || bit_energies[2] > maxpeak) continue;
|
||||
if(bit_energies[3] < minpeak || bit_energies[3] > maxpeak) continue;
|
||||
|
||||
}
|
||||
|
||||
//just for kicks, after validating a preamble, you might want to use all four peaks to form a more accurate "average" center sample time, so that if noise corrupts the first leading edge
|
||||
//sample, you don't mis-sample the entire packet.
|
||||
|
||||
//this could also be done in a separate packet, although it probably saves CPU to do it here
|
||||
//for the 2 samples per chip case, you can just add up the peaks at the expected peak times, then do the same for +1, and -1.
|
||||
if(valid_preamble) {
|
||||
|
||||
gate_sum_now = bit_energies[0] + bit_energies[1] + bit_energies[2] + bit_energies[3];
|
||||
// gate_sum_early = bit_energy(&inraw[pulse_offsets[0]-1], d_samples_per_chip)
|
||||
// + bit_energy(&inraw[pulse_offsets[1]-1], d_samples_per_chip)
|
||||
// + bit_energy(&inraw[pulse_offsets[2]-1], d_samples_per_chip)
|
||||
// + bit_energy(&inraw[pulse_offsets[3]-1], d_samples_per_chip);
|
||||
gate_sum_late = bit_energy(&inraw[pulse_offsets[0]+1], d_samples_per_chip)
|
||||
+ bit_energy(&inraw[pulse_offsets[1]+1], d_samples_per_chip)
|
||||
+ bit_energy(&inraw[pulse_offsets[2]+1], d_samples_per_chip)
|
||||
+ bit_energy(&inraw[pulse_offsets[3]+1], d_samples_per_chip);
|
||||
/*
|
||||
if(d_samples_per_chip <= 2) {
|
||||
gate_sum_now = inraw[pulse_offsets[0]+0] + inraw[pulse_offsets[1]+0] + inraw[pulse_offsets[2]+0] + inraw[pulse_offsets[3]+0];
|
||||
gate_sum_early = inraw[pulse_offsets[0]-1] + inraw[pulse_offsets[1]-1] + inraw[pulse_offsets[2]-1] + inraw[pulse_offsets[3]-1];
|
||||
gate_sum_late = inraw[pulse_offsets[0]+1] + inraw[pulse_offsets[1]+1] + inraw[pulse_offsets[2]+1] + inraw[pulse_offsets[3]+1];
|
||||
} else {
|
||||
for(int j = 1-d_samples_per_chip/2; j < d_samples_per_chip/2; j++) {
|
||||
gate_sum_now += inraw[j+pulse_offsets[0]+0] + inraw[j+pulse_offsets[1]+0] + inraw[j+pulse_offsets[2]+0] + inraw[j+pulse_offsets[3]+0];
|
||||
gate_sum_early += inraw[j+pulse_offsets[0]-1] + inraw[j+pulse_offsets[1]-1] + inraw[j+pulse_offsets[2]-1] + inraw[j+pulse_offsets[3]-1];
|
||||
gate_sum_late += inraw[j+pulse_offsets[0]+1] + inraw[j+pulse_offsets[1]+1] + inraw[j+pulse_offsets[2]+1] + inraw[j+pulse_offsets[3]+1];
|
||||
}
|
||||
}
|
||||
*/
|
||||
// if(gate_sum_early > gate_sum_now) { //i think this is redundant
|
||||
// outattrib[i-1] = 1;
|
||||
// }
|
||||
/*else*/ if(gate_sum_late > gate_sum_now) {
|
||||
outattrib[i+1] = 1;
|
||||
i+=1; //so we skip the next one and don't overwrite it
|
||||
}
|
||||
else outattrib[i] = 1;
|
||||
|
||||
} else outattrib[i] = 0;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef INCLUDED_AIR_MODES_PREAMBLE_H
|
||||
#define INCLUDED_AIR_MODES_PREAMBLE_H
|
||||
|
||||
#include <gr_sync_block.h>
|
||||
|
||||
class air_modes_preamble;
|
||||
typedef boost::shared_ptr<air_modes_preamble> air_modes_preamble_sptr;
|
||||
|
||||
air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshold_db);
|
||||
|
||||
/*!
|
||||
* \brief mode select preamble detection
|
||||
* \ingroup block
|
||||
*/
|
||||
class air_modes_preamble : public gr_sync_block
|
||||
{
|
||||
private:
|
||||
friend air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshold_db);
|
||||
air_modes_preamble(int channel_rate, float threshold_db);
|
||||
|
||||
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;
|
||||
|
||||
public:
|
||||
int work (int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items);
|
||||
};
|
||||
|
||||
#endif /* INCLUDED_AIR_MODES_PREAMBLE_H */
|
||||
@@ -0,0 +1,220 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <air_modes_slicer.h>
|
||||
#include <gr_io_signature.h>
|
||||
#include <air_modes_types.h>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <modes_parity.h>
|
||||
#include <modes_energy.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
}
|
||||
|
||||
air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr_msg_queue_sptr queue)
|
||||
{
|
||||
return air_modes_slicer_sptr (new air_modes_slicer(channel_rate, queue));
|
||||
}
|
||||
|
||||
air_modes_slicer::air_modes_slicer(int channel_rate, gr_msg_queue_sptr queue) :
|
||||
gr_sync_block ("modes_slicer",
|
||||
gr_make_io_signature2 (2, 2, sizeof(float), sizeof(unsigned char)), //stream 0 is received data, stream 1 is binary preamble detector output
|
||||
gr_make_io_signature (0, 0, 0) )
|
||||
{
|
||||
//initialize private data here
|
||||
d_chip_rate = 2000000; //2Mchips per second
|
||||
d_samples_per_chip = channel_rate / d_chip_rate; //must be integer number of samples per chip to work
|
||||
d_samples_per_symbol = d_samples_per_chip * 2;
|
||||
d_check_width = 120 * d_samples_per_symbol; //how far you will have to look ahead
|
||||
d_queue = queue;
|
||||
|
||||
set_output_multiple(1+d_check_width * 2); //how do you specify buffer size for sinks?
|
||||
}
|
||||
|
||||
int air_modes_slicer::work(int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items)
|
||||
{
|
||||
//do things!
|
||||
const float *inraw = (const float *) input_items[0];
|
||||
const unsigned char *inattrib = (const unsigned char *) input_items[1];
|
||||
int size = noutput_items - d_check_width; //since it's a sync block, i assume that it runs with ninput_items = noutput_items
|
||||
|
||||
int i;
|
||||
|
||||
for(i = 0; i < size; i++) {
|
||||
if(inattrib[i] == framer_packet_type(Short_Packet) || inattrib[i] == framer_packet_type(Long_Packet)) { //if there's a packet starting here....
|
||||
modes_packet rx_packet;
|
||||
|
||||
int packet_length = 112;
|
||||
if(inattrib[i] == framer_packet_type(Short_Packet)) packet_length = 56;
|
||||
|
||||
//printf("Packet received from framer w/length %i\n", packet_length);
|
||||
|
||||
rx_packet.type = framer_packet_type(inattrib[i]);
|
||||
memset(&rx_packet.data, 0x00, 14 * sizeof(unsigned char));
|
||||
memset(&rx_packet.lowconfbits, 0x00, 24 * sizeof(unsigned char));
|
||||
rx_packet.numlowconf = 0;
|
||||
|
||||
//let's use the preamble marker to get a reference level for the packet
|
||||
rx_packet.reference_level = (bit_energy(&inraw[i], d_samples_per_chip)
|
||||
+ bit_energy(&inraw[i+int(1.0*d_samples_per_symbol)], d_samples_per_chip)
|
||||
+ bit_energy(&inraw[i+int(3.5*d_samples_per_symbol)], d_samples_per_chip)
|
||||
+ bit_energy(&inraw[i+int(4.5*d_samples_per_symbol)], d_samples_per_chip)) / 4;
|
||||
|
||||
i += 8 * d_samples_per_symbol; //move to the center of the first bit of the data
|
||||
|
||||
//here we calculate the total energy contained in each chip of the symbol
|
||||
for(int j = 0; j < packet_length; j++) {
|
||||
int firstchip = i+j*d_samples_per_symbol;
|
||||
int secondchip = firstchip + d_samples_per_chip;
|
||||
bool slice, confidence;
|
||||
float firstchip_energy=0, secondchip_energy=0;
|
||||
|
||||
firstchip_energy = bit_energy(&inraw[firstchip], d_samples_per_chip);
|
||||
secondchip_energy = bit_energy(&inraw[secondchip], d_samples_per_chip);
|
||||
|
||||
//3dB limits for bit slicing and confidence measurement
|
||||
float highlimit=rx_packet.reference_level*2;
|
||||
float lowlimit=rx_packet.reference_level*0.5;
|
||||
bool firstchip_inref = ((firstchip_energy > lowlimit) && (firstchip_energy < highlimit));
|
||||
bool secondchip_inref = ((secondchip_energy > lowlimit) && (secondchip_energy < highlimit));
|
||||
|
||||
//these two lines for a super simple naive slicer.
|
||||
// slice = firstchip_energy > secondchip_energy;
|
||||
// confidence = bool(int(firstchip_inref) + int(secondchip_inref)); //one and only one chip in the reference zone
|
||||
|
||||
//below is the Lincoln Labs slicer. it may produce greater bit errors. supposedly it is more resistant to mode A/C FRUIT.
|
||||
|
||||
if(firstchip_inref && !secondchip_inref) {
|
||||
slice = 1;
|
||||
confidence = 1;
|
||||
}
|
||||
else if(secondchip_inref && !firstchip_inref) {
|
||||
slice = 0;
|
||||
confidence = 1;
|
||||
}
|
||||
else if(firstchip_inref && secondchip_inref) {
|
||||
slice = firstchip_energy > secondchip_energy;
|
||||
confidence = 0;
|
||||
}
|
||||
else if(!firstchip_inref && !secondchip_inref) { //in this case, we determine the bit by whichever is larger, and we determine high confidence if the low chip is 6dB below reference.
|
||||
slice = firstchip_energy > secondchip_energy;
|
||||
if(slice) {
|
||||
if(secondchip_energy < lowlimit * 0.5) confidence = 1;
|
||||
else confidence = 0;
|
||||
} else {
|
||||
if(firstchip_energy < lowlimit * 0.5) confidence = 1;
|
||||
else confidence = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//put the data into the packet
|
||||
if(slice) {
|
||||
rx_packet.data[j/8] += 1 << (7-(j%8));
|
||||
}
|
||||
//put the confidence decision into the packet
|
||||
if(confidence) {
|
||||
//rx_packet.confidence[j/8] += 1 << (7-(j%8));
|
||||
} else {
|
||||
if(rx_packet.numlowconf < 24) rx_packet.lowconfbits[rx_packet.numlowconf++] = j;
|
||||
}
|
||||
|
||||
}
|
||||
i += packet_length * d_samples_per_symbol;
|
||||
|
||||
//here you might want to traverse the whole packet and if you find all 0's, just toss it. don't know why these packets turn up, but they pass ECC.
|
||||
bool zeroes = 1;
|
||||
for(int m = 0; m < 14; m++) {
|
||||
if(rx_packet.data[m]) zeroes = 0;
|
||||
}
|
||||
if(zeroes) continue; //toss it
|
||||
|
||||
rx_packet.message_type = (rx_packet.data[0] >> 3) & 0x1F; //get the message type for the parser to conveniently use, and to make decisions on ECC methods
|
||||
|
||||
//we note that short packets other than type 11 CANNOT be reliably decoded, since the a/c address is encoded with the parity bits.
|
||||
//mode S in production ATC use relies on the fact that these short packets are reply squitters to transponder requests,
|
||||
//and so the radar should already know the expected a/c reply address. so, error-correction makes no sense on short packets (other than type 11)
|
||||
//this means two things: first, we will DROP short packets (other than type 11) with ANY low-confidence bits, since we can't be confident that we're seeing real data
|
||||
//second, we will only perform error correction on LONG type S packets.
|
||||
|
||||
//the limitation on short packets means in practice a short packet has to be at least 6dB above the noise floor in order to be output. long packets can theoretically
|
||||
//be decoded at the 3dB SNR point. below that and the preamble detector won't fire.
|
||||
|
||||
if(rx_packet.type == Short_Packet && rx_packet.message_type != 11 && rx_packet.numlowconf != 0) continue;
|
||||
//if(rx_packet.numlowconf >= 24) continue; //don't even try, this is the maximum number of errors ECC could possibly correct
|
||||
//the above line should be part of ECC, and only checked if the message has parity errors
|
||||
|
||||
rx_packet.parity = modes_check_parity(rx_packet.data, packet_length);
|
||||
|
||||
if(rx_packet.parity && rx_packet.type == Long_Packet) {
|
||||
// long before = rx_packet.parity;
|
||||
bruteResultTypeDef bruteResult = modes_ec_brute(rx_packet);
|
||||
|
||||
if(bruteResult == No_Solution) {
|
||||
//printf("No solution!\n");
|
||||
continue;
|
||||
} else if(bruteResult == Multiple_Solutions) {
|
||||
// printf("Multiple solutions!\n");
|
||||
continue;
|
||||
} else if(bruteResult == Too_Many_LCBs) {
|
||||
//printf("Too many LCBs (%i)!\n", rx_packet.numlowconf);
|
||||
continue;
|
||||
} else if(bruteResult == No_Error) {
|
||||
// printf("No error!\n");
|
||||
} else if(bruteResult == Solution_Found) {
|
||||
// printf("Solution found for %i LCBs!\n", rx_packet.numlowconf);
|
||||
}
|
||||
// rx_packet.parity = modes_check_parity(rx_packet.data, packet_length);
|
||||
// if(rx_packet.parity) printf("Error: packet fails parity check after correction, was %x, now %x\n", before, rx_packet.parity);
|
||||
}
|
||||
|
||||
// if(rx_packet.parity && rx_packet.type == Long_Packet) printf("Error! Bad packet forwarded to the queue.\n");
|
||||
//now we have a complete packet with confidence data, let's print it to the message queue
|
||||
//here, rather than send the entire packet, since we've already done parity checking and ECC in C++, we'll
|
||||
//send just the data (no confidence bits), separated into fields for easier parsing.
|
||||
|
||||
//we'll replicate some data by sending the message type as the first field, followed by the first 8+24=32 bits of the packet, followed by
|
||||
//56 long packet data bits if applicable (zero-padded if not), followed by parity
|
||||
|
||||
d_payload.str("");
|
||||
|
||||
d_payload << std::dec << std::setw(2) << std::setfill('0') << rx_packet.message_type << std::hex << " ";
|
||||
|
||||
for(int m = 0; m < 4; m++) {
|
||||
d_payload << std::setw(2) << std::setfill('0') << unsigned(rx_packet.data[m]);
|
||||
}
|
||||
d_payload << " ";
|
||||
if(packet_length == 112) {
|
||||
for(int m = 4; m < 11; m++) {
|
||||
d_payload << std::setw(2) << std::setfill('0') << unsigned(rx_packet.data[m]);
|
||||
}
|
||||
d_payload << " ";
|
||||
for(int m = 11; m < 14; m++) {
|
||||
d_payload << std::setw(2) << std::setfill('0') << unsigned(rx_packet.data[m]);
|
||||
}
|
||||
} else {
|
||||
for(int m = 4; m < 11; m++) {
|
||||
d_payload << std::setw(2) << std::setfill('0') << unsigned(0);
|
||||
}
|
||||
d_payload << " ";
|
||||
for(int m = 4; m < 7; m++) {
|
||||
d_payload << std::setw(2) << std::setfill('0') << unsigned(rx_packet.data[m]);
|
||||
}
|
||||
}
|
||||
|
||||
d_payload << " " << std::setw(6) << rx_packet.parity << " " << std::dec << rx_packet.reference_level;
|
||||
|
||||
gr_message_sptr msg = gr_make_message_from_string(std::string(d_payload.str()));
|
||||
d_queue->handle(msg);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef INCLUDED_AIR_MODES_slicer_H
|
||||
#define INCLUDED_AIR_MODES_slicer_H
|
||||
|
||||
#include <gr_sync_block.h>
|
||||
#include <gr_msg_queue.h>
|
||||
|
||||
class air_modes_slicer;
|
||||
typedef boost::shared_ptr<air_modes_slicer> air_modes_slicer_sptr;
|
||||
|
||||
air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr_msg_queue_sptr queue);
|
||||
|
||||
/*!
|
||||
* \brief mode select slicer detection
|
||||
* \ingroup block
|
||||
*/
|
||||
class air_modes_slicer : public gr_sync_block
|
||||
{
|
||||
private:
|
||||
friend air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr_msg_queue_sptr queue);
|
||||
air_modes_slicer(int channel_rate, gr_msg_queue_sptr queue);
|
||||
|
||||
int d_check_width;
|
||||
int d_chip_rate;
|
||||
int d_samples_per_chip;
|
||||
int d_samples_per_symbol;
|
||||
gr_msg_queue_sptr d_queue;
|
||||
std::ostringstream d_payload;
|
||||
|
||||
public:
|
||||
int work (int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items);
|
||||
};
|
||||
|
||||
#endif /* INCLUDED_AIR_MODES_slicer_H */
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef AIR_MODES_TYPES_H
|
||||
#define AIR_MODES_TYPES_H
|
||||
|
||||
typedef enum { No_Packet = 0, Short_Packet = 1, Fruited_Packet = 2, Long_Packet = 3 } framer_packet_type;
|
||||
typedef enum { No_Error = 0, Solution_Found, Too_Many_LCBs, No_Solution, Multiple_Solutions } bruteResultTypeDef;
|
||||
|
||||
struct modes_packet {
|
||||
unsigned char data[14];
|
||||
// unsigned char confidence[14]; //112 bits of boolean high/low confidence data for each bit
|
||||
unsigned char lowconfbits[24]; //positions of low confidence bits within the packet
|
||||
|
||||
unsigned long parity;
|
||||
unsigned int numlowconf;
|
||||
framer_packet_type type; //what length packet are we
|
||||
unsigned int message_type;
|
||||
float reference_level;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <air_modes_types.h>
|
||||
#include <modes_energy.h>
|
||||
|
||||
//helper functions to calculate bit energy and Eb/No
|
||||
|
||||
//this is a really cheesy early/late gate synchronizer. compares bit energy
|
||||
int early_late(const float *data, int samples_per_chip) {
|
||||
float gate_sum_early=0, gate_sum_now=0, gate_sum_late=0;
|
||||
|
||||
gate_sum_early = bit_energy(&data[-1], samples_per_chip);
|
||||
gate_sum_now = bit_energy(&data[0], samples_per_chip);
|
||||
gate_sum_late = bit_energy(&data[1], samples_per_chip);
|
||||
|
||||
if(gate_sum_early > gate_sum_now) return -1;
|
||||
else if(gate_sum_late > gate_sum_now) return 1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
//return total bit energy of a chip centered at the current point (we bias right for even samples per chip)
|
||||
float bit_energy(const float *data, int samples_per_chip) {
|
||||
float energy = 0;
|
||||
if(samples_per_chip <= 2) {
|
||||
energy = data[0];
|
||||
} else {
|
||||
for(int j = 1-samples_per_chip/2; j < samples_per_chip/2; j++) {
|
||||
energy += data[j];
|
||||
}
|
||||
}
|
||||
return energy;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#include <air_modes_types.h>
|
||||
|
||||
int early_late(const float *data, int samples_per_chip);
|
||||
float bit_energy(const float *data, int samples_per_chip);
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright 2007 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GNU Radio
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 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"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <air_modes_types.h>
|
||||
#include <modes_parity.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* 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_parity_table[112] =
|
||||
{
|
||||
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,
|
||||
};
|
||||
int modes_check_parity(unsigned char data[], int length)
|
||||
{
|
||||
int short_crc, long_crc, i;
|
||||
// Check both long and short
|
||||
short_crc = 0;
|
||||
long_crc = 0;
|
||||
for(i = 0; i < 56; i++)
|
||||
{
|
||||
if(data[i/8] & (1 << (7-(i%8))))
|
||||
{
|
||||
short_crc ^= modes_parity_table[i+56];
|
||||
long_crc ^= modes_parity_table[i];
|
||||
|
||||
}
|
||||
}
|
||||
for( ; i < length; i++)
|
||||
{
|
||||
if(data[i/8] & (1 << (7-(i%8))))
|
||||
{
|
||||
long_crc ^= modes_parity_table[i];
|
||||
}
|
||||
}
|
||||
if(length == 112) return long_crc;
|
||||
else return short_crc;
|
||||
}
|
||||
|
||||
bruteResultTypeDef modes_ec_brute(modes_packet &err_packet)
|
||||
{
|
||||
//here we basically crib EC's air_ms_ec_brute algorithm, because wherever he got it, it's perfect, and that comparison thing is fast to boot.
|
||||
//we assume that the syndrome result has already been calculated
|
||||
//how many bits shall we attempt to flip? let's say a max of 8 bits, to start. remember we're only going after long packets here.
|
||||
|
||||
//want to speed things up? instead of going through the "search codes" in numeric order, let's find a way to order them probablistically.
|
||||
//that is, right now, EC's algorithm uses a "search order" which starts with ALL possible low-confidence bits flipped, and goes down counting in binary.
|
||||
//statistically it's far more likely that a single bit was flipped somewhere, so we should go through those codes first. THEN we move on to two bits flipped, and so on.
|
||||
|
||||
if(err_packet.parity == 0) return No_Error;
|
||||
if(err_packet.numlowconf > 4) return Too_Many_LCBs;
|
||||
if(err_packet.type != Long_Packet) return No_Solution;
|
||||
|
||||
unsigned crc;
|
||||
unsigned answer;
|
||||
unsigned found = 0;
|
||||
//so in order for this to work, we need the positions of the LCBs. should we be calculating these as we go? ok, done.
|
||||
|
||||
unsigned lastone = (1 << err_packet.numlowconf) - 1;
|
||||
|
||||
// int numflipped; //for debugging
|
||||
|
||||
//here it would be a little faster if we ran through the parity table looking for single-bit errors. then we could start
|
||||
//the loop at i=2 instead.
|
||||
|
||||
for(int i = 1; i <= err_packet.numlowconf; i++) {
|
||||
unsigned j = (1 << i) - 1;
|
||||
while(j < lastone) {
|
||||
crc = 0;
|
||||
//calc syndrome
|
||||
for(int k = 0; k < err_packet.numlowconf; k++) {
|
||||
if((j >> k) & 1) crc ^= modes_parity_table[err_packet.lowconfbits[k]];
|
||||
}
|
||||
//then test
|
||||
if(crc == err_packet.parity) {
|
||||
answer = j;
|
||||
found++;
|
||||
if(found > 1) break;
|
||||
}
|
||||
//then increment
|
||||
j = next_set_of_n_elements(j);
|
||||
}
|
||||
if(found > 1) break;
|
||||
}
|
||||
if(found > 1) return Multiple_Solutions;
|
||||
else if(found == 1) {
|
||||
//fix the packet, verify the CRC, and return
|
||||
//the bits that need to be flipped are in answer.
|
||||
// numflipped=0; //just for debugging, so i can see
|
||||
for(int i = 0; i < err_packet.numlowconf; i++) {
|
||||
if( (answer >> i) & 1) {
|
||||
// numflipped++;
|
||||
unsigned mask = 1 << (7 - (err_packet.lowconfbits[i] % 8)); //create a bitmask
|
||||
err_packet.data[err_packet.lowconfbits[i]/8] ^= mask; //flip the bit
|
||||
}
|
||||
}
|
||||
//printf("Flipped %i bits\n", numflipped);
|
||||
err_packet.parity = 0; //since you found it
|
||||
return Solution_Found;
|
||||
|
||||
} else return No_Solution;
|
||||
}
|
||||
|
||||
|
||||
//from hackersdelight. given a number with x bits set, gives you the next number in that set.
|
||||
unsigned next_set_of_n_elements(unsigned x)
|
||||
{
|
||||
unsigned smallest, ripple, new_smallest, ones;
|
||||
|
||||
if (x == 0) return 0;
|
||||
smallest = (x & -x);
|
||||
ripple = x + smallest;
|
||||
new_smallest = (ripple & -ripple);
|
||||
ones = ((new_smallest/smallest) >> 1) - 1;
|
||||
return ripple | ones;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef INCLUDED_MODES_PARITY_H
|
||||
#define INCLUDED_MODES_PARITY_H
|
||||
extern const unsigned int modes_parity_table[112];
|
||||
int modes_check_parity(unsigned char data[], int length);
|
||||
bruteResultTypeDef modes_ec_brute(modes_packet &err_packet);
|
||||
unsigned next_set_of_n_elements(unsigned x);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright 2004 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU Radio
|
||||
#
|
||||
# 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 3, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# 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 GNU Radio; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
EXTRA_DIST = run_tests.in
|
||||
|
||||
|
||||
TESTS = \
|
||||
run_tests
|
||||
|
||||
|
||||
noinst_PYTHON = \
|
||||
qa_howto.py
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python
|
||||
#from string import split, join
|
||||
|
||||
#betcha this would be faster if you used a table for mode C
|
||||
#you could strip out D1 since it's never used, that leaves 11 bits (table is 2048 entries)
|
||||
def decode_alt(alt, bit13):
|
||||
if alt & 0x40 and bit13 is True:
|
||||
return "METRIC ERROR"
|
||||
|
||||
if alt & 0x10: #a mode S-style reply
|
||||
if bit13 is True:
|
||||
tmp1 = (alt & 0x1F80) >> 2 #first 6 bits get shifted 2 down
|
||||
tmp2 = (alt & 0x20) >> 1 #that bit gets shifted 1 down
|
||||
else:
|
||||
tmp1 = (alt & 0x0FE0) >> 1 #first 7 bits get shifted 1 down but there are only 12 bits in the representation
|
||||
tmp2 = 0
|
||||
|
||||
decoded_alt = ((alt & 0x0F) | tmp1 | tmp2) * 25 - 1000
|
||||
|
||||
else: #a mode C-style reply
|
||||
#okay, the order they come in is:
|
||||
#C1 A1 C2 A2 C4 A4 X B1 D1 B2 D2 B4 D4
|
||||
#the order we want them in is:
|
||||
#D2 D4 A1 A2 A4 B1 B2 B4
|
||||
#so we'll reassemble into a Gray-coded representation
|
||||
|
||||
if bit13 is False:
|
||||
alt = (alt & 0x003F) | (alt & 0x0FC0 << 1)
|
||||
|
||||
C1 = 0x1000
|
||||
A1 = 0x0800
|
||||
C2 = 0x0400
|
||||
A2 = 0x0200 #this represents the order in which the bits come
|
||||
C4 = 0x0100
|
||||
A4 = 0x0080
|
||||
B1 = 0x0020
|
||||
D1 = 0x0010
|
||||
B2 = 0x0008
|
||||
D2 = 0x0004
|
||||
B4 = 0x0002
|
||||
D4 = 0x0001
|
||||
|
||||
bigpart = ((alt & B4) >> 1) + ((alt & B2) >> 2) + ((alt & B1) >> 3) + ((alt & A4) >> 4) + ((alt & A2) >> 5) + ((alt & A1) >> 6) + ((alt & D4) << 6) + ((alt & D2) << 5)
|
||||
|
||||
#bigpart is now the 500-foot-resolution Gray-coded binary part
|
||||
decoded_alt = gray2bin(bigpart)
|
||||
#real_alt is now the 500-foot-per-tick altitude
|
||||
|
||||
cbits = ((alt & C4) >> 8) + ((alt & C2) >> 9) + ((alt & C1) >> 10)
|
||||
cval = gray2bin(cbits) #turn them into a real number
|
||||
|
||||
if cval == 7:
|
||||
cval = 5 #not a real gray code after all
|
||||
|
||||
if decoded_alt % 2:
|
||||
cval = 6 - cval #since the code is symmetric this unwraps it to see whether to subtract the C bits or add them
|
||||
|
||||
decoded_alt *= 500 #take care of the A,B,D data
|
||||
decoded_alt += cval * 100 #factor in the C data
|
||||
decoded_alt -= 1300 #subtract the offset
|
||||
|
||||
return decoded_alt
|
||||
|
||||
def gray2bin(gray):
|
||||
i = gray >> 1
|
||||
|
||||
while i != 0:
|
||||
gray ^= i
|
||||
i >>= 1
|
||||
|
||||
return gray
|
||||
Binary file not shown.
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from modes_parse import *
|
||||
from cpr import *
|
||||
import sys
|
||||
|
||||
my_location = [37.76225, -122.44254]
|
||||
|
||||
shortdata = long(sys.argv[1], 16)
|
||||
longdata = long(sys.argv[2], 16)
|
||||
parity = long(sys.argv[3], 16)
|
||||
ecc = long(sys.argv[4], 16)
|
||||
|
||||
[altitude, decoded_lat, decoded_lon, rnge, bearing] = parseBDS05(shortdata, longdata, parity, ecc)
|
||||
|
||||
if decoded_lat is not None:
|
||||
print "Altitude: %i\nLatitude: %.6f\nLongitude: %.6f\nRange: %.2f\nBearing: %i\n" % (altitude, decoded_lat, decoded_lon, rnge, bearing,)
|
||||
|
||||
print "Decomposing...\n"
|
||||
|
||||
subtype = (longdata >> 51) & 0x1F;
|
||||
|
||||
encoded_lon = longdata & 0x1FFFF
|
||||
encoded_lat = (longdata >> 17) & 0x1FFFF
|
||||
cpr_format = (longdata >> 34) & 1
|
||||
|
||||
enc_alt = (longdata >> 36) & 0x0FFF
|
||||
|
||||
print "Subtype: %i\nEncoded longitude: %x\nEncoded latitude: %x\nCPR format: %i\nEncoded altitude: %x\n" % (subtype, encoded_lon, encoded_lat, cpr_format, enc_alt,)
|
||||
|
||||
#print "First argument is order %i, second %i" % ((evendata >> 34) & 1, (odddata >> 34) & 1,)
|
||||
|
||||
#evenencpos = [(evendata >> 17) & 0x1FFFF, evendata & 0x1FFFF]
|
||||
#oddencpos = [(odddata >> 17) & 0x1FFFF, odddata & 0x1FFFF]
|
||||
|
||||
#[declat, declon] = cpr_decode_global(evenencpos, oddencpos, newer)
|
||||
|
||||
#print "Global latitude: %.6f\nGlobal longitude: %.6f" % (declat, declon,)
|
||||
@@ -0,0 +1,246 @@
|
||||
#!/usr/bin/env python
|
||||
#from string import split, join
|
||||
#from math import pi, floor, cos, acos
|
||||
import math, time
|
||||
#this implements CPR position decoding. local only for now.
|
||||
|
||||
latz = 15
|
||||
nbits = 17
|
||||
my_lat = 37.76225 #update these later!
|
||||
my_lon = -122.44254
|
||||
|
||||
|
||||
def nz(ctype):
|
||||
return 4 * latz - ctype
|
||||
|
||||
def dlat(ctype, surface):
|
||||
if surface == 1:
|
||||
tmp = 90.0
|
||||
else:
|
||||
tmp = 360.0
|
||||
|
||||
nzcalc = nz(ctype)
|
||||
if nzcalc == 0:
|
||||
return tmp
|
||||
else:
|
||||
return tmp / nzcalc
|
||||
|
||||
def nl_eo(declat_in, ctype):
|
||||
return nl(declat_in) - ctype
|
||||
|
||||
def nl(declat_in):
|
||||
return math.floor( (2.0*math.pi) * pow(math.acos(1.0- (1.0-math.cos(math.pi/(2.0*latz))) / pow( math.cos( (math.pi/180.0)*abs(declat_in) ) ,2.0) ),-1.0))
|
||||
|
||||
def dlon(declat_in, ctype, surface):
|
||||
if surface == 1:
|
||||
tmp = 90.0
|
||||
else:
|
||||
tmp = 360.0
|
||||
nlcalc = nl_eo(declat_in, ctype)
|
||||
if nlcalc == 0:
|
||||
return tmp
|
||||
else:
|
||||
return tmp / nlcalc
|
||||
|
||||
def decode_lat(enclat, ctype, my_lat, surface):
|
||||
tmp1 = dlat(ctype, surface)
|
||||
tmp2 = float(enclat) / (2**nbits)
|
||||
j = math.floor(my_lat/tmp1) + math.floor(0.5 + (mod(my_lat, tmp1) / tmp1) - tmp2)
|
||||
# print "dlat gives " + "%.6f " % tmp1 + "with j = " + "%.6f " % j + " and tmp2 = " + "%.6f" % tmp2 + " given enclat " + "%x" % enclat
|
||||
|
||||
return tmp1 * (j + tmp2)
|
||||
|
||||
def decode_lon(declat, enclon, ctype, my_lon, surface):
|
||||
tmp1 = dlon(declat, ctype, surface)
|
||||
tmp2 = float(enclon) / (2.0**nbits)
|
||||
m = math.floor(my_lon / tmp1) + math.floor(0.5 + (mod(my_lon, tmp1) / tmp1) - tmp2)
|
||||
# print "dlon gives " + "%.6f " % tmp1 + "with m = " + "%.6f " % m + " and tmp2 = " + "%.6f" % tmp2 + " given enclon " + "%x" % enclon
|
||||
|
||||
return tmp1 * (m + tmp2)
|
||||
|
||||
|
||||
def mod(a, b):
|
||||
if a < 0:
|
||||
a += 360.0
|
||||
|
||||
return a - b * math.floor(a / b)
|
||||
|
||||
def cpr_resolve_local(my_location, encoded_location, ctype, surface):
|
||||
[my_lat, my_lon] = my_location
|
||||
[enclat, enclon] = encoded_location
|
||||
|
||||
|
||||
|
||||
decoded_lat = decode_lat(enclat, ctype, my_lat, surface)
|
||||
decoded_lon = decode_lon(decoded_lat, enclon, ctype, my_lon, surface)
|
||||
|
||||
return [decoded_lat, decoded_lon]
|
||||
|
||||
def cpr_resolve_global(evenpos, oddpos, mostrecent, surface): #ok this is considered working, tentatively
|
||||
dlateven = dlat(0, surface);
|
||||
dlatodd = dlat(1, surface);
|
||||
|
||||
# print dlateven;
|
||||
# print dlatodd;
|
||||
|
||||
evenpos = [float(evenpos[0]), float(evenpos[1])]
|
||||
oddpos = [float(oddpos[0]), float(oddpos[1])]
|
||||
|
||||
#print "Even position: %x, %x\nOdd position: %x, %x" % (evenpos[0], evenpos[1], oddpos[0], oddpos[1],)
|
||||
|
||||
j = math.floor(((59*evenpos[0] - 60*oddpos[0])/2**nbits) + 0.5) #latitude index
|
||||
|
||||
#print "Latitude index: %i" % j #should be 6, getting 5?
|
||||
|
||||
rlateven = dlateven * (mod(j, 60)+evenpos[0]/2**nbits)
|
||||
rlatodd = dlatodd * (mod(j, 59)+ oddpos[0]/2**nbits)
|
||||
|
||||
#print "Rlateven: %f\nRlatodd: %f" % (rlateven, rlatodd,)
|
||||
|
||||
if nl(rlateven) != nl(rlatodd):
|
||||
#print "Boundary straddle!"
|
||||
return (None, None,)
|
||||
|
||||
if mostrecent == 0:
|
||||
rlat = rlateven
|
||||
else:
|
||||
rlat = rlatodd
|
||||
|
||||
if rlat > 90:
|
||||
rlat = rlat - 180.0
|
||||
|
||||
dl = dlon(rlat, mostrecent, surface)
|
||||
nlthing = nl(rlat)
|
||||
ni = nlthing - mostrecent
|
||||
|
||||
#print "ni is %i" % ni
|
||||
|
||||
m = math.floor(((evenpos[1]*(nlthing-1)-oddpos[1]*(nlthing))/2**nbits)+0.5) #longitude index, THIS LINE IS CORRECT
|
||||
#print "m is %f" % m #should be -16
|
||||
|
||||
|
||||
if mostrecent == 0:
|
||||
enclon = evenpos[1]
|
||||
else:
|
||||
enclon = oddpos[1]
|
||||
|
||||
rlon = dl * (mod(ni+m, ni)+enclon/2**nbits)
|
||||
|
||||
if rlon > 180:
|
||||
rlon = rlon - 360.0
|
||||
|
||||
return [rlat, rlon]
|
||||
|
||||
|
||||
def cpr_decode(icao24, encoded_lat, encoded_lon, cpr_format, evenlist, oddlist, lkplist, surface, longdata):
|
||||
#this is a stopgap measure to catch those packets which aren't really position packets. what gives?
|
||||
# if encoded_lat == 0 or encoded_lon == 0:
|
||||
#print "debug: lat or lon zero for longdata %x" % (longdata,)
|
||||
# return [None, None, None, None]
|
||||
|
||||
if cpr_format==1:
|
||||
oddlist[icao24] = [encoded_lat, encoded_lon, time.time()]
|
||||
else:
|
||||
evenlist[icao24] = [encoded_lat, encoded_lon, time.time()]
|
||||
|
||||
[decoded_lat, decoded_lon] = [None, None]
|
||||
|
||||
#okay, let's traverse the lists and weed out those entries that are older than 15 minutes, as they're unlikely to be useful.
|
||||
for key, item in lkplist.items():
|
||||
if time.time() - item[2] > 900:
|
||||
del lkplist[key]
|
||||
|
||||
for key, item in evenlist.items():
|
||||
if time.time() - item[2] > 900:
|
||||
del evenlist[key]
|
||||
|
||||
for key, item in oddlist.items():
|
||||
if time.time() - item[2] > 900:
|
||||
del oddlist[key]
|
||||
|
||||
#here we perform global/emitter-centered CPR decoding as follows:
|
||||
#first, check for the ICAO number in the planelist. if there is a decoded position in there, use that for emitter-centered decoding and be done with it.
|
||||
|
||||
if surface==1:
|
||||
validrange = 45
|
||||
else:
|
||||
validrange = 180
|
||||
|
||||
if icao24 in lkplist:
|
||||
#print "debug: icao found in LKP table. EC decoding with local position list %s" % str(lkplist[icao24][0:2])
|
||||
[decoded_lat, decoded_lon] = cpr_resolve_local(lkplist[icao24][0:2], [encoded_lat, encoded_lon], cpr_format, surface) #do emitter-centered local decoding
|
||||
lkplist[icao24] = [decoded_lat, decoded_lon, time.time()] #update the local position for next time
|
||||
|
||||
############debug info for plotting strange position reports###############
|
||||
# [lkprange, lkpbearing] = range_bearing(lkplist[icao24][0:2], [decoded_lat, decoded_lon])
|
||||
# lkpdeltat = time.time() - lkplist[icao24][2]
|
||||
# #the units are now mi/sec
|
||||
# #an SR-71 can move at 0.6 miles per second, so let's say if it's over 1.0mi/s it's probably a bug
|
||||
# if lkprange / lkpdeltat > 1.0:
|
||||
# print "debug: buggy position packet detected from icao %x, encoded lat %x, encoded lon %x, CPR format %i, longdata %x." % (icao24, encoded_lat, encoded_lon, cpr_format, longdata)
|
||||
#
|
||||
############debug info for plotting strange position reports###############
|
||||
|
||||
else: #no LKP available
|
||||
#print "debug: icao %x not found. attempting local decode." % icao24
|
||||
[local_lat, local_lon] = cpr_resolve_local([my_lat, my_lon], [encoded_lat, encoded_lon], cpr_format, surface) #try local decoding
|
||||
# print "debug: local resolve gives %.6f, %.6f" % (local_lat, local_lon)
|
||||
[rnge, bearing] = range_bearing([my_lat, my_lon], [local_lat, local_lon])
|
||||
if rnge < validrange: #if the local decoding can be guaranteed valid
|
||||
#print "debug: range < 180nm, position valid."
|
||||
lkplist[icao24] = [local_lat, local_lon, time.time()] #update the local position for next time
|
||||
[decoded_lat, decoded_lon] = [local_lat, local_lon]
|
||||
else: #if the local decoding can't be guaranteed valid AND you couldn't find an LKP
|
||||
# print "debug: range > %inm, attempting global decode." % validrange
|
||||
#attempt global decode
|
||||
if (icao24 in evenlist) and (icao24 in oddlist):
|
||||
# print "debug: ICAOs found in both lists."
|
||||
if abs(evenlist[icao24][2] - oddlist[icao24][2]) < 10: #if there's less than 10 seconds of time difference between the reports
|
||||
# print "debug: valid even/odd positions, performing global decode."
|
||||
newer = (oddlist[icao24][2] - evenlist[icao24][2]) > 0 #figure out which report is newer
|
||||
[decoded_lat, decoded_lon] = cpr_resolve_global(evenlist[icao24][0:2], oddlist[icao24][0:2], newer, surface) #do a global decode
|
||||
if decoded_lat is not None:
|
||||
lkplist[icao24] = [decoded_lat, decoded_lon, time.time()]
|
||||
# else:
|
||||
# print "debug: timestamps not close enough to be valid."
|
||||
# else:
|
||||
# print "debug: even/odd information not found."
|
||||
|
||||
#print "settled on position: %.6f, %.6f" % (decoded_lat, decoded_lon,)
|
||||
if decoded_lat is not None:
|
||||
[rnge, bearing] = range_bearing([my_lat, my_lon], [decoded_lat, decoded_lon])
|
||||
else:
|
||||
rnge = None
|
||||
bearing = None
|
||||
|
||||
return [decoded_lat, decoded_lon, rnge, bearing]
|
||||
|
||||
|
||||
|
||||
def range_bearing(loc_a, loc_b):
|
||||
[a_lat, a_lon] = loc_a
|
||||
[b_lat, b_lon] = loc_b
|
||||
|
||||
esquared = (1/298.257223563)*(2-(1/298.257223563))
|
||||
earth_radius_mi = 3963.19059 * (math.pi / 180)
|
||||
|
||||
delta_lat = b_lat - a_lat
|
||||
delta_lon = b_lon - a_lon
|
||||
|
||||
avg_lat = (a_lat + b_lat) / 2.0
|
||||
|
||||
R1 = earth_radius_mi*(1.0-esquared)/pow((1.0-esquared*pow(math.sin(avg_lat),2)),1.5)
|
||||
|
||||
R2 = earth_radius_mi/math.sqrt(1.0-esquared*pow(math.sin(avg_lat),2))
|
||||
|
||||
distance_North = R1*delta_lat
|
||||
distance_East = R2*math.cos(avg_lat)*delta_lon
|
||||
|
||||
bearing = math.atan2(distance_East,distance_North) * (180.0 / math.pi)
|
||||
if bearing < 0.0:
|
||||
bearing += 360.0
|
||||
|
||||
rnge = math.hypot(distance_East,distance_North)
|
||||
|
||||
|
||||
return [rnge, bearing]
|
||||
Binary file not shown.
@@ -0,0 +1,247 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import time, os, sys
|
||||
from string import split, join
|
||||
from altitude import decode_alt
|
||||
from cpr import cpr_decode
|
||||
import math
|
||||
|
||||
def parse0(shortdata, parity, ecc):
|
||||
# shortdata = long(shortdata, 16)
|
||||
#parity = long(parity)
|
||||
|
||||
vs = bool(shortdata >> 26 & 0x1) #ground sensor -- airborne when 0
|
||||
cc = bool(shortdata >> 25 & 0x1) #crosslink capability, binary
|
||||
sl = shortdata >> 21 & 0x07 #operating sensitivity of onboard TCAS system. 0 means no TCAS sensitivity reported, 1-7 give TCAS sensitivity
|
||||
ri = shortdata >> 15 & 0x0F #speed coding: 0 = no onboard TCAS, 1 = NA, 2 = TCAS w/inhib res, 3 = TCAS w/vert only, 4 = TCAS w/vert+horiz, 5-7 = NA, 8 = no max A/S avail,
|
||||
#9 = A/S <= 75kt, 10 = A/S (75-150]kt, 11 = (150-300]kt, 12 = (300-600]kt, 13 = (600-1200]kt, 14 = >1200kt, 15 = NA
|
||||
|
||||
altitude = decode_alt(shortdata & 0x1FFF, True) #bit 13 is set for type 0
|
||||
|
||||
return [vs, cc, sl, ri, altitude]
|
||||
|
||||
def parse4(shortdata, parity, ecc):
|
||||
# shortdata = long(shortdata, 16)
|
||||
fs = shortdata >> 24 & 0x07 #flight status: 0 is airborne normal, 1 is ground normal, 2 is airborne alert, 3 is ground alert, 4 is alert SPI, 5 is normal SPI
|
||||
dr = shortdata >> 19 & 0x1F #downlink request: 0 means no req, bit 0 is Comm-B msg rdy bit, bit 1 is TCAS info msg rdy, bit 2 is Comm-B bcast #1 msg rdy, bit2+bit0 is Comm-B bcast #2 msg rdy,
|
||||
#bit2+bit1 is TCAS info and Comm-B bcast #1 msg rdy, bit2+bit1+bit0 is TCAS info and Comm-B bcast #2 msg rdy, 8-15 N/A, 16-31 req to send N-15 segments
|
||||
um = shortdata >> 13 & 0x3F #transponder status readouts, no decoding information available
|
||||
|
||||
altitude = decode_alt(shortdata & 0x1FFF, True)
|
||||
|
||||
return [fs, dr, um, altitude]
|
||||
|
||||
|
||||
|
||||
def parse5(shortdata, parity, ecc):
|
||||
# shortdata = long(shortdata, 16)
|
||||
fs = shortdata >> 24 & 0x07 #flight status: 0 is airborne normal, 1 is ground normal, 2 is airborne alert, 3 is ground alert, 4 is alert SPI, 5 is normal SPI
|
||||
dr = shortdata >> 19 & 0x1F #downlink request: 0 means no req, bit 0 is Comm-B msg rdy bit, bit 1 is TCAS info msg rdy, bit 2 is Comm-B bcast #1 msg rdy, bit2+bit0 is Comm-B bcast #2 msg rdy,
|
||||
#bit2+bit1 is TCAS info and Comm-B bcast #1 msg rdy, bit2+bit1+bit0 is TCAS info and Comm-B bcast #2 msg rdy, 8-15 N/A, 16-31 req to send N-15 segments
|
||||
um = shortdata >> 13 & 0x3F #transponder status readouts, no decoding information available
|
||||
|
||||
return [fs, dr, um]
|
||||
|
||||
def parse11(shortdata, parity, ecc):
|
||||
# shortdata = long(shortdata, 16)
|
||||
interrogator = ecc & 0x0F
|
||||
|
||||
ca = shortdata >> 13 & 0x3F #capability
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
|
||||
return [icao24, interrogator, ca]
|
||||
|
||||
#def parse17(shortdata, longdata, parity, ecc):
|
||||
# shortdata = long(shortdata, 16)
|
||||
# longdata = long(longdata, 16)
|
||||
# parity = long(parity, 16)
|
||||
# ecc = long(ecc, 16)
|
||||
|
||||
# subtype = (longdata >> 51) & 0x1F;
|
||||
|
||||
#the subtypes are:
|
||||
#0: No position information
|
||||
#1: Identification (Category set D)
|
||||
#2: Identification (Category set C)
|
||||
#3: "" (B)
|
||||
#4: "" (A)
|
||||
#5: Surface position accurate to 7.5m
|
||||
#6: "" to 25m
|
||||
#7: "" to 185.2m (0.1nm)
|
||||
#8: "" above 185.2m
|
||||
#9: Airborne position to 7.5m
|
||||
#10-18: Same with less accuracy
|
||||
#19: Airborne velocity
|
||||
#20: Airborne position w/GNSS height above earth
|
||||
#21: same to 25m
|
||||
#22: same above 25m
|
||||
#23: Reserved
|
||||
#24: Reserved for surface system status
|
||||
#25-27: Reserved
|
||||
#28: Extended squitter aircraft status
|
||||
#29: Current/next trajectory change point
|
||||
#30: Aircraft operational coordination
|
||||
#31: Aircraft operational status
|
||||
|
||||
|
||||
# if subtype == 4:
|
||||
# retstr = parseBDS08(shortdata, longdata, parity, ecc)
|
||||
# elif subtype >= 9 and subtype <= 18:
|
||||
# retstr = parseBDS05(shortdata, longdata, parity, ecc)
|
||||
# elif subtype == 19:
|
||||
# subsubtype = (longdata >> 48) & 0x07
|
||||
# if subsubtype == 0:
|
||||
# retstr = parseBDS09_0(shortdata, longdata, parity, ecc)
|
||||
# elif subsubtype == 1:
|
||||
# retstr = parseBDS09_1(shortdata, longdata, parity, ecc)
|
||||
# else:
|
||||
# retstr = "BDS09 subtype " + str(subsubtype) + " not implemented"
|
||||
# else:
|
||||
# retstr = "Type 17, subtype " + str(subtype) + " not implemented"
|
||||
|
||||
# return retstr
|
||||
|
||||
def parseBDS08(shortdata, longdata, parity, ecc):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
|
||||
msg = ""
|
||||
for i in range(0, 8):
|
||||
msg += charmap( longdata >> (42-6*i) & 0x3F)
|
||||
|
||||
#retstr = "Type 17 subtype 04 (ident) from " + "%x" % icao24 + " with data " + msg
|
||||
|
||||
return msg
|
||||
|
||||
def charmap(d):
|
||||
if d > 0 and d < 27:
|
||||
retval = chr(ord("A")+d-1)
|
||||
elif d == 32:
|
||||
retval = " "
|
||||
elif d > 47 and d < 58:
|
||||
retval = chr(ord("0")+d-48)
|
||||
else:
|
||||
retval = " "
|
||||
|
||||
return retval
|
||||
|
||||
|
||||
#lkplist is the last known position, for emitter-centered decoding. evenlist and oddlist are the last
|
||||
#received encoded position data for each reporting type. all dictionaries indexed by ICAO number.
|
||||
lkplist = {}
|
||||
evenlist = {}
|
||||
oddlist = {}
|
||||
evenlist_ground = {}
|
||||
oddlist_ground = {}
|
||||
|
||||
#the above dictionaries are all in the format [lat, lon, time].
|
||||
|
||||
def parseBDS05(shortdata, longdata, parity, ecc):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
|
||||
encoded_lon = longdata & 0x1FFFF
|
||||
encoded_lat = (longdata >> 17) & 0x1FFFF
|
||||
cpr_format = (longdata >> 34) & 1
|
||||
|
||||
enc_alt = (longdata >> 36) & 0x0FFF
|
||||
|
||||
altitude = decode_alt(enc_alt, False)
|
||||
|
||||
[decoded_lat, decoded_lon, rnge, bearing] = cpr_decode(icao24, encoded_lat, encoded_lon, cpr_format, evenlist, oddlist, lkplist, 0, longdata)
|
||||
|
||||
return [altitude, decoded_lat, decoded_lon, rnge, bearing]
|
||||
|
||||
|
||||
#welp turns out it looks like there's only 17 bits in the BDS0,6 ground packet after all. fuck.
|
||||
def parseBDS06(shortdata, longdata, parity, ecc):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
|
||||
encoded_lon = longdata & 0x1FFFF
|
||||
encoded_lat = (longdata >> 17) & 0x1FFFF
|
||||
cpr_format = (longdata >> 34) & 1
|
||||
|
||||
# enc_alt = (longdata >> 36) & 0x0FFF
|
||||
|
||||
altitude = 0
|
||||
|
||||
[decoded_lat, decoded_lon, rnge, bearing] = cpr_decode(icao24, encoded_lat, encoded_lon, cpr_format, evenlist_ground, oddlist_ground, lkplist, 1, longdata)
|
||||
|
||||
return [altitude, decoded_lat, decoded_lon, rnge, bearing]
|
||||
|
||||
|
||||
def parseBDS09_0(shortdata, longdata, parity, ecc):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
vert_spd = ((longdata >> 6) & 0x1FF) * 32
|
||||
ud = bool((longdata >> 15) & 1)
|
||||
if ud:
|
||||
vert_spd = 0 - vert_spd
|
||||
turn_rate = (longdata >> 16) & 0x3F
|
||||
turn_rate = turn_rate * 15/62
|
||||
rl = bool((longdata >> 22) & 1)
|
||||
if rl:
|
||||
turn_rate = 0 - turn_rate
|
||||
ns_vel = (longdata >> 23) & 0x7FF - 1
|
||||
ns = bool((longdata >> 34) & 1)
|
||||
ew_vel = (longdata >> 35) & 0x7FF - 1
|
||||
ew = bool((longdata >> 46) & 1)
|
||||
subtype = (longdata >> 48) & 0x07
|
||||
|
||||
velocity = math.hypot(ns_vel, ew_vel)
|
||||
if ew:
|
||||
ew_vel = 0 - ew_vel
|
||||
if ns:
|
||||
ns_vel = 0 - ns_vel
|
||||
heading = math.atan2(ew_vel, ns_vel) * (180.0 / math.pi)
|
||||
if heading < 0:
|
||||
heading += 360
|
||||
|
||||
#retstr = "Type 17 subtype 09-0 (track report) from " + "%x" % icao24 + " with velocity " + "%.0f" % velocity + "kt heading " + "%.0f" % heading + " VS " + "%.0f" % vert_spd
|
||||
|
||||
return [velocity, heading, vert_spd]
|
||||
|
||||
def parseBDS09_1(shortdata, longdata, parity, ecc):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
alt_geo_diff = longdata & 0x7F - 1
|
||||
above_below = bool((longdata >> 7) & 1)
|
||||
if above_below:
|
||||
alt_geo_diff = 0 - alt_geo_diff;
|
||||
vert_spd = float((longdata >> 10) & 0x1FF - 1)
|
||||
ud = bool((longdata >> 19) & 1)
|
||||
if ud:
|
||||
vert_spd = 0 - vert_spd
|
||||
vert_src = bool((longdata >> 20) & 1)
|
||||
ns_vel = float((longdata >> 21) & 0x3FF - 1)
|
||||
ns = bool((longdata >> 31) & 1)
|
||||
ew_vel = float((longdata >> 32) & 0x3FF - 1)
|
||||
ew = bool((longdata >> 42) & 1)
|
||||
subtype = (longdata >> 48) & 0x07
|
||||
|
||||
|
||||
if subtype == 0x02:
|
||||
ns_vel *= 4
|
||||
ew_vel *= 4
|
||||
|
||||
|
||||
vert_spd *= 64
|
||||
alt_geo_diff *= 25
|
||||
|
||||
velocity = math.hypot(ns_vel, ew_vel)
|
||||
if ew:
|
||||
ew_vel = 0 - ew_vel
|
||||
|
||||
if ns_vel == 0:
|
||||
heading = 0
|
||||
else:
|
||||
heading = math.atan(float(ew_vel) / float(ns_vel)) * (180.0 / math.pi)
|
||||
if ns:
|
||||
heading = 180 - heading
|
||||
if heading < 0:
|
||||
heading += 360
|
||||
|
||||
#retstr = "Type 17 subtype 09-1 (track report) from " + "%x" % icao24 + " with velocity " + "%.0f" % velocity + "kt heading " + "%.0f" % heading + " VS " + "%.0f" % vert_spd
|
||||
|
||||
return [velocity, heading, vert_spd]
|
||||
|
||||
def parse20(shortdata, longdata, parity, ecc):
|
||||
return "Message 20 not yet implemented"
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python
|
||||
import time, os, sys
|
||||
from string import split, join
|
||||
from modes_parse import *
|
||||
|
||||
def modes_print(message):
|
||||
|
||||
#a mode S parser for all message types. first, split the input into data fields
|
||||
[msgtype, shortdata, longdata, parity, ecc, reference] = message.split()
|
||||
|
||||
shortdata = long(shortdata, 16)
|
||||
longdata = long(longdata, 16)
|
||||
parity = long(parity, 16)
|
||||
ecc = long(ecc, 16)
|
||||
reference = float(reference)
|
||||
|
||||
msgtype = int(msgtype)
|
||||
|
||||
output = str("")
|
||||
|
||||
if msgtype == 0:
|
||||
output = print0(shortdata, parity, ecc)
|
||||
elif msgtype == 4:
|
||||
output = print4(shortdata, parity, ecc)
|
||||
elif msgtype == 5:
|
||||
output = print5(shortdata, parity, ecc)
|
||||
elif msgtype == 11:
|
||||
output = print11(shortdata, parity, ecc)
|
||||
elif msgtype == 17:
|
||||
output = print17(shortdata, longdata, parity, ecc)
|
||||
elif msgtype == 20:
|
||||
output = parse20(shortdata, longdata, parity, ecc)
|
||||
else:
|
||||
output = "No handler for message type " + str(msgtype) + " from " + str(ecc)
|
||||
|
||||
output = "(%.0f) " % float(reference) + output
|
||||
|
||||
return output
|
||||
|
||||
def print0(shortdata, parity, ecc):
|
||||
[vs, cc, sl, ri, altitude] = parse0(shortdata, parity, ecc)
|
||||
|
||||
retstr = "Type 0 (short A-A surveillance) from " + "%x" % ecc + " at " + str(altitude) + "ft"
|
||||
# the ri values below 9 are used for other things. might want to print those someday.
|
||||
if ri == 9:
|
||||
retstr = retstr + " (speed <75kt)"
|
||||
elif ri > 9:
|
||||
retstr = retstr + " (speed " + str(75 * (1 << (ri-10))) + "-" + str(75 * (1 << (ri-9))) + "kt)"
|
||||
|
||||
if vs is True:
|
||||
retstr = retstr + " (aircraft is on the ground)"
|
||||
|
||||
return retstr
|
||||
|
||||
def print4(shortdata, parity, ecc):
|
||||
|
||||
[fs, dr, um, altitude] = parse4(shortdata, parity, ecc)
|
||||
|
||||
retstr = "Type 4 (short surveillance altitude reply) from " + "%x" % ecc + " at " + str(altitude) + "ft"
|
||||
|
||||
if fs == 1:
|
||||
retstr = retstr + " (aircraft is on the ground)"
|
||||
elif fs == 2:
|
||||
retstr = retstr + " (AIRBORNE ALERT)"
|
||||
elif fs == 3:
|
||||
retstr = retstr + " (GROUND ALERT)"
|
||||
elif fs == 4:
|
||||
retstr = retstr + " (SPI ALERT)"
|
||||
elif fs == 5:
|
||||
retstr = retstr + " (SPI)"
|
||||
|
||||
return retstr
|
||||
|
||||
def print5(shortdata, parity, ecc):
|
||||
[fs, dr, um] = parse5(shortdata, parity, ecc)
|
||||
|
||||
retstr = "Type 5 (short surveillance ident reply) from " + "%x" % ecc + " with ident " + str(shortdata & 0x1FFF)
|
||||
|
||||
if fs == 1:
|
||||
retstr = retstr + " (aircraft is on the ground)"
|
||||
elif fs == 2:
|
||||
retstr = retstr + " (AIRBORNE ALERT)"
|
||||
elif fs == 3:
|
||||
retstr = retstr + " (GROUND ALERT)"
|
||||
elif fs == 4:
|
||||
retstr = retstr + " (SPI ALERT)"
|
||||
elif fs == 5:
|
||||
retstr = retstr + " (SPI)"
|
||||
|
||||
return retstr
|
||||
|
||||
def print11(shortdata, parity, ecc):
|
||||
[icao24, interrogator, ca] = parse11(shortdata, parity, ecc)
|
||||
|
||||
retstr = "Type 11 (all call reply) from " + "%x" % icao24 + " in reply to interrogator " + str(interrogator)
|
||||
return retstr
|
||||
|
||||
def print17(shortdata, longdata, parity, ecc):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
subtype = (longdata >> 51) & 0x1F;
|
||||
|
||||
if subtype == 4:
|
||||
msg = parseBDS08(shortdata, longdata, parity, ecc)
|
||||
retstr = "Type 17 subtype 04 (ident) from " + "%x" % icao24 + " with data " + msg
|
||||
|
||||
elif subtype >= 5 and subtype <= 8:
|
||||
[altitude, decoded_lat, decoded_lon, rnge, bearing] = parseBDS06(shortdata, longdata, parity, ecc)
|
||||
if decoded_lat==0: #no unambiguously valid position available
|
||||
retstr = ""
|
||||
else:
|
||||
#retstr = "INSERT INTO plane_positions (icao, seen, alt, lat, lon) VALUES ('" + "%x" % icao24 + "', now(), " + str(altitude) + ", " + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ")"
|
||||
retstr = "Type 17 subtype 06 (surface report) from " + "%x" % icao24 + " at (" + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ") (" + "%.2f" % rnge + " @ " + "%.0f" % bearing + ")"
|
||||
|
||||
elif subtype >= 9 and subtype <= 18:
|
||||
[altitude, decoded_lat, decoded_lon, rnge, bearing] = parseBDS05(shortdata, longdata, parity, ecc)
|
||||
|
||||
retstr = "Type 17 subtype 05 (position report) from " + "%x" % icao24 + " at (" + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ") (" + "%.2f" % rnge + " @ " + "%.0f" % bearing + ") at " + str(altitude) + "ft"
|
||||
|
||||
# this is a trigger to capture the bizarre BDS0,5 squitters you keep seeing on the map with latitudes all over the place
|
||||
# if icao24 == 0xa1ede9:
|
||||
# print "Buggy squitter with shortdata %s longdata %s parity %s ecc %s" % (str(shortdata), str(longdata), str(parity), str(ecc),)
|
||||
|
||||
elif subtype == 19:
|
||||
subsubtype = (longdata >> 48) & 0x07
|
||||
if subsubtype == 0:
|
||||
[velocity, heading, vert_spd] = parseBDS09_0(shortdata, longdata, parity, ecc)
|
||||
retstr = "Type 17 subtype 09-0 (track report) from " + "%x" % icao24 + " with velocity " + "%.0f" % velocity + "kt heading " + "%.0f" % heading + " VS " + "%.0f" % vert_spd
|
||||
|
||||
elif subsubtype == 1:
|
||||
[velocity, heading, vert_spd] = parseBDS09_1(shortdata, longdata, parity, ecc)
|
||||
retstr = "Type 17 subtype 09-1 (track report) from " + "%x" % icao24 + " with velocity " + "%.0f" % velocity + "kt heading " + "%.0f" % heading + " VS " + "%.0f" % vert_spd
|
||||
|
||||
else:
|
||||
retstr = "BDS09 subtype " + str(subsubtype) + " not implemented"
|
||||
else:
|
||||
retstr = "Type 17, subtype " + str(subtype) + " not implemented"
|
||||
|
||||
return retstr
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python
|
||||
import time, os, sys
|
||||
from string import split, join
|
||||
from modes_parse import *
|
||||
|
||||
def modes_sql(message):
|
||||
|
||||
#assembles a MySQLdb query tailored to Owen's database
|
||||
#this version ignores anything that isn't Type 17 for now, because we just don't care
|
||||
|
||||
[msgtype, shortdata, longdata, parity, ecc, reference] = message.split()
|
||||
|
||||
shortdata = long(shortdata, 16)
|
||||
longdata = long(longdata, 16)
|
||||
parity = long(parity, 16)
|
||||
ecc = long(ecc, 16)
|
||||
# reference = float(reference)
|
||||
|
||||
msgtype = int(msgtype)
|
||||
|
||||
query = None
|
||||
|
||||
# if msgtype == 0:
|
||||
# query = sql0(shortdata, parity, ecc)
|
||||
# elif msgtype == 4:
|
||||
# query = sql4(shortdata, parity, ecc)
|
||||
# elif msgtype == 5:
|
||||
# query = sql5(shortdata, parity, ecc)
|
||||
# elif msgtype == 11:
|
||||
# query = sql11(shortdata, parity, ecc)
|
||||
# elif msgtype == 17:
|
||||
if msgtype == 17:
|
||||
query = sql17(shortdata, longdata, parity, ecc)
|
||||
# elif msgtype == 20:
|
||||
# output = parse20(shortdata, longdata, parity, ecc)
|
||||
# else:
|
||||
#output = "No handler for message type " + str(msgtype) + " from " + str(ecc)
|
||||
|
||||
# output = "(%.0f) " % float(reference) + output
|
||||
|
||||
return query
|
||||
|
||||
def sql17(shortdata, longdata, parity, ecc):
|
||||
icao24 = shortdata & 0xFFFFFF
|
||||
subtype = (longdata >> 51) & 0x1F
|
||||
|
||||
retstr = None
|
||||
|
||||
if subtype == 4:
|
||||
msg = parseBDS08(shortdata, longdata, parity, ecc)
|
||||
retstr = "INSERT INTO plane_metadata (icao, ident) VALUES ('" + "%x" % icao24 + "', '" + msg + "') ON DUPLICATE KEY UPDATE seen=now(), ident=values(ident)"
|
||||
|
||||
elif subtype >= 5 and subtype <= 8:
|
||||
[altitude, decoded_lat, decoded_lon, rnge, bearing] = parseBDS06(shortdata, longdata, parity, ecc)
|
||||
if decoded_lat is None: #no unambiguously valid position available
|
||||
retstr = None
|
||||
else:
|
||||
retstr = "INSERT INTO plane_positions (icao, seen, alt, lat, lon) VALUES ('" + "%x" % icao24 + "', now(), " + str(altitude) + ", " + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ")"
|
||||
|
||||
elif subtype >= 9 and subtype <= 18 and subtype != 15: #i'm eliminating type 15 records because they don't appear to be valid position reports.
|
||||
[altitude, decoded_lat, decoded_lon, rnge, bearing] = parseBDS05(shortdata, longdata, parity, ecc)
|
||||
if decoded_lat is None: #no unambiguously valid position available
|
||||
retstr = None
|
||||
else:
|
||||
retstr = "INSERT INTO plane_positions (icao, seen, alt, lat, lon) VALUES ('" + "%x" % icao24 + "', now(), " + str(altitude) + ", " + "%.6f" % decoded_lat + ", " + "%.6f" % decoded_lon + ")"
|
||||
|
||||
elif subtype == 19:
|
||||
subsubtype = (longdata >> 48) & 0x07
|
||||
if subsubtype == 0:
|
||||
[velocity, heading, vert_spd] = parseBDS09_0(shortdata, longdata, parity, ecc)
|
||||
retstr = "INSERT INTO plane_metadata (icao, seen, speed, heading, vertical) VALUES ('" + "%x" % icao24 + "', now(), " + "%.0f" % velocity + ", " + "%.0f" % heading + ", " + "%.0f" % vert_spd + ") ON DUPLICATE KEY UPDATE seen=now(), speed=values(speed), heading=values(heading), vertical=values(vertical)"
|
||||
|
||||
elif subsubtype == 1:
|
||||
[velocity, heading, vert_spd] = parseBDS09_1(shortdata, longdata, parity, ecc)
|
||||
retstr = "INSERT INTO plane_metadata (icao, seen, speed, heading, vertical) VALUES ('" + "%x" % icao24 + "', now(), " + "%.0f" % velocity + ", " + "%.0f" % heading + ", " + "%.0f" % vert_spd + ") ON DUPLICATE KEY UPDATE seen=now(), speed=values(speed), heading=values(heading), vertical=values(vertical)"
|
||||
|
||||
else:
|
||||
print "debug (modes_sql): unknown subtype %i with data %x %x %x" % (subtype, shortdata, longdata, parity,)
|
||||
|
||||
|
||||
return retstr
|
||||
|
||||
|
||||
|
||||
|
||||
Executable
+183
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from gnuradio import gr, gru, usrp, optfir, eng_notation, blks2, air
|
||||
from gnuradio.eng_option import eng_option
|
||||
from optparse import OptionParser
|
||||
import time, os, sys
|
||||
from string import split, join
|
||||
from usrpm import usrp_dbid
|
||||
from modes_print import modes_print
|
||||
from modes_sql import modes_sql
|
||||
import gnuradio.gr.gr_threading as _threading
|
||||
import MySQLdb
|
||||
|
||||
|
||||
class top_block_runner(_threading.Thread):
|
||||
def __init__(self, tb):
|
||||
_threading.Thread.__init__(self)
|
||||
self.setDaemon(1)
|
||||
self.tb = tb
|
||||
self.done = False
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
self.tb.run()
|
||||
self.done = True
|
||||
|
||||
|
||||
|
||||
def pick_subdevice(u):
|
||||
#this should pick which USRP subdevice if none was specified on the command line
|
||||
#since the only thing that will receive ADS-B appears to be the DBSRX, we'll default to that
|
||||
|
||||
return usrp.pick_subdev(u, (usrp_dbid.DBS_RX,
|
||||
usrp_dbid.DBS_RX_REV_2_1,
|
||||
usrp_dbid.BASIC_RX))
|
||||
"""
|
||||
|
||||
The following are optional command line parameters:
|
||||
|
||||
-R SUBDEV Daughter board specification, defaults to first found
|
||||
-f FREQ USRP receive frequency (1090 MHz Default)
|
||||
-g GAIN Daughterboard gain setting. Defaults to mid-range.
|
||||
-d DECIM USRP decimation rate
|
||||
-t THRESH Receiver valid pulse threshold
|
||||
-a Output all frames. Defaults only output frames
|
||||
|
||||
Once the program is running, ctrl-break (Ctrl-C) stops operation.
|
||||
"""
|
||||
|
||||
class adsb_rx_block (gr.top_block):
|
||||
|
||||
def __init__(self, options, args, queue):
|
||||
gr.top_block.__init__(self)
|
||||
|
||||
self.options = options
|
||||
self.args = args
|
||||
|
||||
if options.filename is None:
|
||||
if options.decim < 8:
|
||||
self.fpga_filename="std_4rx_0tx.rbf" #can go down to decim 4
|
||||
self.u = usrp.source_c(fpga_filename=self.fpga_filename)
|
||||
else :
|
||||
self.u = usrp.source_c()
|
||||
|
||||
if options.rx_subdev_spec is None:
|
||||
options.rx_subdev_spec = pick_subdevice(self.u)
|
||||
|
||||
self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec))
|
||||
self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec)
|
||||
print "Using RX d'board %s" % (self.subdev.side_and_name(),)
|
||||
self.u.set_decim_rate(options.decim)
|
||||
|
||||
if options.gain is None: #set to halfway
|
||||
g = self.subdev.gain_range()
|
||||
options.gain = (g[0]+g[1]) / 2.0
|
||||
|
||||
if not(self.tune(options.freq)):
|
||||
print "Failed to set initial frequency"
|
||||
|
||||
print "Setting gain to %i" % (options.gain,)
|
||||
self.subdev.set_gain(options.gain)
|
||||
self.subdev.set_bw(self.options.bandwidth) #only for DBSRX
|
||||
|
||||
rate = self.u.adc_rate() / options.decim
|
||||
|
||||
else:
|
||||
rate = int(64e6 / options.decim)
|
||||
self.u = gr.file_source(gr.sizeof_gr_complex, options.filename)
|
||||
|
||||
print "Rate is %i" % (rate,)
|
||||
pass_all = 0
|
||||
if options.output_all :
|
||||
pass_all = 1
|
||||
|
||||
|
||||
|
||||
self.demod = gr.complex_to_mag()
|
||||
self.avg = gr.moving_average_ff(100, 1.0/100, 400);
|
||||
self.preamble = air.modes_preamble(rate, options.threshold)
|
||||
self.framer = air.modes_framer(rate)
|
||||
self.slicer = air.modes_slicer(rate, queue)
|
||||
|
||||
if options.decim < 16:
|
||||
#there's a really nasty spur at 1088 caused by a multiple of the USRP xtal. if you use a decimation of 16, it gets filtered out by the CIC. if not, it really fucks with you unless you filter it out.
|
||||
filter_coeffs = gr.firdes.band_reject(1.0, rate, 1.7e6, 2.3e6, 0.5e6, gr.firdes.WIN_HAMMING)
|
||||
self.filt = gr.fir_filter_ccf(1, filter_coeffs)
|
||||
self.connect(self.u, self.filt)
|
||||
else:
|
||||
self.filt = self.u
|
||||
|
||||
self.connect(self.filt, self.demod)
|
||||
self.connect(self.demod, self.avg)
|
||||
self.connect(self.demod, (self.preamble, 0))
|
||||
self.connect(self.avg, (self.preamble, 1))
|
||||
self.connect(self.demod, (self.framer, 0))
|
||||
self.connect(self.preamble, (self.framer, 1))
|
||||
self.connect(self.demod, (self.slicer, 0))
|
||||
self.connect(self.framer, (self.slicer, 1))
|
||||
|
||||
def tune(self, freq):
|
||||
result = usrp.tune(self.u, 0, self.subdev, freq)
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
usage = "%prog: [options] output filename"
|
||||
parser = OptionParser(option_class=eng_option, usage=usage)
|
||||
parser.add_option("-R", "--rx-subdev-spec", type="subdev",
|
||||
help="select USRP Rx side A or B", metavar="SUBDEV")
|
||||
parser.add_option("-f", "--freq", type="eng_float", default=1090e6,
|
||||
help="set receive frequency in Hz [default=%default]", metavar="FREQ")
|
||||
parser.add_option("-g", "--gain", type="int", default=None,
|
||||
help="set RF gain", metavar="dB")
|
||||
parser.add_option("-d", "--decim", type="int", default=16,
|
||||
help="set fgpa decimation rate [default=%default]")
|
||||
parser.add_option("-T", "--threshold", type="eng_float", default=3.0,
|
||||
help="set pulse detection threshold above noise in dB [default=%default]")
|
||||
parser.add_option("-a","--output-all", action="store_true", default=False,
|
||||
help="output all frames")
|
||||
parser.add_option("-b","--bandwidth", type="eng_float", default=5e6,
|
||||
help="set DBSRX front-end bandwidth in Hz [default=5e6]")
|
||||
parser.add_option("-F","--filename", type="string", default=None,
|
||||
help="read data from file instead of USRP")
|
||||
parser.add_option("-D","--database", action="store_true", default=False,
|
||||
help="send to database instead of printing to screen")
|
||||
(options, args) = parser.parse_args()
|
||||
# if len(args) != 1:
|
||||
# parser.print_help()
|
||||
# sys.exit(1)
|
||||
|
||||
# filename = args[0]
|
||||
|
||||
queue = gr.msg_queue()
|
||||
|
||||
if options.database is True:
|
||||
db = MySQLdb.connect(host="localhost", user="planes", passwd="planes", db="planes")
|
||||
|
||||
fg = adsb_rx_block(options, args, queue)
|
||||
runner = top_block_runner(fg)
|
||||
|
||||
while 1:
|
||||
try:
|
||||
if queue.empty_p() == 0 :
|
||||
while queue.empty_p() == 0 :
|
||||
msg = queue.delete_head() #blocking read
|
||||
if options.database is False:
|
||||
print modes_print(msg.to_string())
|
||||
else:
|
||||
query = modes_sql(msg.to_string())
|
||||
if query is not None:
|
||||
c = db.cursor()
|
||||
c.execute(query)
|
||||
|
||||
elif runner.done:
|
||||
break
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
fg.stop()
|
||||
runner = None
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user