1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-21 23:55:13 -05:00

BladerRF2 input support (2)

This commit is contained in:
f4exb 2018-09-23 19:56:24 +02:00
parent d808f049f6
commit f7af0a4ac2
23 changed files with 1904 additions and 31 deletions

View File

@ -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`
&#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>

View File

@ -2,10 +2,12 @@ project(bladerf2device)
set(bladerf2device_SOURCES
devicebladerf2.cpp
devicebladerf2shared.cpp
)
set(bladerf2device_HEADERS
devicebladerf2.h
devicebladerf2shared.h
)
if (BUILD_DEBIAN)

View File

@ -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));
}
}
}

View File

@ -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);
};

View 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()
{}

View 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_ */

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -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>

View File

@ -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)

View File

@ -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>

View 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)

View 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);
}

View 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_ */

View 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>

View File

@ -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);

View 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);
}

View 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_ */

View File

@ -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)