summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2021-07-08 15:49:23 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2021-11-26 16:51:25 +0100
commit558a210085b61b84e3be5a420d86d2ee6a500c8e (patch)
treecb16ff88132ef9e7c946a4e3f5523f9113cf4aed /src
parent22fd7d757f11a6c976d7e711fd46cf2ce3247c44 (diff)
downloaddabmod-558a210085b61b84e3be5a420d86d2ee6a500c8e.tar.gz
dabmod-558a210085b61b84e3be5a420d86d2ee6a500c8e.tar.bz2
dabmod-558a210085b61b84e3be5a420d86d2ee6a500c8e.zip
Add support for BladeRF devices
Many thanks to Steven Rossel for the work he did during his student project.
Diffstat (limited to 'src')
-rw-r--r--src/ConfigParser.cpp32
-rw-r--r--src/ConfigParser.h5
-rw-r--r--src/DabMod.cpp24
-rwxr-xr-xsrc/output/BladeRF.cpp321
-rwxr-xr-xsrc/output/BladeRF.h112
-rw-r--r--src/output/SDRDevice.h3
6 files changed, 494 insertions, 3 deletions
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp
index 73e51dd..ee7acc3 100644
--- a/src/ConfigParser.cpp
+++ b/src/ConfigParser.cpp
@@ -342,6 +342,36 @@ static void parse_configfile(
mod_settings.useLimeOutput = true;
}
#endif
+
+#if defined(HAVE_BLADERF)
+ else if (output_selected == "bladerf") {
+ auto& outputbladerf_conf = mod_settings.sdr_device_config;
+ outputbladerf_conf.device = pt.Get("bladerfoutput.device", "");
+ outputbladerf_conf.refclk_src = pt.Get("bladerfoutput.refclk_source", "");
+ outputbladerf_conf.txgain = pt.GetReal("bladerfoutput.txgain", 0.0);
+ outputbladerf_conf.frequency = pt.GetReal("bladerfoutput.frequency", 0);
+ outputbladerf_conf.bandwidth = pt.GetReal("bladerfoutput.bandwidth", 0);
+ std::string chan = pt.Get("bladerfoutput.channel", "");
+ outputbladerf_conf.dabMode = mod_settings.dabMode;
+
+ if (outputbladerf_conf.frequency == 0 && chan == "") {
+ std::cerr << " BladeRF output enabled, but neither frequency nor channel defined.\n";
+ throw std::runtime_error("Configuration error");
+ }
+ else if (outputbladerf_conf.frequency == 0) {
+ outputbladerf_conf.frequency = parseChannel(chan);
+ }
+ else if (outputbladerf_conf.frequency != 0 && chan != "") {
+ std::cerr << " BladeRF output: cannot define both frequency and channel.\n";
+ throw std::runtime_error("Configuration error");
+ }
+
+ outputbladerf_conf.dpdFeedbackServerPort = pt.GetInteger("bladerfoutput.dpd_port", 0);
+
+ mod_settings.useBladeRFOutput = true;
+ }
+#endif
+
#if defined(HAVE_ZEROMQ)
else if (output_selected == "zmq") {
mod_settings.outputName = pt.Get("zmqoutput.listen", "");
@@ -354,6 +384,7 @@ static void parse_configfile(
throw std::runtime_error("Configuration error");
}
+
#if defined(HAVE_OUTPUT_UHD)
mod_settings.sdr_device_config.enableSync = (pt.GetInteger("delaymanagement.synchronous", 0) == 1);
mod_settings.sdr_device_config.muteNoTimestamps = (pt.GetInteger("delaymanagement.mutenotimestamps", 0) == 1);
@@ -378,6 +409,7 @@ static void parse_configfile(
#endif
+
/* Read TII parameters from config file */
mod_settings.tiiConfig.enable = pt.GetInteger("tii.enable", 0);
mod_settings.tiiConfig.comb = pt.GetInteger("tii.comb", 0);
diff --git a/src/ConfigParser.h b/src/ConfigParser.h
index 33d7824..574caa2 100644
--- a/src/ConfigParser.h
+++ b/src/ConfigParser.h
@@ -38,6 +38,7 @@
#include "output/UHD.h"
#include "output/Soapy.h"
#include "output/Lime.h"
+#include "output/BladeRF.h"
#define ZMQ_INPUT_MAX_FRAME_QUEUE 500
@@ -51,6 +52,8 @@ struct mod_settings_t {
bool useUHDOutput = false;
bool useSoapyOutput = false;
bool useLimeOutput = false;
+ bool useBladeRFOutput = false;
+ const std::string BladeRFOutputFormat = "s16"; // to transmit SC16 IQ
size_t outputRate = 2048000;
size_t clockRate = 0;
@@ -84,7 +87,7 @@ struct mod_settings_t {
// Settings for the OFDM windowing
size_t ofdmWindowOverlap = 0;
-#if defined(HAVE_OUTPUT_UHD) || defined(HAVE_SOAPYSDR) || defined(HAVE_LIMESDR)
+#if defined(HAVE_OUTPUT_UHD) || defined(HAVE_SOAPYSDR) || defined(HAVE_LIMESDR) || defined(HAVE_BLADERF)
Output::SDRDeviceConfig sdr_device_config;
#endif
diff --git a/src/DabMod.cpp b/src/DabMod.cpp
index 979df87..f97c05d 100644
--- a/src/DabMod.cpp
+++ b/src/DabMod.cpp
@@ -57,6 +57,7 @@
#include "output/UHD.h"
#include "output/Soapy.h"
#include "output/Lime.h"
+#include "output/BladeRF.h"
#include "OutputZeroMQ.h"
#include "InputReader.h"
#include "PcDebug.h"
@@ -158,6 +159,13 @@ static void printModSettings(const mod_settings_t& mod_settings)
mod_settings.sdr_device_config.masterClockRate << "\n";
}
#endif
+#if defined(HAVE_BLADERF)
+ else if (mod_settings.useBladeRFOutput) {
+ ss << " BladeRF\n"
+ " Device: " << mod_settings.sdr_device_config.device << "\n" <<
+ " refclk: " << mod_settings.sdr_device_config.refclk_src << "\n";
+ }
+#endif
else if (mod_settings.useZeroMQOutput) {
ss << " ZeroMQ\n" <<
" Listening on: " << mod_settings.outputName << "\n" <<
@@ -251,6 +259,16 @@ static shared_ptr<ModOutput> prepare_output(
rcs.enrol((Output::SDR*)output.get());
}
#endif
+#if defined(HAVE_BLADERF)
+ else if (s.useBladeRFOutput) {
+ /* We normalise specifically for the BladeRF output : range [-2048; 2047] */
+ s.normalise = 2047.0f / normalise_factor;
+ s.sdr_device_config.sampleRate = s.outputRate;
+ auto bladerfdevice = make_shared<Output::BladeRF>(s.sdr_device_config);
+ output = make_shared<Output::SDR>(s.sdr_device_config, bladerfdevice);
+ rcs.enrol((Output::SDR*)output.get());
+ }
+#endif
#if defined(HAVE_ZEROMQ)
else if (s.useZeroMQOutput) {
/* We normalise the same way as for the UHD output */
@@ -301,7 +319,8 @@ int launch_modulator(int argc, char* argv[])
mod_settings.useUHDOutput or
mod_settings.useZeroMQOutput or
mod_settings.useSoapyOutput or
- mod_settings.useLimeOutput)) {
+ mod_settings.useLimeOutput or
+ mod_settings.useBladeRFOutput)) {
throw std::runtime_error("Configuration error: Output not specified");
}
@@ -314,6 +333,9 @@ int launch_modulator(int argc, char* argv[])
mod_settings.fileOutputFormat == "s16")) {
format_converter = make_shared<FormatConverter>(mod_settings.fileOutputFormat);
}
+ else if (mod_settings.useBladeRFOutput) {
+ format_converter = make_shared<FormatConverter>(mod_settings.BladeRFOutputFormat);
+ }
auto output = prepare_output(mod_settings);
diff --git a/src/output/BladeRF.cpp b/src/output/BladeRF.cpp
new file mode 100755
index 0000000..a6ad0cc
--- /dev/null
+++ b/src/output/BladeRF.cpp
@@ -0,0 +1,321 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2019
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ Copyright (C) 2021
+ Steven Rossel, steven.rossel@bluewin.ch
+
+ http://opendigitalradio.org
+ */
+/*
+ This file is part of ODR-DabMod.
+
+ ODR-DabMod is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMod 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 ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "BladeRF.h"
+
+#ifdef HAVE_BLADERF
+
+#include <chrono>
+#include <limits>
+#include <cstdio>
+#include <iomanip>
+#include <algorithm>
+#include <iterator>
+
+#include "Log.h"
+#include "Utils.h"
+
+using namespace std;
+
+namespace Output
+{
+
+BladeRF::BladeRF(SDRDeviceConfig &config) : SDRDevice(), m_conf(config)
+{
+
+ etiLog.level(info) << "BladeRF:Creating the device with: " << m_conf.device;
+
+ struct bladerf_devinfo devinfo;
+
+ // init device infos
+ bladerf_init_devinfo(&devinfo); // this function does not return a status
+
+ int status = bladerf_open_with_devinfo(&m_device, &devinfo); // open device with info
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error making BladeRF device: %s " << bladerf_strerror(status);
+ throw runtime_error("Cannot open BladeRF output device");
+ }
+
+ if (m_conf.refclk_src == "pps")
+ {
+ status = bladerf_set_vctcxo_tamer_mode(m_device, BLADERF_VCTCXO_TAMER_1_PPS); // 1 PPS tames the clock
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error making BladeRF device: %s " << bladerf_strerror(status);
+ throw runtime_error("Cannot set BladeRF refclk to pps");
+ }
+ }
+ else if (m_conf.refclk_src == "10mhz")
+ {
+ status = bladerf_set_vctcxo_tamer_mode(m_device, BLADERF_VCTCXO_TAMER_10_MHZ); // 10 MHz tames the clock
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error making BladeRF device: %s " << bladerf_strerror(status);
+ throw runtime_error("Cannot set BladeRF refclk to 10 MHz");
+ }
+ }
+
+ status = bladerf_set_sample_rate(m_device, m_channel, (bladerf_sample_rate)m_conf.sampleRate, NULL);
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error making BladeRF device: %s " << bladerf_strerror(status);
+ throw runtime_error("Cannot set BladeRF sample rate");
+ }
+
+ bladerf_sample_rate host_sample_rate = 0;
+ status = bladerf_get_sample_rate(m_device, m_channel, &host_sample_rate);
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error making BladeRF device: %s " << bladerf_strerror(status);
+ throw runtime_error("Cannot get BladeRF sample rate");
+ }
+ etiLog.level(info) << "BladeRF sample rate set to " << std::to_string(host_sample_rate / 1000.0) << " kHz";
+
+ tune(m_conf.lo_offset, m_conf.frequency);
+
+ bladerf_frequency cur_frequency = 0;
+
+ status = bladerf_get_frequency(m_device, m_channel, &cur_frequency);
+ if(status < 0)
+ {
+ etiLog.level(error) << "Error making BladeRF device: %s " << bladerf_strerror(status);
+ throw runtime_error("Cannot get BladeRF frequency");
+ }
+ etiLog.level(info) << "BladeRF:Actual frequency: " << fixed << setprecision(3) << cur_frequency / 1000.0 << " kHz.";
+
+ status = bladerf_set_gain(m_device, m_channel, (bladerf_gain)m_conf.txgain); // gain in [dB]
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error making BladeRF device: %s " << bladerf_strerror(status);
+ throw runtime_error("Cannot set BladeRF gain");
+ }
+
+ bladerf_bandwidth cur_bandwidth = 0;
+ status = bladerf_set_bandwidth(m_device, m_channel, (bladerf_bandwidth)m_conf.bandwidth, &cur_bandwidth);
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error making BladeRF device: %s " << bladerf_strerror(status);
+ throw runtime_error("Cannot set BladeRF bandwidth");
+ }
+
+ /* ---------------------------- Streaming Config ---------------------------- */
+ const unsigned int num_buffers = 16; // Number of buffers to use in the underlying data stream
+ const unsigned int buffer_size = 8192; // "to hold 2048 samples for one channel, a buffer must be at least 8192 bytes large"
+ const unsigned int num_transfers = 8; // active USB transfers
+ const unsigned int timeout_ms = 3500;
+
+ /* Configure the device's x1 TX (SISO) channel for use with the
+ * synchronous interface. SC16 Q11 samples *without* metadata are used. */
+ status = bladerf_sync_config(m_device, BLADERF_TX_X1, BLADERF_FORMAT_SC16_Q11, num_buffers,
+ buffer_size, num_transfers, timeout_ms);
+ if (status != 0) {
+ etiLog.level(error) << "Error making BladeRF device: %s " << bladerf_strerror(status);
+ throw runtime_error("Cannot setup BladeRF stream");
+ }
+
+ status = bladerf_enable_module(m_device, m_channel, true);
+ if(status < 0)
+ {
+ etiLog.level(error) << "Error making BladeRF device: %s " << bladerf_strerror(status);
+ throw runtime_error("Cannot enable BladeRF channel");
+ }
+
+}
+
+BladeRF::~BladeRF()
+{
+ if (m_device != nullptr)
+ {
+ //bladerf_deinit_stream(m_stream); /* Asynchronous API function*/
+ bladerf_enable_module(m_device, m_channel, false);
+ bladerf_close(m_device);
+ }
+}
+
+void BladeRF::tune(double lo_offset, double frequency)
+{
+ int status;
+ if (not m_device)
+ throw runtime_error("BladeRF device not set up");
+
+ if (lo_offset != 0)
+ {
+ etiLog.level(info) << "lo_offset cannot be set at "<< std::to_string(lo_offset) << " with BladeRF output, has to be 0"
+ << "\nlo_offset is now set to 0";
+ m_conf.lo_offset = 0;
+ }
+
+ status = bladerf_set_frequency(m_device, m_channel, (bladerf_frequency)m_conf.frequency);
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error setting BladeRF TX frequency: %s " << bladerf_strerror(status);
+ }
+}
+
+double BladeRF::get_tx_freq(void) const
+{
+ if (not m_device)
+ throw runtime_error("Lime device not set up");
+
+ int status;
+ bladerf_frequency cur_frequency = 0;
+
+ status = bladerf_get_frequency(m_device, m_channel, &cur_frequency);
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error getting BladeRF TX frequency: %s " << bladerf_strerror(status);
+ }
+
+ return (double)cur_frequency;
+}
+
+void BladeRF::set_txgain(double txgain)
+{
+ m_conf.txgain = txgain;
+ if (not m_device)
+ throw runtime_error("Lime device not set up");
+
+ int status;
+ status = bladerf_set_gain(m_device, m_channel, (bladerf_gain)m_conf.txgain); // gain in [dB]
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error making BladeRF device: %s " << bladerf_strerror(status);
+ }
+}
+
+double BladeRF::get_txgain(void) const
+{
+ if (not m_device)
+ throw runtime_error("BladeRF device not set up");
+
+ bladerf_gain txgain = 0;
+ int status;
+
+ status = bladerf_get_gain(m_device, m_channel, &txgain);
+
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error getting BladeRF TX gain: %s " << bladerf_strerror(status);
+ }
+ return (double)txgain;
+}
+
+void BladeRF::set_bandwidth(double bandwidth)
+{
+ bladerf_set_bandwidth(m_device, m_channel, (bladerf_bandwidth)m_conf.bandwidth, NULL);
+}
+
+double BladeRF::get_bandwidth(void) const
+{
+ bladerf_bandwidth bw;
+ bladerf_get_bandwidth(m_device, m_channel, &bw);
+ return (double)bw;
+}
+
+SDRDevice::RunStatistics BladeRF::get_run_statistics(void) const
+{
+ RunStatistics rs;
+ rs.num_underruns = underflows;
+ rs.num_overruns = overflows;
+ rs.num_late_packets = late_packets;
+ rs.num_frames_modulated = num_frames_modulated;
+ return rs;
+}
+
+double BladeRF::get_real_secs(void) const
+{
+ // TODO
+ return 0.0;
+}
+
+void BladeRF::set_rxgain(double rxgain)
+{
+ // TODO
+}
+
+double BladeRF::get_rxgain(void) const
+{
+ // TODO
+ return 0.0;
+}
+
+size_t BladeRF::receive_frame(
+ complexf *buf,
+ size_t num_samples,
+ struct frame_timestamp &ts,
+ double timeout_secs)
+{
+ // TODO
+ return 0;
+}
+
+bool BladeRF::is_clk_source_ok() const
+{
+ // TODO
+ return true;
+}
+
+const char *BladeRF::device_name(void) const
+{
+ return "BladeRF";
+}
+
+double BladeRF::get_temperature(void) const
+{
+ if (not m_device)
+ throw runtime_error("BladeRF device not set up");
+
+ float temp = 0.0;
+
+ int status = bladerf_get_rfic_temperature(m_device, &temp);
+ if (status < 0)
+ {
+ etiLog.level(error) << "Error getting BladeRF temperature: %s " << bladerf_strerror(status);
+ }
+
+ return (double)temp;
+}
+
+
+void BladeRF::transmit_frame(const struct FrameData &frame) // SC16 frames
+{
+ const size_t num_samples = frame.buf.size() / (2*sizeof(int16_t));
+
+ const int status = bladerf_sync_tx(m_device, frame.buf.data(), num_samples, NULL, 0);
+ if (status < 0) {
+ etiLog.level(error) << "Error transmitting samples with BladeRF: %s " << bladerf_strerror(status);
+ throw runtime_error("Cannot transmit TX samples");
+ }
+
+ num_frames_modulated++;
+}
+} // namespace Output
+
+#endif // HAVE_BLADERF
diff --git a/src/output/BladeRF.h b/src/output/BladeRF.h
new file mode 100755
index 0000000..bc6db38
--- /dev/null
+++ b/src/output/BladeRF.h
@@ -0,0 +1,112 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Copyright (C) 2019
+ Matthias P. Braendli, matthias.braendli@mpb.li
+
+ Copyright (C) 2021
+ Steven Rossel, steven.rossel@bluewin.ch
+
+ http://opendigitalradio.org
+
+DESCRIPTION:
+ It is an output driver for the BladeRF family of devices, and uses the libbladerf
+ library.
+*/
+
+/*
+ This file is part of ODR-DabMod.
+
+ ODR-DabMod is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMod 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 ODR-DabMod. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+ #pragma once
+
+ #ifdef HAVE_CONFIG_H
+ # include <config.h>
+ #endif
+
+
+#ifdef HAVE_BLADERF
+ #define SAMPLES_LEN 10000 // for transmit_frame() purpose, may be any (reasonable) size
+
+ #include <chrono>
+ #include <memory>
+ #include <string>
+ #include <atomic>
+ #include <thread>
+ #include <libbladeRF.h>
+
+ #include "Log.h"
+ #include "output/SDR.h"
+ #include "TimestampDecoder.h"
+ #include "RemoteControl.h"
+ #include "ThreadsafeQueue.h"
+
+ #include <stdio.h>
+ #include <sys/types.h>
+
+namespace Output {
+
+class BladeRF : public Output::SDRDevice
+{
+ public:
+ BladeRF(SDRDeviceConfig& config);
+ BladeRF(const BladeRF& other) = delete;
+ BladeRF& operator=(const BladeRF& other) = delete;
+ ~BladeRF();
+
+ virtual void tune(double lo_offset, double frequency) override;
+ virtual double get_tx_freq(void) const override;
+ virtual void set_txgain(double txgain) override;
+ virtual double get_txgain(void) const override;
+ virtual void set_bandwidth(double bandwidth) override;
+ virtual double get_bandwidth(void) const override;
+ virtual void transmit_frame(const struct FrameData& frame) override;
+ virtual RunStatistics get_run_statistics(void) const override;
+ virtual double get_real_secs(void) const override;
+
+ virtual void set_rxgain(double rxgain) override;
+ virtual double get_rxgain(void) const override;
+ virtual size_t receive_frame(
+ complexf *buf,
+ size_t num_samples,
+ struct frame_timestamp& ts,
+ double timeout_secs) override;
+
+ // Return true if GPS and reference clock inputs are ok
+ virtual bool is_clk_source_ok(void) const override;
+ virtual const char* device_name(void) const override;
+
+ virtual double get_temperature(void) const override;
+
+
+ private:
+ SDRDeviceConfig& m_conf;
+ struct bladerf *m_device;
+ bladerf_channel m_channel = BLADERF_CHANNEL_TX(0); // channel TX0
+ //struct bladerf_stream* m_stream; /* used for asynchronous api */
+
+ size_t underflows = 0;
+ size_t overflows = 0;
+ size_t late_packets = 0;
+ size_t num_frames_modulated = 0;
+ //size_t num_underflows_previous = 0;
+ //size_t num_late_packets_previous = 0;
+};
+
+} // namespace Output
+
+#endif // HAVE_BLADERF \ No newline at end of file
diff --git a/src/output/SDRDevice.h b/src/output/SDRDevice.h
index d84ebf9..bb63f60 100644
--- a/src/output/SDRDevice.h
+++ b/src/output/SDRDevice.h
@@ -72,7 +72,8 @@ struct SDRDeviceConfig {
unsigned dabMode = 0;
unsigned maxGPSHoldoverTime = 0;
- /* allowed values for UHD : auto, int, sma, mimo */
+ /* allowed values for UHD : auto, int, sma, mimo */
+ /* allowed values for BladeRF : pps, 10mhz */
std::string refclk_src;
/* allowed values for UHD : int, sma, mimo */