Factor out the sample -> magnitude conversion code and make everything a little less sample-rate-dependent.

Add optional noise measurement (cheaper than the old version)
Add optional DC filter (expensive, not really needed with rtlsdr input)
This commit is contained in:
Oliver Jowett 2015-06-15 22:14:37 +01:00
parent f58ff14d7c
commit 03b53c2d29
7 changed files with 454 additions and 156 deletions

View file

@ -25,7 +25,7 @@ all: dump1090 view1090
%.o: %.c *.h %.o: %.c *.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRACFLAGS) -c $< -o $@ $(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRACFLAGS) -c $< -o $@
dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demod_2000.o demod_2400.o stats.o cpr.o icao_filter.o track.o util.o dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demod_2000.o demod_2400.o stats.o cpr.o icao_filter.o track.o util.o convert.o
$(CC) -g -o $@ $^ $(LIBS) $(LIBS_RTL) $(LDFLAGS) $(CC) -g -o $@ $^ $(LIBS) $(LIBS_RTL) $(LDFLAGS)
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 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

317
convert.c Normal file
View file

@ -0,0 +1,317 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// convert.c: support for various IQ -> magnitude conversions
//
// Copyright (c) 2015 Oliver Jowett <oliver@mutability.co.uk>
//
// 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"
struct converter_state {
float dc_a;
float dc_b;
float z1_I;
float z1_Q;
};
static void convert_uc8_nodc_nopower(void *iq_data,
uint16_t *mag_data,
unsigned nsamples,
struct converter_state *state,
double *out_power)
{
uint16_t *in = iq_data;
unsigned i;
MODES_NOTUSED(state);
// unroll this a bit
for (i = 0; i < (nsamples>>3); ++i) {
*mag_data++ = Modes.maglut[*in++];
*mag_data++ = Modes.maglut[*in++];
*mag_data++ = Modes.maglut[*in++];
*mag_data++ = Modes.maglut[*in++];
*mag_data++ = Modes.maglut[*in++];
*mag_data++ = Modes.maglut[*in++];
*mag_data++ = Modes.maglut[*in++];
*mag_data++ = Modes.maglut[*in++];
}
for (i = 0; i < (nsamples&7); ++i) {
*mag_data++ = Modes.maglut[*in++];
}
if (out_power)
*out_power = 0.0; // not measured
}
static void convert_uc8_nodc_power(void *iq_data,
uint16_t *mag_data,
unsigned nsamples,
struct converter_state *state,
double *out_power)
{
uint16_t *in = iq_data;
unsigned i;
uint16_t mag;
uint64_t power = 0;
MODES_NOTUSED(state);
// unroll this a bit
for (i = 0; i < (nsamples>>3); ++i) {
mag = Modes.maglut[*in++];
*mag_data++ = mag;
power += mag*mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
power += mag*mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
power += mag*mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
power += mag*mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
power += mag*mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
power += mag*mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
power += mag*mag;
mag = Modes.maglut[*in++];
*mag_data++ = mag;
power += mag*mag;
}
for (i = 0; i < (nsamples&7); ++i) {
mag = Modes.maglut[*in++];
*mag_data++ = mag;
power += mag*mag;
}
if (out_power)
*out_power = power / (65535.0 * 65535.0);
}
static void convert_uc8_generic(void *iq_data,
uint16_t *mag_data,
unsigned nsamples,
struct converter_state *state,
double *out_power)
{
uint8_t *in = iq_data;
float power = 0.0;
float z1_I = state->z1_I;
float z1_Q = state->z1_Q;
const float dc_a = state->dc_a;
const float dc_b = state->dc_b;
unsigned i;
uint8_t I, Q;
float fI, fQ, magsq;
for (i = 0; i < nsamples; ++i) {
I = *in++;
Q = *in++;
fI = (I - 127.5) / 127.5;
fQ = (Q - 127.5) / 127.5;
// DC block
z1_I = fI * dc_a + z1_I * dc_b;
z1_Q = fQ * dc_a + z1_Q * dc_b;
fI -= z1_I;
fQ -= z1_Q;
magsq = fI * fI + fQ * fQ;
if (magsq > 1)
magsq = 1;
power += magsq;
*mag_data++ = (uint16_t)(sqrtf(magsq) * 65535.0 + 0.5);
}
state->z1_I = z1_I;
state->z1_Q = z1_Q;
if (out_power)
*out_power = power;
}
static void convert_sc16_generic(void *iq_data,
uint16_t *mag_data,
unsigned nsamples,
struct converter_state *state,
double *out_power)
{
uint16_t *in = iq_data;
float power = 0.0;
float z1_I = state->z1_I;
float z1_Q = state->z1_Q;
const float dc_a = state->dc_a;
const float dc_b = state->dc_b;
unsigned i;
int16_t I, Q;
float fI, fQ, magsq;
for (i = 0; i < nsamples; ++i) {
I = (int16_t)le16toh(*in++);
Q = (int16_t)le16toh(*in++);
fI = I / 32768.0;
fQ = Q / 32768.0;
// DC block
z1_I = fI * dc_a + z1_I * dc_b;
z1_Q = fQ * dc_a + z1_Q * dc_b;
fI -= z1_I;
fQ -= z1_Q;
magsq = fI * fI + fQ * fQ;
if (magsq > 1)
magsq = 1;
power += magsq;
*mag_data++ = (uint16_t)(sqrtf(magsq) * 65535.0 + 0.5);
}
state->z1_I = z1_I;
state->z1_Q = z1_Q;
if (out_power)
*out_power = power;
}
static void convert_sc16q11_generic(void *iq_data,
uint16_t *mag_data,
unsigned nsamples,
struct converter_state *state,
double *out_power)
{
uint16_t *in = iq_data;
float power = 0.0;
float z1_I = state->z1_I;
float z1_Q = state->z1_Q;
const float dc_a = state->dc_a;
const float dc_b = state->dc_b;
unsigned i;
int16_t I, Q;
float fI, fQ, magsq;
for (i = 0; i < nsamples; ++i) {
I = (int16_t)le16toh(*in++);
Q = (int16_t)le16toh(*in++);
fI = I / 2048.0;
fQ = Q / 2048.0;
// DC block
z1_I = fI * dc_a + z1_I * dc_b;
z1_Q = fQ * dc_a + z1_Q * dc_b;
fI -= z1_I;
fQ -= z1_Q;
magsq = fI * fI + fQ * fQ;
if (magsq > 1)
magsq = 1;
power += magsq;
*mag_data++ = (uint16_t)(sqrtf(magsq) * 65535.0 + 0.5);
}
state->z1_I = z1_I;
state->z1_Q = z1_Q;
if (out_power)
*out_power = power;
}
static struct {
input_format_t format;
int can_filter_dc;
int can_compute_power;
iq_convert_fn fn;
const char *description;
} converters_table[] = {
// In order of preference
{ INPUT_UC8, 0, 0, convert_uc8_nodc_nopower, "UC8, integer/table path" },
{ INPUT_UC8, 0, 1, convert_uc8_nodc_power, "UC8, integer/table path, with power measurement" },
{ INPUT_UC8, 1, 1, convert_uc8_generic, "UC8, float path" },
{ INPUT_SC16, 1, 1, convert_sc16_generic, "SC16, float path" },
{ INPUT_SC16Q11, 1, 1, convert_sc16q11_generic, "SC16Q11, float path" },
{ 0, 0, 0, NULL, NULL }
};
iq_convert_fn init_converter(input_format_t format,
double sample_rate,
int filter_dc,
int compute_power,
struct converter_state **out_state)
{
int i;
for (i = 0; converters_table[i].fn; ++i) {
if (converters_table[i].format != format)
continue;
if (filter_dc && !converters_table[i].can_filter_dc)
continue;
if (compute_power && !converters_table[i].can_compute_power)
continue;
break;
}
if (!converters_table[i].fn) {
fprintf(stderr, "no suitable converter for format=%d power=%d dc=%d\n",
format, compute_power, filter_dc);
return NULL;
}
fprintf(stderr, "Using sample converter: %s\n", converters_table[i].description);
*out_state = malloc(sizeof(struct converter_state));
if (! *out_state) {
fprintf(stderr, "can't allocate converter state\n");
return NULL;
}
(*out_state)->z1_I = 0;
(*out_state)->z1_Q = 0;
if (filter_dc) {
// init DC block @ 1Hz
(*out_state)->dc_b = exp(-2.0 * M_PI * 1.0 / sample_rate);
(*out_state)->dc_a = 1.0 - (*out_state)->dc_b;
} else {
// if the converter does filtering, make sure it has no effect
(*out_state)->dc_b = 1.0;
(*out_state)->dc_a = 0.0;
}
return converters_table[i].fn;
}
void cleanup_converter(struct converter_state *state)
{
free(state);
}

40
convert.h Normal file
View file

@ -0,0 +1,40 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// convert.h: support for various IQ -> magnitude conversions
//
// Copyright (c) 2015 Oliver Jowett <oliver@mutability.co.uk>
//
// 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 DUMP1090_CONVERT_H
#define DUMP1090_CONVERT_H
struct converter_state;
typedef enum { INPUT_UC8=0, INPUT_SC16, INPUT_SC16Q11 } input_format_t;
typedef void (*iq_convert_fn)(void *iq_data,
uint16_t *mag_data,
unsigned nsamples,
struct converter_state *state,
double *out_power);
iq_convert_fn init_converter(input_format_t format,
double sample_rate,
int filter_dc,
int compute_power,
struct converter_state **out_state);
void cleanup_converter(struct converter_state *state);
#endif

View file

@ -19,12 +19,6 @@
#include "dump1090.h" #include "dump1090.h"
//
// Measuring the noise power is actually surprisingly expensive on an ARM -
// it increases the CPU use of the demodulator by 1/3. So it's off by default.
// You can turn it back on here:
#undef MEASURE_NOISE
// 2.4MHz sampling rate version // 2.4MHz sampling rate version
// //
// When sampling at 2.4MHz we have exactly 6 samples per 5 symbols. // When sampling at 2.4MHz we have exactly 6 samples per 5 symbols.
@ -155,26 +149,20 @@ static int best_phase(uint16_t *m) {
// Given 'mlen' magnitude samples in 'm', sampled at 2.4MHz, // Given 'mlen' magnitude samples in 'm', sampled at 2.4MHz,
// try to demodulate some Mode S messages. // try to demodulate some Mode S messages.
// //
void demodulate2400(struct mag_buf *mag) { void demodulate2400(struct mag_buf *mag)
{
struct modesMessage mm; struct modesMessage mm;
unsigned char msg1[MODES_LONG_MSG_BYTES], msg2[MODES_LONG_MSG_BYTES], *msg; unsigned char msg1[MODES_LONG_MSG_BYTES], msg2[MODES_LONG_MSG_BYTES], *msg;
uint32_t j; uint32_t j;
#ifdef MEASURE_NOISE
uint32_t last_message_end = 0;
#endif
unsigned char *bestmsg; unsigned char *bestmsg;
int bestscore, bestphase; int bestscore, bestphase;
#ifdef MEASURE_NOISE
// noise floor:
uint32_t noise_power_count = 0;
uint64_t noise_power_sum = 0;
#endif
uint16_t *m = mag->data; uint16_t *m = mag->data;
uint32_t mlen = mag->length; uint32_t mlen = mag->length;
double total_signal_power = 0.0;
memset(&mm, 0, sizeof(mm)); memset(&mm, 0, sizeof(mm));
msg = msg1; msg = msg1;
@ -185,19 +173,6 @@ void demodulate2400(struct mag_buf *mag) {
int initial_phase, first_phase, last_phase, try_phase; int initial_phase, first_phase, last_phase, try_phase;
int msglen; int msglen;
#ifdef MEASURE_NOISE
// update noise for all samples that aren't part of a message
// (we don't know if m[j] is or not, yet, so work one sample
// in arrears)
if (j > last_message_end+1) {
// There seems to be a weird compiler bug I'm hitting here..
// if you compute the square directly, it occasionally gets mangled.
uint64_t s = TRUE_AMPLITUDE(m[j-1]);
noise_power_sum += s * s;
noise_power_count++;
}
#endif
// Look for a message starting at around sample 0 with phase offset 3..7 // Look for a message starting at around sample 0 with phase offset 3..7
// Ideal sample values for preambles with different phase // Ideal sample values for preambles with different phase
@ -445,7 +420,7 @@ void demodulate2400(struct mag_buf *mag) {
int k; int k;
for (k = 0; k < signal_len; ++k) { for (k = 0; k < signal_len; ++k) {
uint64_t s = TRUE_AMPLITUDE(m[j+19+k]); uint64_t s = m[j+19+k];
signal_power_sum += s * s; signal_power_sum += s * s;
} }
@ -457,6 +432,8 @@ void demodulate2400(struct mag_buf *mag) {
Modes.stats_current.peak_signal_power = signal_power; Modes.stats_current.peak_signal_power = signal_power;
if (signal_power > 0.50119) if (signal_power > 0.50119)
Modes.stats_current.strong_signal_count++; // signal power above -3dBFS Modes.stats_current.strong_signal_count++; // signal power above -3dBFS
total_signal_power += signal_power_sum / MAX_POWER;
} }
// Decode the received message // Decode the received message
@ -480,18 +457,16 @@ void demodulate2400(struct mag_buf *mag) {
// where the preamble of the second message clobbered the last // where the preamble of the second message clobbered the last
// few bits of the first message, but the message bits didn't // few bits of the first message, but the message bits didn't
// overlap) // overlap)
#ifdef MEASURE_NOISE
last_message_end = j + (8 + msglen)*12/5;
#endif
j += (8 + msglen - 8)*12/5 - 1; j += (8 + msglen - 8)*12/5 - 1;
// Pass data to the next layer // Pass data to the next layer
useModesMessage(&mm); useModesMessage(&mm);
} }
#ifdef MEASURE_NOISE /* update noise power if measured */
Modes.stats_current.noise_power_sum += (noise_power_sum / MAX_POWER / noise_power_count); if (Modes.measure_noise) {
Modes.stats_current.noise_power_sum += (mag->total_power - total_signal_power) / mag->length;
Modes.stats_current.noise_power_count ++; Modes.stats_current.noise_power_count ++;
#endif }
} }

View file

@ -166,9 +166,16 @@ void modesInit(void) {
pthread_mutex_init(&Modes.data_mutex,NULL); pthread_mutex_init(&Modes.data_mutex,NULL);
pthread_cond_init(&Modes.data_cond,NULL); pthread_cond_init(&Modes.data_cond,NULL);
if (Modes.oversample)
Modes.sample_rate = 2400000.0;
else
Modes.sample_rate = 2000000.0;
// Allocate the various buffers used by Modes // Allocate the various buffers used by Modes
Modes.trailing_samples = (Modes.oversample ? (MODES_OS_PREAMBLE_SAMPLES + MODES_OS_LONG_MSG_SAMPLES) : (MODES_PREAMBLE_SAMPLES + MODES_LONG_MSG_SAMPLES)) + 16; 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) || if ( ((Modes.maglut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) ||
((Modes.magsqlut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) ||
((Modes.log10lut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) ) ((Modes.log10lut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) )
{ {
fprintf(stderr, "Out of memory allocating data buffer.\n"); fprintf(stderr, "Out of memory allocating data buffer.\n");
@ -213,54 +220,26 @@ void modesInit(void) {
if (Modes.net_sndbuf_size > (MODES_NET_SNDBUF_MAX)) if (Modes.net_sndbuf_size > (MODES_NET_SNDBUF_MAX))
{Modes.net_sndbuf_size = MODES_NET_SNDBUF_MAX;} {Modes.net_sndbuf_size = MODES_NET_SNDBUF_MAX;}
// Each I and Q value varies from 0 to 255, which represents a range from -1 to +1. To get from the // compute UC8 magnitude lookup table
// unsigned (0-255) range you therefore subtract 127 (or 128 or 127.5) from each I and Q, giving you
// a range from -127 to +128 (or -128 to +127, or -127.5 to +127.5)..
//
// To decode the AM signal, you need the magnitude of the waveform, which is given by sqrt((I^2)+(Q^2))
// The most this could be is if I&Q are both 128 (or 127 or 127.5), so you could end up with a magnitude
// of 181.019 (or 179.605, or 180.312)
//
// However, in reality the magnitude of the signal should never exceed the range -1 to +1, because the
// values are I = rCos(w) and Q = rSin(w). Therefore the integer computed magnitude should (can?) never
// exceed 128 (or 127, or 127.5 or whatever)
//
// If we scale up the results so that they range from 0 to 65535 (16 bits) then we need to multiply
// by 511.99, (or 516.02 or 514). antirez's original code multiplies by 360, presumably because he's
// assuming the maximim calculated amplitude is 181.019, and (181.019 * 360) = 65166.
//
// So lets see if we can improve things by subtracting 127.5, Well in integer arithmatic we can't
// subtract half, so, we'll double everything up and subtract one, and then compensate for the doubling
// in the multiplier at the end.
//
// If we do this we can never have I or Q equal to 0 - they can only be as small as +/- 1.
// This gives us a minimum magnitude of root 2 (0.707), so the dynamic range becomes (1.414-255). This
// also affects our scaling value, which is now 65535/(255 - 1.414), or 258.433254
//
// The sums then become mag = 258.433254 * (sqrt((I*2-255)^2 + (Q*2-255)^2) - 1.414)
// or mag = (258.433254 * sqrt((I*2-255)^2 + (Q*2-255)^2)) - 365.4798
//
// We also need to clip mag just incaes any rogue I/Q values somehow do have a magnitude greater than 255.
//
for (i = 0; i <= 255; i++) { for (i = 0; i <= 255; i++) {
for (q = 0; q <= 255; q++) { for (q = 0; q <= 255; q++) {
int mag, mag_i, mag_q; float fI, fQ, magsq;
mag_i = (i * 2) - 255; fI = (i - 127.5) / 127.5;
mag_q = (q * 2) - 255; fQ = (q - 127.5) / 127.5;
magsq = fI * fI + fQ * fQ;
if (magsq > 1)
magsq = 1;
mag = (int) round((sqrt((mag_i*mag_i)+(mag_q*mag_q)) * 258.433254) - 365.4798); Modes.magsqlut[le16toh((i*256)+q)] = (uint16_t) round(magsq * 65535.0);
Modes.maglut[le16toh((i*256)+q)] = (uint16_t) round(sqrtf(magsq) * 65535.0);
Modes.maglut[(i*256)+q] = (uint16_t) ((mag < 65535) ? mag : 65535);
} }
} }
// Prepare the log10 lookup table. // Prepare the log10 lookup table: 100log10(x)
// This maps from a magnitude value x (scaled as above) to 100log10(x) Modes.log10lut[0] = 0; // poorly defined..
for (i = 0; i <= 65535; i++) { for (i = 1; i <= 65535; i++) {
int l10 = (int) round(100 * log10( (i + 365.4798) / 258.433254) ); Modes.log10lut[i] = (uint16_t) round(100.0 * log10(i));
Modes.log10lut[i] = (uint16_t) ((l10 < 65535 ? l10 : 65535));
} }
// Prepare error correction tables // Prepare error correction tables
@ -269,7 +248,32 @@ void modesInit(void) {
if (Modes.show_only) if (Modes.show_only)
icaoFilterAdd(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.measure_noise, /* total power is interesting if we want noise */
&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 *power)
{
Modes.converter_function(iq, mag, nsamples, Modes.converter_state, power);
}
// //
// =============================== RTLSDR handling ========================== // =============================== RTLSDR handling ==========================
// //
@ -361,7 +365,7 @@ int modesInitRTLSDR(void) {
rtlsdr_set_freq_correction(Modes.dev, Modes.ppm_error); rtlsdr_set_freq_correction(Modes.dev, Modes.ppm_error);
if (Modes.enable_agc) rtlsdr_set_agc_mode(Modes.dev, 1); if (Modes.enable_agc) rtlsdr_set_agc_mode(Modes.dev, 1);
rtlsdr_set_center_freq(Modes.dev, Modes.freq); rtlsdr_set_center_freq(Modes.dev, Modes.freq);
rtlsdr_set_sample_rate(Modes.dev, Modes.oversample ? MODES_OVERSAMPLE_RATE : MODES_DEFAULT_RATE); rtlsdr_set_sample_rate(Modes.dev, (unsigned)Modes.sample_rate);
rtlsdr_reset_buffer(Modes.dev); rtlsdr_reset_buffer(Modes.dev);
fprintf(stderr, "Gain reported by device: %.2f dB\n", fprintf(stderr, "Gain reported by device: %.2f dB\n",
@ -386,7 +390,6 @@ static struct timespec reader_thread_start;
void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) { void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) {
struct mag_buf *outbuf; struct mag_buf *outbuf;
struct mag_buf *lastbuf; struct mag_buf *lastbuf;
uint16_t *p, *q;
uint32_t slen; uint32_t slen;
unsigned next_free_buffer; unsigned next_free_buffer;
unsigned free_bufs; unsigned free_bufs;
@ -445,13 +448,8 @@ void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) {
pthread_mutex_unlock(&Modes.data_mutex); pthread_mutex_unlock(&Modes.data_mutex);
// Compute the sample timestamp and system timestamp for the start of the block // Compute the sample timestamp and system timestamp for the start of the block
if (Modes.oversample) { outbuf->sampleTimestamp = lastbuf->sampleTimestamp + 12e6 * (lastbuf->length + outbuf->dropped) / Modes.sample_rate;
outbuf->sampleTimestamp = lastbuf->sampleTimestamp + (lastbuf->length + outbuf->dropped) * 5; block_duration = 1e9 * slen / Modes.sample_rate;
block_duration = slen * 5000U / 12;
} else {
outbuf->sampleTimestamp = lastbuf->sampleTimestamp + (lastbuf->length + outbuf->dropped) * 6;
block_duration = slen * 6000U / 12;
}
// Get the approx system time for the start of this block // Get the approx system time for the start of this block
clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp); clock_gettime(CLOCK_REALTIME, &outbuf->sysTimestamp);
@ -462,15 +460,12 @@ void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) {
if (outbuf->dropped == 0 && lastbuf->length >= Modes.trailing_samples) { 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)); memcpy(outbuf->data, lastbuf->data + lastbuf->length - Modes.trailing_samples, Modes.trailing_samples * sizeof(uint16_t));
} else { } else {
memset(outbuf->data, 127, Modes.trailing_samples * sizeof(uint16_t)); memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t));
} }
// Convert the new data // Convert the new data
outbuf->length = slen; outbuf->length = slen;
p = (uint16_t*)buf; convert_samples(buf, &outbuf->data[Modes.trailing_samples], slen, &outbuf->total_power);
q = &outbuf->data[Modes.trailing_samples];
while (slen-- > 0)
*q++ = Modes.maglut[*p++];
// Push the new data to the demodulation thread // Push the new data to the demodulation thread
pthread_mutex_lock(&Modes.data_mutex); pthread_mutex_lock(&Modes.data_mutex);
@ -498,7 +493,7 @@ void readDataFromFile(void) {
void *readbuf; void *readbuf;
int bytes_per_sample = 0; int bytes_per_sample = 0;
switch (Modes.file_format) { switch (Modes.input_format) {
case INPUT_UC8: case INPUT_UC8:
bytes_per_sample = 2; bytes_per_sample = 2;
break; break;
@ -518,14 +513,10 @@ void readDataFromFile(void) {
pthread_mutex_lock(&Modes.data_mutex); pthread_mutex_lock(&Modes.data_mutex);
while (!Modes.exit && !eof) { while (!Modes.exit && !eof) {
ssize_t nread, toread; ssize_t nread, toread;
void *r; void *r;
uint16_t *in, *out;
struct mag_buf *outbuf, *lastbuf; struct mag_buf *outbuf, *lastbuf;
unsigned next_free_buffer; unsigned next_free_buffer;
unsigned slen; unsigned slen;
unsigned i;
next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS; next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS;
if (next_free_buffer == Modes.first_filled_buffer) { if (next_free_buffer == Modes.first_filled_buffer) {
@ -539,11 +530,7 @@ void readDataFromFile(void) {
pthread_mutex_unlock(&Modes.data_mutex); pthread_mutex_unlock(&Modes.data_mutex);
// Compute the sample timestamp and system timestamp for the start of the block // Compute the sample timestamp and system timestamp for the start of the block
if (Modes.oversample) { outbuf->sampleTimestamp = lastbuf->sampleTimestamp + 12e6 * lastbuf->length / Modes.sample_rate;
outbuf->sampleTimestamp = lastbuf->sampleTimestamp + lastbuf->length * 5;
} else {
outbuf->sampleTimestamp = lastbuf->sampleTimestamp + lastbuf->length * 6;
}
// Copy trailing data from last block (or reset if not valid) // Copy trailing data from last block (or reset if not valid)
if (lastbuf->length >= Modes.trailing_samples) { if (lastbuf->length >= Modes.trailing_samples) {
@ -571,42 +558,7 @@ void readDataFromFile(void) {
slen = outbuf->length = MODES_MAG_BUF_SAMPLES - toread/bytes_per_sample; slen = outbuf->length = MODES_MAG_BUF_SAMPLES - toread/bytes_per_sample;
// Convert the new data // Convert the new data
out = outbuf->data + Modes.trailing_samples; convert_samples(readbuf, &outbuf->data[Modes.trailing_samples], slen, &outbuf->total_power);
in = (uint16_t*)readbuf;
switch (Modes.file_format) {
case INPUT_UC8:
for (i = 0; i < slen; ++i)
*out++ = Modes.maglut[*in++];
break;
case INPUT_SC16:
for (i = 0; i < slen; ++i) {
int16_t I, Q;
float mag;
I = (int16_t)le16toh(*in++);
Q = (int16_t)le16toh(*in++);
mag = sqrtf(I*I + Q*Q) * (65536.0 / 32768.0);
if (mag > 65535)
mag = 65535;
*out++ = (uint16_t)mag;
}
break;
case INPUT_SC16Q11:
for (i = 0; i < slen; ++i) {
int16_t I, Q;
float mag;
I = (int16_t)le16toh(*in++);
Q = (int16_t)le16toh(*in++);
mag = sqrtf(I*I + Q*Q) * (65536.0 / 2048.0);
if (mag > 65535)
mag = 65535;
*out++ = (uint16_t)mag;
}
break;
}
if (Modes.interactive) { if (Modes.interactive) {
// Wait until we are allowed to release this buffer to the main thread // Wait until we are allowed to release this buffer to the main thread
@ -614,7 +566,7 @@ void readDataFromFile(void) {
; ;
// compute the time we can deliver the next buffer. // compute the time we can deliver the next buffer.
next_buffer_delivery.tv_nsec += (outbuf->length * (Modes.oversample ? 5000 : 6000) / 12); next_buffer_delivery.tv_nsec += outbuf->length * 1e9 / Modes.sample_rate;
normalize_timespec(&next_buffer_delivery); normalize_timespec(&next_buffer_delivery);
} }
@ -719,6 +671,7 @@ void showHelp(void) {
"--enable-agc Enable the Automatic Gain Control (default: off)\n" "--enable-agc Enable the Automatic Gain Control (default: off)\n"
"--freq <hz> Set frequency (default: 1090 Mhz)\n" "--freq <hz> Set frequency (default: 1090 Mhz)\n"
"--ifile <filename> Read data from file (use '-' for stdin)\n" "--ifile <filename> Read data from file (use '-' for stdin)\n"
"--iformat <format> Sample format for --ifile: UC8 (default), SC16, or SC16Q11\n"
"--interactive Interactive mode refreshing data on screen\n" "--interactive Interactive mode refreshing data on screen\n"
"--interactive-rows <num> Max number of rows in interactive mode (default: 15)\n" "--interactive-rows <num> Max number of rows in interactive mode (default: 15)\n"
"--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60)\n" "--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60)\n"
@ -758,11 +711,12 @@ void showHelp(void) {
"--quiet Disable output to stdout. Use for daemon applications\n" "--quiet Disable output to stdout. Use for daemon applications\n"
"--show-only <addr> Show only messages from the given ICAO on stdout\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" "--ppm <error> Set receiver error in parts per million (default 0)\n"
"--no-decode Don't decode the message contents beyond the minimum necessary\n"
"--write-json <dir> Periodically write json output to <dir> (for serving by a separate webserver)\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" "--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" "--json-location-accuracy <n> Accuracy of receiver location in json metadata: 0=no location, 1=approximate, 2=exact\n"
"--oversample Enable oversampling at 2.4MHz\n" "--oversample Use the 2.4MHz demodulator\n"
"--dcfilter Apply a 1Hz DC filter to input data (requires lots more CPU)\n"
"--measure-noise Measure noise power (requires slightly more CPU)\n"
"--help Show this help\n" "--help Show this help\n"
"\n" "\n"
"Debug mode flags: d = Log frames decoded with errors\n" "Debug mode flags: d = Log frames decoded with errors\n"
@ -974,16 +928,20 @@ int main(int argc, char **argv) {
} else if (!strcmp(argv[j],"--iformat") && more) { } else if (!strcmp(argv[j],"--iformat") && more) {
++j; ++j;
if (!strcasecmp(argv[j], "uc8")) { if (!strcasecmp(argv[j], "uc8")) {
Modes.file_format = INPUT_UC8; Modes.input_format = INPUT_UC8;
} else if (!strcasecmp(argv[j], "sc16")) { } else if (!strcasecmp(argv[j], "sc16")) {
Modes.file_format = INPUT_SC16; Modes.input_format = INPUT_SC16;
} else if (!strcasecmp(argv[j], "sc16q11")) { } else if (!strcasecmp(argv[j], "sc16q11")) {
Modes.file_format = INPUT_SC16Q11; Modes.input_format = INPUT_SC16Q11;
} else { } else {
fprintf(stderr, "Input format '%s' not understood (supported values: UC8, SC16, SC16Q11)\n", fprintf(stderr, "Input format '%s' not understood (supported values: UC8, SC16, SC16Q11)\n",
argv[j]); argv[j]);
exit(1); exit(1);
} }
} else if (!strcmp(argv[j],"--dcfilter")) {
Modes.dc_filter = 1;
} else if (!strcmp(argv[j],"--measure-noise")) {
Modes.measure_noise = 1;
} else if (!strcmp(argv[j],"--fix")) { } else if (!strcmp(argv[j],"--fix")) {
Modes.nfix_crc = 1; Modes.nfix_crc = 1;
} else if (!strcmp(argv[j],"--no-fix")) { } else if (!strcmp(argv[j],"--no-fix")) {
@ -1224,10 +1182,11 @@ int main(int argc, char **argv) {
// stuff at the same time. // stuff at the same time.
pthread_mutex_unlock(&Modes.data_mutex); pthread_mutex_unlock(&Modes.data_mutex);
if (Modes.oversample) if (Modes.oversample) {
demodulate2400(buf); demodulate2400(buf);
else } else {
demodulate2000(buf); demodulate2000(buf);
}
Modes.stats_current.samples_processed += buf->length; Modes.stats_current.samples_processed += buf->length;
Modes.stats_current.samples_dropped += buf->dropped; Modes.stats_current.samples_dropped += buf->dropped;
@ -1262,6 +1221,7 @@ int main(int argc, char **argv) {
display_total_stats(); display_total_stats();
} }
cleanup_converter(Modes.converter_state);
log_with_timestamp("Normal exit."); log_with_timestamp("Normal exit.");
#ifndef _WIN32 #ifndef _WIN32

View file

@ -205,10 +205,8 @@
#define MODES_NOTUSED(V) ((void) V) #define MODES_NOTUSED(V) ((void) V)
// adjust for zero offset of amplitude values #define MAX_AMPLITUDE 65535.0
#define TRUE_AMPLITUDE(x) ((x) + 365) #define MAX_POWER (MAX_AMPLITUDE * MAX_AMPLITUDE)
#define MAX_AMPLITUDE TRUE_AMPLITUDE(65535)
#define MAX_POWER (1.0 * MAX_AMPLITUDE * MAX_AMPLITUDE)
// Include subheaders after all the #defines are in place // Include subheaders after all the #defines are in place
@ -220,6 +218,7 @@
#include "stats.h" #include "stats.h"
#include "cpr.h" #include "cpr.h"
#include "icao_filter.h" #include "icao_filter.h"
#include "convert.h"
//======================== structure declarations ========================= //======================== structure declarations =========================
@ -248,10 +247,9 @@ struct mag_buf {
uint64_t sampleTimestamp; // Clock timestamp of the start of this block, 12MHz clock uint64_t sampleTimestamp; // Clock timestamp of the start of this block, 12MHz clock
struct timespec sysTimestamp; // Estimated system time at start of block struct timespec sysTimestamp; // Estimated system time at start of block
uint32_t dropped; // Number of dropped samples preceding this buffer uint32_t dropped; // Number of dropped samples preceding this buffer
double total_power; // Sum of per-sample input power (in the range [0.0,1.0] per sample), or 0 if not measured
}; };
typedef enum { INPUT_UC8=0, INPUT_SC16, INPUT_SC16Q11 } input_format_t;
// Program global state // Program global state
struct { // Internal state struct { // Internal state
pthread_t reader_thread; pthread_t reader_thread;
@ -265,13 +263,21 @@ struct { // Internal state
struct timespec reader_cpu_accumulator; // CPU time used by the reader thread, copied out and reset by the main thread under the mutex struct timespec reader_cpu_accumulator; // CPU time used by the reader thread, copied out and reset by the main thread under the mutex
unsigned trailing_samples; // extra trailing samples in magnitude buffers 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 int fd; // --ifile option file descriptor
input_format_t file_format; // --iformat option input_format_t input_format; // --iformat option
uint16_t *maglut; // I/Q -> Magnitude lookup table uint16_t *maglut; // I/Q -> Magnitude lookup table
uint16_t *magsqlut; // I/Q -> Magnitude-squared lookup table
uint16_t *log10lut; // Magnitude -> log10 lookup table uint16_t *log10lut; // Magnitude -> log10 lookup table
int exit; // Exit from the main loop when true int exit; // Exit from the main loop when true
// Sample conversion
int dc_filter; // should we apply a DC filter?
int measure_noise; // should we measure noise power?
iq_convert_fn converter_function;
struct converter_state *converter_state;
// RTLSDR // RTLSDR
char * dev_name; char * dev_name;
int gain; int gain;

View file

@ -306,7 +306,7 @@ int detectModeA(uint16_t *m, struct modesMessage *mm)
if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) ) if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) )
{return (ModeABits = 0);} {return (ModeABits = 0);}
mm->signalLevel = 1.0 * TRUE_AMPLITUDE(fSig + fNoise) * TRUE_AMPLITUDE(fSig + fNoise) / MAX_POWER; mm->signalLevel = (fSig + fNoise) * (fSig + fNoise) / MAX_POWER;
return ModeABits; return ModeABits;
} }