From 03b53c2d29fa3d17325f6638c94a2cdf36a16625 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 15 Jun 2015 22:14:37 +0100 Subject: [PATCH] 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) --- Makefile | 2 +- convert.c | 317 +++++++++++++++++++++++++++++++++++++++++++++++++++ convert.h | 40 +++++++ demod_2400.c | 49 ++------ dump1090.c | 180 ++++++++++++----------------- dump1090.h | 20 ++-- mode_ac.c | 2 +- 7 files changed, 454 insertions(+), 156 deletions(-) create mode 100644 convert.c create mode 100644 convert.h diff --git a/Makefile b/Makefile index 65059390..3106403e 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ all: dump1090 view1090 %.o: %.c *.h $(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) 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 diff --git a/convert.c b/convert.c new file mode 100644 index 00000000..f09990ad --- /dev/null +++ b/convert.c @@ -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 +// +// 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 . + +#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); +} diff --git a/convert.h b/convert.h new file mode 100644 index 00000000..4b267efe --- /dev/null +++ b/convert.h @@ -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 +// +// 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 . + +#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 diff --git a/demod_2400.c b/demod_2400.c index 487418aa..363cf337 100644 --- a/demod_2400.c +++ b/demod_2400.c @@ -19,12 +19,6 @@ #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 // // 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, // try to demodulate some Mode S messages. // -void demodulate2400(struct mag_buf *mag) { +void demodulate2400(struct mag_buf *mag) +{ struct modesMessage mm; unsigned char msg1[MODES_LONG_MSG_BYTES], msg2[MODES_LONG_MSG_BYTES], *msg; uint32_t j; -#ifdef MEASURE_NOISE - uint32_t last_message_end = 0; -#endif unsigned char *bestmsg; 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; uint32_t mlen = mag->length; + double total_signal_power = 0.0; + memset(&mm, 0, sizeof(mm)); msg = msg1; @@ -185,19 +173,6 @@ void demodulate2400(struct mag_buf *mag) { int initial_phase, first_phase, last_phase, try_phase; 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 // Ideal sample values for preambles with different phase @@ -445,7 +420,7 @@ void demodulate2400(struct mag_buf *mag) { int 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; } @@ -457,6 +432,8 @@ void demodulate2400(struct mag_buf *mag) { Modes.stats_current.peak_signal_power = signal_power; if (signal_power > 0.50119) Modes.stats_current.strong_signal_count++; // signal power above -3dBFS + + total_signal_power += signal_power_sum / MAX_POWER; } // Decode the received message @@ -480,18 +457,16 @@ void demodulate2400(struct mag_buf *mag) { // where the preamble of the second message clobbered the last // few bits of the first message, but the message bits didn't // overlap) -#ifdef MEASURE_NOISE - last_message_end = j + (8 + msglen)*12/5; -#endif j += (8 + msglen - 8)*12/5 - 1; // Pass data to the next layer useModesMessage(&mm); } -#ifdef MEASURE_NOISE - Modes.stats_current.noise_power_sum += (noise_power_sum / MAX_POWER / noise_power_count); - Modes.stats_current.noise_power_count ++; -#endif + /* update noise power if measured */ + if (Modes.measure_noise) { + Modes.stats_current.noise_power_sum += (mag->total_power - total_signal_power) / mag->length; + Modes.stats_current.noise_power_count ++; + } } diff --git a/dump1090.c b/dump1090.c index 020bc62c..5941ca21 100644 --- a/dump1090.c +++ b/dump1090.c @@ -166,9 +166,16 @@ void modesInit(void) { pthread_mutex_init(&Modes.data_mutex,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 - 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) || + ((Modes.magsqlut = (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"); @@ -213,54 +220,26 @@ void modesInit(void) { if (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 - // 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. - // - + // compute UC8 magnitude lookup table for (i = 0; i <= 255; i++) { for (q = 0; q <= 255; q++) { - int mag, mag_i, mag_q; + float fI, fQ, magsq; - mag_i = (i * 2) - 255; - mag_q = (q * 2) - 255; + fI = (i - 127.5) / 127.5; + 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.maglut[(i*256)+q] = (uint16_t) ((mag < 65535) ? mag : 65535); + 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); } } - // Prepare the log10 lookup table. - // This maps from a magnitude value x (scaled as above) to 100log10(x) - for (i = 0; i <= 65535; i++) { - int l10 = (int) round(100 * log10( (i + 365.4798) / 258.433254) ); - Modes.log10lut[i] = (uint16_t) ((l10 < 65535 ? l10 : 65535)); + // Prepare the log10 lookup table: 100log10(x) + Modes.log10lut[0] = 0; // poorly defined.. + for (i = 1; i <= 65535; i++) { + Modes.log10lut[i] = (uint16_t) round(100.0 * log10(i)); } // Prepare error correction tables @@ -269,7 +248,32 @@ void modesInit(void) { 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.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 ========================== // @@ -361,7 +365,7 @@ int modesInitRTLSDR(void) { 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, Modes.oversample ? MODES_OVERSAMPLE_RATE : MODES_DEFAULT_RATE); + rtlsdr_set_sample_rate(Modes.dev, (unsigned)Modes.sample_rate); rtlsdr_reset_buffer(Modes.dev); 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) { struct mag_buf *outbuf; struct mag_buf *lastbuf; - uint16_t *p, *q; uint32_t slen; unsigned next_free_buffer; unsigned free_bufs; @@ -445,13 +448,8 @@ void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) { pthread_mutex_unlock(&Modes.data_mutex); // Compute the sample timestamp and system timestamp for the start of the block - if (Modes.oversample) { - outbuf->sampleTimestamp = lastbuf->sampleTimestamp + (lastbuf->length + outbuf->dropped) * 5; - block_duration = slen * 5000U / 12; - } else { - outbuf->sampleTimestamp = lastbuf->sampleTimestamp + (lastbuf->length + outbuf->dropped) * 6; - block_duration = slen * 6000U / 12; - } + 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); @@ -462,15 +460,12 @@ void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) { 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, 127, Modes.trailing_samples * sizeof(uint16_t)); + memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t)); } // Convert the new data outbuf->length = slen; - p = (uint16_t*)buf; - q = &outbuf->data[Modes.trailing_samples]; - while (slen-- > 0) - *q++ = Modes.maglut[*p++]; + convert_samples(buf, &outbuf->data[Modes.trailing_samples], slen, &outbuf->total_power); // Push the new data to the demodulation thread pthread_mutex_lock(&Modes.data_mutex); @@ -498,7 +493,7 @@ void readDataFromFile(void) { void *readbuf; int bytes_per_sample = 0; - switch (Modes.file_format) { + switch (Modes.input_format) { case INPUT_UC8: bytes_per_sample = 2; break; @@ -518,14 +513,10 @@ void readDataFromFile(void) { pthread_mutex_lock(&Modes.data_mutex); while (!Modes.exit && !eof) { ssize_t nread, toread; - void *r; - uint16_t *in, *out; - struct mag_buf *outbuf, *lastbuf; unsigned next_free_buffer; unsigned slen; - unsigned i; next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS; if (next_free_buffer == Modes.first_filled_buffer) { @@ -539,11 +530,7 @@ void readDataFromFile(void) { pthread_mutex_unlock(&Modes.data_mutex); // Compute the sample timestamp and system timestamp for the start of the block - if (Modes.oversample) { - outbuf->sampleTimestamp = lastbuf->sampleTimestamp + lastbuf->length * 5; - } else { - outbuf->sampleTimestamp = lastbuf->sampleTimestamp + lastbuf->length * 6; - } + 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) { @@ -571,42 +558,7 @@ void readDataFromFile(void) { slen = outbuf->length = MODES_MAG_BUF_SAMPLES - toread/bytes_per_sample; // Convert the new data - out = outbuf->data + Modes.trailing_samples; - 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; - } + convert_samples(readbuf, &outbuf->data[Modes.trailing_samples], slen, &outbuf->total_power); if (Modes.interactive) { // 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. - 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); } @@ -719,6 +671,7 @@ void showHelp(void) { "--enable-agc Enable the Automatic Gain Control (default: off)\n" "--freq Set frequency (default: 1090 Mhz)\n" "--ifile Read data from file (use '-' for stdin)\n" +"--iformat Sample format for --ifile: UC8 (default), SC16, or SC16Q11\n" "--interactive Interactive mode refreshing data on screen\n" "--interactive-rows Max number of rows in interactive mode (default: 15)\n" "--interactive-ttl Remove from list if idle for (default: 60)\n" @@ -758,11 +711,12 @@ void showHelp(void) { "--quiet Disable output to stdout. Use for daemon applications\n" "--show-only Show only messages from the given ICAO on stdout\n" "--ppm 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 Periodically write json output to (for serving by a separate webserver)\n" "--write-json-every Write json output every t seconds (default 1)\n" "--json-location-accuracy 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" "\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) { ++j; if (!strcasecmp(argv[j], "uc8")) { - Modes.file_format = INPUT_UC8; + Modes.input_format = INPUT_UC8; } else if (!strcasecmp(argv[j], "sc16")) { - Modes.file_format = INPUT_SC16; + Modes.input_format = INPUT_SC16; } else if (!strcasecmp(argv[j], "sc16q11")) { - Modes.file_format = INPUT_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")) { + Modes.measure_noise = 1; } else if (!strcmp(argv[j],"--fix")) { Modes.nfix_crc = 1; } else if (!strcmp(argv[j],"--no-fix")) { @@ -1224,10 +1182,11 @@ int main(int argc, char **argv) { // stuff at the same time. pthread_mutex_unlock(&Modes.data_mutex); - if (Modes.oversample) + if (Modes.oversample) { demodulate2400(buf); - else + } else { demodulate2000(buf); + } Modes.stats_current.samples_processed += buf->length; Modes.stats_current.samples_dropped += buf->dropped; @@ -1262,6 +1221,7 @@ int main(int argc, char **argv) { display_total_stats(); } + cleanup_converter(Modes.converter_state); log_with_timestamp("Normal exit."); #ifndef _WIN32 diff --git a/dump1090.h b/dump1090.h index 4b636908..999814ad 100644 --- a/dump1090.h +++ b/dump1090.h @@ -205,10 +205,8 @@ #define MODES_NOTUSED(V) ((void) V) -// adjust for zero offset of amplitude values -#define TRUE_AMPLITUDE(x) ((x) + 365) -#define MAX_AMPLITUDE TRUE_AMPLITUDE(65535) -#define MAX_POWER (1.0 * MAX_AMPLITUDE * MAX_AMPLITUDE) +#define MAX_AMPLITUDE 65535.0 +#define MAX_POWER (MAX_AMPLITUDE * MAX_AMPLITUDE) // Include subheaders after all the #defines are in place @@ -220,6 +218,7 @@ #include "stats.h" #include "cpr.h" #include "icao_filter.h" +#include "convert.h" //======================== structure declarations ========================= @@ -248,10 +247,9 @@ struct mag_buf { uint64_t sampleTimestamp; // Clock timestamp of the start of this block, 12MHz clock struct timespec sysTimestamp; // Estimated system time at start of block 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 struct { // Internal state 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 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 file_format; // --iformat option + input_format_t input_format; // --iformat option uint16_t *maglut; // I/Q -> Magnitude lookup table + uint16_t *magsqlut; // I/Q -> Magnitude-squared 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? + int measure_noise; // should we measure noise power? + iq_convert_fn converter_function; + struct converter_state *converter_state; + // RTLSDR char * dev_name; int gain; diff --git a/mode_ac.c b/mode_ac.c index b4ecd11a..e9a78f59 100644 --- a/mode_ac.c +++ b/mode_ac.c @@ -306,7 +306,7 @@ int detectModeA(uint16_t *m, struct modesMessage *mm) if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) ) {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; }