From f7af0a4ac283aa2d6e92ff8f86d225a0cf38a56a Mon Sep 17 00:00:00 2001
From: f4exb <f4exb06@gmail.com>
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
 
 &#9758; From version 3.12.0 the Linux binaries are built with the 24 bit Rx option.
 
-<h2>BladeRF</h2>
+<h2>BladeRF classic (v.1)</h2>
 
-[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`
 
-&#9758; 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`
+&#9758; 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)
+
+<h2>BladeRF micro (v.2)</h2>
+
+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)
 
 <h2>FunCube Dongle</h2>
 
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 <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#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 <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#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 @@
-<h1>BladeRF output plugin</h1>
+<h1>BladeRF classic (v1) output plugin</h1>
 
 <h2>Introduction</h2>
 
-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.
 
 <h2>Build</h2>
 
 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.
 
 <h2>Interface</h2>
 
-![BladeRF output plugin GUI](../../../doc/img/BladeRFOutput_plugin.png)
+![BladeRF1 output plugin GUI](../../../doc/img/BladeRF1Output_plugin.png)
 
 <h3>1: Start/Stop</h3>
 
@@ -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) 
   
 <h3>3: Frequency</h3>
 
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 @@
-<h1>BladeRF input plugin</h1>
+<h1>BladeRF classic (v1) input plugin</h1>
 
 <h2>Introduction</h2>
 
-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.
 
 <h2>Build</h2>
 
 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.
 
 <h2>Interface</h2>
 
-![BladeRF input plugin GUI](../../../doc/img/BladeRFInput_plugin.png)
+![BladeRF1 input plugin GUI](../../../doc/img/BladeRF1Input_plugin.png)
 
 <h3>1: Common stream parameters</h3>
 
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 <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include <QDebug>
+
+#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 <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef PLUGINS_SAMPLESOURCE_BLADERF2INPUT_BLADERF2INPUT_H_
+#define PLUGINS_SAMPLESOURCE_BLADERF2INPUT_BLADERF2INPUT_H_
+
+#include <QString>
+#include <QByteArray>
+#include <stdint.h>
+
+#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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Bladerf2InputGui</class>
+ <widget class="QWidget" name="Bladerf2InputGui">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>310</width>
+    <height>265</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>310</width>
+    <height>250</height>
+   </size>
+  </property>
+  <property name="font">
+   <font>
+    <family>Liberation Sans</family>
+    <pointsize>9</pointsize>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>BladeRF2</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>3</number>
+   </property>
+   <property name="leftMargin">
+    <number>2</number>
+   </property>
+   <property name="topMargin">
+    <number>2</number>
+   </property>
+   <property name="rightMargin">
+    <number>2</number>
+   </property>
+   <property name="bottomMargin">
+    <number>2</number>
+   </property>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_freq">
+     <property name="topMargin">
+      <number>4</number>
+     </property>
+     <item>
+      <layout class="QVBoxLayout" name="deviceUILayout">
+       <item>
+        <layout class="QHBoxLayout" name="deviceButtonsLayout">
+         <item>
+          <widget class="ButtonSwitch" name="startStop">
+           <property name="toolTip">
+            <string>start/stop acquisition</string>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+           <property name="icon">
+            <iconset resource="../../../sdrgui/resources/res.qrc">
+             <normaloff>:/play.png</normaloff>
+             <normalon>:/stop.png</normalon>:/play.png</iconset>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="ButtonSwitch" name="record">
+           <property name="toolTip">
+            <string>Toggle record I/Q samples from device</string>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+           <property name="icon">
+            <iconset resource="../../../sdrgui/resources/res.qrc">
+             <normaloff>:/record_off.png</normaloff>
+             <normalon>:/record_on.png</normalon>:/record_off.png</iconset>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="deviceRateLayout">
+         <item>
+          <widget class="QLabel" name="deviceRateLabel">
+           <property name="toolTip">
+            <string>I/Q sample rate kS/s</string>
+           </property>
+           <property name="text">
+            <string>00000k</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <spacer name="freqLeftSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>0</width>
+         <height>0</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="ValueDial" name="centerFrequency" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>32</width>
+         <height>16</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <family>Liberation Mono</family>
+         <pointsize>20</pointsize>
+        </font>
+       </property>
+       <property name="cursor">
+        <cursorShape>PointingHandCursor</cursorShape>
+       </property>
+       <property name="focusPolicy">
+        <enum>Qt::StrongFocus</enum>
+       </property>
+       <property name="toolTip">
+        <string>Tuner center frequency in kHz</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="freqUnits">
+       <property name="text">
+        <string> kHz</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="freqRightlSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>0</width>
+         <height>0</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout_corr">
+     <item row="0" column="2">
+      <widget class="ButtonSwitch" name="iqImbalance">
+       <property name="toolTip">
+        <string>Automatic IQ imbalance correction</string>
+       </property>
+       <property name="text">
+        <string>IQ</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="ButtonSwitch" name="dcOffset">
+       <property name="toolTip">
+        <string>Automatic DC offset removal</string>
+       </property>
+       <property name="text">
+        <string>DC</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="3">
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="0" column="0">
+      <widget class="QLabel" name="corrLabel">
+       <property name="text">
+        <string>Auto</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="4">
+      <widget class="QLabel" name="xb200Label">
+       <property name="text">
+        <string>xb200</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="5">
+      <widget class="QComboBox" name="xb200">
+       <property name="toolTip">
+        <string>XB200 board mode</string>
+       </property>
+       <property name="currentText">
+        <string>None</string>
+       </property>
+       <property name="currentIndex">
+        <number>0</number>
+       </property>
+       <property name="maxVisibleItems">
+        <number>5</number>
+       </property>
+       <item>
+        <property name="text">
+         <string>None</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Bypass</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Auto 1dB</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Auto 3dB</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Custom</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>50M</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>144M</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>222M</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line_freq">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="sampleRateLayout">
+     <property name="topMargin">
+      <number>2</number>
+     </property>
+     <property name="bottomMargin">
+      <number>2</number>
+     </property>
+     <item>
+      <widget class="QLabel" name="samplerateLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>SR</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="ValueDial" name="sampleRate" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>32</width>
+         <height>16</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <family>Liberation Mono</family>
+         <pointsize>12</pointsize>
+        </font>
+       </property>
+       <property name="cursor">
+        <cursorShape>PointingHandCursor</cursorShape>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="samplerateUnit">
+       <property name="text">
+        <string>S/s</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_5">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_decim">
+       <property name="text">
+        <string>Dec</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QComboBox" name="decim">
+       <property name="maximumSize">
+        <size>
+         <width>50</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Decimation factor</string>
+       </property>
+       <property name="currentIndex">
+        <number>0</number>
+       </property>
+       <item>
+        <property name="text">
+         <string>1</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>2</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>4</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>8</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>16</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>32</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>64</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout_decim" columnstretch="0,0,0,0,0,0,0,0,0,0,0,0">
+     <property name="spacing">
+      <number>3</number>
+     </property>
+     <item row="0" column="4">
+      <spacer name="fcPosRightSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item row="0" column="5">
+      <widget class="QLabel" name="bandwidthLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>BW  </string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <widget class="QLabel" name="label_fcPos">
+       <property name="text">
+        <string>Fp</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="3">
+      <widget class="QComboBox" name="fcPos">
+       <property name="toolTip">
+        <string>Relative position of device center frequency</string>
+       </property>
+       <item>
+        <property name="text">
+         <string>Inf</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Sup</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Cen</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+     <item row="0" column="9">
+      <widget class="QLabel" name="lnaGainLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>LNA </string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="10">
+      <widget class="QComboBox" name="lna">
+       <property name="maximumSize">
+        <size>
+         <width>40</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <item>
+        <property name="text">
+         <string>0</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>3</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>6</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+     <item row="0" column="11">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>dB</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="7">
+      <widget class="QLabel" name="bandwidthUnit">
+       <property name="text">
+        <string>kHz</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="6">
+      <widget class="QComboBox" name="bandwidth">
+       <property name="maximumSize">
+        <size>
+         <width>70</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>IF bandwidth in kHz</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="8">
+      <spacer name="horizontalSpacer_3">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line_lna">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout_vga1">
+     <property name="spacing">
+      <number>3</number>
+     </property>
+     <item row="0" column="1">
+      <widget class="QSlider" name="vga1">
+       <property name="toolTip">
+        <string>Amplifier before filtering gain (dB)</string>
+       </property>
+       <property name="minimum">
+        <number>5</number>
+       </property>
+       <property name="maximum">
+        <number>30</number>
+       </property>
+       <property name="pageStep">
+        <number>1</number>
+       </property>
+       <property name="value">
+        <number>20</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <widget class="QLabel" name="vga1Text">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>20</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="0">
+      <widget class="QLabel" name="vga1Label">
+       <property name="text">
+        <string>VGA1</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line_vga1">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout_vga2" columnstretch="0,0,0">
+     <property name="spacing">
+      <number>3</number>
+     </property>
+     <item row="0" column="0">
+      <widget class="QLabel" name="vga2Label">
+       <property name="text">
+        <string>VGA2</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QSlider" name="vga2">
+       <property name="toolTip">
+        <string>Amplifier before ADC gain (dB)</string>
+       </property>
+       <property name="maximum">
+        <number>30</number>
+       </property>
+       <property name="singleStep">
+        <number>3</number>
+       </property>
+       <property name="pageStep">
+        <number>3</number>
+       </property>
+       <property name="value">
+        <number>9</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <widget class="QLabel" name="vga2Text">
+       <property name="minimumSize">
+        <size>
+         <width>40</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>9</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="padLayout">
+     <item>
+      <spacer name="verticalPadSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>40</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line_vga2">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>ValueDial</class>
+   <extends>QWidget</extends>
+   <header>gui/valuedial.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>ButtonSwitch</class>
+   <extends>QToolButton</extends>
+   <header>gui/buttonswitch.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources>
+  <include location="../../../sdrgui/resources/res.qrc"/>
+ </resources>
+ <connections/>
+</ui>
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 <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#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 <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#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 <QThread>
+#include <QMutex>
+#include <QWaitCondition>
+
+#include <libbladeRF.h>
+
+#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<qint32, qint16, SDR_RX_SAMP_SZ, 12> 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)