Initial commit

This commit is contained in:
Nick Foster
2010-09-14 22:01:56 -07:00
commit e39021ff8a
189 changed files with 21343 additions and 0 deletions
+22
View File
@@ -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
+84
View File
@@ -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)
+52
View File
@@ -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);
};
// ----------------------------------------------------------------
+92
View File
@@ -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;
}
+33
View File
@@ -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 */
+147
View File
@@ -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;
}
+35
View File
@@ -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 */
+220
View File
@@ -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;
}
+35
View File
@@ -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 */
+19
View File
@@ -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
+30
View File
@@ -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;
}
+4
View File
@@ -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);
+256
View File
@@ -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;
}
+8
View File
@@ -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
+32
View File
@@ -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
+71
View File
@@ -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.
+38
View File
@@ -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,)
+246
View File
@@ -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.
+247
View File
@@ -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.
+142
View File
@@ -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
+85
View File
@@ -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
+183
View File
@@ -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