BladerRF2 input support (2)
26
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.
|
||||
|
||||
<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`
|
||||
|
||||
☞ 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)
|
||||
|
||||
<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>
|
||||
|
||||
|
@ -2,10 +2,12 @@ project(bladerf2device)
|
||||
|
||||
set(bladerf2device_SOURCES
|
||||
devicebladerf2.cpp
|
||||
devicebladerf2shared.cpp
|
||||
)
|
||||
|
||||
set(bladerf2device_HEADERS
|
||||
devicebladerf2.h
|
||||
devicebladerf2shared.h
|
||||
)
|
||||
|
||||
if (BUILD_DEBIAN)
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
28
devices/bladerf2/devicebladerf2shared.cpp
Normal file
@ -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()
|
||||
{}
|
||||
|
||||
|
||||
|
62
devices/bladerf2/devicebladerf2shared.h
Normal file
@ -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_ */
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@ -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>
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
||||
|
79
plugins/samplesource/bladerf2input/CMakeLists.txt
Normal file
@ -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)
|
220
plugins/samplesource/bladerf2input/bladerf2input.cpp
Normal file
@ -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);
|
||||
}
|
||||
|
154
plugins/samplesource/bladerf2input/bladerf2input.h
Normal file
@ -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_ */
|
727
plugins/samplesource/bladerf2input/bladerf2inputgui.ui
Normal file
@ -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>
|
@ -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);
|
||||
|
264
plugins/samplesource/bladerf2input/bladerf2inputthread.cpp
Normal file
@ -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);
|
||||
}
|
||||
|
90
plugins/samplesource/bladerf2input/bladerf2inputthread.h
Normal file
@ -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_ */
|
@ -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)
|
||||
|