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
9 changed files with 800 additions and 500 deletions
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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue