diff --git a/Makefile b/Makefile index 4a7ecd80f..7bf957fec 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,10 @@ ifeq ($(PKGCONFIG), yes) LIMESDR := $(shell pkg-config --exists LimeSuite && echo "yes" || echo "no") endif + ifndef UHD + UHD := $(shell pkg-config --exists uhd && echo "yes" || echo "no") + endif + ifndef SOAPYSDR SOAPYSDR := $(shell pkg-config --exists SoapySDR && echo "yes" || echo "no") endif @@ -41,6 +45,7 @@ else BLADERF ?= no HACKRF ?= no LIMESDR ?= no + UHD ?= no SOAPYSDR ?= no endif @@ -160,6 +165,13 @@ ifeq ($(LIMESDR), yes) LIBS_SDR += $(shell pkg-config --libs LimeSuite) endif +ifeq ($(UHD), yes) + SDR_OBJ += sdr_uhd.o + DUMP1090_CPPFLAGS += -DENABLE_UHD + DUMP1090_CFLAGS += $(shell pkg-config --cflags uhd) + LIBS_SDR += $(shell pkg-config --libs uhd) +endif + ifeq ($(SOAPYSDR), yes) SDR_OBJ += sdr_soapy.o DUMP1090_CPPFLAGS += -DENABLE_SOAPYSDR @@ -167,7 +179,6 @@ ifeq ($(SOAPYSDR), yes) LIBS_SDR += $(shell pkg-config --libs SoapySDR) endif - ## ## starch (runtime DSP code selection) mix, architecture-specific ## @@ -210,13 +221,14 @@ include dsp/generated/makefile.$(STARCH_MIX) showconfig: @echo "Building with:" >&2 - @echo " Version string: $(DUMP1090_VERSION)" >&2 - @echo " Architecture: $(ARCH)" >&2 - @echo " DSP mix: $(STARCH_MIX)" >&2 - @echo " RTLSDR support: $(RTLSDR)" >&2 - @echo " BladeRF support: $(BLADERF)" >&2 - @echo " HackRF support: $(HACKRF)" >&2 - @echo " LimeSDR support: $(LIMESDR)" >&2 + @echo " Version string: $(DUMP1090_VERSION)" >&2 + @echo " Architecture: $(ARCH)" >&2 + @echo " DSP mix: $(STARCH_MIX)" >&2 + @echo " RTLSDR support: $(RTLSDR)" >&2 + @echo " BladeRF support: $(BLADERF)" >&2 + @echo " HackRF support: $(HACKRF)" >&2 + @echo " LimeSDR support: $(LIMESDR)" >&2 + @echo " UHD support : $(UHD)" >&2 @echo " SoapySDR support: $(SOAPYSDR)" >&2 %.o: %.c *.h diff --git a/README.md b/README.md index acafb9aaf..fc2af297d 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ many other Linux or Unix-like systems. ## Building under bullseye, buster, or stretch ```bash -$ sudo apt-get install build-essential fakeroot debhelper librtlsdr-dev pkg-config libncurses5-dev libbladerf-dev libhackrf-dev liblimesuite-dev libsoapysdr-dev +$ sudo apt-get install build-essential fakeroot debhelper librtlsdr-dev pkg-config libncurses5-dev libbladerf-dev libhackrf-dev liblimesuite-dev libsoapysdr-dev libuhd-dev $ ./prepare-build.sh bullseye # or buster, or stretch $ cd package-bullseye # or buster, or stretch $ dpkg-buildpackage -b --no-sign @@ -35,7 +35,7 @@ limited SDR support only. Pass `--build-profiles` to `dpkg-buildpackage` with a comma-separated list of profiles. The list of profiles should include `custom` and zero or more of -`rtlsdr`, `bladerf`, `hackrf`, `limesdr`, 'soapysdr' depending on what you want: +`rtlsdr`, `bladerf`, `hackrf`, `limesdr`, 'soapysdr', `uhd` depending on what you want: ```bash $ dpkg-buildpackage -b --no-sign --build-profiles=custom,rtlsdr # builds with rtlsdr support only @@ -62,6 +62,9 @@ libhackrf. ``make LIMESDR=no`` will disable LimeSDR support and remove the dependency on libLimeSuite. +``make UHD=no`` will disable UHD support and remove the dependency on +uhd-dev. + ``make SOAPYSDR=no`` will disable SoapySDR support and remove the dependency on libSoapySDR. diff --git a/dump1090.h b/dump1090.h index 4c4dd9273..9ce6ff674 100644 --- a/dump1090.h +++ b/dump1090.h @@ -299,7 +299,7 @@ typedef enum { //======================== structure declarations ========================= typedef enum { - SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF, SDR_HACKRF, SDR_LIMESDR, SDR_SOAPYSDR + SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF, SDR_HACKRF, SDR_LIMESDR, SDR_SOAPYSDR, SDR_UHD } sdr_type_t; // Program global state diff --git a/sdr.c b/sdr.c index 765d2edb7..7f16ca085 100644 --- a/sdr.c +++ b/sdr.c @@ -33,6 +33,9 @@ #ifdef ENABLE_LIMESDR # include "sdr_limesdr.h" #endif +#ifdef ENABLE_UHD +# include "sdr_uhd.h" +#endif #ifdef ENABLE_SOAPYSDR # include "sdr_soapy.h" #endif @@ -131,6 +134,9 @@ static sdr_handler sdr_handlers[] = { #ifdef ENABLE_LIMESDR { "limesdr", SDR_LIMESDR, limesdrInitConfig, limesdrShowHelp, limesdrHandleOption, limesdrOpen, limesdrRun, noStop, limesdrClose, noGetGain, noGetMaxGain, noGetGainDb, noSetGain }, #endif +#ifdef ENABLE_UHD + { "uhd", SDR_UHD, uhdInitConfig, uhdShowHelp, uhdHandleOption, uhdOpen, uhdRun, noStop, uhdClose, noGetGain, noGetMaxGain, noGetGainDb, noSetGain }, +#endif #ifdef ENABLE_SOAPYSDR { "soapy", SDR_SOAPYSDR, soapyInitConfig, soapyShowHelp, soapyHandleOption, soapyOpen, soapyRun, noStop, soapyClose, soapyGetGain, soapyGetMaxGain, soapyGetGainDb, soapySetGain }, #endif diff --git a/sdr_uhd.c b/sdr_uhd.c new file mode 100644 index 000000000..558288cdb --- /dev/null +++ b/sdr_uhd.c @@ -0,0 +1,240 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// sdr_bladerf.h: UHD support (header) +// +// 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_uhd.h" + +#include + +// complex int16 +#define SAMPLE_SIZE 4 + +static struct { + uhd_usrp_handle *usrp; + uhd_rx_metadata_handle *md; + uhd_rx_streamer_handle *rx_streamer; + char *device_args; + size_t channel; + char *sample_data; + + iq_convert_fn converter; + struct converter_state *converter_state; +} uhd; + +void uhdClose() { + if (uhd.rx_streamer) { + uhd_rx_streamer_free(uhd.rx_streamer); + free(uhd.rx_streamer); + uhd.rx_streamer = NULL; + } + if (uhd.md) { + uhd_rx_metadata_free(uhd.md); + free(uhd.md); + uhd.md = NULL; + } + if (uhd.usrp) { + uhd_usrp_free(uhd.usrp); + free(uhd.usrp); + uhd.usrp = NULL; + } + if (uhd.sample_data) { + free(uhd.sample_data); + uhd.sample_data = NULL; + } + if (uhd.device_args) { + free(uhd.device_args); + uhd.device_args = NULL; + } +} + +void uhdInitConfig() { + uhd.usrp = NULL; + uhd.md = NULL; + uhd.rx_streamer = NULL; + uhd.device_args = NULL; + uhd.sample_data = NULL; + uhd.channel = 0; +} + +void uhdShowHelp() { + printf(" uhd-specific options (use with --device-type uhd)\n"); + printf("\n"); + printf("--device-args UHD device args\n"); + printf("\n"); +} + +bool uhdHandleOption(int argc, char **argv, int *jptr) { + int j = *jptr; + bool more = (j+1 < argc); + if (!strcmp(argv[j], "--uhd-device-args") && more) { + uhd.device_args = strdup(argv[++j]); + } else { + return false; + } + + *jptr = j; + return true; +} + +bool uhdOpen() { + uhd_tune_request_t tune_request = { + .target_freq = Modes.freq, + .rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, + .dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, + }; + uhd_tune_result_t tune_result; + + if (!uhd.device_args) { + uhd.device_args = strdup(""); + } + + int uhd_error; + + uhd.usrp = malloc(sizeof(uhd_usrp_handle)); + uhd_error = uhd_usrp_make(uhd.usrp, uhd.device_args); + if (uhd_error) { + fprintf(stderr, "uhd_usrp_make() failed: %x", uhd_error); + goto failed; + } + + uhd.md = malloc(sizeof(uhd_rx_metadata_handle)); + uhd_error = uhd_rx_metadata_make(uhd.md); + if (uhd_error) { + fprintf(stderr, "uhd_rx_metadata_make() failed: %x", uhd_error); + goto failed; + } + + uhd.rx_streamer = malloc(sizeof(uhd_rx_streamer_handle)); + uhd_error = uhd_rx_streamer_make(uhd.rx_streamer); + if (uhd_error) { + fprintf(stderr, "uhd_rx_streamer_make() failed: %x", uhd_error); + goto failed; + } + + fprintf(stderr, "setting bw, rx rate to %fMsps\n", Modes.sample_rate / 1e6); + uhd_error = uhd_usrp_set_rx_rate(*uhd.usrp, Modes.sample_rate, uhd.channel); + if (uhd_error) { + fprintf(stderr, "uhd_usrp_set_rx_rate() failed: %x", uhd_error); + goto failed; + } + + uhd_error = uhd_usrp_set_rx_bandwidth(*uhd.usrp, Modes.sample_rate, uhd.channel); + if (uhd_error) { + fprintf(stderr, "uhd_usrp_set_rx_bandwidth() failed: %x", uhd_error); + goto failed; + } + + if (Modes.gain != MODES_DEFAULT_GAIN) { + fprintf(stderr, "setting rx gain to %f\n", Modes.gain); + uhd_error = uhd_usrp_set_rx_gain(*uhd.usrp, Modes.gain, uhd.channel, ""); + if (uhd_error) { + fprintf(stderr, "uhd_usrp_set_rx_gain() failed: %x", uhd_error); + goto failed; + } + } + + fprintf(stderr, "setting rx freq to %fMHz\n", Modes.freq / 1e6); + uhd_error = uhd_usrp_set_rx_freq(*uhd.usrp, &tune_request, uhd.channel, &tune_result); + if (uhd_error) { + fprintf(stderr, "uhd_usrp_set_rx_freq() failed: %x", uhd_error); + goto failed; + } + + uhd.converter = init_converter( + INPUT_SC16Q11, Modes.sample_rate, Modes.dc_filter, &uhd.converter_state); + + return true; + + failed: + uhdClose(); + return false; +} + +void uhdRun() { + uhd_stream_args_t stream_args = { + .cpu_format = "sc16", + .otw_format = "sc16", + .args = "", + .channel_list = &uhd.channel, + .n_channels = 1 + }; + + uhd_stream_cmd_t stream_cmd = { + .stream_mode = UHD_STREAM_MODE_START_CONTINUOUS, + .stream_now = true + }; + + uhd_stream_cmd_t stop_stream_cmd = { + .stream_mode = UHD_STREAM_MODE_STOP_CONTINUOUS, + }; + + size_t num_rx_samps = 0; + struct mag_buf *outbuf = NULL; + int uhd_error; + + uhd_error = uhd_usrp_get_rx_stream(*uhd.usrp, &stream_args, *uhd.rx_streamer); + if (uhd_error) { + fprintf(stderr, "uhd_usrp_get_rx_stream() failed: %x\n", uhd_error); + return; + } + + fprintf(stderr, "streaming...\n"); + uhd.sample_data = malloc(MODES_MAG_BUF_SAMPLES * SAMPLE_SIZE); + int64_t full_secs; + double frac_secs; + uhd_rx_metadata_time_spec(*uhd.md, &full_secs, &frac_secs); + double first_recv_time = full_secs + frac_secs; + + uhd_error = uhd_rx_streamer_issue_stream_cmd(*uhd.rx_streamer, &stream_cmd); + if (uhd_error) { + fprintf(stderr, "uhd_rx_streamer_issue_stream_cmd() failed: %x\n", uhd_error); + return; + } + + for (;;) { + outbuf = fifo_acquire(0); + if (!outbuf) { + if (Modes.exit) { + break; + } + fprintf(stderr, "could not get outbuf\n"); + continue; + } + + outbuf->sysTimestamp = mstime(); + uhd_rx_streamer_recv(*uhd.rx_streamer, (void **) &uhd.sample_data, MODES_MAG_BUF_SAMPLES, uhd.md, 3.0, false, &num_rx_samps); + uhd_rx_metadata_error_code_t error_code; + uhd_rx_metadata_error_code(*uhd.md, &error_code); + if (error_code != UHD_RX_METADATA_ERROR_CODE_NONE) { + fprintf(stderr, "error code while streaming: %x\n", error_code); + break; + } + uhd_rx_metadata_time_spec(*uhd.md, &full_secs, &frac_secs); + double recv_time = full_secs + frac_secs; + outbuf->sampleTimestamp = (recv_time - first_recv_time) * 12e6 / Modes.sample_rate; + + uhd.converter(uhd.sample_data, &outbuf->data[outbuf->overlap], num_rx_samps, uhd.converter_state, &outbuf->mean_level, &outbuf->mean_power); + outbuf->validLength = outbuf->overlap + num_rx_samps; + outbuf->flags = 0; + + fifo_enqueue(outbuf); + } + + uhd_rx_streamer_issue_stream_cmd(*uhd.rx_streamer, &stop_stream_cmd); +} diff --git a/sdr_uhd.h b/sdr_uhd.h new file mode 100644 index 000000000..d591bdf96 --- /dev/null +++ b/sdr_uhd.h @@ -0,0 +1,34 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// sdr_bladerf.h: UHD support (header) +// +// 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 UHD_H +#define UHD_H + +#include + +// Support for UHD SDR + +void uhdInitConfig(); +void uhdShowHelp(); +bool uhdHandleOption(int argc, char **argv, int *jptr); +bool uhdOpen(); +void uhdRun(); +void uhdClose(); + +#endif