Merge branch 'master' of github.com:drakeapps/dump1090

This commit is contained in:
James Wilson 2017-02-22 12:18:05 -06:00
commit 9621cadbd3
25 changed files with 2091 additions and 613 deletions

View file

@ -1,13 +1,11 @@
PROGNAME=dump1090
RTLSDR ?= yes
BLADERF ?= yes
CC=gcc
CPPFLAGS += -DMODES_DUMP1090_VERSION=\"$(DUMP1090_VERSION)\" -DMODES_DUMP1090_VARIANT=\"dump1090-fa\"
ifneq ($(RTLSDR_PREFIX),"")
CPPFLAGS += -I$(RTLSDR_PREFIX)/include
LDFLAGS += -L$(RTLSDR_PREFIX)/lib
endif
ifneq ($(HTMLPATH),"")
CPPFLAGS += -DHTMLPATH=\"$(HTMLPATH)\"
endif
@ -16,10 +14,30 @@ DIALECT = -std=c11
CFLAGS += $(DIALECT) -O2 -g -Wall -Werror -W -D_DEFAULT_SOURCE
LIBS = -lpthread -lm -lrt
ifeq ($(STATIC), yes)
LIBS_RTLSDR = -Wl,-Bstatic -lrtlsdr -Wl,-Bdynamic -lusb-1.0
else
LIBS_RTLSDR = -lrtlsdr -lusb-1.0
ifeq ($(RTLSDR), yes)
SDR_OBJ += sdr_rtlsdr.o
CPPFLAGS += -DENABLE_RTLSDR
ifdef RTLSDR_PREFIX
CPPFLAGS += -I$(RTLSDR_PREFIX)/include
LDFLAGS += -L$(RTLSDR_PREFIX)/lib
else
CFLAGS += $(shell pkg-config --cflags librtlsdr)
LDFLAGS += $(shell pkg-config --libs-only-L librtlsdr)
endif
ifeq ($(STATIC), yes)
LIBS_SDR += -Wl,-Bstatic -lrtlsdr -Wl,-Bdynamic -lusb-1.0
else
LIBS_SDR += -lrtlsdr -lusb-1.0
endif
endif
ifeq ($(BLADERF), yes)
SDR_OBJ += sdr_bladerf.o
CPPFLAGS += -DENABLE_BLADERF
CFLAGS += $(shell pkg-config --cflags libbladeRF)
LIBS_SDR += $(shell pkg-config --libs libbladeRF)
endif
all: dump1090 view1090
@ -27,8 +45,8 @@ all: dump1090 view1090
%.o: %.c *.h
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demod_2400.o stats.o cpr.o icao_filter.o track.o util.o convert.o $(COMPAT)
$(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_RTLSDR) -lncurses
dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demod_2400.o stats.o cpr.o icao_filter.o track.o util.o convert.o sdr_ifile.o sdr.o $(SDR_OBJ) $(COMPAT)
$(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_SDR) -lncurses
view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o $(COMPAT)
$(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) -lncurses
@ -37,7 +55,7 @@ faup1090: faup1090.o anet.o mode_ac.o mode_s.o net_io.o crc.o stats.o cpr.o icao
$(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS)
clean:
rm -f *.o compat/clock_gettime/*.o compat/clock_nanosleep/*.o dump1090 view1090 faup1090 cprtests crctests
rm -f *.o compat/clock_gettime/*.o compat/clock_nanosleep/*.o dump1090 view1090 faup1090 cprtests crctests convert_benchmark
test: cprtests
./cprtests
@ -47,3 +65,9 @@ cprtests: cpr.o cprtests.o
crctests: crc.c crc.h
$(CC) $(CPPFLAGS) $(CFLAGS) -g -DCRCDEBUG -o $@ $<
benchmarks: convert_benchmark
./convert_benchmark
convert_benchmark: convert_benchmark.o convert.o util.o
$(CC) $(CPPFLAGS) $(CFLAGS) -g -o $@ $^ -lm

View file

@ -8,10 +8,43 @@ It is designed to build as a Debian package.
## Building under jessie
### Dependencies - bladeRF
You will need a build of libbladeRF. You can build packages from source:
$ git clone https://github.com/Nuand/bladeRF.git
$ cd bladeRF
$ dpkg-buildpackage -b
Or Nuand has some build/install instructions including an Ubuntu PPA
at https://github.com/Nuand/bladeRF/wiki/Getting-Started:-Linux
Or FlightAware provides armhf packages as part of the piaware repository;
see https://flightaware.com/adsb/piaware/install
### Dependencies - rtlsdr
This is packaged with jessie. "sudo apt-get install librtlsdr-dev"
### Actually building it
Nothing special, just build it ("dpkg-buildpackage -b")
## Building under wheezy
First run "prepare-wheezy-tree.sh". This will create a package tre in
First run "prepare-wheezy-tree.sh". This will create a package tree in
package-wheezy/. Build in there ("dpkg-buildpackage -b")
The wheezy build does not include bladeRF support.
## Building manually
You can probably just run "make" after installing the required dependencies.
Binaries are built in the source directory; you will need to arrange to
install them (and a method for starting them) yourself.
"make BLADERF=no" will disable bladeRF support and remove the dependency on
libbladeRF.
"make RTLSDR=no" will disable rtl-sdr support and remove the dependency on
librtlsdr.

43
bladerf/README Normal file
View file

@ -0,0 +1,43 @@
bladeRF support
---------------
There is basic support for the bladeRF included in dump1090-fa.
It uses the same demodulator as the regular rtlsdr version and it does not
take advantage of the bladeRF's improved sampling rate or FPGA (but see below)
To use it:
$ dump1090-fa --device-type bladerf --gain 66 (.. other options ..)
The bladeRF has sensitivity problems when receiving ADS-B and will benefit
from all the gain you can give it. 66dB is the maximum configurable gain on
the board itself, you may want to add an external LNA too.
bladeRF custom FPGA bitstream
-----------------------------
The package includes a custom FPGA bitstream for the bladeRF which can
improve ADS-B reception with dump1090.
It adds a Fs/4 mixer step to avoid the DC offset of the bladeRF, and
decimate-by-8 downsampling step to improve sensitivity. Some of the bladeRF
parameters need to be adjusted so that it produces correctly tuned/downsampled
data in the form that dump1090 is expecting.
To use it:
$ dump1090-fa --device-type bladerf --gain 66 \
--bladerf-fpga /usr/share/dump1090-fa/bladerf/decimate8-x40.rbf \
--bladerf-decimation 8 \
--bladerf-bandwidth 14000000 \
--freq 1085200000 \
(other options ...)
The bladeRF will be configured for a 19.2MHz sampling rate and will tune to
1085.2MHz so that the 1090MHz signal appears around 4.8MHz in the baseband
samples. The FPGA Fs/4 mixing step shifts the baseband signal so that it is centered
around 0, and the FPGA downsampling step produces samples at 2.4MHz, which is what
dump1090 is expecting.
This FPGA bitstream is built for the 40kLE bladeRF.

BIN
bladerf/decimate8-x40.rbf Normal file

Binary file not shown.

296
convert.c
View file

@ -26,6 +26,36 @@ struct converter_state {
float z1_Q;
};
static uint16_t *uc8_lookup;
static bool init_uc8_lookup()
{
if (uc8_lookup)
return true;
uc8_lookup = malloc(sizeof(uint16_t) * 256 * 256);
if (!uc8_lookup) {
fprintf(stderr, "can't allocate UC8 conversion lookup table\n");
return false;
}
for (int i = 0; i <= 255; i++) {
for (int q = 0; q <= 255; q++) {
float fI, fQ, magsq;
fI = (i - 127.5) / 127.5;
fQ = (q - 127.5) / 127.5;
magsq = fI * fI + fQ * fQ;
if (magsq > 1)
magsq = 1;
float mag = sqrtf(magsq);
uc8_lookup[le16toh((i*256)+q)] = (uint16_t) (mag * 65535.0f + 0.5f);
}
}
return true;
}
static void convert_uc8_nodc(void *iq_data,
uint16_t *mag_data,
unsigned nsamples,
@ -42,55 +72,33 @@ static void convert_uc8_nodc(void *iq_data,
MODES_NOTUSED(state);
// unroll this a bit
#define DO_ONE_SAMPLE \
do { \
mag = uc8_lookup[*in++]; \
*mag_data++ = mag; \
sum_level += mag; \
sum_power += (uint32_t)mag * (uint32_t)mag; \
} while(0)
// unroll this a bit
for (i = 0; i < (nsamples>>3); ++i) {
mag = Modes.maglut[*in++];
*mag_data++ = mag;
sum_level += mag;
sum_power += (uint32_t)mag * (uint32_t)mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
sum_level += mag;
sum_power += (uint32_t)mag * (uint32_t)mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
sum_level += mag;
sum_power += (uint32_t)mag * (uint32_t)mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
sum_level += mag;
sum_power += (uint32_t)mag * (uint32_t)mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
sum_level += mag;
sum_power += (uint32_t)mag * (uint32_t)mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
sum_level += mag;
sum_power += (uint32_t)mag * (uint32_t)mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
sum_level += mag;
sum_power += (uint32_t)mag * (uint32_t)mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
sum_level += mag;
sum_power += (uint32_t)mag * (uint32_t)mag;
DO_ONE_SAMPLE;
DO_ONE_SAMPLE;
DO_ONE_SAMPLE;
DO_ONE_SAMPLE;
DO_ONE_SAMPLE;
DO_ONE_SAMPLE;
DO_ONE_SAMPLE;
DO_ONE_SAMPLE;
}
for (i = 0; i < (nsamples&7); ++i) {
mag = Modes.maglut[*in++];
*mag_data++ = mag;
sum_level += mag;
sum_power += (uint32_t)mag * (uint32_t)mag;
DO_ONE_SAMPLE;
}
#undef DO_ONE_SAMPLE
if (out_mean_level) {
*out_mean_level = sum_level / 65536.0 / nsamples;
}
@ -121,8 +129,8 @@ static void convert_uc8_generic(void *iq_data,
for (i = 0; i < nsamples; ++i) {
I = *in++;
Q = *in++;
fI = (I - 127.5) / 127.5;
fQ = (Q - 127.5) / 127.5;
fI = (I - 127.5f) / 127.5f;
fQ = (Q - 127.5f) / 127.5f;
// DC block
z1_I = fI * dc_a + z1_I * dc_b;
@ -137,7 +145,7 @@ static void convert_uc8_generic(void *iq_data,
float mag = sqrtf(magsq);
sum_power += magsq;
sum_level += mag;
*mag_data++ = (uint16_t)(mag * 65535.0 + 0.5);
*mag_data++ = (uint16_t)(mag * 65535.0f + 0.5f);
}
state->z1_I = z1_I;
@ -173,8 +181,8 @@ static void convert_sc16_generic(void *iq_data,
for (i = 0; i < nsamples; ++i) {
I = (int16_t)le16toh(*in++);
Q = (int16_t)le16toh(*in++);
fI = I / 32768.0;
fQ = Q / 32768.0;
fI = I / 32768.0f;
fQ = Q / 32768.0f;
// DC block
z1_I = fI * dc_a + z1_I * dc_b;
@ -189,7 +197,7 @@ static void convert_sc16_generic(void *iq_data,
float mag = sqrtf(magsq);
sum_power += magsq;
sum_level += mag;
*mag_data++ = (uint16_t)(mag * 65535.0 + 0.5);
*mag_data++ = (uint16_t)(mag * 65535.0f + 0.5f);
}
state->z1_I = z1_I;
@ -204,6 +212,168 @@ static void convert_sc16_generic(void *iq_data,
}
}
static void convert_sc16_nodc(void *iq_data,
uint16_t *mag_data,
unsigned nsamples,
struct converter_state *state,
double *out_mean_level,
double *out_mean_power)
{
MODES_NOTUSED(state);
uint16_t *in = iq_data;
unsigned i;
int16_t I, Q;
float fI, fQ, magsq;
float sum_level = 0, sum_power = 0;
for (i = 0; i < nsamples; ++i) {
I = (int16_t)le16toh(*in++);
Q = (int16_t)le16toh(*in++);
fI = I / 32768.0f;
fQ = Q / 32768.0f;
magsq = fI * fI + fQ * fQ;
if (magsq > 1)
magsq = 1;
float mag = sqrtf(magsq);
sum_power += magsq;
sum_level += mag;
*mag_data++ = (uint16_t)(mag * 65535.0f + 0.5f);
}
if (out_mean_level) {
*out_mean_level = sum_level / nsamples;
}
if (out_mean_power) {
*out_mean_power = sum_power / nsamples;
}
}
// SC16Q11_TABLE_BITS controls the size of the lookup table
// for SC16Q11 data. The size of the table is 2 * (1 << (2*BITS))
// bytes. Reducing the number of bits reduces precision but
// can run substantially faster by staying in cache.
// See convert_benchmark.c for some numbers.
// Leaving SC16QQ_TABLE_BITS undefined will disable the table lookup and always use
// the floating-point path, which may be faster on some systems
#if defined(SC16Q11_TABLE_BITS)
#define USE_BITS SC16Q11_TABLE_BITS
#define LOSE_BITS (11 - SC16Q11_TABLE_BITS)
static uint16_t *sc16q11_lookup;
static bool init_sc16q11_lookup()
{
if (sc16q11_lookup)
return true;
sc16q11_lookup = malloc(sizeof(uint16_t) * (1 << (USE_BITS * 2)));
if (!sc16q11_lookup) {
fprintf(stderr, "can't allocate SC16Q11 conversion lookup table\n");
return false;
}
for (int i = 0; i < 2048; i += (1 << LOSE_BITS)) {
for (int q = 0; q < 2048; q += (1 << LOSE_BITS)) {
float fI = i / 2048.0, fQ = q / 2048.0;
float magsq = fI * fI + fQ * fQ;
if (magsq > 1)
magsq = 1;
float mag = sqrtf(magsq);
unsigned index = ((i >> LOSE_BITS) << USE_BITS) | (q >> LOSE_BITS);
sc16q11_lookup[index] = (uint16_t)(mag * 65535.0f + 0.5f);
}
}
return true;
}
static void convert_sc16q11_table(void *iq_data,
uint16_t *mag_data,
unsigned nsamples,
struct converter_state *state,
double *out_mean_level,
double *out_mean_power)
{
uint16_t *in = iq_data;
unsigned i;
uint16_t I, Q;
uint64_t sum_level = 0;
uint64_t sum_power = 0;
uint16_t mag;
MODES_NOTUSED(state);
for (i = 0; i < nsamples; ++i) {
I = abs((int16_t)le16toh(*in++)) & 2047;
Q = abs((int16_t)le16toh(*in++)) & 2047;
mag = sc16q11_lookup[((I >> LOSE_BITS) << USE_BITS) | (Q >> LOSE_BITS)];
*mag_data++ = mag;
sum_level += mag;
sum_power += (uint32_t)mag * (uint32_t)mag;
}
if (out_mean_level) {
*out_mean_level = sum_level / 65536.0 / nsamples;
}
if (out_mean_power) {
*out_mean_power = sum_power / 65535.0 / 65535.0 / nsamples;
}
}
#else /* ! defined(SC16Q11_TABLE_BITS) */
static void convert_sc16q11_nodc(void *iq_data,
uint16_t *mag_data,
unsigned nsamples,
struct converter_state *state,
double *out_mean_level,
double *out_mean_power)
{
MODES_NOTUSED(state);
uint16_t *in = iq_data;
unsigned i;
int16_t I, Q;
float fI, fQ, magsq;
float sum_level = 0, sum_power = 0;
for (i = 0; i < nsamples; ++i) {
I = (int16_t)le16toh(*in++);
Q = (int16_t)le16toh(*in++);
fI = I / 2048.0f;
fQ = Q / 2048.0f;
magsq = fI * fI + fQ * fQ;
if (magsq > 1)
magsq = 1;
float mag = sqrtf(magsq);
sum_power += magsq;
sum_level += mag;
*mag_data++ = (uint16_t)(mag * 65535.0f + 0.5f);
}
if (out_mean_level) {
*out_mean_level = sum_level / nsamples;
}
if (out_mean_power) {
*out_mean_power = sum_power / nsamples;
}
}
#endif /* defined(SC16Q11_TABLE_BITS) */
static void convert_sc16q11_generic(void *iq_data,
uint16_t *mag_data,
unsigned nsamples,
@ -225,8 +395,8 @@ static void convert_sc16q11_generic(void *iq_data,
for (i = 0; i < nsamples; ++i) {
I = (int16_t)le16toh(*in++);
Q = (int16_t)le16toh(*in++);
fI = I / 2048.0;
fQ = Q / 2048.0;
fI = I / 2048.0f;
fQ = Q / 2048.0f;
// DC block
z1_I = fI * dc_a + z1_I * dc_b;
@ -241,7 +411,7 @@ static void convert_sc16q11_generic(void *iq_data,
float mag = sqrtf(magsq);
sum_power += magsq;
sum_level += mag;
*mag_data++ = (uint16_t)(sqrtf(magsq) * 65535.0 + 0.5);
*mag_data++ = (uint16_t)(mag * 65535.0f + 0.5f);
}
state->z1_I = z1_I;
@ -261,13 +431,20 @@ static struct {
int can_filter_dc;
iq_convert_fn fn;
const char *description;
bool (*init)();
} converters_table[] = {
// In order of preference
{ INPUT_UC8, 0, convert_uc8_nodc, "UC8, integer/table path" },
{ INPUT_UC8, 1, convert_uc8_generic, "UC8, float path" },
{ INPUT_SC16, 1, convert_sc16_generic, "SC16, float path" },
{ INPUT_SC16Q11, 1, convert_sc16q11_generic, "SC16Q11, float path" },
{ 0, 0, NULL, NULL }
{ INPUT_UC8, 0, convert_uc8_nodc, "UC8, integer/table path", init_uc8_lookup },
{ INPUT_UC8, 1, convert_uc8_generic, "UC8, float path", NULL },
{ INPUT_SC16, 0, convert_sc16_nodc, "SC16, float path, no DC", NULL },
{ INPUT_SC16, 1, convert_sc16_generic, "SC16, float path", NULL },
#if defined(SC16Q11_TABLE_BITS)
{ INPUT_SC16Q11, 0, convert_sc16q11_table, "SC16Q11, integer/table path", init_sc16q11_lookup },
#else
{ INPUT_SC16Q11, 0, convert_sc16q11_nodc, "SC16Q11, float path, no DC", NULL },
#endif
{ INPUT_SC16Q11, 1, convert_sc16q11_generic, "SC16Q11, float path", NULL },
{ 0, 0, NULL, NULL, NULL }
};
iq_convert_fn init_converter(input_format_t format,
@ -291,7 +468,10 @@ iq_convert_fn init_converter(input_format_t format,
return NULL;
}
fprintf(stderr, "Using sample converter: %s\n", converters_table[i].description);
if (converters_table[i].init) {
if (!converters_table[i].init())
return NULL;
}
*out_state = malloc(sizeof(struct converter_state));
if (! *out_state) {

148
convert_benchmark.c Normal file
View file

@ -0,0 +1,148 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// convert_benchmark.c: benchmarks for IQ sample converters
//
// Copyright (c) 2016-2017 Oliver Jowett <oliver@mutability.co.uk>
// Copyright (c) 2017 FlightAware LLC
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
//
// This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
#include "dump1090.h"
static void **testdata_uc8;
static void **testdata_sc16;
static void **testdata_sc16q11;
static uint16_t *outdata;
// SC16Q11_TABLE_BITS notes:
// 11 bits (8MB) gives you full precision, but a large table that doesn't fit in cache
// 9 bits (512kB) will fit in the Pi 2/3's shared L2 cache
// (but there will be contention from other cores)
// 8 bits (128kB) will fit in the Pi 1's L2 cache
// 7 bits (32kB) will fit in the Pi 1/2/3's L1 cache
// Sample results for "SC16Q11, no DC":
// Core i7-3610QM @ 2300MHz
// SC16Q11_TABLE_BITS undefined: 152.80M samples/second
// SC16Q11_TABLE_BITS=11: 101.22M samples/second
// SC16Q11_TABLE_BITS=9: 243.04M samples/second
// SC16Q11_TABLE_BITS=8: 316.84M samples/second
// SC16Q11_TABLE_BITS=7: 375.70M samples/second
// Pi3B @ 1200MHz
// SC16Q11_TABLE_BITS undefined: 22.19M samples/second
// SC16Q11_TABLE_BITS=11: 5.86M samples/second
// SC16Q11_TABLE_BITS=9: 19.33M samples/second
// SC16Q11_TABLE_BITS=8: 33.50M samples/second
// SC16Q11_TABLE_BITS=7: 59.78M samples/second
// Pi1B @ 700MHz
// SC16Q11_TABLE_BITS undefined: 5.24M samples/second
// SC16Q11_TABLE_BITS=11: 2.53M samples/second
// SC16Q11_TABLE_BITS=9: 3.23M samples/second
// SC16Q11_TABLE_BITS=8: 5.77M samples/second
// SC16Q11_TABLE_BITS=7: 10.23M samples/second
void prepare()
{
srand(1);
testdata_uc8 = calloc(10, sizeof(void*));
testdata_sc16 = calloc(10, sizeof(void*));
testdata_sc16q11 = calloc(10, sizeof(void*));
outdata = calloc(MODES_MAG_BUF_SAMPLES, sizeof(uint16_t));
for (int buf = 0; buf < 10; ++buf) {
uint8_t *uc8 = calloc(MODES_MAG_BUF_SAMPLES, 2);
testdata_uc8[buf] = uc8;;
uint16_t *sc16 = calloc(MODES_MAG_BUF_SAMPLES, 4);
testdata_sc16[buf] = sc16;
uint16_t *sc16q11 = calloc(MODES_MAG_BUF_SAMPLES, 4);
testdata_sc16q11[buf] = sc16q11;
for (unsigned i = 0; i < MODES_MAG_BUF_SAMPLES; ++i) {
double I = 2.0 * rand() / (RAND_MAX + 1.0) - 1.0;
double Q = 2.0 * rand() / (RAND_MAX + 1.0) - 1.0;
uc8[i*2] = (uint8_t) (I * 128 + 128);
uc8[i*2+1] = (uint8_t) (Q * 128 + 128);
sc16[i*2] = htole16( (int16_t) (I * 32768.0) );
sc16[i*2+1] = htole16( (int16_t) (Q * 32768.0) );
sc16q11[i*2] = htole16( (int16_t) (I * 2048.0) );
sc16q11[i*2+1] = htole16( (int16_t) (Q * 2048.0) );
}
}
}
void test(const char *what, input_format_t format, void **data, double sample_rate, bool filter_dc) {
fprintf(stderr, "Benchmarking: %s ", what);
struct converter_state *state;
iq_convert_fn converter = init_converter(format, sample_rate, filter_dc, &state);
if (!converter) {
fprintf(stderr, "Can't initialize converter\n");
return;
}
struct timespec total = { 0, 0 };
int iterations = 0;
// Run it once to force init.
converter(data[0], outdata, MODES_MAG_BUF_SAMPLES, state, NULL, NULL);
while (total.tv_sec < 5) {
fprintf(stderr, ".");
struct timespec start;
start_cpu_timing(&start);
for (int i = 0; i < 10; ++i) {
converter(data[i], outdata, MODES_MAG_BUF_SAMPLES, state, NULL, NULL);
}
end_cpu_timing(&start, &total);
iterations++;
}
fprintf(stderr, "\n");
cleanup_converter(state);
double samples = 10.0 * iterations * MODES_MAG_BUF_SAMPLES;
double nanos = total.tv_sec * 1e9 + total.tv_nsec;
fprintf(stderr, " %.2fM samples in %.6f seconds\n",
samples / 1e6, nanos / 1e9);
fprintf(stderr, " %.2fM samples/second\n",
samples / nanos * 1e3);
}
int main(int argc, char **argv)
{
MODES_NOTUSED(argc);
MODES_NOTUSED(argv);
prepare();
test("SC16Q11, DC", INPUT_SC16Q11, testdata_sc16q11, 2400000, true);
test("SC16Q11, no DC", INPUT_SC16Q11, testdata_sc16q11, 2400000, false);
test("UC8, DC", INPUT_UC8, testdata_uc8, 2400000, true);
test("UC8, no DC", INPUT_UC8, testdata_uc8, 2400000, false);
test("SC16, DC", INPUT_SC16, testdata_sc16, 2400000, true);
test("SC16, no DC", INPUT_SC16, testdata_sc16, 2400000, false);
}

View file

@ -22,7 +22,7 @@ install_rtlsdr:
cd rtl-sdr-build && cmake ../rtl-sdr -DCMAKE_INSTALL_PREFIX=./install && make install
override_dh_auto_build: install_rtlsdr
dh_auto_build -- DIALECT=-std=gnu1x HTMLPATH=/usr/share/$(SRCNAME)/html DUMP1090_VERSION=$(DUMP1090_VERSION) STATIC=yes RTLSDR_PREFIX=rtl-sdr-build/install
dh_auto_build -- RTLSDR=yes BLADERF=no DIALECT=-std=gnu1x HTMLPATH=/usr/share/$(SRCNAME)/html DUMP1090_VERSION=$(DUMP1090_VERSION) STATIC=yes RTLSDR_PREFIX=rtl-sdr-build/install
override_dh_auto_test:
dh_auto_test -- DIALECT=-std=gnu1x

8
debian/changelog vendored
View file

@ -1,3 +1,11 @@
dump1090-fa (3.4.0) stable; urgency=medium
* Add bladeRF support.
* Clean up the FlightAware photos link so it works for registrations containing
non-alphanumeric characters.
-- Oliver Jowett <oliver@mutability.co.uk> Thu, 09 Feb 2017 17:41:43 +0000
dump1090-fa (3.3.0) stable; urgency=medium
* Improvements to the Mode A/C demodulator.

4
debian/control vendored
View file

@ -2,7 +2,7 @@ Source: dump1090-fa
Section: embedded
Priority: extra
Maintainer: Oliver Jowett <oliver@mutability.co.uk>
Build-Depends: debhelper(>=9), librtlsdr-dev, libusb-1.0-0-dev, pkg-config, dh-systemd, libncurses5-dev
Build-Depends: debhelper(>=9), librtlsdr-dev, libusb-1.0-0-dev, pkg-config, dh-systemd, libncurses5-dev, libbladerf-dev
Standards-Version: 3.9.3
Homepage: https://github.com/mutability/dump1090
Vcs-Git: https://github.com/mutability/dump1090.git
@ -19,7 +19,7 @@ Description: transitional dummy package for dump1090
Package: dump1090-fa
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, lighttpd
Depends: ${shlibs:Depends}, ${misc:Depends}, libbladerf1(>=2016.06), libbladerf-udev, adduser, lighttpd
Replaces: dump1090 (<< 3.0)
Breaks: dump1090 (<< 3.0)
Description: ADS-B Ground Station System for RTL-SDR

View file

@ -1,2 +1,3 @@
public_html/* usr/share/dump1090-fa/html
debian/lighttpd/* etc/lighttpd/conf-available
bladerf/* /usr/share/dump1090-fa/bladerf

View file

@ -31,6 +31,9 @@ case "$1" in
adduser --system --home /usr/share/$NAME --no-create-home --quiet "$RUNAS"
fi
# plugdev required for bladeRF USB access
adduser "$RUNAS" plugdev
# set up lighttpd
echo "Enabling lighttpd integration.." >&2
lighty-enable-mod dump1090-fa || true

10
debian/rules vendored
View file

@ -12,13 +12,15 @@ export DH_VERBOSE=1
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
DPKG_EXPORT_BUILDFLAGS = 1
include /usr/share/dpkg/buildflags.mk
include /usr/share/dpkg/default.mk
DUMP1090_VERSION=$(shell dpkg-parsechangelog | sed -n 's/^Version: /v/p')
SRCNAME=$(shell dpkg-parsechangelog | sed -n 's/^Source: //p')
ifeq ($(DEB_HOST_ARCH),armhf)
# Assume a Pi-like target, where using an 8-bit table is a fairly big win over the float path
CPPFLAGS += -DSC16Q11_TABLE_BITS=8
endif
override_dh_auto_build:
dh_auto_build -- HTMLPATH=/usr/share/$(SRCNAME)/html DUMP1090_VERSION=$(DUMP1090_VERSION) RTLSDR_PREFIX=/usr
dh_auto_build -- RTLSDR=yes BLADERF=yes HTMLPATH=/usr/share/$(DEB_SOURCE)/html DUMP1090_VERSION=$(DEB_VERSION)
override_dh_install:
dh_install

View file

@ -49,12 +49,8 @@
#include "dump1090.h"
#include <rtl-sdr.h>
#include <stdarg.h>
static int verbose_device_search(char *s);
//
// ============================= Utility functions ==========================
//
@ -96,21 +92,6 @@ static void sigtermHandler(int dummy) {
log_with_timestamp("Caught SIGTERM, shutting down..\n");
}
static void start_cpu_timing(struct timespec *start_time)
{
clock_gettime(CLOCK_THREAD_CPUTIME_ID, start_time);
}
static void end_cpu_timing(const struct timespec *start_time, struct timespec *add_to)
{
struct timespec end_time;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end_time);
add_to->tv_sec += (end_time.tv_sec - start_time->tv_sec - 1);
add_to->tv_nsec += (1000000000L + end_time.tv_nsec - start_time->tv_nsec);
add_to->tv_sec += add_to->tv_nsec / 1000000000L;
add_to->tv_nsec = add_to->tv_nsec % 1000000000L;
}
void receiverPositionChanged(float lat, float lon, float alt)
{
log_with_timestamp("Autodetected receiver location: %.5f, %.5f at %.0fm AMSL", lat, lon, alt);
@ -128,7 +109,6 @@ void modesInitConfig(void) {
// Now initialise things that should not be 0/NULL to their defaults
Modes.gain = MODES_MAX_GAIN;
Modes.freq = MODES_DEFAULT_FREQ;
Modes.ppm_error = MODES_DEFAULT_PPM;
Modes.check_crc = 1;
Modes.net_heartbeat_interval = MODES_NET_HEARTBEAT_INTERVAL;
Modes.net_input_raw_ports = strdup("30001");
@ -142,12 +122,14 @@ void modesInitConfig(void) {
Modes.json_location_accuracy = 1;
Modes.maxRange = 1852 * 300; // 300NM default max range
Modes.mode_ac_auto = 1;
sdrInitConfig();
}
//
//=========================================================================
//
void modesInit(void) {
int i, q;
int i;
pthread_mutex_init(&Modes.data_mutex,NULL);
pthread_cond_init(&Modes.data_cond,NULL);
@ -157,8 +139,7 @@ void modesInit(void) {
// Allocate the various buffers used by Modes
Modes.trailing_samples = (MODES_PREAMBLE_US + MODES_LONG_MSG_BITS + 16) * 1e-6 * Modes.sample_rate;
if ( ((Modes.maglut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) ||
((Modes.log10lut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) )
if ( ((Modes.log10lut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) )
{
fprintf(stderr, "Out of memory allocating data buffer.\n");
exit(1);
@ -202,21 +183,6 @@ void modesInit(void) {
if (Modes.net_sndbuf_size > (MODES_NET_SNDBUF_MAX))
{Modes.net_sndbuf_size = MODES_NET_SNDBUF_MAX;}
// compute UC8 magnitude lookup table
for (i = 0; i <= 255; i++) {
for (q = 0; q <= 255; q++) {
float fI, fQ, magsq;
fI = (i - 127.5) / 127.5;
fQ = (q - 127.5) / 127.5;
magsq = fI * fI + fQ * fQ;
if (magsq > 1)
magsq = 1;
Modes.maglut[le16toh((i*256)+q)] = (uint16_t) round(sqrtf(magsq) * 65535.0);
}
}
// Prepare the log10 lookup table: 100log10(x)
Modes.log10lut[0] = 0; // poorly defined..
for (i = 1; i <= 65535; i++) {
@ -227,138 +193,11 @@ void modesInit(void) {
modesChecksumInit(Modes.nfix_crc);
icaoFilterInit();
modeACInit();
interactiveInit();
if (Modes.show_only)
icaoFilterAdd(Modes.show_only);
// Prepare sample conversion
if (!Modes.net_only) {
if (Modes.filename == NULL) // using a real RTLSDR, use UC8 input always
Modes.input_format = INPUT_UC8;
Modes.converter_function = init_converter(Modes.input_format,
Modes.sample_rate,
Modes.dc_filter,
&Modes.converter_state);
if (!Modes.converter_function) {
fprintf(stderr, "Can't initialize sample converter, giving up.\n");
exit(1);
}
}
}
static void convert_samples(void *iq,
uint16_t *mag,
unsigned nsamples,
double *mean_level,
double *mean_power)
{
Modes.converter_function(iq, mag, nsamples, Modes.converter_state, mean_level, mean_power);
}
//
// =============================== RTLSDR handling ==========================
//
int modesInitRTLSDR(void) {
int j;
int device_count, dev_index = 0;
char vendor[256], product[256], serial[256];
if (Modes.dev_name) {
if ( (dev_index = verbose_device_search(Modes.dev_name)) < 0 )
return -1;
}
device_count = rtlsdr_get_device_count();
if (!device_count) {
fprintf(stderr, "No supported RTLSDR devices found.\n");
return -1;
}
fprintf(stderr, "Found %d device(s):\n", device_count);
for (j = 0; j < device_count; j++) {
if (rtlsdr_get_device_usb_strings(j, vendor, product, serial) != 0) {
fprintf(stderr, "%d: unable to read device details\n", j);
} else {
fprintf(stderr, "%d: %s, %s, SN: %s %s\n", j, vendor, product, serial,
(j == dev_index) ? "(currently selected)" : "");
}
}
if (rtlsdr_open(&Modes.dev, dev_index) < 0) {
fprintf(stderr, "Error opening the RTLSDR device: %s\n",
strerror(errno));
return -1;
}
// Set gain, frequency, sample rate, and reset the device
rtlsdr_set_tuner_gain_mode(Modes.dev,
(Modes.gain == MODES_AUTO_GAIN) ? 0 : 1);
if (Modes.gain != MODES_AUTO_GAIN) {
int *gains;
int numgains;
numgains = rtlsdr_get_tuner_gains(Modes.dev, NULL);
if (numgains <= 0) {
fprintf(stderr, "Error getting tuner gains\n");
return -1;
}
gains = malloc(numgains * sizeof(int));
if (rtlsdr_get_tuner_gains(Modes.dev, gains) != numgains) {
fprintf(stderr, "Error getting tuner gains\n");
free(gains);
return -1;
}
if (Modes.gain == MODES_MAX_GAIN) {
int highest = -1;
int i;
for (i = 0; i < numgains; ++i) {
if (gains[i] > highest)
highest = gains[i];
}
Modes.gain = highest;
fprintf(stderr, "Max available gain is: %.2f dB\n", Modes.gain/10.0);
} else {
int closest = -1;
int i;
for (i = 0; i < numgains; ++i) {
if (closest == -1 || abs(gains[i] - Modes.gain) < abs(closest - Modes.gain))
closest = gains[i];
}
if (closest != Modes.gain) {
Modes.gain = closest;
fprintf(stderr, "Closest available gain: %.2f dB\n", Modes.gain/10.0);
}
}
free(gains);
fprintf(stderr, "Setting gain to: %.2f dB\n", Modes.gain/10.0);
if (rtlsdr_set_tuner_gain(Modes.dev, Modes.gain) < 0) {
fprintf(stderr, "Error setting tuner gains\n");
return -1;
}
} else {
fprintf(stderr, "Using automatic gain control.\n");
}
rtlsdr_set_freq_correction(Modes.dev, Modes.ppm_error);
if (Modes.enable_agc) rtlsdr_set_agc_mode(Modes.dev, 1);
rtlsdr_set_center_freq(Modes.dev, Modes.freq);
rtlsdr_set_sample_rate(Modes.dev, (unsigned)Modes.sample_rate);
rtlsdr_reset_buffer(Modes.dev);
fprintf(stderr, "Gain reported by device: %.2f dB\n",
rtlsdr_get_tuner_gain(Modes.dev)/10.0);
return 0;
}
//
//=========================================================================
//
@ -371,208 +210,6 @@ int modesInitRTLSDR(void) {
// A Mutex is used to avoid races with the decoding thread.
//
static struct timespec reader_thread_start;
void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) {
struct mag_buf *outbuf;
struct mag_buf *lastbuf;
uint32_t slen;
unsigned next_free_buffer;
unsigned free_bufs;
unsigned block_duration;
static int was_odd = 0; // paranoia!!
static int dropping = 0;
MODES_NOTUSED(ctx);
// Lock the data buffer variables before accessing them
pthread_mutex_lock(&Modes.data_mutex);
if (Modes.exit) {
rtlsdr_cancel_async(Modes.dev); // ask our caller to exit
}
next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS;
outbuf = &Modes.mag_buffers[Modes.first_free_buffer];
lastbuf = &Modes.mag_buffers[(Modes.first_free_buffer + MODES_MAG_BUFFERS - 1) % MODES_MAG_BUFFERS];
free_bufs = (Modes.first_filled_buffer - next_free_buffer + MODES_MAG_BUFFERS) % MODES_MAG_BUFFERS;
// Paranoia! Unlikely, but let's go for belt and suspenders here
if (len != MODES_RTL_BUF_SIZE) {
fprintf(stderr, "weirdness: rtlsdr gave us a block with an unusual size (got %u bytes, expected %u bytes)\n",
(unsigned)len, (unsigned)MODES_RTL_BUF_SIZE);
if (len > MODES_RTL_BUF_SIZE) {
// wat?! Discard the start.
unsigned discard = (len - MODES_RTL_BUF_SIZE + 1) / 2;
outbuf->dropped += discard;
buf += discard*2;
len -= discard*2;
}
}
if (was_odd) {
// Drop a sample so we are in sync with I/Q samples again (hopefully)
++buf;
--len;
++outbuf->dropped;
}
was_odd = (len & 1);
slen = len/2;
if (free_bufs == 0 || (dropping && free_bufs < MODES_MAG_BUFFERS/2)) {
// FIFO is full. Drop this block.
dropping = 1;
outbuf->dropped += slen;
pthread_mutex_unlock(&Modes.data_mutex);
return;
}
dropping = 0;
pthread_mutex_unlock(&Modes.data_mutex);
// Compute the sample timestamp and system timestamp for the start of the block
outbuf->sampleTimestamp = lastbuf->sampleTimestamp + 12e6 * (lastbuf->length + outbuf->dropped) / Modes.sample_rate;
block_duration = 1e9 * slen / Modes.sample_rate;
// Get the approx system time for the start of this block
clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp);
outbuf->sysTimestamp.tv_nsec -= block_duration;
normalize_timespec(&outbuf->sysTimestamp);
// Copy trailing data from last block (or reset if not valid)
if (outbuf->dropped == 0 && lastbuf->length >= Modes.trailing_samples) {
memcpy(outbuf->data, lastbuf->data + lastbuf->length - Modes.trailing_samples, Modes.trailing_samples * sizeof(uint16_t));
} else {
memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t));
}
// Convert the new data
outbuf->length = slen;
convert_samples(buf, &outbuf->data[Modes.trailing_samples], slen, &outbuf->mean_level, &outbuf->mean_power);
// Push the new data to the demodulation thread
pthread_mutex_lock(&Modes.data_mutex);
Modes.mag_buffers[next_free_buffer].dropped = 0;
Modes.mag_buffers[next_free_buffer].length = 0; // just in case
Modes.first_free_buffer = next_free_buffer;
// accumulate CPU while holding the mutex, and restart measurement
end_cpu_timing(&reader_thread_start, &Modes.reader_cpu_accumulator);
start_cpu_timing(&reader_thread_start);
pthread_cond_signal(&Modes.data_cond);
pthread_mutex_unlock(&Modes.data_mutex);
}
//
//=========================================================================
//
// This is used when --ifile is specified in order to read data from file
// instead of using an RTLSDR device
//
void readDataFromFile(void) {
int eof = 0;
struct timespec next_buffer_delivery;
void *readbuf;
int bytes_per_sample = 0;
switch (Modes.input_format) {
case INPUT_UC8:
bytes_per_sample = 2;
break;
case INPUT_SC16:
case INPUT_SC16Q11:
bytes_per_sample = 4;
break;
}
if (!(readbuf = malloc(MODES_MAG_BUF_SAMPLES * bytes_per_sample))) {
fprintf(stderr, "failed to allocate read buffer\n");
exit(1);
}
clock_gettime(CLOCK_MONOTONIC, &next_buffer_delivery);
pthread_mutex_lock(&Modes.data_mutex);
while (!Modes.exit && !eof) {
ssize_t nread, toread;
void *r;
struct mag_buf *outbuf, *lastbuf;
unsigned next_free_buffer;
unsigned slen;
next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS;
if (next_free_buffer == Modes.first_filled_buffer) {
// no space for output yet
pthread_cond_wait(&Modes.data_cond, &Modes.data_mutex);
continue;
}
outbuf = &Modes.mag_buffers[Modes.first_free_buffer];
lastbuf = &Modes.mag_buffers[(Modes.first_free_buffer + MODES_MAG_BUFFERS - 1) % MODES_MAG_BUFFERS];
pthread_mutex_unlock(&Modes.data_mutex);
// Compute the sample timestamp and system timestamp for the start of the block
outbuf->sampleTimestamp = lastbuf->sampleTimestamp + 12e6 * lastbuf->length / Modes.sample_rate;
// Copy trailing data from last block (or reset if not valid)
if (lastbuf->length >= Modes.trailing_samples) {
memcpy(outbuf->data, lastbuf->data + lastbuf->length - Modes.trailing_samples, Modes.trailing_samples * sizeof(uint16_t));
} else {
memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t));
}
// Get the system time for the start of this block
clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp);
toread = MODES_MAG_BUF_SAMPLES * bytes_per_sample;
r = readbuf;
while (toread) {
nread = read(Modes.fd, r, toread);
if (nread <= 0) {
// Done.
eof = 1;
break;
}
r += nread;
toread -= nread;
}
slen = outbuf->length = MODES_MAG_BUF_SAMPLES - toread/bytes_per_sample;
// Convert the new data
convert_samples(readbuf, &outbuf->data[Modes.trailing_samples], slen, &outbuf->mean_level, &outbuf->mean_power);
if (Modes.throttle) {
// Wait until we are allowed to release this buffer to the main thread
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_buffer_delivery, NULL) == EINTR)
;
// compute the time we can deliver the next buffer.
next_buffer_delivery.tv_nsec += outbuf->length * 1e9 / Modes.sample_rate;
normalize_timespec(&next_buffer_delivery);
}
// Push the new data to the main thread
pthread_mutex_lock(&Modes.data_mutex);
Modes.first_free_buffer = next_free_buffer;
// accumulate CPU while holding the mutex, and restart measurement
end_cpu_timing(&reader_thread_start, &Modes.reader_cpu_accumulator);
start_cpu_timing(&reader_thread_start);
pthread_cond_signal(&Modes.data_cond);
}
free(readbuf);
// Wait for the main thread to consume all data
while (!Modes.exit && Modes.first_filled_buffer != Modes.first_free_buffer)
pthread_cond_wait(&Modes.data_cond, &Modes.data_mutex);
pthread_mutex_unlock(&Modes.data_mutex);
}
//
//=========================================================================
//
@ -580,36 +217,11 @@ void readDataFromFile(void) {
// without caring about data acquisition
//
void *readerThreadEntryPoint(void *arg) {
void *readerThreadEntryPoint(void *arg)
{
MODES_NOTUSED(arg);
start_cpu_timing(&reader_thread_start); // we accumulate in rtlsdrCallback() or readDataFromFile()
if (Modes.filename == NULL) {
while (!Modes.exit) {
rtlsdr_read_async(Modes.dev, rtlsdrCallback, NULL,
MODES_RTL_BUFFERS,
MODES_RTL_BUF_SIZE);
if (!Modes.exit) {
log_with_timestamp("Warning: lost the connection to the RTLSDR device.");
rtlsdr_close(Modes.dev);
Modes.dev = NULL;
do {
sleep(5);
log_with_timestamp("Trying to reconnect to the RTLSDR device..");
} while (!Modes.exit && modesInitRTLSDR() < 0);
}
}
if (Modes.dev != NULL) {
rtlsdr_close(Modes.dev);
Modes.dev = NULL;
}
} else {
readDataFromFile();
}
sdrRun();
// Wake the main thread (if it's still waiting)
pthread_mutex_lock(&Modes.data_mutex);
@ -648,17 +260,36 @@ void snipMode(int level) {
// ================================ Main ====================================
//
void showHelp(void) {
printf("-----------------------------------------------------------------------------\n");
printf("| dump1090 ModeS Receiver %45s |\n", MODES_DUMP1090_VARIANT " " MODES_DUMP1090_VERSION);
printf("| build options: %-58s |\n",
""
#ifdef ENABLE_RTLSDR
"ENABLE_RTLSDR "
#endif
#ifdef ENABLE_BLADERF
"ENABLE_BLADERF "
#endif
#ifdef SC16Q11_TABLE_BITS
// This is a little silly, but that's how the preprocessor works..
#define _stringize(x) #x
#define stringize(x) _stringize(x)
"SC16Q11_TABLE_BITS=" stringize(SC16Q11_TABLE_BITS)
#undef stringize
#undef _stringize
#endif
);
printf("-----------------------------------------------------------------------------\n");
printf("\n");
sdrShowHelp();
printf(
"-----------------------------------------------------------------------------\n"
"| dump1090 ModeS Receiver %45s |\n"
"-----------------------------------------------------------------------------\n"
"--device-index <index> Select RTL device (default: 0)\n"
" Common options\n"
"\n"
"--gain <db> Set gain (default: max gain. Use -10 for auto-gain)\n"
"--enable-agc Enable the Automatic Gain Control (default: off)\n"
"--freq <hz> Set frequency (default: 1090 Mhz)\n"
"--ifile <filename> Read data from file (use '-' for stdin)\n"
"--iformat <format> Sample format for --ifile: UC8 (default), SC16, or SC16Q11\n"
"--throttle When reading from a file, play back in realtime, not at max speed\n"
"--interactive Interactive mode refreshing data on screen. Implies --throttle\n"
"--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60)\n"
"--raw Show only messages hex values\n"
@ -698,12 +329,11 @@ void showHelp(void) {
"--debug <flags> Debug mode (verbose), see README for details\n"
"--quiet Disable output to stdout. Use for daemon applications\n"
"--show-only <addr> Show only messages from the given ICAO on stdout\n"
"--ppm <error> Set receiver error in parts per million (default 0)\n"
"--html-dir <dir> Use <dir> as base directory for the internal HTTP server. Defaults to " HTMLPATH "\n"
"--write-json <dir> Periodically write json output to <dir> (for serving by a separate webserver)\n"
"--write-json-every <t> Write json output every t seconds (default 1)\n"
"--json-location-accuracy <n> Accuracy of receiver location in json metadata: 0=no location, 1=approximate, 2=exact\n"
"--dcfilter Apply a 1Hz DC filter to input data (requires lots more CPU)\n"
"--dcfilter Apply a 1Hz DC filter to input data (requires more CPU)\n"
"--help Show this help\n"
"\n"
"Debug mode flags: d = Log frames decoded with errors\n"
@ -712,8 +342,7 @@ void showHelp(void) {
" C = Log frames with good CRC\n"
" p = Log frames with bad preamble\n"
" n = Log network debugging info\n"
" j = Log frames to frames.js, loadable by debug.html\n",
MODES_DUMP1090_VARIANT " " MODES_DUMP1090_VERSION
" j = Log frames to frames.js, loadable by debug.html\n"
);
}
@ -830,68 +459,6 @@ void backgroundTasks(void) {
//
//=========================================================================
//
int verbose_device_search(char *s)
{
int i, device_count, device, offset;
char *s2;
char vendor[256], product[256], serial[256];
device_count = rtlsdr_get_device_count();
if (!device_count) {
fprintf(stderr, "No supported devices found.\n");
return -1;
}
fprintf(stderr, "Found %d device(s):\n", device_count);
for (i = 0; i < device_count; i++) {
if (rtlsdr_get_device_usb_strings(i, vendor, product, serial) != 0) {
fprintf(stderr, " %d: unable to read device details\n", i);
} else {
fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial);
}
}
fprintf(stderr, "\n");
/* does string look like raw id number */
device = (int)strtol(s, &s2, 0);
if (s2[0] == '\0' && device >= 0 && device < device_count) {
fprintf(stderr, "Using device %d: %s\n",
device, rtlsdr_get_device_name((uint32_t)device));
return device;
}
/* does string exact match a serial */
for (i = 0; i < device_count; i++) {
rtlsdr_get_device_usb_strings(i, vendor, product, serial);
if (strcmp(s, serial) != 0) {
continue;}
device = i;
fprintf(stderr, "Using device %d: %s\n",
device, rtlsdr_get_device_name((uint32_t)device));
return device;
}
/* does string prefix match a serial */
for (i = 0; i < device_count; i++) {
rtlsdr_get_device_usb_strings(i, vendor, product, serial);
if (strncmp(s, serial, strlen(s)) != 0) {
continue;}
device = i;
fprintf(stderr, "Using device %d: %s\n",
device, rtlsdr_get_device_name((uint32_t)device));
return device;
}
/* does string suffix match a serial */
for (i = 0; i < device_count; i++) {
rtlsdr_get_device_usb_strings(i, vendor, product, serial);
offset = strlen(serial) - strlen(s);
if (offset < 0) {
continue;}
if (strncmp(s, serial+offset, strlen(s)) != 0) {
continue;}
device = i;
fprintf(stderr, "Using device %d: %s\n",
device, rtlsdr_get_device_name((uint32_t)device));
return device;
}
fprintf(stderr, "No matching devices found.\n");
return -1;
}
//
//=========================================================================
//
@ -909,29 +476,12 @@ int main(int argc, char **argv) {
for (j = 1; j < argc; j++) {
int more = j+1 < argc; // There are more arguments
if (!strcmp(argv[j],"--device-index") && more) {
if (!strcmp(argv[j],"--freq") && more) {
Modes.freq = (int) strtoll(argv[++j],NULL,10);
} else if ( (!strcmp(argv[j], "--device") || !strcmp(argv[j], "--device-index")) && more) {
Modes.dev_name = strdup(argv[++j]);
} else if (!strcmp(argv[j],"--gain") && more) {
Modes.gain = (int) (atof(argv[++j])*10); // Gain is in tens of DBs
} else if (!strcmp(argv[j],"--enable-agc")) {
Modes.enable_agc++;
} else if (!strcmp(argv[j],"--freq") && more) {
Modes.freq = (int) strtoll(argv[++j],NULL,10);
} else if (!strcmp(argv[j],"--ifile") && more) {
Modes.filename = strdup(argv[++j]);
} else if (!strcmp(argv[j],"--iformat") && more) {
++j;
if (!strcasecmp(argv[j], "uc8")) {
Modes.input_format = INPUT_UC8;
} else if (!strcasecmp(argv[j], "sc16")) {
Modes.input_format = INPUT_SC16;
} else if (!strcasecmp(argv[j], "sc16q11")) {
Modes.input_format = INPUT_SC16Q11;
} else {
fprintf(stderr, "Input format '%s' not understood (supported values: UC8, SC16, SC16Q11)\n",
argv[j]);
exit(1);
}
} else if (!strcmp(argv[j],"--dcfilter")) {
Modes.dc_filter = 1;
} else if (!strcmp(argv[j],"--measure-noise")) {
@ -957,7 +507,7 @@ int main(int argc, char **argv) {
fprintf(stderr, "--net-beast ignored, use --net-bo-port to control where Beast output is generated\n");
} else if (!strcmp(argv[j],"--net-only")) {
Modes.net = 1;
Modes.net_only = 1;
Modes.sdr_type = SDR_NONE;
} else if (!strcmp(argv[j],"--net-heartbeat") && more) {
Modes.net_heartbeat_interval = (uint64_t)(1000 * atof(argv[++j]));
} else if (!strcmp(argv[j],"--net-ro-size") && more) {
@ -1007,9 +557,7 @@ int main(int argc, char **argv) {
fprintf(stderr, "warning: --aggressive not supported in this build, option ignored.\n");
#endif
} else if (!strcmp(argv[j],"--interactive")) {
Modes.interactive = Modes.throttle = 1;
} else if (!strcmp(argv[j],"--throttle")) {
Modes.throttle = 1;
Modes.interactive = 1;
} else if (!strcmp(argv[j],"--interactive-ttl") && more) {
Modes.interactive_display_ttl = (uint64_t)(1000 * atof(argv[++j]));
} else if (!strcmp(argv[j],"--lat") && more) {
@ -1049,8 +597,6 @@ int main(int argc, char **argv) {
} else if (!strcmp(argv[j],"--help")) {
showHelp();
exit(0);
} else if (!strcmp(argv[j],"--ppm") && more) {
Modes.ppm_error = atoi(argv[++j]);
} else if (!strcmp(argv[j],"--quiet")) {
Modes.quiet = 1;
} else if (!strcmp(argv[j],"--show-only") && more) {
@ -1071,6 +617,8 @@ int main(int argc, char **argv) {
} else if (!strcmp(argv[j], "--json-location-accuracy") && more) {
Modes.json_location_accuracy = atoi(argv[++j]);
#endif
} else if (sdrHandleOption(argc, argv, &j)) {
/* handled */
} else {
fprintf(stderr,
"Unknown or not enough arguments for option '%s'.\n\n",
@ -1089,27 +637,13 @@ int main(int argc, char **argv) {
log_with_timestamp("%s %s starting up.", MODES_DUMP1090_VARIANT, MODES_DUMP1090_VERSION);
modesInit();
if (Modes.net_only) {
fprintf(stderr,"Net-only mode, no RTL device or file open.\n");
} else if (Modes.filename == NULL) {
if (modesInitRTLSDR() < 0) {
exit(1);
}
} else {
if (Modes.filename[0] == '-' && Modes.filename[1] == '\0') {
Modes.fd = STDIN_FILENO;
} else if ((Modes.fd = open(Modes.filename,
#ifdef _WIN32
(O_RDONLY | O_BINARY)
#else
(O_RDONLY)
#endif
)) == -1) {
perror("Opening data file");
exit(1);
}
if (!sdrOpen()) {
exit(1);
}
if (Modes.net) {
modesInitNet();
}
if (Modes.net) modesInitNet();
// init stats:
Modes.stats_current.start = Modes.stats_current.end =
@ -1126,9 +660,11 @@ int main(int argc, char **argv) {
writeJsonToFile("stats.json", generateStatsJson);
writeJsonToFile("aircraft.json", generateAircraftJson);
interactiveInit();
// If the user specifies --net-only, just run in order to serve network
// clients without reading data from the RTL device
if (Modes.net_only) {
if (Modes.sdr_type == SDR_NONE) {
while (!Modes.exit) {
struct timespec start_time;
@ -1145,7 +681,7 @@ int main(int argc, char **argv) {
pthread_mutex_lock(&Modes.data_mutex);
pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL);
while (Modes.exit == 0) {
while (!Modes.exit) {
struct timespec start_time;
if (Modes.first_free_buffer == Modes.first_filled_buffer) {
@ -1201,7 +737,7 @@ int main(int argc, char **argv) {
// Nothing to process this time around.
pthread_mutex_unlock(&Modes.data_mutex);
if (--watchdogCounter <= 0) {
log_with_timestamp("No data received from the dongle for a long time, it may have wedged");
log_with_timestamp("No data received from the SDR for a long time, it may have wedged");
watchdogCounter = 600;
}
}
@ -1228,9 +764,10 @@ int main(int argc, char **argv) {
display_total_stats();
}
cleanup_converter(Modes.converter_state);
log_with_timestamp("Normal exit.");
sdrClose();
#ifndef _WIN32
pthread_exit(0);
#else

View file

@ -65,6 +65,7 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <pthread.h>
#include <stdint.h>
#include <errno.h>
@ -84,12 +85,8 @@
#include "compat/compat.h"
// Avoid a dependency on rtl-sdr except where it's really needed.
typedef struct rtlsdr_dev rtlsdr_dev_t;
// ============================= #defines ===============================
#define MODES_DEFAULT_PPM 52
#define MODES_DEFAULT_FREQ 1090000000
#define MODES_DEFAULT_WIDTH 1000
#define MODES_DEFAULT_HEIGHT 700
@ -245,9 +242,14 @@ typedef enum {
#include "cpr.h"
#include "icao_filter.h"
#include "convert.h"
#include "sdr.h"
//======================== structure declarations =========================
typedef enum {
SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF
} sdr_type_t;
// Structure representing one magnitude buffer
struct mag_buf {
uint16_t *data; // Magnitude data. Starts with Modes.trailing_samples worth of overlap from the previous block
@ -274,24 +276,16 @@ struct { // Internal state
unsigned trailing_samples; // extra trailing samples in magnitude buffers
double sample_rate; // actual sample rate in use (in hz)
int fd; // --ifile option file descriptor
input_format_t input_format; // --iformat option
uint16_t *maglut; // I/Q -> Magnitude lookup table
uint16_t *log10lut; // Magnitude -> log10 lookup table
int exit; // Exit from the main loop when true
// Sample conversion
int dc_filter; // should we apply a DC filter?
iq_convert_fn converter_function;
struct converter_state *converter_state;
// RTLSDR
char * dev_name;
int gain;
int enable_agc;
rtlsdr_dev_t *dev;
int freq;
int ppm_error;
// Networking
char aneterr[ANET_ERR_LEN];
@ -308,7 +302,7 @@ struct { // Internal state
#endif
// Configuration
char *filename; // Input form file, --ifile option
sdr_type_t sdr_type; // where are we getting data from?
int nfix_crc; // Number of crc bit error(s) to correct
int check_crc; // Only display messages with good CRC
int raw; // Raw output format
@ -343,7 +337,6 @@ struct { // Internal state
uint64_t json_interval; // Interval between rewriting the json aircraft file, in milliseconds; also the advertised map refresh interval
char *html_dir; // Path to www base directory.
int json_location_accuracy; // Accuracy of location metadata: 0=none, 1=approx, 2=exact
int throttle; // When reading from a file, throttle file playback to realtime?
int json_aircraft_history_next;
struct {

View file

@ -1623,7 +1623,7 @@ function getFlightAwareModeSLink(code, ident, linkText) {
function getFlightAwarePhotoLink(registration) {
if (registration !== null && registration !== "") {
return "<a target=\"_blank\" href=\"https://flightaware.com/photos/aircraft/" + registration.trim() + "\">See Aircraft Photos</a>";
return "<a target=\"_blank\" href=\"https://flightaware.com/photos/aircraft/" + registration.replace(/[^0-9a-z]/ig,'') + "\">See Aircraft Photos</a>";
}
return "";

169
sdr.c Normal file
View file

@ -0,0 +1,169 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// sdr.c: generic SDR infrastructure
//
// Copyright (c) 2016-2017 Oliver Jowett <oliver@mutability.co.uk>
// Copyright (c) 2017 FlightAware LLC
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
//
// This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
#include "dump1090.h"
#include "sdr_ifile.h"
#ifdef ENABLE_RTLSDR
# include "sdr_rtlsdr.h"
#endif
#ifdef ENABLE_BLADERF
# include "sdr_bladerf.h"
#endif
typedef struct {
const char *name;
sdr_type_t sdr_type;
void (*initConfig)();
void (*showHelp)();
bool (*handleOption)(int, char**, int*);
bool (*open)();
void (*run)();
void (*close)();
} sdr_handler;
static void noInitConfig()
{
}
static void noShowHelp()
{
}
static bool noHandleOption(int argc, char **argv, int *jptr)
{
MODES_NOTUSED(argc);
MODES_NOTUSED(argv);
MODES_NOTUSED(jptr);
return false;
}
static bool noOpen()
{
fprintf(stderr, "Net-only mode, no SDR device or file open.\n");
return true;
}
static void noRun()
{
}
static void noClose()
{
}
static bool unsupportedOpen()
{
fprintf(stderr, "Support for this SDR type was not enabled in this build.\n");
return false;
}
static sdr_handler sdr_handlers[] = {
#ifdef ENABLE_RTLSDR
{ "rtlsdr", SDR_RTLSDR, rtlsdrInitConfig, rtlsdrShowHelp, rtlsdrHandleOption, rtlsdrOpen, rtlsdrRun, rtlsdrClose },
#endif
#ifdef ENABLE_BLADERF
{ "bladerf", SDR_BLADERF, bladeRFInitConfig, bladeRFShowHelp, bladeRFHandleOption, bladeRFOpen, bladeRFRun, bladeRFClose },
#endif
{ "ifile", SDR_IFILE, ifileInitConfig, ifileShowHelp, ifileHandleOption, ifileOpen, ifileRun, ifileClose },
{ "none", SDR_NONE, noInitConfig, noShowHelp, noHandleOption, noOpen, noRun, noClose },
{ NULL, SDR_NONE, NULL, NULL, NULL, NULL, NULL, NULL } /* must come last */
};
void sdrInitConfig()
{
// Default SDR is the first type available in the handlers array.
Modes.sdr_type = sdr_handlers[0].sdr_type;
for (int i = 0; sdr_handlers[i].name; ++i) {
sdr_handlers[i].initConfig();
}
}
void sdrShowHelp()
{
printf("--device-type <type> Select SDR type (default: %s)\n", sdr_handlers[0].name);
printf("\n");
for (int i = 0; sdr_handlers[i].name; ++i) {
sdr_handlers[i].showHelp();
}
}
bool sdrHandleOption(int argc, char **argv, int *jptr)
{
int j = *jptr;
if (!strcmp(argv[j], "--device-type") && (j+1) < argc) {
++j;
for (int i = 0; sdr_handlers[i].name; ++i) {
if (!strcasecmp(sdr_handlers[i].name, argv[j])) {
Modes.sdr_type = sdr_handlers[i].sdr_type;
*jptr = j;
return true;
}
}
fprintf(stderr, "SDR type '%s' not recognized; supported SDR types are:\n", argv[j]);
for (int i = 0; sdr_handlers[i].name; ++i) {
fprintf(stderr, " %s\n", sdr_handlers[i].name);
}
return false;
}
for (int i = 0; sdr_handlers[i].name; ++i) {
if (sdr_handlers[i].handleOption(argc, argv, jptr))
return true;
}
return false;
}
static sdr_handler *current_handler()
{
static sdr_handler unsupported_handler = { "unsupported", SDR_NONE, noInitConfig, noShowHelp, noHandleOption, unsupportedOpen, noRun, noClose };
for (int i = 0; sdr_handlers[i].name; ++i) {
if (Modes.sdr_type == sdr_handlers[i].sdr_type) {
return &sdr_handlers[i];
}
}
return &unsupported_handler;
}
bool sdrOpen()
{
return current_handler()->open();
}
void sdrRun()
{
return current_handler()->run();
}
void sdrClose()
{
current_handler()->close();
}

33
sdr.h Normal file
View file

@ -0,0 +1,33 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// sdr.h: generic SDR infrastructure (header)
//
// Copyright (c) 2016-2017 Oliver Jowett <oliver@mutability.co.uk>
// Copyright (c) 2017 FlightAware LLC
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
//
// This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef SDR_H
#define SDR_H
// Common interface to different SDR inputs.
void sdrInitConfig();
void sdrShowHelp();
bool sdrHandleOption(int argc, char **argv, int *jptr);
bool sdrOpen();
void sdrRun();
void sdrClose();
#endif

515
sdr_bladerf.c Normal file
View file

@ -0,0 +1,515 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// sdr_bladerf.c: bladeRF support
//
// Copyright (c) 2017 FlightAware LLC
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
//
// This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
#include "dump1090.h"
#include "sdr_bladerf.h"
#include <libbladeRF.h>
#include <inttypes.h>
static struct {
const char *device_str;
const char *fpga_path;
unsigned decimation;
bladerf_lpf_mode lpf_mode;
unsigned lpf_bandwidth;
struct bladerf *device;
iq_convert_fn converter;
struct converter_state *converter_state;
unsigned block_size;
} BladeRF;
void bladeRFInitConfig()
{
BladeRF.device_str = NULL;
BladeRF.fpga_path = NULL;
BladeRF.decimation = 1;
BladeRF.lpf_mode = BLADERF_LPF_NORMAL;
BladeRF.lpf_bandwidth = 1750000;
BladeRF.device = NULL;
}
bool bladeRFHandleOption(int argc, char **argv, int *jptr)
{
int j = *jptr;
bool more = (j+1 < argc);
if (!strcmp(argv[j], "--bladerf-fpga") && more) {
BladeRF.fpga_path = strdup(argv[++j]);
} else if (!strcmp(argv[j], "--bladerf-decimation") && more) {
BladeRF.decimation = atoi(argv[++j]);
} else if (!strcmp(argv[j], "--bladerf-bandwidth") && more) {
++j;
if (!strcasecmp(argv[j], "bypass")) {
BladeRF.lpf_mode = BLADERF_LPF_BYPASSED;
} else {
BladeRF.lpf_mode = BLADERF_LPF_NORMAL;
BladeRF.lpf_bandwidth = atoi(argv[j]);
}
} else {
return false;
}
*jptr = j;
return true;
}
void bladeRFShowHelp()
{
printf(" bladeRF-specific options (use with --device-type bladerf)\n");
printf("\n");
printf("--device <ident> select device by bladeRF 'device identifier'\n");
printf("--bladerf-fpga <path> use alternative FPGA bitstream ('' to disable FPGA load)\n");
printf("--bladerf-decimation <N> assume FPGA decimates by a factor of N\n");
printf("--bladerf-bandwidth <hz> set LPF bandwidth ('bypass' to bypass the LPF)\n");
printf("\n");
}
static int lna_gain_db(bladerf_lna_gain gain)
{
switch (gain) {
case BLADERF_LNA_GAIN_BYPASS:
return 0;
case BLADERF_LNA_GAIN_MID:
return BLADERF_LNA_GAIN_MID_DB;
case BLADERF_LNA_GAIN_MAX:
return BLADERF_LNA_GAIN_MAX_DB;
default:
return -1;
}
}
static void show_config()
{
int status;
unsigned rate;
unsigned freq;
bladerf_lpf_mode lpf_mode;
unsigned lpf_bw;
bladerf_lna_gain lna_gain;
int rxvga1_gain;
int rxvga2_gain;
int16_t lms_dc_i, lms_dc_q;
int16_t fpga_phase, fpga_gain;
struct bladerf_lms_dc_cals dc_cals;
if ((status = bladerf_get_sample_rate(BladeRF.device, BLADERF_MODULE_RX, &rate)) < 0 ||
(status = bladerf_get_frequency(BladeRF.device, BLADERF_MODULE_RX, &freq)) < 0 ||
(status = bladerf_get_lpf_mode(BladeRF.device, BLADERF_MODULE_RX, &lpf_mode)) < 0 ||
(status = bladerf_get_bandwidth(BladeRF.device, BLADERF_MODULE_RX, &lpf_bw)) < 0 ||
(status = bladerf_get_lna_gain(BladeRF.device, &lna_gain)) < 0 ||
(status = bladerf_get_rxvga1(BladeRF.device, &rxvga1_gain)) < 0 ||
(status = bladerf_get_rxvga2(BladeRF.device, &rxvga2_gain)) < 0 ||
(status = bladerf_get_correction(BladeRF.device, BLADERF_MODULE_RX, BLADERF_CORR_LMS_DCOFF_I, &lms_dc_i)) < 0 ||
(status = bladerf_get_correction(BladeRF.device, BLADERF_MODULE_RX, BLADERF_CORR_LMS_DCOFF_Q, &lms_dc_q)) < 0 ||
(status = bladerf_get_correction(BladeRF.device, BLADERF_MODULE_RX, BLADERF_CORR_FPGA_PHASE, &fpga_phase)) < 0 ||
(status = bladerf_get_correction(BladeRF.device, BLADERF_MODULE_RX, BLADERF_CORR_FPGA_GAIN, &fpga_gain)) < 0 ||
(status = bladerf_lms_get_dc_cals(BladeRF.device, &dc_cals)) < 0) {
fprintf(stderr, "bladeRF: couldn't read back device configuration\n");
return;
}
fprintf(stderr, "bladeRF: sampling rate: %.1f MHz\n", rate/1e6);
fprintf(stderr, "bladeRF: frequency: %.1f MHz\n", freq/1e6);
fprintf(stderr, "bladeRF: LNA gain: %ddB\n", lna_gain_db(lna_gain));
fprintf(stderr, "bladeRF: RXVGA1 gain: %ddB\n", rxvga1_gain);
fprintf(stderr, "bladeRF: RXVGA2 gain: %ddB\n", rxvga2_gain);
switch (lpf_mode) {
case BLADERF_LPF_NORMAL:
fprintf(stderr, "bladeRF: LPF bandwidth: %.2f MHz\n", lpf_bw/1e6);
break;
case BLADERF_LPF_BYPASSED:
fprintf(stderr, "bladeRF: LPF bypassed\n");
break;
case BLADERF_LPF_DISABLED:
fprintf(stderr, "bladeRF: LPF disabled\n");
break;
default:
fprintf(stderr, "bladeRF: LPF in unknown state\n");
break;
}
fprintf(stderr, "bladeRF: calibration settings:\n");
fprintf(stderr, " LMS DC adjust: I=%d Q=%d\n", lms_dc_i, lms_dc_q);
fprintf(stderr, " FPGA phase adjust: %+.3f degrees\n", fpga_phase * 10.0 / 4096);
fprintf(stderr, " FPGA gain adjust: %+.3f\n", fpga_gain * 1.0 / 4096);
fprintf(stderr, " LMS LPF tuning: %d\n", dc_cals.lpf_tuning);
fprintf(stderr, " LMS RX LPF filter: I=%d Q=%d\n", dc_cals.rx_lpf_i, dc_cals.rx_lpf_q);
fprintf(stderr, " LMS RXVGA2 DC ref: %d\n", dc_cals.dc_ref);
fprintf(stderr, " LMS RXVGA2A: I=%d Q=%d\n", dc_cals.rxvga2a_i, dc_cals.rxvga2a_q);
fprintf(stderr, " LMS RXVGA2B: I=%d Q=%d\n", dc_cals.rxvga2b_i, dc_cals.rxvga2b_q);
}
bool bladeRFOpen()
{
if (BladeRF.device) {
return true;
}
int status;
bladerf_set_usb_reset_on_open(true);
if ((status = bladerf_open(&BladeRF.device, Modes.dev_name)) < 0) {
fprintf(stderr, "Failed to open bladeRF: %s\n", bladerf_strerror(status));
goto error;
}
const char *fpga_path;
if (BladeRF.fpga_path) {
fpga_path = BladeRF.fpga_path;
} else {
bladerf_fpga_size size;
if ((status = bladerf_get_fpga_size(BladeRF.device, &size)) < 0) {
fprintf(stderr, "bladerf_get_fpga_size failed: %s\n", bladerf_strerror(status));
goto error;
}
switch (size) {
case BLADERF_FPGA_40KLE:
fpga_path = "/usr/share/Nuand/bladeRF/hostedx40.rbf";
break;
case BLADERF_FPGA_115KLE:
fpga_path = "/usr/share/Nuand/bladeRF/hostedx115.rbf";
break;
default:
fprintf(stderr, "bladeRF: unknown FPGA size, skipping FPGA load");
fpga_path = NULL;
break;
}
}
if (fpga_path && fpga_path[0]) {
fprintf(stderr, "bladeRF: loading FPGA bitstream from %s\n", fpga_path);
if ((status = bladerf_load_fpga(BladeRF.device, fpga_path)) < 0) {
fprintf(stderr, "bladerf_load_fpga() failed: %s\n", bladerf_strerror(status));
goto error;
}
}
switch (bladerf_device_speed(BladeRF.device)) {
case BLADERF_DEVICE_SPEED_HIGH:
BladeRF.block_size = 1024;
break;
case BLADERF_DEVICE_SPEED_SUPER:
BladeRF.block_size = 2048;
break;
default:
fprintf(stderr, "couldn't determine bladerf device speed\n");
goto error;
}
if ((status = bladerf_set_sample_rate(BladeRF.device, BLADERF_MODULE_RX, Modes.sample_rate * BladeRF.decimation, NULL)) < 0) {
fprintf(stderr, "bladerf_set_sample_rate failed: %s\n", bladerf_strerror(status));
goto error;
}
if ((status = bladerf_set_frequency(BladeRF.device, BLADERF_MODULE_RX, Modes.freq)) < 0) {
fprintf(stderr, "bladerf_set_frequency failed: %s\n", bladerf_strerror(status));
goto error;
}
if ((status = bladerf_set_lpf_mode(BladeRF.device, BLADERF_MODULE_RX, BladeRF.lpf_mode)) < 0) {
fprintf(stderr, "bladerf_set_lpf_mode failed: %s\n", bladerf_strerror(status));
goto error;
}
if ((status = bladerf_set_bandwidth(BladeRF.device, BLADERF_MODULE_RX, BladeRF.lpf_bandwidth, NULL)) < 0) {
fprintf(stderr, "bladerf_set_lpf_bandwidth failed: %s\n", bladerf_strerror(status));
goto error;
}
/* turn the tx gain right off, just in case */
if ((status = bladerf_set_gain(BladeRF.device, BLADERF_MODULE_TX, -100)) < 0) {
fprintf(stderr, "bladerf_set_gain(TX) failed: %s\n", bladerf_strerror(status));
goto error;
}
if ((status = bladerf_set_gain(BladeRF.device, BLADERF_MODULE_RX, Modes.gain / 10.0)) < 0) {
fprintf(stderr, "bladerf_set_gain(RX) failed: %s\n", bladerf_strerror(status));
goto error;
}
if ((status = bladerf_set_loopback(BladeRF.device, BLADERF_LB_NONE)) < 0) {
fprintf(stderr, "bladerf_set_loopback() failed: %s\n", bladerf_strerror(status));
goto error;
}
if ((status = bladerf_calibrate_dc(BladeRF.device, BLADERF_DC_CAL_LPF_TUNING)) < 0) {
fprintf(stderr, "bladerf_calibrate_dc(LPF_TUNING) failed: %s\n", bladerf_strerror(status));
goto error;
}
if ((status = bladerf_calibrate_dc(BladeRF.device, BLADERF_DC_CAL_RX_LPF)) < 0) {
fprintf(stderr, "bladerf_calibrate_dc(RX_LPF) failed: %s\n", bladerf_strerror(status));
goto error;
}
if ((status = bladerf_calibrate_dc(BladeRF.device, BLADERF_DC_CAL_RXVGA2)) < 0) {
fprintf(stderr, "bladerf_calibrate_dc(RXVGA2) failed: %s\n", bladerf_strerror(status));
goto error;
}
show_config();
BladeRF.converter = init_converter(INPUT_SC16Q11,
Modes.sample_rate,
Modes.dc_filter,
&BladeRF.converter_state);
if (!BladeRF.converter) {
fprintf(stderr, "can't initialize sample converter\n");
goto error;
}
return true;
error:
if (BladeRF.device) {
bladerf_close(BladeRF.device);
BladeRF.device = NULL;
}
return false;
}
static struct timespec thread_cpu;
static unsigned timeouts = 0;
static void *handle_bladerf_samples(struct bladerf *dev,
struct bladerf_stream *stream,
struct bladerf_metadata *meta,
void *samples,
size_t num_samples,
void *user_data)
{
static uint64_t nextTimestamp = 0;
static bool dropping = false;
MODES_NOTUSED(dev);
MODES_NOTUSED(stream);
MODES_NOTUSED(meta);
MODES_NOTUSED(user_data);
MODES_NOTUSED(num_samples);
// record initial time for later sys timestamp calculation
struct timespec entryTimestamp;
clock_gettime(CLOCK_REALTIME, &entryTimestamp);
pthread_mutex_lock(&Modes.data_mutex);
if (Modes.exit) {
pthread_mutex_unlock(&Modes.data_mutex);
return BLADERF_STREAM_SHUTDOWN;
}
unsigned next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS;
struct mag_buf *outbuf = &Modes.mag_buffers[Modes.first_free_buffer];
struct mag_buf *lastbuf = &Modes.mag_buffers[(Modes.first_free_buffer + MODES_MAG_BUFFERS - 1) % MODES_MAG_BUFFERS];
unsigned free_bufs = (Modes.first_filled_buffer - next_free_buffer + MODES_MAG_BUFFERS) % MODES_MAG_BUFFERS;
if (free_bufs == 0 || (dropping && free_bufs < MODES_MAG_BUFFERS/2)) {
// FIFO is full. Drop this block.
dropping = true;
pthread_mutex_unlock(&Modes.data_mutex);
return samples;
}
dropping = false;
pthread_mutex_unlock(&Modes.data_mutex);
// Copy trailing data from last block (or reset if not valid)
if (outbuf->dropped == 0) {
memcpy(outbuf->data, lastbuf->data + lastbuf->length, Modes.trailing_samples * sizeof(uint16_t));
} else {
memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t));
}
// start handling metadata blocks
outbuf->dropped = 0;
outbuf->length = 0;
outbuf->mean_level = outbuf->mean_power = 0;
unsigned blocks_processed = 0;
unsigned samples_per_block = (BladeRF.block_size - 16) / 4;
static bool overrun = true; // ignore initial overruns as we get up to speed
static bool first_buffer = true;
for (unsigned offset = 0; offset < MODES_MAG_BUF_SAMPLES * 4; offset += BladeRF.block_size) {
// read the next metadata header
uint8_t *header = ((uint8_t*)samples) + offset;
uint64_t metadata_magic = le32toh(*(uint32_t*)(header));
uint64_t metadata_timestamp = le64toh(*(uint64_t*)(header + 4));
uint32_t metadata_flags = le32toh(*(uint32_t*)(header + 12));
void *sample_data = header + 16;
if (metadata_magic != 0x12344321) {
// first buffer is often in the wrong mode
if (!first_buffer) {
fprintf(stderr, "bladeRF: wrong metadata header magic value, skipping rest of buffer\n");
}
break;
}
if (metadata_flags & BLADERF_META_STATUS_OVERRUN) {
if (!overrun) {
fprintf(stderr, "bladeRF: receive overrun\n");
}
overrun = true;
} else {
overrun = false;
}
#ifndef BROKEN_FPGA_METADATA
// this needs a fixed decimating FPGA image that handles the timestamp correctly
if (nextTimestamp && nextTimestamp != metadata_timestamp) {
// dropped data or lost sync. start again.
if (metadata_timestamp > nextTimestamp)
outbuf->dropped += (metadata_timestamp - nextTimestamp);
outbuf->dropped += outbuf->length;
outbuf->length = 0;
blocks_processed = 0;
outbuf->mean_level = outbuf->mean_power = 0;
nextTimestamp = metadata_timestamp;
}
#else
MODES_NOTUSED(metadata_timestamp);
#endif
if (!blocks_processed) {
// Compute the sample timestamp for the start of the block
outbuf->sampleTimestamp = nextTimestamp * 12e6 / Modes.sample_rate / BladeRF.decimation;
}
// Convert a block of data
double mean_level, mean_power;
BladeRF.converter(sample_data, &outbuf->data[Modes.trailing_samples + outbuf->length], samples_per_block, BladeRF.converter_state, &mean_level, &mean_power);
outbuf->length += samples_per_block;
outbuf->mean_level += mean_level;
outbuf->mean_power += mean_power;
nextTimestamp += samples_per_block * BladeRF.decimation;
++blocks_processed;
timeouts = 0;
}
first_buffer = false;
if (blocks_processed) {
// Get the approx system time for the start of this block
unsigned block_duration = 1e9 * outbuf->length / Modes.sample_rate;
outbuf->sysTimestamp = entryTimestamp;
outbuf->sysTimestamp.tv_nsec -= block_duration;
normalize_timespec(&outbuf->sysTimestamp);
outbuf->mean_level /= blocks_processed;
outbuf->mean_power /= blocks_processed;
// Push the new data to the demodulation thread
pthread_mutex_lock(&Modes.data_mutex);
// accumulate CPU while holding the mutex, and restart measurement
end_cpu_timing(&thread_cpu, &Modes.reader_cpu_accumulator);
start_cpu_timing(&thread_cpu);
Modes.mag_buffers[next_free_buffer].dropped = 0;
Modes.mag_buffers[next_free_buffer].length = 0; // just in case
Modes.first_free_buffer = next_free_buffer;
pthread_cond_signal(&Modes.data_cond);
pthread_mutex_unlock(&Modes.data_mutex);
}
return samples;
}
void bladeRFRun()
{
if (!BladeRF.device) {
return;
}
unsigned transfers = 7;
int status;
struct bladerf_stream *stream = NULL;
void **buffers = NULL;
if ((status = bladerf_init_stream(&stream,
BladeRF.device,
handle_bladerf_samples,
&buffers,
/* num_buffers */ transfers,
BLADERF_FORMAT_SC16_Q11_META,
/* samples_per_buffer */ MODES_MAG_BUF_SAMPLES,
/* num_transfers */ transfers,
/* user_data */ NULL)) < 0) {
fprintf(stderr, "bladerf_init_stream() failed: %s\n", bladerf_strerror(status));
goto out;
}
unsigned ms_per_transfer = 1000 * MODES_MAG_BUF_SAMPLES / Modes.sample_rate;
if ((status = bladerf_set_stream_timeout(BladeRF.device, BLADERF_MODULE_RX, ms_per_transfer * (transfers + 2))) < 0) {
fprintf(stderr, "bladerf_set_stream_timeout() failed: %s\n", bladerf_strerror(status));
goto out;
}
if ((status = bladerf_enable_module(BladeRF.device, BLADERF_MODULE_RX, true) < 0)) {
fprintf(stderr, "bladerf_enable_module(RX, true) failed: %s\n", bladerf_strerror(status));
goto out;
}
start_cpu_timing(&thread_cpu);
timeouts = 0; // reset to zero when we get a callback with some data
retry:
if ((status = bladerf_stream(stream, BLADERF_MODULE_RX)) < 0) {
fprintf(stderr, "bladerf_stream() failed: %s\n", bladerf_strerror(status));
if (status == BLADERF_ERR_TIMEOUT) {
if (++timeouts < 5)
goto retry;
fprintf(stderr, "bladerf is wedged, giving up.\n");
}
goto out;
}
out:
if ((status = bladerf_enable_module(BladeRF.device, BLADERF_MODULE_RX, false) < 0)) {
fprintf(stderr, "bladerf_enable_module(RX, false) failed: %s\n", bladerf_strerror(status));
}
if (stream) {
bladerf_deinit_stream(stream);
}
}
void bladeRFClose()
{
if (BladeRF.converter) {
cleanup_converter(BladeRF.converter_state);
BladeRF.converter = NULL;
BladeRF.converter_state = NULL;
}
if (BladeRF.device) {
bladerf_close(BladeRF.device);
BladeRF.device = NULL;
}
}

32
sdr_bladerf.h Normal file
View file

@ -0,0 +1,32 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// sdr_bladerf.h: bladeRF support (header)
//
// Copyright (c) 2017 FlightAware LLC
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
//
// This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef BLADERF_H
#define BLADERF_H
// Support for the Nuand bladeRF SDR
void bladeRFInitConfig();
void bladeRFShowHelp();
bool bladeRFHandleOption(int argc, char **argv, int *jptr);
bool bladeRFOpen();
void bladeRFRun();
void bladeRFClose();
#endif

286
sdr_ifile.c Normal file
View file

@ -0,0 +1,286 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// sdr_ifile.c: "file" SDR support
//
// Copyright (c) 2014-2017 Oliver Jowett <oliver@mutability.co.uk>
// Copyright (c) 2017 FlightAware LLC
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
//
// This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
// This file incorporates work covered by the following copyright and
// permission notice:
//
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "dump1090.h"
#include "sdr_ifile.h"
static struct {
const char *filename;
input_format_t input_format;
bool throttle;
int fd;
unsigned bytes_per_sample;
void *readbuf;
iq_convert_fn converter;
struct converter_state *converter_state;
} ifile;
void ifileInitConfig(void)
{
ifile.filename = NULL;
ifile.input_format = INPUT_UC8;
ifile.throttle = false;
ifile.fd = -1;
ifile.bytes_per_sample = 0;
ifile.readbuf = NULL;
ifile.converter = NULL;
ifile.converter_state = NULL;
}
void ifileShowHelp()
{
printf(" ifile-specific options (use with --ifile)\n");
printf("\n");
printf("--ifile <path> read samples from given file ('-' for stdin)\n");
printf("--iformat <type> set sample format (UC8, SC16, SC16Q11)\n");
printf("--throttle process samples at the original capture speed\n");
printf("\n");
}
bool ifileHandleOption(int argc, char **argv, int *jptr)
{
int j = *jptr;
bool more = (j +1 < argc);
if (!strcmp(argv[j], "--ifile") && more) {
// implies --device-type ifile
ifile.filename = strdup(argv[++j]);
Modes.sdr_type = SDR_IFILE;
} else if (!strcmp(argv[j],"--iformat") && more) {
++j;
if (!strcasecmp(argv[j], "uc8")) {
ifile.input_format = INPUT_UC8;
} else if (!strcasecmp(argv[j], "sc16")) {
ifile.input_format = INPUT_SC16;
} else if (!strcasecmp(argv[j], "sc16q11")) {
ifile.input_format = INPUT_SC16Q11;
} else {
fprintf(stderr, "Input format '%s' not understood (supported values: UC8, SC16, SC16Q11)\n",
argv[j]);
return false;
}
} else if (!strcmp(argv[j],"--throttle")) {
ifile.throttle = true;
} else {
return false;
}
*jptr = j;
return true;
}
//
//=========================================================================
//
// This is used when --ifile is specified in order to read data from file
// instead of using an RTLSDR device
//
bool ifileOpen(void)
{
if (!ifile.filename) {
fprintf(stderr, "SDR type 'ifile' requires an --ifile argument\n");
return false;
}
if (!strcmp(ifile.filename, "-")) {
ifile.fd = STDIN_FILENO;
} else if ((ifile.fd = open(ifile.filename, O_RDONLY)) < 0) {
fprintf(stderr, "ifile: could not open %s: %s\n",
ifile.filename, strerror(errno));
return false;
}
switch (ifile.input_format) {
case INPUT_UC8:
ifile.bytes_per_sample = 2;
break;
case INPUT_SC16:
case INPUT_SC16Q11:
ifile.bytes_per_sample = 4;
break;
default:
fprintf(stderr, "ifile: unhandled input format\n");
ifileClose();
return false;
}
if (!(ifile.readbuf = malloc(MODES_MAG_BUF_SAMPLES * ifile.bytes_per_sample))) {
fprintf(stderr, "ifile: failed to allocate read buffer\n");
ifileClose();
return false;
}
ifile.converter = init_converter(ifile.input_format,
Modes.sample_rate,
Modes.dc_filter,
&ifile.converter_state);
if (!ifile.converter) {
fprintf(stderr, "ifile: can't initialize sample converter\n");
ifileClose();
return false;
}
return true;
}
void ifileRun()
{
if (ifile.fd < 0)
return;
int eof = 0;
struct timespec next_buffer_delivery;
struct timespec thread_cpu;
start_cpu_timing(&thread_cpu);
uint64_t sampleCounter = 0;
clock_gettime(CLOCK_MONOTONIC, &next_buffer_delivery);
pthread_mutex_lock(&Modes.data_mutex);
while (!Modes.exit && !eof) {
ssize_t nread, toread;
void *r;
struct mag_buf *outbuf, *lastbuf;
unsigned next_free_buffer;
unsigned slen;
next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS;
if (next_free_buffer == Modes.first_filled_buffer) {
// no space for output yet
pthread_cond_wait(&Modes.data_cond, &Modes.data_mutex);
continue;
}
outbuf = &Modes.mag_buffers[Modes.first_free_buffer];
lastbuf = &Modes.mag_buffers[(Modes.first_free_buffer + MODES_MAG_BUFFERS - 1) % MODES_MAG_BUFFERS];
pthread_mutex_unlock(&Modes.data_mutex);
// Compute the sample timestamp for the start of the block
outbuf->sampleTimestamp = sampleCounter * 12e6 / Modes.sample_rate;
sampleCounter += MODES_MAG_BUF_SAMPLES;
// Copy trailing data from last block (or reset if not valid)
if (lastbuf->length >= Modes.trailing_samples) {
memcpy(outbuf->data, lastbuf->data + lastbuf->length, Modes.trailing_samples * sizeof(uint16_t));
} else {
memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t));
}
// Get the system time for the start of this block
clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp);
toread = MODES_MAG_BUF_SAMPLES * ifile.bytes_per_sample;
r = ifile.readbuf;
while (toread) {
nread = read(ifile.fd, r, toread);
if (nread <= 0) {
if (nread < 0) {
fprintf(stderr, "ifile: error reading input file: %s\n", strerror(errno));
}
// Done.
eof = 1;
break;
}
r += nread;
toread -= nread;
}
slen = outbuf->length = MODES_MAG_BUF_SAMPLES - toread / ifile.bytes_per_sample;
// Convert the new data
ifile.converter(ifile.readbuf, &outbuf->data[Modes.trailing_samples], slen, ifile.converter_state, &outbuf->mean_level, &outbuf->mean_power);
if (ifile.throttle || Modes.interactive) {
// Wait until we are allowed to release this buffer to the main thread
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_buffer_delivery, NULL) == EINTR)
;
// compute the time we can deliver the next buffer.
next_buffer_delivery.tv_nsec += outbuf->length * 1e9 / Modes.sample_rate;
normalize_timespec(&next_buffer_delivery);
}
// Push the new data to the main thread
pthread_mutex_lock(&Modes.data_mutex);
Modes.first_free_buffer = next_free_buffer;
// accumulate CPU while holding the mutex, and restart measurement
end_cpu_timing(&thread_cpu, &Modes.reader_cpu_accumulator);
start_cpu_timing(&thread_cpu);
pthread_cond_signal(&Modes.data_cond);
}
// Wait for the main thread to consume all data
while (!Modes.exit && Modes.first_filled_buffer != Modes.first_free_buffer)
pthread_cond_wait(&Modes.data_cond, &Modes.data_mutex);
pthread_mutex_unlock(&Modes.data_mutex);
}
void ifileClose()
{
if (ifile.converter) {
cleanup_converter(ifile.converter_state);
ifile.converter = NULL;
ifile.converter_state = NULL;
}
if (ifile.readbuf) {
free(ifile.readbuf);
ifile.readbuf = NULL;
}
if (ifile.fd >= 0 && ifile.fd != STDIN_FILENO) {
close(ifile.fd);
ifile.fd = -1;
}
}

33
sdr_ifile.h Normal file
View file

@ -0,0 +1,33 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// sdr_ifile.c: "file" SDR support (header)
//
// Copyright (c) 2016-2017 Oliver Jowett <oliver@mutability.co.uk>
// Copyright (c) 2017 FlightAware LLC
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
//
// This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef SDR_IFILE_H
#define SDR_IFILE_H
// Pseudo-SDR that reads from a sample file
void ifileInitConfig();
void ifileShowHelp();
bool ifileHandleOption(int argc, char **argv, int *jptr);
bool ifileOpen();
void ifileRun();
void ifileClose();
#endif

385
sdr_rtlsdr.c Normal file
View file

@ -0,0 +1,385 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// sdr_rtlsdr.c: rtlsdr dongle support
//
// Copyright (c) 2014-2017 Oliver Jowett <oliver@mutability.co.uk>
// Copyright (c) 2017 FlightAware LLC
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
//
// This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
// This file incorporates work covered by the following copyright and
// permission notice:
//
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "dump1090.h"
#include "sdr_rtlsdr.h"
#include <rtl-sdr.h>
static struct {
rtlsdr_dev_t *dev;
bool digital_agc;
int ppm_error;
iq_convert_fn converter;
struct converter_state *converter_state;
} RTLSDR;
//
// =============================== RTLSDR handling ==========================
//
void rtlsdrInitConfig()
{
RTLSDR.dev = NULL;
RTLSDR.digital_agc = false;
RTLSDR.ppm_error = 0;
RTLSDR.converter = NULL;
RTLSDR.converter_state = NULL;
}
static void show_rtlsdr_devices()
{
int device_count = rtlsdr_get_device_count();
fprintf(stderr, "rtlsdr: found %d device(s):\n", device_count);
for (int i = 0; i < device_count; i++) {
char vendor[256], product[256], serial[256];
if (rtlsdr_get_device_usb_strings(i, vendor, product, serial) != 0) {
fprintf(stderr, " %d: unable to read device details\n", i);
} else {
fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial);
}
}
}
static int find_device_index(char *s)
{
int device_count = rtlsdr_get_device_count();
if (!device_count) {
return -1;
}
/* does string look like raw id number */
if (!strcmp(s, "0")) {
return 0;
} else if (s[0] != '0') {
char *s2;
int device = (int)strtol(s, &s2, 10);
if (s2[0] == '\0' && device >= 0 && device < device_count) {
return device;
}
}
/* does string exact match a serial */
for (int i = 0; i < device_count; i++) {
char serial[256];
if (rtlsdr_get_device_usb_strings(i, NULL, NULL, serial) == 0 && !strcmp(s, serial)) {
return i;
}
}
/* does string prefix match a serial */
for (int i = 0; i < device_count; i++) {
char serial[256];
if (rtlsdr_get_device_usb_strings(i, NULL, NULL, serial) == 0 && !strncmp(s, serial, strlen(s))) {
return i;
}
}
/* does string suffix match a serial */
for (int i = 0; i < device_count; i++) {
char serial[256];
if (rtlsdr_get_device_usb_strings(i, NULL, NULL, serial) == 0 && strlen(s) < strlen(serial) && !strcmp(serial + strlen(serial) - strlen(s), s)) {
return i;
}
}
return -1;
}
void rtlsdrShowHelp()
{
printf(" rtlsdr-specific options (use with --device-type rtlsdr)\n");
printf("\n");
printf("--device <index|serial> select device by index or serial number\n");
printf("--enable-agc enable digital AGC (not tuner AGC!)\n");
printf("--ppm <correction> set oscillator frequency correction in PPM\n");
printf("\n");
}
bool rtlsdrHandleOption(int argc, char **argv, int *jptr)
{
int j = *jptr;
bool more = (j +1 < argc);
if (!strcmp(argv[j], "--enable-agc")) {
RTLSDR.digital_agc = true;
} else if (!strcmp(argv[j], "--ppm") && more) {
RTLSDR.ppm_error = atoi(argv[++j]);
} else {
return false;
}
*jptr = j;
return true;
}
bool rtlsdrOpen(void) {
if (!rtlsdr_get_device_count()) {
fprintf(stderr, "rtlsdr: no supported devices found.\n");
return false;
}
int dev_index = 0;
if (Modes.dev_name) {
if ((dev_index = find_device_index(Modes.dev_name)) < 0) {
fprintf(stderr, "rtlsdr: no device matching '%s' found.\n", Modes.dev_name);
show_rtlsdr_devices();
return false;
}
}
char manufacturer[256];
char product[256];
char serial[256];
if (rtlsdr_get_device_usb_strings(dev_index, manufacturer, product, serial) < 0) {
fprintf(stderr, "rtlsdr: error querying device #%d: %s\n", dev_index, strerror(errno));
return false;
}
fprintf(stderr, "rtlsdr: using device #%d: %s (%s, %s, SN %s)\n",
dev_index, rtlsdr_get_device_name(dev_index),
manufacturer, product, serial);
if (rtlsdr_open(&RTLSDR.dev, dev_index) < 0) {
fprintf(stderr, "rtlsdr: error opening the RTLSDR device: %s\n",
strerror(errno));
return false;
}
// Set gain, frequency, sample rate, and reset the device
if (Modes.gain == MODES_AUTO_GAIN) {
fprintf(stderr, "rtlsdr: enabling tuner AGC\n");
rtlsdr_set_tuner_gain_mode(RTLSDR.dev, 1);
} else {
int *gains;
int numgains;
numgains = rtlsdr_get_tuner_gains(RTLSDR.dev, NULL);
if (numgains <= 0) {
fprintf(stderr, "rtlsdr: error getting tuner gains\n");
return false;
}
gains = malloc(numgains * sizeof(int));
if (rtlsdr_get_tuner_gains(RTLSDR.dev, gains) != numgains) {
fprintf(stderr, "rtlsdr: error getting tuner gains\n");
free(gains);
return false;
}
int target = (Modes.gain == MODES_MAX_GAIN ? 9999 : Modes.gain);
int closest = -1;
for (int i = 0; i < numgains; ++i) {
if (closest == -1 || abs(gains[i] - target) < abs(gains[closest] - target))
closest = i;
}
free(gains);
rtlsdr_set_tuner_gain(RTLSDR.dev, gains[closest]);
fprintf(stderr, "rtlsdr: tuner gain set to %.1f dB\n",
rtlsdr_get_tuner_gain(RTLSDR.dev)/10.0);
}
if (RTLSDR.digital_agc) {
fprintf(stderr, "rtlsdr: enabling digital AGC\n");
rtlsdr_set_agc_mode(RTLSDR.dev, 1);
}
rtlsdr_set_freq_correction(RTLSDR.dev, RTLSDR.ppm_error);
rtlsdr_set_center_freq(RTLSDR.dev, Modes.freq);
rtlsdr_set_sample_rate(RTLSDR.dev, (unsigned)Modes.sample_rate);
rtlsdr_reset_buffer(RTLSDR.dev);
RTLSDR.converter = init_converter(INPUT_UC8,
Modes.sample_rate,
Modes.dc_filter,
&RTLSDR.converter_state);
if (!RTLSDR.converter) {
fprintf(stderr, "rtlsdr: can't initialize sample converter\n");
rtlsdrClose();
return false;
}
return true;
}
static struct timespec rtlsdr_thread_cpu;
void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) {
struct mag_buf *outbuf;
struct mag_buf *lastbuf;
uint32_t slen;
unsigned next_free_buffer;
unsigned free_bufs;
unsigned block_duration;
static int was_odd = 0; // paranoia!!
static int dropping = 0;
static uint64_t sampleCounter = 0;
MODES_NOTUSED(ctx);
// Lock the data buffer variables before accessing them
pthread_mutex_lock(&Modes.data_mutex);
if (Modes.exit) {
rtlsdr_cancel_async(RTLSDR.dev); // ask our caller to exit
}
next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS;
outbuf = &Modes.mag_buffers[Modes.first_free_buffer];
lastbuf = &Modes.mag_buffers[(Modes.first_free_buffer + MODES_MAG_BUFFERS - 1) % MODES_MAG_BUFFERS];
free_bufs = (Modes.first_filled_buffer - next_free_buffer + MODES_MAG_BUFFERS) % MODES_MAG_BUFFERS;
// Paranoia! Unlikely, but let's go for belt and suspenders here
if (len != MODES_RTL_BUF_SIZE) {
fprintf(stderr, "weirdness: rtlsdr gave us a block with an unusual size (got %u bytes, expected %u bytes)\n",
(unsigned)len, (unsigned)MODES_RTL_BUF_SIZE);
if (len > MODES_RTL_BUF_SIZE) {
// wat?! Discard the start.
unsigned discard = (len - MODES_RTL_BUF_SIZE + 1) / 2;
outbuf->dropped += discard;
buf += discard*2;
len -= discard*2;
}
}
if (was_odd) {
// Drop a sample so we are in sync with I/Q samples again (hopefully)
++buf;
--len;
++outbuf->dropped;
}
was_odd = (len & 1);
slen = len/2;
if (free_bufs == 0 || (dropping && free_bufs < MODES_MAG_BUFFERS/2)) {
// FIFO is full. Drop this block.
dropping = 1;
outbuf->dropped += slen;
sampleCounter += slen;
pthread_mutex_unlock(&Modes.data_mutex);
return;
}
dropping = 0;
pthread_mutex_unlock(&Modes.data_mutex);
// Compute the sample timestamp and system timestamp for the start of the block
outbuf->sampleTimestamp = sampleCounter * 12e6 / Modes.sample_rate;
sampleCounter += slen;
block_duration = 1e9 * slen / Modes.sample_rate;
// Get the approx system time for the start of this block
clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp);
outbuf->sysTimestamp.tv_nsec -= block_duration;
normalize_timespec(&outbuf->sysTimestamp);
// Copy trailing data from last block (or reset if not valid)
if (outbuf->dropped == 0) {
memcpy(outbuf->data, lastbuf->data + lastbuf->length, Modes.trailing_samples * sizeof(uint16_t));
} else {
memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t));
}
// Convert the new data
outbuf->length = slen;
RTLSDR.converter(buf, &outbuf->data[Modes.trailing_samples], slen, RTLSDR.converter_state, &outbuf->mean_level, &outbuf->mean_power);
// Push the new data to the demodulation thread
pthread_mutex_lock(&Modes.data_mutex);
Modes.mag_buffers[next_free_buffer].dropped = 0;
Modes.mag_buffers[next_free_buffer].length = 0; // just in case
Modes.first_free_buffer = next_free_buffer;
// accumulate CPU while holding the mutex, and restart measurement
end_cpu_timing(&rtlsdr_thread_cpu, &Modes.reader_cpu_accumulator);
start_cpu_timing(&rtlsdr_thread_cpu);
pthread_cond_signal(&Modes.data_cond);
pthread_mutex_unlock(&Modes.data_mutex);
}
void rtlsdrRun()
{
if (!RTLSDR.dev) {
return;
}
start_cpu_timing(&rtlsdr_thread_cpu);
while (!Modes.exit) {
rtlsdr_read_async(RTLSDR.dev, rtlsdrCallback, NULL,
/* MODES_RTL_BUFFERS */ 4,
MODES_RTL_BUF_SIZE);
}
}
void rtlsdrClose()
{
if (RTLSDR.dev) {
rtlsdr_close(RTLSDR.dev);
RTLSDR.dev = NULL;
}
if (RTLSDR.converter) {
cleanup_converter(RTLSDR.converter_state);
RTLSDR.converter = NULL;
RTLSDR.converter_state = NULL;
}
}

31
sdr_rtlsdr.h Normal file
View file

@ -0,0 +1,31 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// sdr_rtlsdr.h: rtlsdr dongle support (header)
//
// Copyright (c) 2016-2017 Oliver Jowett <oliver@mutability.co.uk>
// Copyright (c) 2017 FlightAware LLC
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
//
// This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef SDR_RTLSDR_H
#define SDR_RTLSDR_H
void rtlsdrInitConfig();
void rtlsdrShowHelp();
bool rtlsdrOpen();
void rtlsdrRun();
void rtlsdrClose();
bool rtlsdrHandleOption(int argc, char **argv, int *jptr);
#endif

18
util.c
View file

@ -47,7 +47,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "util.h"
#include "dump1090.h"
#include <stdlib.h>
#include <sys/time.h>
@ -79,3 +79,19 @@ void normalize_timespec(struct timespec *ts)
ts->tv_nsec = (ts->tv_nsec + 1000000000 * adjust) % 1000000000;
}
}
/* record current CPU time in start_time */
void start_cpu_timing(struct timespec *start_time)
{
clock_gettime(CLOCK_THREAD_CPUTIME_ID, start_time);
}
/* add difference between start_time and the current CPU time to add_to */
void end_cpu_timing(const struct timespec *start_time, struct timespec *add_to)
{
struct timespec end_time;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end_time);
add_to->tv_sec += end_time.tv_sec - start_time->tv_sec;
add_to->tv_nsec += end_time.tv_nsec - start_time->tv_nsec;
normalize_timespec(add_to);
}

6
util.h
View file

@ -36,4 +36,10 @@ int64_t receiveclock_ns_elapsed(uint64_t t1, uint64_t t2);
struct timespec;
void normalize_timespec(struct timespec *ts);
/* record current CPU time in start_time */
void start_cpu_timing(struct timespec *start_time);
/* add difference between start_time and the current CPU time to add_to */
void end_cpu_timing(const struct timespec *start_time, struct timespec *add_to);
#endif