From f7af0a4ac283aa2d6e92ff8f86d225a0cf38a56a Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 23 Sep 2018 19:56:24 +0200 Subject: [PATCH] BladerRF2 input support (2) --- Readme.md | 26 +- devices/bladerf2/CMakeLists.txt | 2 + devices/bladerf2/devicebladerf2.cpp | 198 ++++- devices/bladerf2/devicebladerf2.h | 18 +- devices/bladerf2/devicebladerf2shared.cpp | 28 + devices/bladerf2/devicebladerf2shared.h | 62 ++ ...ut_plugin.png => BladeRF1Input_plugin.png} | Bin ...ut_plugin.xcf => BladeRF1Input_plugin.xcf} | Bin ...t_plugin.png => BladeRF1Output_plugin.png} | Bin ...t_plugin.xcf => BladeRF1Output_plugin.xcf} | Bin ...g => BladeRF1Output_plugin_fifodly_32.png} | Bin ...> BladeRF1Output_plugin_fifodly_other.png} | Bin plugins/samplesink/bladerf1output/readme.md | 16 +- plugins/samplesource/CMakeLists.txt | 2 + plugins/samplesource/bladerf1input/readme.md | 10 +- .../samplesource/bladerf2input/CMakeLists.txt | 79 ++ .../bladerf2input/bladerf2input.cpp | 220 ++++++ .../bladerf2input/bladerf2input.h | 154 ++++ .../bladerf2input/bladerf2inputgui.ui | 727 ++++++++++++++++++ .../bladerf2input/bladerf2inputplugin.cpp | 25 +- .../bladerf2input/bladerf2inputthread.cpp | 264 +++++++ .../bladerf2input/bladerf2inputthread.h | 90 +++ .../limesdrinput/limesdrinput.cpp | 14 + 23 files changed, 1904 insertions(+), 31 deletions(-) create mode 100644 devices/bladerf2/devicebladerf2shared.cpp create mode 100644 devices/bladerf2/devicebladerf2shared.h rename doc/img/{BladeRFInput_plugin.png => BladeRF1Input_plugin.png} (100%) rename doc/img/{BladeRFInput_plugin.xcf => BladeRF1Input_plugin.xcf} (100%) rename doc/img/{BladeRFOutput_plugin.png => BladeRF1Output_plugin.png} (100%) rename doc/img/{BladeRFOutput_plugin.xcf => BladeRF1Output_plugin.xcf} (100%) rename doc/img/{BladeRFOutput_plugin_fifodly_32.png => BladeRF1Output_plugin_fifodly_32.png} (100%) rename doc/img/{BladeRFOutput_plugin_fifodly_other.png => BladeRF1Output_plugin_fifodly_other.png} (100%) create mode 100644 plugins/samplesource/bladerf2input/CMakeLists.txt create mode 100644 plugins/samplesource/bladerf2input/bladerf2input.cpp create mode 100644 plugins/samplesource/bladerf2input/bladerf2input.h create mode 100644 plugins/samplesource/bladerf2input/bladerf2inputgui.ui create mode 100644 plugins/samplesource/bladerf2input/bladerf2inputthread.cpp create mode 100644 plugins/samplesource/bladerf2input/bladerf2inputthread.h diff --git a/Readme.md b/Readme.md index fe8969996..4a08d8ce5 100644 --- a/Readme.md +++ b/Readme.md @@ -36,7 +36,7 @@ Since version 2 SDRangel can integrate more than one hardware device running con Since version 3 transmission or signal generation is supported for BladeRF, HackRF (since version 3.1), LimeSDR (since version 3.4) and PlutoSDR (since version 3.7.8) using a sample sink plugin. These plugins are: - - [BladeRF output plugin](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/bladerf1output) + - [BladeRF1 output plugin](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/bladerf1output) - [HackRF output plugin](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/hackrfoutput) - [LimeSDR output plugin](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/limesdroutput) - [PlutoSDR output plugin](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/plutosdroutput) @@ -96,15 +96,31 @@ It is recommended to add `-DRX_SAMPLE_24BIT=ON` on the cmake command line to act ☞ From version 3.12.0 the Linux binaries are built with the 24 bit Rx option. -

BladeRF

+

BladeRF classic (v.1)

-[BladeRF](https://www.nuand.com/) is supported through the libbladerf library that should be installed in your system for proper build of the software and operation support. Add `libbladerf-dev` to the list of dependencies to install. +Linux only. -If you use your own location for libbladeRF install directory you need to specify library and include locations. Example with `/opt/install/libbladerf` with the following defines on `cmake` command line: +[BladeRF1](https://www.nuand.com/bladerf-1) is supported through the libbladeRF library that should be installed in your system for proper build of the software and operation support. Add `libbladerf-dev` to the list of dependencies to install. Note that libbladeRF v2 is used since version 4.2.0 (git tag 2018.08). + +If you compile and use your own location for libbladeRF install directory you need to specify library and include locations. Example with `/opt/install/libbladerf` with the following defines on `cmake` command line: `-DLIBBLADERF_LIBRARIES=/opt/install/libbladeRF/lib/libbladeRF.so -DLIBBLADERF_INCLUDE_DIR=/opt/install/libbladeRF/include` -☞ Please note that if you use your own library the FPGA image `hostedx40.rbf` or `hostedx115.rbf` shoud be placed in e.g. `/opt/install/libbladeRF/share/Nuand/bladeRF` +☞ Please note that if you use your own library the FPGA image `hostedx40.rbf` or `hostedx115.rbf` shoud be placed in e.g. `/opt/install/libbladeRF/share/Nuand/bladeRF` unless you have flashed the FPGA image inside the BladeRF. + +The plugins used to support BladeRF classic are specific to this version of the BladeRF: + - [BladeRF1 input](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesource/bladerf1input) + - [BladeRF1 output](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/bladerf1output) + +

BladeRF micro (v.2)

+ +Linux only. From version 4.2.0 + +[BladeRF 2 micro](https://www.nuand.com/bladerf-2-0-micro/) is also supported using libbladeRF library that should be installed and configured in the same way as for BladeRF1. + +The plugins used to support BladeRF 2 micro are specific to this version of the BladeRF: + - [BladeRF2 input](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesource/bladerf2input) + - [BladeRF2 output](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/bladerf2output)

FunCube Dongle

diff --git a/devices/bladerf2/CMakeLists.txt b/devices/bladerf2/CMakeLists.txt index f92ad7686..2b0c96490 100644 --- a/devices/bladerf2/CMakeLists.txt +++ b/devices/bladerf2/CMakeLists.txt @@ -2,10 +2,12 @@ project(bladerf2device) set(bladerf2device_SOURCES devicebladerf2.cpp + devicebladerf2shared.cpp ) set(bladerf2device_HEADERS devicebladerf2.h + devicebladerf2shared.h ) if (BUILD_DEBIAN) diff --git a/devices/bladerf2/devicebladerf2.cpp b/devices/bladerf2/devicebladerf2.cpp index a82e9f493..7b7e4a772 100644 --- a/devices/bladerf2/devicebladerf2.cpp +++ b/devices/bladerf2/devicebladerf2.cpp @@ -22,7 +22,11 @@ #include "devicebladerf2.h" DeviceBladeRF2::DeviceBladeRF2() : - m_dev(0) + m_dev(0), + m_nbRxChannels(0), + m_nbTxChannels(0), + m_rxOpen(0), + m_txOpen(0) {} DeviceBladeRF2::~DeviceBladeRF2() @@ -32,6 +36,14 @@ DeviceBladeRF2::~DeviceBladeRF2() bladerf_close(m_dev); m_dev = 0; } + + if (m_rxOpen) { + delete[] m_rxOpen; + } + + if (m_txOpen) { + delete[] m_txOpen; + } } bool DeviceBladeRF2::open(const char *serial) @@ -58,6 +70,12 @@ bool DeviceBladeRF2::open(const char *serial) return false; } + m_nbRxChannels = bladerf_get_channel_count(m_dev, BLADERF_RX); + m_nbTxChannels = bladerf_get_channel_count(m_dev, BLADERF_TX); + + m_rxOpen = new bool[m_nbRxChannels]; + m_txOpen = new bool[m_nbTxChannels]; + return true; } @@ -100,7 +118,157 @@ struct bladerf *DeviceBladeRF2::open_bladerf_from_serial(const char *serial) } } -void DeviceBladeRF2::getFrequencyRangeRx(int& min, int& max, int& step) +bool DeviceBladeRF2::openRx(int channel) +{ + if (!m_dev) { + return false; + } + + if ((channel < 0) || (channel >= m_nbRxChannels)) + { + qCritical("DeviceBladeRF2::openRx: invalid Rx channel index %d", channel); + return false; + } + + int status; + + if (!m_rxOpen[channel]) + { + status = bladerf_enable_module(m_dev, BLADERF_CHANNEL_RX(channel), true); + + if (status < 0) + { + qCritical("DeviceBladeRF2::openRx: Failed to enable Rx channel %d: %s", + channel, bladerf_strerror(status)); + return false; + } + else + { + qDebug("DeviceBladeRF2::openRx: Rx channel %d enabled", channel); + m_rxOpen[channel] = true; + return true; + } + } + else + { + qCritical("DeviceBladeRF2::openRx: Rx channel %d already opened", channel); + return false; + } +} + +bool DeviceBladeRF2::openTx(int channel) +{ + if (!m_dev) { + return false; + } + + if ((channel < 0) || (channel >= m_nbTxChannels)) + { + qCritical("DeviceBladeRF2::openTx: invalid Tx channel index %d", channel); + return false; + } + + int status; + + if (!m_txOpen[channel]) + { + status = bladerf_enable_module(m_dev, BLADERF_CHANNEL_TX(0), true); + + if (status < 0) + { + qCritical("DeviceBladeRF2::openTx: Failed to enable Tx channel %d: %s", + channel, bladerf_strerror(status)); + return false; + } + else + { + qDebug("DeviceBladeRF2::openTx: Tx channel %d enabled", channel); + m_txOpen[channel] = true; + return true; + } + } + else + { + qCritical("DeviceBladeRF2::openTx: Tx channel %d already opened", channel); + return false; + } +} + +void DeviceBladeRF2::closeRx(int channel) +{ + if (!m_dev) { + return; + } + + if ((channel < 0) || (channel >= m_nbRxChannels)) + { + qCritical("DeviceBladeRF2::closeRx: invalid Rx channel index %d", channel); + return; + } + + if (m_rxOpen[channel]) + { + for (int i = 0; i < m_nbRxChannels; i++) + { + if ((i != channel) && (m_rxOpen[i])) + { + qDebug("DeviceBladeRF2::closeRx: not closing channel %d as %d is still open", channel, i); + } + } + + int status = bladerf_enable_module(m_dev, BLADERF_CHANNEL_RX(channel), false); + m_rxOpen[channel] = false; + + if (status < 0) { + qCritical("DeviceBladeRF2::closeRx: cannot close channel %d: %s", channel, bladerf_strerror(status)); + } else { + qDebug("DeviceBladeRF2::closeRx: channel %d closed", channel); + } + } + else + { + qCritical("DeviceBladeRF2::closeRx: Rx channel %d already closed", channel); + } +} + +void DeviceBladeRF2::closeTx(int channel) +{ + if (!m_dev) { + return; + } + + if ((channel < 0) || (channel >= m_nbTxChannels)) + { + qCritical("DeviceBladeRF2::closeTx: invalid Tx channel index %d", channel); + return; + } + + if (m_txOpen[channel]) + { + for (int i = 0; i < m_nbTxChannels; i++) + { + if ((i != channel) && (m_txOpen[i])) + { + qDebug("DeviceBladeRF2::closeTx: not closing channel %d as %d is still open", channel, i); + } + } + + int status = bladerf_enable_module(m_dev, BLADERF_CHANNEL_TX(channel), false); + m_txOpen[channel] = false; + + if (status < 0) { + qCritical("DeviceBladeRF2::closeTx: cannot close channel %d: %s", channel, bladerf_strerror(status)); + } else { + qDebug("DeviceBladeRF2::closeTx: channel %d closed", channel); + } + } + else + { + qCritical("DeviceBladeRF2::closeTx: Rx channel %d already closed", channel); + } +} + +void DeviceBladeRF2::getFrequencyRangeRx(uint64_t& min, uint64_t& max, int& step) { if (m_dev) { @@ -123,7 +291,7 @@ void DeviceBladeRF2::getFrequencyRangeRx(int& min, int& max, int& step) } } -void DeviceBladeRF2::getFrequencyRangeTx(int& min, int& max, int& step) +void DeviceBladeRF2::getFrequencyRangeTx(uint64_t& min, uint64_t& max, int& step) { if (m_dev) { @@ -284,3 +452,27 @@ void DeviceBladeRF2::getGlobalGainRangeTx(int& min, int& max, int& step) } } } + +void DeviceBladeRF2::setBiasTeeRx(bool enable) +{ + if (m_dev) + { + int status = bladerf_set_bias_tee(m_dev, BLADERF_CHANNEL_RX(0), enable); + + if (status < 0) { + qCritical("DeviceBladeRF2::setBiasTeeRx: Failed to set Rx bias tee: %s", bladerf_strerror(status)); + } + } +} + +void DeviceBladeRF2::setBiasTeeTx(bool enable) +{ + if (m_dev) + { + int status = bladerf_set_bias_tee(m_dev, BLADERF_CHANNEL_TX(0), enable); + + if (status < 0) { + qCritical("DeviceBladeRF2::setBiasTeeTx: Failed to set Tx bias tee: %s", bladerf_strerror(status)); + } + } +} diff --git a/devices/bladerf2/devicebladerf2.h b/devices/bladerf2/devicebladerf2.h index 76e8c4815..30b2cf6a8 100644 --- a/devices/bladerf2/devicebladerf2.h +++ b/devices/bladerf2/devicebladerf2.h @@ -31,17 +31,31 @@ public: bool open(const char *serial); void close(); - void getFrequencyRangeRx(int& min, int& max, int& step); - void getFrequencyRangeTx(int& min, int& max, int& step); + bool openRx(int channel); + bool openTx(int channel); + void closeRx(int channel); + void closeTx(int channel); + + void getFrequencyRangeRx(uint64_t& min, uint64_t& max, int& step); + void getFrequencyRangeTx(uint64_t& min, uint64_t& max, int& step); void getSampleRateRangeRx(int& min, int& max, int& step); void getSampleRateRangeTx(int& min, int& max, int& step); void getBandwidthRangeRx(int& min, int& max, int& step); void getBandwidthRangeTx(int& min, int& max, int& step); void getGlobalGainRangeRx(int& min, int& max, int& step); void getGlobalGainRangeTx(int& min, int& max, int& step); + void setBiasTeeRx(bool enable); + void setBiasTeeTx(bool enable); + + static const unsigned int blockSize = (1<<14); private: bladerf *m_dev; + int m_nbRxChannels; + int m_nbTxChannels; + bool *m_rxOpen; + bool *m_txOpen; + static struct bladerf *open_bladerf_from_serial(const char *serial); }; diff --git a/devices/bladerf2/devicebladerf2shared.cpp b/devices/bladerf2/devicebladerf2shared.cpp new file mode 100644 index 000000000..4bb577367 --- /dev/null +++ b/devices/bladerf2/devicebladerf2shared.cpp @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "devicebladerf2shared.h" + +DeviceBladeRF2Shared::DeviceBladeRF2Shared() : + m_dev(0), + m_channel(-1) +{} + +DeviceBladeRF2Shared::~DeviceBladeRF2Shared() +{} + + + diff --git a/devices/bladerf2/devicebladerf2shared.h b/devices/bladerf2/devicebladerf2shared.h new file mode 100644 index 000000000..0f6378191 --- /dev/null +++ b/devices/bladerf2/devicebladerf2shared.h @@ -0,0 +1,62 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef DEVICES_BLADERF2_DEVICEBLADERF2SHARED_H_ +#define DEVICES_BLADERF2_DEVICEBLADERF2SHARED_H_ + +#include "devicebladerf2.h" + +class SampleSinkFifo; +class SampleSourceFifo; + +/** + * Structure shared by a buddy with other buddies + */ +class DEVICES_API DeviceBladeRF2Shared +{ +public: + class InputThreadInterface + { + public: + virtual void startWork() = 0; + virtual void stopWork() = 0; + virtual bool isRunning() const = 0; + virtual void setFifo(unsigned int channel, SampleSinkFifo *fifo) = 0; + virtual SampleSinkFifo *getFifo(unsigned int channel) = 0; + }; + + class OutputThreadInterface + { + public: + virtual void startWork() = 0; + virtual void stopWork() = 0; + virtual bool isRunning() = 0; + virtual void setFifo(unsigned int channel, SampleSourceFifo *fifo) = 0; + virtual SampleSourceFifo *getFifo(unsigned int channel) = 0; + }; + + DeviceBladeRF2Shared(); + ~DeviceBladeRF2Shared(); + + DeviceBladeRF2 *m_dev; + int m_channel; //!< allocated channel (-1 if none) + InputThreadInterface *m_inputThread; //!< The SISO/MIMO input thread + OutputThreadInterface *m_outputThread; //!< The SISO/MIMO output thread +}; + + + +#endif /* DEVICES_BLADERF2_DEVICEBLADERF2SHARED_H_ */ diff --git a/doc/img/BladeRFInput_plugin.png b/doc/img/BladeRF1Input_plugin.png similarity index 100% rename from doc/img/BladeRFInput_plugin.png rename to doc/img/BladeRF1Input_plugin.png diff --git a/doc/img/BladeRFInput_plugin.xcf b/doc/img/BladeRF1Input_plugin.xcf similarity index 100% rename from doc/img/BladeRFInput_plugin.xcf rename to doc/img/BladeRF1Input_plugin.xcf diff --git a/doc/img/BladeRFOutput_plugin.png b/doc/img/BladeRF1Output_plugin.png similarity index 100% rename from doc/img/BladeRFOutput_plugin.png rename to doc/img/BladeRF1Output_plugin.png diff --git a/doc/img/BladeRFOutput_plugin.xcf b/doc/img/BladeRF1Output_plugin.xcf similarity index 100% rename from doc/img/BladeRFOutput_plugin.xcf rename to doc/img/BladeRF1Output_plugin.xcf diff --git a/doc/img/BladeRFOutput_plugin_fifodly_32.png b/doc/img/BladeRF1Output_plugin_fifodly_32.png similarity index 100% rename from doc/img/BladeRFOutput_plugin_fifodly_32.png rename to doc/img/BladeRF1Output_plugin_fifodly_32.png diff --git a/doc/img/BladeRFOutput_plugin_fifodly_other.png b/doc/img/BladeRF1Output_plugin_fifodly_other.png similarity index 100% rename from doc/img/BladeRFOutput_plugin_fifodly_other.png rename to doc/img/BladeRF1Output_plugin_fifodly_other.png diff --git a/plugins/samplesink/bladerf1output/readme.md b/plugins/samplesink/bladerf1output/readme.md index 5b724dec5..fde65d313 100644 --- a/plugins/samplesink/bladerf1output/readme.md +++ b/plugins/samplesink/bladerf1output/readme.md @@ -1,20 +1,22 @@ -

BladeRF output plugin

+

BladeRF classic (v1) output plugin

Introduction

-This output sample sink plugin sends its samples to a [BladeRF device](https://www.nuand.com/). +This output sample sink plugin sends its samples to a [BladeRF1 device](https://www.nuand.com/bladerf-1). -Warning to Windows users: concurrent use of Rx and Tx does not work correctly hence full duplex is not fully operational. For best results use BladeRF as a half duplex device like HackRF i.e. do not run Tx and Rx concurrently. +Warning to Windows users: concurrent use of Rx and Tx does not work correctly hence full duplex is not fully operational. For best results use BladeRF as a half duplex device like HackRF i.e. do not run Tx and Rx concurrently. Anyway from version 4.2.0 using LibbladeRF v.2 this is available in Linux distributions only.

Build

The plugin will be built only if the [BladeRF host library](https://github.com/Nuand/bladeRF) is installed in your system. If you build it from source and install it in a custom location say: `/opt/install/libbladeRF` you will have to add `-DLIBBLADERF_INCLUDE_DIR=/opt/install/libbladeRF/include -DLIBBLADERF_LIBRARIES=/opt/install/libbladeRF/lib/libbladeRF.so` to the cmake command line. -The BladeRF Host library is also provided by many Linux distributions and is built in the SDRangel binary releases. +Note that since version 4.2.0 the libbladeRF v2 (specifically the git tag 2018.08) is used. + +The BladeRF Host library is also provided by many Linux distributions (check its version) and is built in the SDRangel binary releases.

Interface

-![BladeRF output plugin GUI](../../../doc/img/BladeRFOutput_plugin.png) +![BladeRF1 output plugin GUI](../../../doc/img/BladeRF1Output_plugin.png)

1: Start/Stop

@@ -32,11 +34,11 @@ Transmission latency depends essentially in the delay in the sample FIFO. The FI For interpolation by 32 the size is fixed at 150000 samples, Delay is 150000 / B where B is the baseband sample rate. Below is the delay in seconds vs baseband sample rate in kS/s from 48 to 500 kS/s: -![BladeRF output plugin FIFO delay 32](../../../doc/img/BladeRFOutput_plugin_fifodly_32.png) +![BladeRF1 output plugin FIFO delay 32](../../../doc/img/BladeRF1Output_plugin_fifodly_32.png) For lower interpolation rates the size is calculated to give a fixed delay of 250 ms or 75000 samples whichever is bigger. Below is the delay in seconds vs baseband sample rate in kS/s from 48 to 400 kS/s. The 250 ms delay is reached at 300 kS/s: -![BladeRF output plugin FIFO delay other](../../../doc/img/BladeRFOutput_plugin_fifodly_other.png) +![BladeRF1 output plugin FIFO delay other](../../../doc/img/BladeRF1Output_plugin_fifodly_other.png)

3: Frequency

diff --git a/plugins/samplesource/CMakeLists.txt b/plugins/samplesource/CMakeLists.txt index d4698339d..c3c54d52a 100644 --- a/plugins/samplesource/CMakeLists.txt +++ b/plugins/samplesource/CMakeLists.txt @@ -26,6 +26,7 @@ endif(LIBUSB_FOUND AND LIBAIRSPYHF_FOUND) find_package(LibBLADERF) if(LIBUSB_FOUND AND LIBBLADERF_FOUND) add_subdirectory(bladerf1input) + add_subdirectory(bladerf2input) endif(LIBUSB_FOUND AND LIBBLADERF_FOUND) if(LIBUSB_FOUND AND UNIX) @@ -80,6 +81,7 @@ if (BUILD_DEBIAN) add_subdirectory(airspy) add_subdirectory(airspyhf) add_subdirectory(bladerf1input) + add_subdirectory(bladerf2input) add_subdirectory(hackrfinput) add_subdirectory(limesdrinput) add_subdirectory(perseus) diff --git a/plugins/samplesource/bladerf1input/readme.md b/plugins/samplesource/bladerf1input/readme.md index 2f0e8f01e..cdfe764a2 100644 --- a/plugins/samplesource/bladerf1input/readme.md +++ b/plugins/samplesource/bladerf1input/readme.md @@ -1,18 +1,20 @@ -

BladeRF input plugin

+

BladeRF classic (v1) input plugin

Introduction

-This input sample source plugin gets its samples from a [BladeRF device](https://www.nuand.com/). +This input sample source plugin gets its samples from a [BladeRF1 device](https://www.nuand.com/bladerf-1). From version 4.2.0 using LibbladeRF v.2 this is available in Linux distributions only.

Build

The plugin will be built only if the [BladeRF host library](https://github.com/Nuand/bladeRF) is installed in your system. If you build it from source and install it in a custom location say: `/opt/install/libbladeRF` you will have to add `-DLIBBLADERF_INCLUDE_DIR=/opt/install/libbladeRF/include -DLIBBLADERF_LIBRARIES=/opt/install/libbladeRF/lib/libbladeRF.so` to the cmake command line. -The BladeRF Host library is also provided by many Linux distributions and is built in the SDRangel binary releases. +Note that since version 4.2.0 the libbladeRF v2 (specifically the git tag 2018.08) is used. + +The BladeRF Host library is also provided by many Linux distributions (check its version) and is built in the SDRangel binary releases.

Interface

-![BladeRF input plugin GUI](../../../doc/img/BladeRFInput_plugin.png) +![BladeRF1 input plugin GUI](../../../doc/img/BladeRF1Input_plugin.png)

1: Common stream parameters

diff --git a/plugins/samplesource/bladerf2input/CMakeLists.txt b/plugins/samplesource/bladerf2input/CMakeLists.txt new file mode 100644 index 000000000..6594e1cff --- /dev/null +++ b/plugins/samplesource/bladerf2input/CMakeLists.txt @@ -0,0 +1,79 @@ +project(bladerf2input) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +set(bladerf2input_SOURCES + #bladerf2inputgui.cpp + bladerf2input.cpp + #bladerf2inputplugin.cpp + bladerf2inputsettings.cpp + bladerf2inputthread.cpp +) + +set(bladerf2input_HEADERS + #bladerf2inputgui.h + bladerf2input.h + #bladerf2inputplugin.h + bladerf2inputsettings.h + bladerf2inputthread.h +) + +set(bladerf2input_FORMS + bladerf2inputgui.ui +) + +if (BUILD_DEBIAN) +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${CMAKE_SOURCE_DIR}/devices + ${LIBBLADERFLIBSRC}/include + ${LIBBLADERFLIBSRC}/src +) +else (BUILD_DEBIAN) +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${CMAKE_SOURCE_DIR}/devices + ${LIBBLADERF_INCLUDE_DIR} +) +endif (BUILD_DEBIAN) + +#include(${QT_USE_FILE}) +add_definitions(${QT_DEFINITIONS}) +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_SHARED) + +qt5_wrap_ui(bladerf2input_FORMS_HEADERS ${bladerf2input_FORMS}) + +add_library(inputbladerf2 SHARED + ${bladerf2input_SOURCES} + ${bladerf2input_HEADERS_MOC} + ${bladerf2input_FORMS_HEADERS} +) + +if (BUILD_DEBIAN) +target_link_libraries(inputbladerf2 + ${QT_LIBRARIES} + bladerf + sdrbase + sdrgui + swagger + bladerf2device +) +else (BUILD_DEBIAN) +target_link_libraries(inputbladerf2 + ${QT_LIBRARIES} + ${LIBBLADERF_LIBRARIES} + sdrbase + sdrgui + swagger + bladerf2device +) +endif (BUILD_DEBIAN) + +target_link_libraries(inputbladerf2 Qt5::Core Qt5::Widgets) + +install(TARGETS inputbladerf2 DESTINATION lib/plugins/samplesource) diff --git a/plugins/samplesource/bladerf2input/bladerf2input.cpp b/plugins/samplesource/bladerf2input/bladerf2input.cpp new file mode 100644 index 000000000..ebbee96a3 --- /dev/null +++ b/plugins/samplesource/bladerf2input/bladerf2input.cpp @@ -0,0 +1,220 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "SWGDeviceSettings.h" +#include "SWGBladeRF2InputSettings.h" +#include "SWGDeviceState.h" +#include "SWGDeviceReport.h" +#include "SWGBladeRF2InputReport.h" + +#include "device/devicesourceapi.h" +#include "device/devicesinkapi.h" +#include "dsp/dspcommands.h" +#include "dsp/filerecord.h" +#include "dsp/dspengine.h" + +#include "bladerf2/devicebladerf2shared.h" +#include "bladerf2/devicebladerf2.h" +#include "bladerf2input.h" + + +MESSAGE_CLASS_DEFINITION(BladeRF2Input::MsgConfigureBladeRF2, Message) +MESSAGE_CLASS_DEFINITION(BladeRF2Input::MsgFileRecord, Message) +MESSAGE_CLASS_DEFINITION(BladeRF2Input::MsgStartStop, Message) + +BladeRF2Input::BladeRF2Input(DeviceSourceAPI *deviceAPI) : + m_deviceAPI(deviceAPI), + m_settings(), + m_deviceDescription("BladeRF2Input"), + m_running(false) +{ + openDevice(); + + m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID())); + m_deviceAPI->addSink(m_fileSink); +} + +BladeRF2Input::~BladeRF2Input() +{ + if (m_running) { + stop(); + } + + m_deviceAPI->removeSink(m_fileSink); + delete m_fileSink; + closeDevice(); +} + +void BladeRF2Input::destroy() +{ + delete this; +} + +bool BladeRF2Input::openDevice() +{ + if (!m_sampleFifo.setSize(96000 * 4)) + { + qCritical("BladeRF2Input::openDevice: could not allocate SampleFifo"); + return false; + } + else + { + qDebug("BladeRF2Input::openDevice: allocated SampleFifo"); + } + + // look for Rx buddies and get reference to the device object + // if there is a channel left take the first available + if (m_deviceAPI->getSourceBuddies().size() > 0) // look source sibling first + { + qDebug("BladeRF2Input::openDevice: look in Rx buddies"); + + DeviceSourceAPI *sourceBuddy = m_deviceAPI->getSourceBuddies()[0]; + DeviceBladeRF2Shared *deviceBladeRF2Shared = (DeviceBladeRF2Shared*) sourceBuddy->getBuddySharedPtr(); + + if (deviceBladeRF2Shared == 0) + { + qCritical("BladeRF2Input::openDevice: the source buddy shared pointer is null"); + return false; + } + + DeviceBladeRF2 *device = deviceBladeRF2Shared->m_dev; + + if (device == 0) + { + qCritical("BladeRF2Input::openDevice: cannot get device pointer from Rx buddy"); + return false; + } + + m_deviceShared.m_dev = device; + int requestedChannel = m_deviceAPI->getItemIndex(); + + if (requestedChannel == deviceBladeRF2Shared->m_channel) + { + qCritical("BladeRF2Input::openDevice: channel %u already in use", requestedChannel); + return false; + } + + if (!device->openRx(requestedChannel)) + { + qCritical("BladeRF2Input::openDevice: channel %u cannot be enabled", requestedChannel); + return false; + } + else + { + m_deviceShared.m_channel = requestedChannel; + qDebug("BladeRF2Input::openDevice: channel %u enabled", requestedChannel); + } + } + // look for Tx buddies and get reference to the device object + // allocate the Rx channel unconditionally + else if (m_deviceAPI->getSinkBuddies().size() > 0) // then sink + { + qDebug("BladeRF2Input::openDevice: look in Tx buddies"); + + DeviceSinkAPI *sinkBuddy = m_deviceAPI->getSinkBuddies()[0]; + DeviceBladeRF2Shared *deviceBladeRF2Shared = (DeviceBladeRF2Shared*) sinkBuddy->getBuddySharedPtr(); + + if (deviceBladeRF2Shared == 0) + { + qCritical("BladeRF2Input::openDevice: the sink buddy shared pointer is null"); + return false; + } + + DeviceBladeRF2 *device = deviceBladeRF2Shared->m_dev; + + if (device == 0) + { + qCritical("BladeRF2Input::openDevice: cannot get device pointer from Rx buddy"); + return false; + } + + m_deviceShared.m_dev = device; + int requestedChannel = m_deviceAPI->getItemIndex(); + + if (!device->openRx(requestedChannel)) + { + qCritical("BladeRF2Input::openDevice: channel %u cannot be enabled", requestedChannel); + return false; + } + else + { + m_deviceShared.m_channel = requestedChannel; + qDebug("BladeRF2Input::openDevice: channel %u enabled", requestedChannel); + } + } + // There are no buddies then create the first BladeRF2 device + // allocate the Rx channel unconditionally + else + { + qDebug("BladeRF2Input::openDevice: open device here"); + + m_deviceShared.m_dev = new DeviceBladeRF2(); + char serial[256]; + strcpy(serial, qPrintable(m_deviceAPI->getSampleSourceSerial())); + + if (!m_deviceShared.m_dev->open(serial)) + { + qCritical("BladeRF2Input::openDevice: cannot open BladeRF2 device"); + return false; + } + + int requestedChannel = m_deviceAPI->getItemIndex(); + + if (!m_deviceShared.m_dev->openRx(requestedChannel)) + { + qCritical("BladeRF2Input::openDevice: channel %u cannot be enabled", requestedChannel); + return false; + } + else + { + m_deviceShared.m_channel = requestedChannel; + qDebug("BladeRF2Input::openDevice: channel %u enabled", requestedChannel); + } + } + + m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API + return true; +} + +void BladeRF2Input::closeDevice() +{ + if (m_deviceShared.m_dev == 0) { // was never open + return; + } + + if (m_running) { + stop(); + } + + m_deviceShared.m_channel = -1; + + // No buddies so effectively close the device + + if ((m_deviceAPI->getSinkBuddies().size() == 0) && (m_deviceAPI->getSourceBuddies().size() == 0)) + { + m_deviceShared.m_dev->close(); + delete m_deviceShared.m_dev; + m_deviceShared.m_dev = 0; + } +} + +void BladeRF2Input::init() +{ + applySettings(m_settings, true); +} + diff --git a/plugins/samplesource/bladerf2input/bladerf2input.h b/plugins/samplesource/bladerf2input/bladerf2input.h new file mode 100644 index 000000000..5798caf25 --- /dev/null +++ b/plugins/samplesource/bladerf2input/bladerf2input.h @@ -0,0 +1,154 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESOURCE_BLADERF2INPUT_BLADERF2INPUT_H_ +#define PLUGINS_SAMPLESOURCE_BLADERF2INPUT_BLADERF2INPUT_H_ + +#include +#include +#include + +#include "dsp/devicesamplesource.h" +#include "bladerf2/devicebladerf2shared.h" +#include "bladerf2inputsettings.h" + +class DeviceSourceAPI; +class LimeSDRInputThread; +class FileRecord; + +class BladeRF2Input : public DeviceSampleSource +{ +public: + class MsgConfigureBladeRF2 : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const BladeRF2InputSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureBladeRF2* create(const BladeRF2InputSettings& settings, bool force) + { + return new MsgConfigureBladeRF2(settings, force); + } + + private: + BladeRF2InputSettings m_settings; + bool m_force; + + MsgConfigureBladeRF2(const BladeRF2InputSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgFileRecord : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + + static MsgFileRecord* create(bool startStop) { + return new MsgFileRecord(startStop); + } + + protected: + bool m_startStop; + + MsgFileRecord(bool startStop) : + Message(), + m_startStop(startStop) + { } + }; + + class MsgStartStop : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + + static MsgStartStop* create(bool startStop) { + return new MsgStartStop(startStop); + } + + protected: + bool m_startStop; + + MsgStartStop(bool startStop) : + Message(), + m_startStop(startStop) + { } + }; + + BladeRF2Input(DeviceSourceAPI *deviceAPI); + virtual ~BladeRF2Input(); + virtual void destroy(); + + virtual void init(); + virtual bool start(); + virtual void stop(); + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; } + virtual const QString& getDeviceDescription() const; + virtual int getSampleRate() const; + virtual quint64 getCenterFrequency() const; + virtual void setCenterFrequency(qint64 centerFrequency); + + virtual bool handleMessage(const Message& message); + + virtual int webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage); + + virtual int webapiReportGet( + SWGSDRangel::SWGDeviceReport& response, + QString& errorMessage); + + virtual int webapiRunGet( + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + virtual int webapiRun( + bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + +private: + DeviceSourceAPI *m_deviceAPI; + QMutex m_mutex; + BladeRF2InputSettings m_settings; + QString m_deviceDescription; + bool m_running; + DeviceBladeRF2Shared m_deviceShared; + FileRecord *m_fileSink; //!< File sink to record device I/Q output + + bool openDevice(); + void closeDevice(); + bool applySettings(const BladeRF2InputSettings& settings, bool force = false); + void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF2InputSettings& settings); + void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); +}; + +#endif /* PLUGINS_SAMPLESOURCE_BLADERF2INPUT_BLADERF2INPUT_H_ */ diff --git a/plugins/samplesource/bladerf2input/bladerf2inputgui.ui b/plugins/samplesource/bladerf2input/bladerf2inputgui.ui new file mode 100644 index 000000000..527110d64 --- /dev/null +++ b/plugins/samplesource/bladerf2input/bladerf2inputgui.ui @@ -0,0 +1,727 @@ + + + Bladerf2InputGui + + + + 0 + 0 + 310 + 265 + + + + + 0 + 0 + + + + + 310 + 250 + + + + + Liberation Sans + 9 + + + + BladeRF2 + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 4 + + + + + + + + + start/stop acquisition + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + Toggle record I/Q samples from device + + + + + + + :/record_off.png + :/record_on.png:/record_off.png + + + + + + + + + + + I/Q sample rate kS/s + + + 00000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 20 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Tuner center frequency in kHz + + + + + + + kHz + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + + + + + + Automatic IQ imbalance correction + + + IQ + + + + + + + Automatic DC offset removal + + + DC + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Auto + + + + + + + xb200 + + + + + + + XB200 board mode + + + None + + + 0 + + + 5 + + + + None + + + + + Bypass + + + + + Auto 1dB + + + + + Auto 3dB + + + + + Custom + + + + + 50M + + + + + 144M + + + + + 222M + + + + + + + + + + Qt::Horizontal + + + + + + + 2 + + + 2 + + + + + + 0 + 0 + + + + SR + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + + + + + S/s + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Dec + + + + + + + + 50 + 16777215 + + + + Decimation factor + + + 0 + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + 64 + + + + + + + + + + 3 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + BW + + + + + + + Fp + + + + + + + Relative position of device center frequency + + + + Inf + + + + + Sup + + + + + Cen + + + + + + + + + 0 + 0 + + + + LNA + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 40 + 16777215 + + + + + 0 + + + + + 3 + + + + + 6 + + + + + + + + dB + + + + + + + kHz + + + + + + + + 70 + 16777215 + + + + IF bandwidth in kHz + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + + + + 3 + + + + + Amplifier before filtering gain (dB) + + + 5 + + + 30 + + + 1 + + + 20 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 20 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + VGA1 + + + + + + + + + Qt::Horizontal + + + + + + + 3 + + + + + VGA2 + + + + + + + Amplifier before ADC gain (dB) + + + 30 + + + 3 + + + 3 + + + 9 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + 9 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+
+ + + + +
diff --git a/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp b/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp index 340069cda..db6b282c4 100644 --- a/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp +++ b/plugins/samplesource/bladerf2input/bladerf2inputplugin.cpp @@ -85,17 +85,22 @@ PluginInterface::SamplingDevices Blderf2InputPlugin::enumSampleSources() if (strcmp(boardName, "bladerf2") == 0) { - QString displayedName(QString("BladeRF2[%1] %2").arg(devinfo[i].instance).arg(devinfo[i].serial)); + unsigned int nbRxChannels = bladerf_get_channel_count(dev, BLADERF_RX); - result.append(SamplingDevice(displayedName, - m_hardwareID, - m_deviceTypeID, - QString(devinfo[i].serial), - i, - PluginInterface::SamplingDevice::PhysicalDevice, - true, - 1, - 0)); + for (int j = 0; j < nbRxChannels; j++) + { + qDebug("Blderf2InputPlugin::enumSampleSources: device #%d (%s) channel %u", i, devinfo[i].serial, j); + QString displayedName(QString("BladeRF2[%1:%2] %3").arg(devinfo[i].instance).arg(j).arg(devinfo[i].serial)); + result.append(SamplingDevice(displayedName, + m_hardwareID, + m_deviceTypeID, + QString(devinfo[i].serial), + i, + PluginInterface::SamplingDevice::PhysicalDevice, + true, + 1, + j)); + } } bladerf_close(dev); diff --git a/plugins/samplesource/bladerf2input/bladerf2inputthread.cpp b/plugins/samplesource/bladerf2input/bladerf2inputthread.cpp new file mode 100644 index 000000000..c84e02fc3 --- /dev/null +++ b/plugins/samplesource/bladerf2input/bladerf2inputthread.cpp @@ -0,0 +1,264 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "bladerf2inputthread.h" + +Bladerf2InputThread::Bladerf2InputThread(struct bladerf* dev, unsigned int nbRxChannels, QObject* parent) : + QThread(parent), + m_running(false), + m_dev(dev), + m_nbChannels(nbRxChannels) +{ + m_channels = new Channel[nbRxChannels]; + m_buf = new qint16[2*DeviceBladeRF2::blockSize*nbRxChannels]; +} + +Bladerf2InputThread::~Bladerf2InputThread() +{ + if (m_running) { + stopWork(); + } + + delete[] m_buf; + delete[] m_channels; +} + +void Bladerf2InputThread::startWork() +{ + m_startWaitMutex.lock(); + start(); + + while(!m_running) { + m_startWaiter.wait(&m_startWaitMutex, 100); + } + + m_startWaitMutex.unlock(); +} + +void Bladerf2InputThread::stopWork() +{ + m_running = false; + wait(); +} + +void Bladerf2InputThread::run() +{ + int res; + + m_running = true; + m_startWaiter.wakeAll(); + + unsigned int nbFifos = getNbFifos(); + + if (nbFifos > 0) + { + int status; + + if (nbFifos > 1) { + status = bladerf_sync_config(m_dev, BLADERF_RX_X2, BLADERF_FORMAT_SC16_Q11, 64, 8192, 32, 10000); + } else { + status = bladerf_sync_config(m_dev, BLADERF_RX_X1, BLADERF_FORMAT_SC16_Q11, 64, 8192, 32, 10000); + } + + if (status < 0) + { + qCritical("Bladerf2InputThread::run: cannot configure streams: %s", bladerf_strerror(status)); + } + else + { + while (m_running) + { + res = bladerf_sync_rx(m_dev, m_buf, DeviceBladeRF2::blockSize, NULL, 10000); + + if (res < 0) + { + qCritical("BladerfThread::run sync Rx error: %s", bladerf_strerror(res)); + break; + } + + if (nbFifos > 1) { + callbackMI(m_buf, DeviceBladeRF2::blockSize); + } else { + callbackSI(m_buf, 2*DeviceBladeRF2::blockSize); + } + } + } + } + else + { + qWarning("Bladerf2InputThread::run: no sample FIFOs registered. Aborting"); + } + + + m_running = false; +} + +unsigned int Bladerf2InputThread::getNbFifos() +{ + unsigned int fifoCount = 0; + + for (int i = 0; i < m_nbChannels; i++) + { + if (m_channels[i].m_sampleFifo) { + fifoCount++; + } + } + + return fifoCount; +} + +void Bladerf2InputThread::setLog2Decimation(unsigned int channel, unsigned int log2_decim) +{ + if ((channel >= 0) && (channel < m_nbChannels)) { + m_channels[channel].m_log2Decim = log2_decim; + } +} + +void Bladerf2InputThread::setFcPos(unsigned int channel, int fcPos) +{ + if ((channel >= 0) && (channel < m_nbChannels)) { + m_channels[channel].m_fcPos = fcPos; + } +} + +void Bladerf2InputThread::setFifo(unsigned int channel, SampleSinkFifo *sampleFifo) +{ + if ((channel >= 0) && (channel < m_nbChannels)) { + m_channels[channel].m_sampleFifo = sampleFifo; + } +} + +SampleSinkFifo *Bladerf2InputThread::getFifo(unsigned int channel) +{ + if ((channel >= 0) && (channel < m_nbChannels)) { + return m_channels[channel].m_sampleFifo; + } else { + return 0; + } +} + +void Bladerf2InputThread::callbackMI(const qint16* buf, qint32 samplesPerChannel) +{ + // TODO: write a set of decimators that can take interleaved samples in input directly + int status = bladerf_deinterleave_stream_buffer(BLADERF_RX_X2, BLADERF_FORMAT_SC16_Q11 , samplesPerChannel*m_nbChannels, (void *) buf); + + if (status < 0) + { + qCritical("Bladerf2InputThread::callbackMI: cannot de-interleave buffer: %s", bladerf_strerror(status)); + return; + } + + for (unsigned int channel = 0; channel < m_nbChannels; channel++) + { + if (m_channels[channel].m_sampleFifo) { + callbackSI(&buf[2*samplesPerChannel*channel], 2*samplesPerChannel, channel); + } + } +} + +void Bladerf2InputThread::callbackSI(const qint16* buf, qint32 len, unsigned int channel) +{ + SampleVector::iterator it = m_channels[channel].m_convertBuffer.begin(); + + if (m_channels[channel].m_log2Decim == 0) + { + m_channels[channel].m_decimators.decimate1(&it, buf, len); + } + else + { + if (m_channels[channel].m_fcPos == 0) // Infra + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators.decimate2_inf(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators.decimate4_inf(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators.decimate8_inf(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators.decimate16_inf(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators.decimate32_inf(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators.decimate64_inf(&it, buf, len); + break; + default: + break; + } + } + else if (m_channels[channel].m_fcPos == 1) // Supra + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators.decimate2_sup(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators.decimate4_sup(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators.decimate8_sup(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators.decimate16_sup(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators.decimate32_sup(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators.decimate64_sup(&it, buf, len); + break; + default: + break; + } + } + else if (m_channels[channel].m_fcPos == 2) // Center + { + switch (m_channels[channel].m_log2Decim) + { + case 1: + m_channels[channel].m_decimators.decimate2_cen(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimators.decimate4_cen(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimators.decimate8_cen(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimators.decimate16_cen(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimators.decimate32_cen(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimators.decimate64_cen(&it, buf, len); + break; + default: + break; + } + } + } + + m_channels[channel].m_sampleFifo->write(m_channels[channel].m_convertBuffer.begin(), it); +} + diff --git a/plugins/samplesource/bladerf2input/bladerf2inputthread.h b/plugins/samplesource/bladerf2input/bladerf2inputthread.h new file mode 100644 index 000000000..23c929f0b --- /dev/null +++ b/plugins/samplesource/bladerf2input/bladerf2inputthread.h @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// This program 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 as version 3 of the License, or // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_SAMPLESOURCE_BLADERF2INPUT_BLADERF2INPUTTHREAD_H_ +#define PLUGINS_SAMPLESOURCE_BLADERF2INPUT_BLADERF2INPUTTHREAD_H_ + +// BladerRF2 is a SISO/MIMO device with a single stream supporting one or two Rx +// Therefore only one thread can be allocated for the Rx side +// All FIFOs must be registered before calling startWork() else SISO/MIMO switch will not work properly +// with unpredicatable results + +#include +#include +#include + +#include + +#include "bladerf2/devicebladerf2shared.h" +#include "dsp/samplesinkfifo.h" +#include "dsp/decimators.h" + +class Bladerf2InputThread : public QThread, public DeviceBladeRF2Shared::InputThreadInterface { + Q_OBJECT + +public: + Bladerf2InputThread(struct bladerf* dev, unsigned int nbRxChannels, QObject* parent = NULL); + virtual ~Bladerf2InputThread(); + + virtual void startWork(); + virtual void stopWork(); + virtual bool isRunning() const { return m_running; } + void setLog2Decimation(unsigned int channel, unsigned int log2_decim); + void setFcPos(unsigned int channel, int fcPos); + virtual void setFifo(unsigned int channel, SampleSinkFifo *sampleFifo); + virtual SampleSinkFifo *getFifo(unsigned int channel); + +private: + struct Channel + { + SampleVector m_convertBuffer; + SampleSinkFifo* m_sampleFifo; + unsigned int m_log2Decim; + int m_fcPos; + Decimators m_decimators; + + Channel() : + m_sampleFifo(0), + m_log2Decim(0), + m_fcPos(0) + {} + + ~Channel() + { + if (m_sampleFifo) { + delete[] m_sampleFifo; + } + } + }; + + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + struct bladerf* m_dev; + + Channel *m_channels; //!< Array of channels dynamically allocated for the given number of Rx channels + qint16 *m_buf; //!< Full buffer for SISO or MIMO operation + unsigned int m_nbChannels; + + void run(); + unsigned int getNbFifos(); + void callbackSI(const qint16* buf, qint32 len, unsigned int channel = 0); + void callbackMI(const qint16* buf, qint32 samplesPerChannel); +}; + + + +#endif /* PLUGINS_SAMPLESOURCE_BLADERF2INPUT_BLADERF2INPUTTHREAD_H_ */ diff --git a/plugins/samplesource/limesdrinput/limesdrinput.cpp b/plugins/samplesource/limesdrinput/limesdrinput.cpp index 8a7aa88fe..f623e31e0 100644 --- a/plugins/samplesource/limesdrinput/limesdrinput.cpp +++ b/plugins/samplesource/limesdrinput/limesdrinput.cpp @@ -103,6 +103,13 @@ bool LimeSDRInput::openDevice() DeviceSourceAPI *sourceBuddy = m_deviceAPI->getSourceBuddies()[0]; //m_deviceShared = *((DeviceLimeSDRShared *) sourceBuddy->getBuddySharedPtr()); // copy shared data DeviceLimeSDRShared *deviceLimeSDRShared = (DeviceLimeSDRShared*) sourceBuddy->getBuddySharedPtr(); + + if (deviceLimeSDRShared == 0) + { + qCritical("LimeSDRInput::openDevice: the source buddy shared pointer is null"); + return false; + } + m_deviceShared.m_deviceParams = deviceLimeSDRShared->m_deviceParams; DeviceLimeSDRParams *deviceParams = m_deviceShared.m_deviceParams; // get device parameters @@ -152,6 +159,13 @@ bool LimeSDRInput::openDevice() DeviceSinkAPI *sinkBuddy = m_deviceAPI->getSinkBuddies()[0]; //m_deviceShared = *((DeviceLimeSDRShared *) sinkBuddy->getBuddySharedPtr()); // copy parameters DeviceLimeSDRShared *deviceLimeSDRShared = (DeviceLimeSDRShared*) sinkBuddy->getBuddySharedPtr(); + + if (deviceLimeSDRShared == 0) + { + qCritical("LimeSDRInput::openDevice: the sink buddy shared pointer is null"); + return false; + } + m_deviceShared.m_deviceParams = deviceLimeSDRShared->m_deviceParams; if (m_deviceShared.m_deviceParams == 0)