Lots of refactoring to move the SDR-type-specific bits out of the
main code and make them optionally buildable.
This commit is contained in:
parent
bfc4b742af
commit
75b221c0e3
2
Makefile
2
Makefile
|
@ -27,7 +27,7 @@ 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)
|
||||
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_rtlsdr.o sdr.o $(COMPAT)
|
||||
$(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_RTLSDR) -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)
|
||||
|
|
515
dump1090.c
515
dump1090.c
|
@ -49,12 +49,8 @@
|
|||
|
||||
#include "dump1090.h"
|
||||
|
||||
#include <rtl-sdr.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
static int verbose_device_search(char *s);
|
||||
|
||||
//
|
||||
// ============================= Utility functions ==========================
|
||||
//
|
||||
|
@ -113,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");
|
||||
|
@ -127,6 +122,8 @@ void modesInitConfig(void) {
|
|||
Modes.json_location_accuracy = 1;
|
||||
Modes.maxRange = 1852 * 300; // 300NM default max range
|
||||
Modes.mode_ac_auto = 1;
|
||||
|
||||
sdrInitConfig();
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
|
@ -212,138 +209,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;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
|
@ -356,208 +226,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);
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
|
@ -565,36 +233,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);
|
||||
|
@ -636,14 +279,16 @@ void showHelp(void) {
|
|||
printf(
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"| dump1090 ModeS Receiver %45s |\n"
|
||||
"-----------------------------------------------------------------------------\n"
|
||||
"--device-index <index> Select RTL device (default: 0)\n"
|
||||
"-----------------------------------------------------------------------------\n",
|
||||
MODES_DUMP1090_VARIANT " " MODES_DUMP1090_VERSION);
|
||||
|
||||
sdrShowHelp();
|
||||
|
||||
printf(
|
||||
" 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"
|
||||
|
@ -683,12 +328,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"
|
||||
|
@ -697,8 +341,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"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -815,68 +458,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;
|
||||
}
|
||||
//
|
||||
//=========================================================================
|
||||
//
|
||||
|
@ -894,29 +475,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")) {
|
||||
|
@ -942,7 +506,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) {
|
||||
|
@ -992,9 +556,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) {
|
||||
|
@ -1034,8 +596,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) {
|
||||
|
@ -1056,6 +616,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",
|
||||
|
@ -1074,27 +636,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) {
|
||||
if (!sdrOpen()) {
|
||||
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 (Modes.net) {
|
||||
modesInitNet();
|
||||
}
|
||||
}
|
||||
if (Modes.net) modesInitNet();
|
||||
|
||||
// init stats:
|
||||
Modes.stats_current.start = Modes.stats_current.end =
|
||||
|
@ -1111,9 +659,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;
|
||||
|
||||
|
@ -1130,7 +680,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) {
|
||||
|
@ -1186,7 +736,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;
|
||||
}
|
||||
}
|
||||
|
@ -1213,9 +763,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
|
||||
|
|
20
dump1090.h
20
dump1090.h
|
@ -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_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,17 @@ 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 +303,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 +338,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 {
|
||||
|
|
145
sdr.c
Normal file
145
sdr.c
Normal file
|
@ -0,0 +1,145 @@
|
|||
#include "dump1090.h"
|
||||
#include "sdr.h"
|
||||
|
||||
#define ENABLE_RTLSDR
|
||||
|
||||
#include "sdr_ifile.h"
|
||||
#ifdef ENABLE_RTLSDR
|
||||
# include "sdr_rtlsdr.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
|
||||
|
||||
{ "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();
|
||||
}
|
13
sdr.h
Normal file
13
sdr.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#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
|
236
sdr_ifile.c
Normal file
236
sdr_ifile.c
Normal file
|
@ -0,0 +1,236 @@
|
|||
#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;
|
||||
}
|
||||
}
|
13
sdr_ifile.h
Normal file
13
sdr_ifile.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#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
|
335
sdr_rtlsdr.c
Normal file
335
sdr_rtlsdr.c
Normal file
|
@ -0,0 +1,335 @@
|
|||
#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;
|
||||
}
|
||||
}
|
13
sdr_rtlsdr.h
Normal file
13
sdr_rtlsdr.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef SDR_RTLSDR_H
|
||||
#define SDR_RTLSDR_H
|
||||
|
||||
// Support for DVB-T dongles in SDR mode via librtlsdr
|
||||
|
||||
void rtlsdrInitConfig();
|
||||
void rtlsdrShowHelp();
|
||||
bool rtlsdrOpen();
|
||||
void rtlsdrRun();
|
||||
void rtlsdrClose();
|
||||
bool rtlsdrHandleOption(int argc, char **argv, int *jptr);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue