1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-06-06 16:05:13 -04: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
+2
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)
+6 -4
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>
@@ -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)
@@ -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);
}
@@ -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_ */
@@ -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);
@@ -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);
}
@@ -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)