From 8001832de2bc9869c67dfcf20e34ae26eb92c4a0 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 10 Jan 2019 22:26:54 +0000 Subject: [PATCH] WIP soapysdr support --- Makefile | 4 + dump1090.h | 2 +- sdr.c | 3 + sdr_soapy.c | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++ sdr_soapy.h | 31 +++++++ 5 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 sdr_soapy.c create mode 100644 sdr_soapy.h diff --git a/Makefile b/Makefile index b4f6aaa..1aba741 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,10 @@ ifeq ($(BLADERF), yes) LIBS_SDR += $(shell pkg-config --libs libbladeRF) endif +SDR_OBJ += sdr_soapy.o +CFLAGS += $(shell pkg-config --cflags SoapySDR) +LIBS_SDR += $(shell pkg-config --libs SoapySDR) + all: dump1090 view1090 %.o: %.c *.h diff --git a/dump1090.h b/dump1090.h index 4e0340c..0692e4a 100644 --- a/dump1090.h +++ b/dump1090.h @@ -274,7 +274,7 @@ typedef enum { //======================== structure declarations ========================= typedef enum { - SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF + SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF, SDR_SOAPY } sdr_type_t; // Structure representing one magnitude buffer diff --git a/sdr.c b/sdr.c index 89fe8f4..b435176 100644 --- a/sdr.c +++ b/sdr.c @@ -28,6 +28,8 @@ # include "sdr_bladerf.h" #endif +#include "sdr_soapy.h" + typedef struct { const char *name; sdr_type_t sdr_type; @@ -85,6 +87,7 @@ static sdr_handler sdr_handlers[] = { { "bladerf", SDR_BLADERF, bladeRFInitConfig, bladeRFShowHelp, bladeRFHandleOption, bladeRFOpen, bladeRFRun, bladeRFClose }, #endif + { "soapy", SDR_SOAPY, soapyInitConfig, soapyShowHelp, soapyHandleOption, soapyOpen, soapyRun, soapyClose }, { "ifile", SDR_IFILE, ifileInitConfig, ifileShowHelp, ifileHandleOption, ifileOpen, ifileRun, ifileClose }, { "none", SDR_NONE, noInitConfig, noShowHelp, noHandleOption, noOpen, noRun, noClose }, diff --git a/sdr_soapy.c b/sdr_soapy.c new file mode 100644 index 0000000..1d39e10 --- /dev/null +++ b/sdr_soapy.c @@ -0,0 +1,228 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// sdr_soapy.c: SoapySDR support +// +// Copyright (c) 2014-2017 Oliver Jowett +// Copyright (c) 2017 FlightAware LLC +// +// This file is free software: you may copy, redistribute and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 2 of the License, or (at your +// option) any later version. +// +// This file is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "dump1090.h" +#include "sdr_soapy.h" + +#include +#include +static struct { + SoapySDRDevice *dev; + SoapySDRStream *stream; + + iq_convert_fn converter; + struct converter_state *converter_state; +} SOAPY; + +// +// =============================== SoapySDR handling ========================== +// + +void soapyInitConfig() +{ + SOAPY.dev = NULL; + SOAPY.stream = NULL; + SOAPY.converter = NULL; + SOAPY.converter_state = NULL; +} + +void soapyShowHelp() +{ + printf(" SoapySDR-specific options (use with --device-type soapy)\n"); + printf("\n"); + printf("--device select/configure device\n"); + printf("\n"); +} + +bool soapyHandleOption(int argc, char **argv, int *jptr) +{ + MODES_NOTUSED(argc); + MODES_NOTUSED(argv); + MODES_NOTUSED(jptr); + return false; +} + +bool soapyOpen(void) +{ + size_t length; + SoapySDRKwargs *results = SoapySDRDevice_enumerate(NULL, &length); + for (size_t i = 0; i < length; i++) + { + printf("Found device #%d: ", (int)i); + for (size_t j = 0; j < results[i].size; j++) + { + printf("%s=%s, ", results[i].keys[j], results[i].vals[j]); + } + printf("\n"); + } + SoapySDRKwargsList_clear(results, length); + + SOAPY.dev = SoapySDRDevice_makeStrArgs(Modes.dev_name); + if (!SOAPY.dev) { + fprintf(stderr, "soapy: failed to create device: %s\n", SoapySDRDevice_lastError()); + return false; + } + + SoapySDRKwargs result = SoapySDRDevice_getHardwareInfo(SOAPY.dev); + for (size_t i = 0; i < result.size; ++i) { + printf("%s=%s\n", result.keys[i], result.vals[i]); + } + SoapySDRKwargs_clear(&result); + + if (SoapySDRDevice_setSampleRate(SOAPY.dev, SOAPY_SDR_RX, 0, Modes.sample_rate) != 0) { + fprintf(stderr, "soapy: setSampleRate failed: %s\n", SoapySDRDevice_lastError()); + goto error; + } + + if (SoapySDRDevice_setFrequency(SOAPY.dev, SOAPY_SDR_RX, 0, Modes.freq, NULL) != 0) { + fprintf(stderr, "soapy: setFrequency failed: %s\n", SoapySDRDevice_lastError()); + goto error; + } + + if (SoapySDRDevice_setGain(SOAPY.dev, SOAPY_SDR_RX, 0, Modes.gain / 10.0) != 0) { + fprintf(stderr, "soapy: setGain failed: %s\n", SoapySDRDevice_lastError()); + goto error; + } + + if (SoapySDRDevice_setBandwidth(SOAPY.dev, SOAPY_SDR_RX, 0, 3.0e6) != 0) { + fprintf(stderr, "soapy: setBandwidth failed: %s\n", SoapySDRDevice_lastError()); + goto error; + } + + if (SoapySDRDevice_setAntenna(SOAPY.dev, SOAPY_SDR_RX, 0, "LNAW") != 0) { + fprintf(stderr, "soapy: setAntenna failed: %s\n", SoapySDRDevice_lastError()); + goto error; + } + + fprintf(stderr, "soapy: frequency is %.1f MHz\n", + SoapySDRDevice_getFrequency(SOAPY.dev, SOAPY_SDR_RX, 0) / 1e6); + fprintf(stderr, "soapy: sample rate is %.1f MHz\n", + SoapySDRDevice_getSampleRate(SOAPY.dev, SOAPY_SDR_RX, 0) / 1e6); + fprintf(stderr, "soapy: bandwidth is %.1f MHz\n", + SoapySDRDevice_getBandwidth(SOAPY.dev, SOAPY_SDR_RX, 0) / 1e6); + fprintf(stderr, "soapy: gain mode is %d\n", + SoapySDRDevice_getGainMode(SOAPY.dev, SOAPY_SDR_RX, 0)); + fprintf(stderr, "soapy: gain is %.1f dB\n", + SoapySDRDevice_getGain(SOAPY.dev, SOAPY_SDR_RX, 0)); + fprintf(stderr, "soapy: antenna is %s\n", + SoapySDRDevice_getAntenna(SOAPY.dev, SOAPY_SDR_RX, 0)); + + size_t channels[1] = { 0 }; + if (SoapySDRDevice_setupStream(SOAPY.dev, &SOAPY.stream, SOAPY_SDR_RX, SOAPY_SDR_CS16, channels, 1, NULL) != 0) { + fprintf(stderr, "soapy: setupStream failed: %s\n", SoapySDRDevice_lastError()); + goto error; + } + + + SOAPY.converter = init_converter(INPUT_SC16Q11, + Modes.sample_rate, + Modes.dc_filter, + &SOAPY.converter_state); + if (!SOAPY.converter) { + fprintf(stderr, "soapy: can't initialize sample converter\n"); + goto error; + } + + return true; + + error: + if (SOAPY.dev != NULL) { + SoapySDRDevice_unmake(SOAPY.dev); + SOAPY.dev = NULL; + } + + return false; +} + +void soapyRun() +{ + if (!SOAPY.dev) { + return; + } + + if (SoapySDRDevice_activateStream(SOAPY.dev, SOAPY.stream, 0, 0, 0) != 0) { + fprintf(stderr, "soapy: activateStream failed: %s\n", SoapySDRDevice_lastError()); + return; + } + + void *buffers[1]; + const int buffer_elements = 131072; + buffers[0] = malloc(buffer_elements * 4); + + while (!Modes.exit) { + int flags; + long long timeNs; + int sample_count = SoapySDRDevice_readStream(SOAPY.dev, SOAPY.stream, buffers, buffer_elements, &flags, &timeNs, 50000); + if (sample_count <= 0) { + fprintf(stderr, "soapy: readStream failed: %s\n", SoapySDRDevice_lastError()); + return; + } + + pthread_mutex_lock(&Modes.data_mutex); + unsigned next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS; + if (next_free_buffer == Modes.first_filled_buffer) { + // drop it + fprintf(stderr, "fifo full\n"); + pthread_mutex_unlock(&Modes.data_mutex); + continue; + } + + struct mag_buf *outbuf = &Modes.mag_buffers[Modes.first_free_buffer]; + struct mag_buf *lastbuf = &Modes.mag_buffers[(Modes.first_free_buffer + MODES_MAG_BUFFERS - 1) % MODES_MAG_BUFFERS]; + pthread_mutex_unlock(&Modes.data_mutex); + + // Copy trailing data from last block + memcpy(outbuf->data, lastbuf->data + lastbuf->length, Modes.trailing_samples * sizeof(uint16_t)); + SOAPY.converter(buffers[0], &outbuf->data[Modes.trailing_samples], sample_count, + SOAPY.converter_state, &outbuf->mean_level, &outbuf->mean_power); + outbuf->dropped = 0; + outbuf->length = sample_count; + + pthread_mutex_lock(&Modes.data_mutex); + Modes.mag_buffers[next_free_buffer].dropped = 0; + Modes.mag_buffers[next_free_buffer].length = 0; + Modes.first_free_buffer = next_free_buffer; + pthread_cond_signal(&Modes.data_cond); + pthread_mutex_unlock(&Modes.data_mutex); + } +} + +void soapyClose() +{ + fprintf(stderr, "close stream\n"); + if (SOAPY.stream) { + SoapySDRDevice_closeStream(SOAPY.dev, SOAPY.stream); + SOAPY.stream = NULL; + } + + fprintf(stderr, "close device\n"); + if (SOAPY.dev) { + SoapySDRDevice_unmake(SOAPY.dev); + SOAPY.dev = NULL; + } + + if (SOAPY.converter) { + cleanup_converter(SOAPY.converter_state); + SOAPY.converter = NULL; + SOAPY.converter_state = NULL; + } + + fprintf(stderr, "all done\n"); +} diff --git a/sdr_soapy.h b/sdr_soapy.h new file mode 100644 index 0000000..c120216 --- /dev/null +++ b/sdr_soapy.h @@ -0,0 +1,31 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// sdr_soapy.h: SoapySDR dongle support (header) +// +// Copyright (c) 2016-2017 Oliver Jowett +// Copyright (c) 2017 FlightAware LLC +// +// This file is free software: you may copy, redistribute and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 2 of the License, or (at your +// option) any later version. +// +// This file is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef SDR_SOAPY_H +#define SDR_SOAPY_H + +void soapyInitConfig(); +void soapyShowHelp(); +bool soapyOpen(); +void soapyRun(); +void soapyClose(); +bool soapyHandleOption(int argc, char **argv, int *jptr); + +#endif