1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-07-24 19:14:15 -04:00

LibbladeRF2: migrate bladerfoutput to bladerf1output

This commit is contained in:
f4exb
2018-09-19 08:42:59 +02:00
parent e130c2213f
commit 0e79f4eccf
19 changed files with 194 additions and 193 deletions
@@ -0,0 +1,80 @@
project(bladerf1output)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(bladerf1output_SOURCES
bladerf1outputgui.cpp
bladerf1output.cpp
bladerf1outputplugin.cpp
bladerf1outputsettings.cpp
bladerf1outputthread.cpp
)
set(bladerf1output_HEADERS
bladerf1outputgui.h
bladerf1output.h
bladerf1soutputplugin.h
bladerf1outputsettings.h
bladerf1outputthread.h
)
set(bladerf1output_FORMS
bladerf1outputgui.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)
#qt4_wrap_cpp(bladerf1output_HEADERS_MOC ${bladerf1output_HEADERS})
qt5_wrap_ui(bladerf1output_FORMS_HEADERS ${bladerf1output_FORMS})
add_library(outputbladerf1 SHARED
${bladerf1output_SOURCES}
${bladerf1output_HEADERS_MOC}
${bladerf1output_FORMS_HEADERS}
)
if (BUILD_DEBIAN)
target_link_libraries(outputbladerf1
${QT_LIBRARIES}
bladerf
sdrbase
sdrgui
swagger
bladerf1device
)
else (BUILD_DEBIAN)
target_link_libraries(outputbladerf1
${QT_LIBRARIES}
${LIBBLADERF_LIBRARIES}
sdrbase
sdrgui
swagger
bladerf1device
)
endif (BUILD_DEBIAN)
target_link_libraries(outputbladerf1 Qt5::Core Qt5::Widgets)
install(TARGETS outputbladerf1 DESTINATION lib/plugins/samplesink)
@@ -0,0 +1,634 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 "bladerf1output.h"
#include <string.h>
#include <errno.h>
#include <QDebug>
#include "SWGDeviceSettings.h"
#include "SWGDeviceState.h"
#include "util/simpleserializer.h"
#include "dsp/dspcommands.h"
#include "dsp/dspengine.h"
#include "device/devicesinkapi.h"
#include "device/devicesourceapi.h"
#include "../../../devices/bladerf1/devicebladerf1shared.h"
#include "bladerf1outputthread.h"
MESSAGE_CLASS_DEFINITION(Bladerf1Output::MsgConfigureBladerf1, Message)
MESSAGE_CLASS_DEFINITION(Bladerf1Output::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(Bladerf1Output::MsgReportBladerf1, Message)
Bladerf1Output::Bladerf1Output(DeviceSinkAPI *deviceAPI) :
m_deviceAPI(deviceAPI),
m_settings(),
m_dev(0),
m_bladerfThread(0),
m_deviceDescription("BladeRFOutput"),
m_running(false)
{
m_sampleSourceFifo.resize(16*BLADERFOUTPUT_BLOCKSIZE);
openDevice();
m_deviceAPI->setBuddySharedPtr(&m_sharedParams);
}
Bladerf1Output::~Bladerf1Output()
{
if (m_running) stop();
closeDevice();
m_deviceAPI->setBuddySharedPtr(0);
}
void Bladerf1Output::destroy()
{
delete this;
}
bool Bladerf1Output::openDevice()
{
if (m_dev != 0)
{
closeDevice();
}
int res;
m_sampleSourceFifo.resize(m_settings.m_devSampleRate/(1<<(m_settings.m_log2Interp <= 4 ? m_settings.m_log2Interp : 4)));
if (m_deviceAPI->getSourceBuddies().size() > 0)
{
DeviceSourceAPI *sourceBuddy = m_deviceAPI->getSourceBuddies()[0];
DeviceBladeRF1Params *buddySharedParams = (DeviceBladeRF1Params *) sourceBuddy->getBuddySharedPtr();
if (buddySharedParams == 0)
{
qCritical("BladerfOutput::start: could not get shared parameters from buddy");
return false;
}
if (buddySharedParams->m_dev == 0) // device is not opened by buddy
{
qCritical("BladerfOutput::start: could not get BladeRF handle from buddy");
return false;
}
m_sharedParams = *(buddySharedParams); // copy parameters from buddy
m_dev = m_sharedParams.m_dev; // get BladeRF handle
}
else
{
if (!DeviceBladeRF1::open_bladerf(&m_dev, qPrintable(m_deviceAPI->getSampleSinkSerial())))
{
qCritical("BladerfOutput::start: could not open BladeRF %s", qPrintable(m_deviceAPI->getSampleSinkSerial()));
return false;
}
m_sharedParams.m_dev = m_dev;
}
// TODO: adjust USB transfer data according to sample rate
if ((res = bladerf_sync_config(m_dev, BLADERF_TX_X1, BLADERF_FORMAT_SC16_Q11, 64, 8192, 32, 10000)) < 0)
{
qCritical("BladerfOutput::start: bladerf_sync_config with return code %d", res);
return false;
}
if ((res = bladerf_enable_module(m_dev, BLADERF_MODULE_TX, true)) < 0)
{
qCritical("BladerfOutput::start: bladerf_enable_module with return code %d", res);
return false;
}
return true;
}
void Bladerf1Output::init()
{
applySettings(m_settings, true);
}
bool Bladerf1Output::start()
{
// QMutexLocker mutexLocker(&m_mutex);
if (!m_dev) {
return false;
}
if (m_running) stop();
m_bladerfThread = new Bladerf1OutputThread(m_dev, &m_sampleSourceFifo);
// mutexLocker.unlock();
applySettings(m_settings, true);
m_bladerfThread->setLog2Interpolation(m_settings.m_log2Interp);
m_bladerfThread->startWork();
qDebug("BladerfOutput::start: started");
m_running = true;
return true;
}
void Bladerf1Output::closeDevice()
{
int res;
if (m_dev == 0) { // was never open
return;
}
if ((res = bladerf_enable_module(m_dev, BLADERF_MODULE_TX, false)) < 0)
{
qCritical("BladerfOutput::closeDevice: bladerf_enable_module with return code %d", res);
}
if (m_deviceAPI->getSourceBuddies().size() == 0)
{
qDebug("BladerfOutput::closeDevice: closing device since Rx side is not open");
if (m_dev != 0) // close BladeRF
{
bladerf_close(m_dev);
}
}
m_sharedParams.m_dev = 0;
m_dev = 0;
}
void Bladerf1Output::stop()
{
// QMutexLocker mutexLocker(&m_mutex);
if (m_bladerfThread != 0)
{
m_bladerfThread->stopWork();
delete m_bladerfThread;
m_bladerfThread = 0;
}
m_running = false;
}
QByteArray Bladerf1Output::serialize() const
{
return m_settings.serialize();
}
bool Bladerf1Output::deserialize(const QByteArray& data)
{
bool success = true;
if (!m_settings.deserialize(data))
{
m_settings.resetToDefaults();
success = false;
}
MsgConfigureBladerf1* message = MsgConfigureBladerf1::create(m_settings, true);
m_inputMessageQueue.push(message);
if (m_guiMessageQueue)
{
MsgConfigureBladerf1* messageToGUI = MsgConfigureBladerf1::create(m_settings, true);
m_guiMessageQueue->push(messageToGUI);
}
return success;
}
const QString& Bladerf1Output::getDeviceDescription() const
{
return m_deviceDescription;
}
int Bladerf1Output::getSampleRate() const
{
int rate = m_settings.m_devSampleRate;
return (rate / (1<<m_settings.m_log2Interp));
}
quint64 Bladerf1Output::getCenterFrequency() const
{
return m_settings.m_centerFrequency;
}
void Bladerf1Output::setCenterFrequency(qint64 centerFrequency)
{
BladeRF1OutputSettings settings = m_settings;
settings.m_centerFrequency = centerFrequency;
MsgConfigureBladerf1* message = MsgConfigureBladerf1::create(settings, false);
m_inputMessageQueue.push(message);
if (m_guiMessageQueue)
{
MsgConfigureBladerf1* messageToGUI = MsgConfigureBladerf1::create(settings, false);
m_guiMessageQueue->push(messageToGUI);
}
}
bool Bladerf1Output::handleMessage(const Message& message)
{
if (MsgConfigureBladerf1::match(message))
{
MsgConfigureBladerf1& conf = (MsgConfigureBladerf1&) message;
qDebug() << "BladerfOutput::handleMessage: MsgConfigureBladerf";
if (!applySettings(conf.getSettings(), conf.getForce()))
{
qDebug("BladeRF config error");
}
return true;
}
else if (MsgStartStop::match(message))
{
MsgStartStop& cmd = (MsgStartStop&) message;
qDebug() << "BladerfOutput::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop");
if (cmd.getStartStop())
{
if (m_deviceAPI->initGeneration())
{
m_deviceAPI->startGeneration();
}
}
else
{
m_deviceAPI->stopGeneration();
}
return true;
}
else
{
return false;
}
}
bool Bladerf1Output::applySettings(const BladeRF1OutputSettings& settings, bool force)
{
bool forwardChange = false;
bool suspendOwnThread = false;
bool threadWasRunning = false;
// QMutexLocker mutexLocker(&m_mutex);
qDebug() << "BladerfOutput::applySettings: m_dev: " << m_dev;
if ((m_settings.m_devSampleRate != settings.m_devSampleRate) ||
(m_settings.m_log2Interp != settings.m_log2Interp) || force)
{
suspendOwnThread = true;
}
if (suspendOwnThread)
{
if (m_bladerfThread)
{
if (m_bladerfThread->isRunning())
{
m_bladerfThread->stopWork();
threadWasRunning = true;
}
}
}
if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || (m_settings.m_log2Interp != settings.m_log2Interp) || force)
{
int fifoSize;
if (settings.m_log2Interp >= 5)
{
fifoSize = DeviceBladeRF1Shared::m_sampleFifoMinSize32;
}
else
{
fifoSize = std::max(
(int) ((settings.m_devSampleRate/(1<<settings.m_log2Interp)) * DeviceBladeRF1Shared::m_sampleFifoLengthInSeconds),
DeviceBladeRF1Shared::m_sampleFifoMinSize);
}
m_sampleSourceFifo.resize(fifoSize);
}
if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force)
{
forwardChange = true;
if (m_dev != 0)
{
unsigned int actualSamplerate;
if (bladerf_set_sample_rate(m_dev, BLADERF_MODULE_TX, settings.m_devSampleRate, &actualSamplerate) < 0)
{
qCritical("BladerfOutput::applySettings: could not set sample rate: %d", settings.m_devSampleRate);
}
else
{
qDebug() << "BladerfOutput::applySettings: bladerf_set_sample_rate(BLADERF_MODULE_TX) actual sample rate is " << actualSamplerate;
}
}
}
if ((m_settings.m_log2Interp != settings.m_log2Interp) || force)
{
forwardChange = true;
if (m_bladerfThread != 0)
{
m_bladerfThread->setLog2Interpolation(settings.m_log2Interp);
qDebug() << "BladerfOutput::applySettings: set interpolation to " << (1<<settings.m_log2Interp);
}
}
if ((m_settings.m_vga1 != settings.m_vga1) || force)
{
if (m_dev != 0)
{
if(bladerf_set_txvga1(m_dev, settings.m_vga1) != 0)
{
qDebug("BladerfOutput::applySettings: bladerf_set_txvga1() failed");
}
else
{
qDebug() << "BladerfOutput::applySettings: VGA1 gain set to " << settings.m_vga1;
}
}
}
if ((m_settings.m_vga2 != settings.m_vga2) || force)
{
if(m_dev != 0)
{
if(bladerf_set_txvga2(m_dev, settings.m_vga2) != 0)
{
qDebug("BladerfOutput::applySettings:bladerf_set_rxvga2() failed");
}
else
{
qDebug() << "BladerfOutput::applySettings: VGA2 gain set to " << settings.m_vga2;
}
}
}
if ((m_settings.m_xb200 != settings.m_xb200) || force)
{
if (m_dev != 0)
{
bool changeSettings;
if (m_deviceAPI->getSourceBuddies().size() > 0)
{
DeviceSourceAPI *buddy = m_deviceAPI->getSourceBuddies()[0];
if (buddy->getDeviceSourceEngine()->state() == DSPDeviceSourceEngine::StRunning) // Tx side running
{
changeSettings = false;
}
else
{
changeSettings = true;
}
}
else // No Rx open
{
changeSettings = true;
}
if (changeSettings)
{
if (settings.m_xb200)
{
if (bladerf_expansion_attach(m_dev, BLADERF_XB_200) != 0)
{
qDebug("BladerfOutput::applySettings: bladerf_expansion_attach(xb200) failed");
}
else
{
qDebug() << "BladerfOutput::applySettings: Attach XB200";
}
}
else
{
if (bladerf_expansion_attach(m_dev, BLADERF_XB_NONE) != 0)
{
qDebug("BladerfOutput::applySettings: bladerf_expansion_attach(none) failed");
}
else
{
qDebug() << "BladerfOutput::applySettings: Detach XB200";
}
}
m_sharedParams.m_xb200Attached = settings.m_xb200;
}
}
}
if ((m_settings.m_xb200Path != settings.m_xb200Path) || force)
{
if (m_dev != 0)
{
if(bladerf_xb200_set_path(m_dev, BLADERF_MODULE_TX, settings.m_xb200Path) != 0)
{
qDebug("BladerfOutput::applySettings: bladerf_xb200_set_path(BLADERF_MODULE_TX) failed");
}
else
{
qDebug() << "BladerfOutput::applySettings: set xb200 path to " << settings.m_xb200Path;
}
}
}
if ((m_settings.m_xb200Filter != settings.m_xb200Filter) || force)
{
if (m_dev != 0)
{
if(bladerf_xb200_set_filterbank(m_dev, BLADERF_MODULE_TX, settings.m_xb200Filter) != 0)
{
qDebug("BladerfOutput::applySettings: bladerf_xb200_set_filterbank(BLADERF_MODULE_TX) failed");
}
else
{
qDebug() << "BladerfOutput::applySettings: set xb200 filter to " << settings.m_xb200Filter;
}
}
}
if ((m_settings.m_bandwidth != settings.m_bandwidth) || force)
{
if(m_dev != 0)
{
unsigned int actualBandwidth;
if( bladerf_set_bandwidth(m_dev, BLADERF_MODULE_TX, settings.m_bandwidth, &actualBandwidth) < 0)
{
qCritical("BladerfOutput::applySettings: could not set bandwidth: %d", settings.m_bandwidth);
}
else
{
qDebug() << "BladerfOutput::applySettings: bladerf_set_bandwidth(BLADERF_MODULE_TX) actual bandwidth is " << actualBandwidth;
}
}
}
if (m_settings.m_centerFrequency != settings.m_centerFrequency)
{
forwardChange = true;
}
if (m_dev != NULL)
{
if (bladerf_set_frequency( m_dev, BLADERF_MODULE_TX, settings.m_centerFrequency ) != 0)
{
qDebug("BladerfOutput::applySettings: bladerf_set_frequency(%lld) failed", settings.m_centerFrequency);
}
}
if (threadWasRunning)
{
m_bladerfThread->startWork();
}
m_settings.m_centerFrequency = settings.m_centerFrequency;
m_settings.m_bandwidth = settings.m_bandwidth;
m_settings.m_xb200Filter = settings.m_xb200Filter;
m_settings.m_xb200 = settings.m_xb200;
m_settings.m_xb200Path = settings.m_xb200Path;
m_settings.m_vga2 = settings.m_vga2;
m_settings.m_vga1 = settings.m_vga1;
m_settings.m_devSampleRate = settings.m_devSampleRate;
m_settings.m_log2Interp = settings.m_log2Interp;
if (forwardChange)
{
int sampleRate = m_settings.m_devSampleRate/(1<<m_settings.m_log2Interp);
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, m_settings.m_centerFrequency);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
}
qDebug() << "BladerfOutput::applySettings: center freq: " << m_settings.m_centerFrequency << " Hz"
<< " device sample rate: " << m_settings.m_devSampleRate << "S/s"
<< " baseband sample rate: " << m_settings.m_devSampleRate/(1<<m_settings.m_log2Interp) << "S/s"
<< " BW: " << m_settings.m_bandwidth << "Hz";
return true;
}
int Bladerf1Output::webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage __attribute__((unused)))
{
response.setBladeRf1OutputSettings(new SWGSDRangel::SWGBladeRF1OutputSettings());
response.getBladeRf1OutputSettings()->init();
webapiFormatDeviceSettings(response, m_settings);
return 200;
}
void Bladerf1Output::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF1OutputSettings& settings)
{
response.getBladeRf1OutputSettings()->setCenterFrequency(settings.m_centerFrequency);
response.getBladeRf1OutputSettings()->setDevSampleRate(settings.m_devSampleRate);
response.getBladeRf1OutputSettings()->setVga1(settings.m_vga1);
response.getBladeRf1OutputSettings()->setVga2(settings.m_vga2);
response.getBladeRf1OutputSettings()->setBandwidth(settings.m_bandwidth);
response.getBladeRf1OutputSettings()->setLog2Interp(settings.m_log2Interp);
response.getBladeRf1OutputSettings()->setXb200(settings.m_xb200 ? 1 : 0);
response.getBladeRf1OutputSettings()->setXb200Path((int) settings.m_xb200Path);
response.getBladeRf1OutputSettings()->setXb200Filter((int) settings.m_xb200Filter);
}
int Bladerf1Output::webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage __attribute__((unused)))
{
BladeRF1OutputSettings settings = m_settings;
if (deviceSettingsKeys.contains("centerFrequency")) {
settings.m_centerFrequency = response.getBladeRf1OutputSettings()->getCenterFrequency();
}
if (deviceSettingsKeys.contains("devSampleRate")) {
settings.m_devSampleRate = response.getBladeRf1OutputSettings()->getDevSampleRate();
}
if (deviceSettingsKeys.contains("vga1")) {
settings.m_vga1 = response.getBladeRf1OutputSettings()->getVga1();
}
if (deviceSettingsKeys.contains("vga2")) {
settings.m_vga2 = response.getBladeRf1OutputSettings()->getVga2();
}
if (deviceSettingsKeys.contains("bandwidth")) {
settings.m_bandwidth = response.getBladeRf1OutputSettings()->getBandwidth();
}
if (deviceSettingsKeys.contains("log2Interp")) {
settings.m_log2Interp = response.getBladeRf1OutputSettings()->getLog2Interp();
}
if (deviceSettingsKeys.contains("xb200")) {
settings.m_xb200 = response.getBladeRf1OutputSettings()->getXb200() == 0 ? 0 : 1;
}
if (deviceSettingsKeys.contains("xb200Path")) {
settings.m_xb200Path = static_cast<bladerf_xb200_path>(response.getBladeRf1OutputSettings()->getXb200Path());
}
if (deviceSettingsKeys.contains("xb200Filter")) {
settings.m_xb200Filter = static_cast<bladerf_xb200_filter>(response.getBladeRf1OutputSettings()->getXb200Filter());
}
MsgConfigureBladerf1 *msg = MsgConfigureBladerf1::create(settings, force);
m_inputMessageQueue.push(msg);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureBladerf1 *msgToGUI = MsgConfigureBladerf1::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatDeviceSettings(response, settings);
return 200;
}
int Bladerf1Output::webapiRunGet(
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage __attribute__((unused)))
{
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
return 200;
}
int Bladerf1Output::webapiRun(
bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage __attribute__((unused)))
{
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
MsgStartStop *message = MsgStartStop::create(run);
m_inputMessageQueue.push(message);
if (m_guiMessageQueue)
{
MsgStartStop *messagetoGui = MsgStartStop::create(run);
m_guiMessageQueue->push(messagetoGui);
}
return 200;
}
@@ -0,0 +1,146 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 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 INCLUDE_BLADERFOUTPUT_H
#define INCLUDE_BLADERFOUTPUT_H
#include <dsp/devicesamplesink.h>
#include <libbladeRF.h>
#include <QString>
#include "../../../devices/bladerf1/devicebladerf1.h"
#include "../../../devices/bladerf1/devicebladerf1param.h"
#include "bladerf1outputsettings.h"
class DeviceSinkAPI;
class Bladerf1OutputThread;
class Bladerf1Output : public DeviceSampleSink {
public:
class MsgConfigureBladerf1 : public Message {
MESSAGE_CLASS_DECLARATION
public:
const BladeRF1OutputSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureBladerf1* create(const BladeRF1OutputSettings& settings, bool force)
{
return new MsgConfigureBladerf1(settings, force);
}
private:
BladeRF1OutputSettings m_settings;
bool m_force;
MsgConfigureBladerf1(const BladeRF1OutputSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
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)
{ }
};
class MsgReportBladerf1 : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgReportBladerf1* create()
{
return new MsgReportBladerf1();
}
protected:
MsgReportBladerf1() :
Message()
{ }
};
Bladerf1Output(DeviceSinkAPI *deviceAPI);
virtual ~Bladerf1Output();
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 webapiRunGet(
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
virtual int webapiRun(
bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
private:
bool openDevice();
void closeDevice();
bool applySettings(const BladeRF1OutputSettings& settings, bool force);
void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF1OutputSettings& settings);
DeviceSinkAPI *m_deviceAPI;
QMutex m_mutex;
BladeRF1OutputSettings m_settings;
struct bladerf* m_dev;
Bladerf1OutputThread* m_bladerfThread;
QString m_deviceDescription;
DeviceBladeRF1Params m_sharedParams;
bool m_running;
};
#endif // INCLUDE_BLADERFOUTPUT_H
@@ -0,0 +1,415 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 <QMessageBox>
#include <libbladeRF.h>
#include "ui_bladerf1outputgui.h"
#include "gui/colormapper.h"
#include "gui/glspectrum.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "device/devicesinkapi.h"
#include "device/deviceuiset.h"
#include "bladerf1outputgui.h"
Bladerf1OutputGui::Bladerf1OutputGui(DeviceUISet *deviceUISet, QWidget* parent) :
QWidget(parent),
ui(new Ui::Bladerf1OutputGui),
m_deviceUISet(deviceUISet),
m_doApplySettings(true),
m_forceSettings(true),
m_settings(),
m_deviceSampleSink(NULL),
m_sampleRate(0),
m_lastEngineState(DSPDeviceSinkEngine::StNotStarted)
{
m_deviceSampleSink = (Bladerf1Output*) m_deviceUISet->m_deviceSinkAPI->getSampleSink();
ui->setupUi(this);
ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000);
ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
ui->sampleRate->setValueRange(8, BLADERF_SAMPLERATE_MIN, BLADERF_SAMPLERATE_REC_MAX);
ui->bandwidth->clear();
for (unsigned int i = 0; i < BladerfBandwidths::getNbBandwidths(); i++)
{
ui->bandwidth->addItem(QString::number(BladerfBandwidths::getBandwidth(i)));
}
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(500);
displaySettings();
char recFileNameCStr[30];
sprintf(recFileNameCStr, "test_%d.sdriq", m_deviceUISet->m_deviceSinkAPI->getDeviceUID());
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
}
Bladerf1OutputGui::~Bladerf1OutputGui()
{
delete ui;
}
void Bladerf1OutputGui::destroy()
{
delete this;
}
void Bladerf1OutputGui::setName(const QString& name)
{
setObjectName(name);
}
QString Bladerf1OutputGui::getName() const
{
return objectName();
}
void Bladerf1OutputGui::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
sendSettings();
}
qint64 Bladerf1OutputGui::getCenterFrequency() const
{
return m_settings.m_centerFrequency;
}
void Bladerf1OutputGui::setCenterFrequency(qint64 centerFrequency)
{
m_settings.m_centerFrequency = centerFrequency;
displaySettings();
sendSettings();
}
QByteArray Bladerf1OutputGui::serialize() const
{
return m_settings.serialize();
}
bool Bladerf1OutputGui::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
displaySettings();
m_forceSettings = true;
sendSettings();
return true;
} else {
resetToDefaults();
return false;
}
}
bool Bladerf1OutputGui::handleMessage(const Message& message)
{
if (Bladerf1Output::MsgConfigureBladerf1::match(message))
{
const Bladerf1Output::MsgConfigureBladerf1& cfg = (Bladerf1Output::MsgConfigureBladerf1&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else if (Bladerf1Output::MsgReportBladerf1::match(message))
{
displaySettings();
return true;
}
else if (Bladerf1Output::MsgStartStop::match(message))
{
Bladerf1Output::MsgStartStop& notif = (Bladerf1Output::MsgStartStop&) message;
blockApplySettings(true);
ui->startStop->setChecked(notif.getStartStop());
blockApplySettings(false);
return true;
}
else
{
return false;
}
}
void Bladerf1OutputGui::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != 0)
{
qDebug("BladerfOutputGui::handleInputMessages: message: %s", message->getIdentifier());
if (DSPSignalNotification::match(*message))
{
DSPSignalNotification* notif = (DSPSignalNotification*) message;
m_sampleRate = notif->getSampleRate();
m_deviceCenterFrequency = notif->getCenterFrequency();
qDebug("BladerfOutputGui::handleInputMessages: DSPSignalNotification: SampleRate:%d, CenterFrequency:%llu", notif->getSampleRate(), notif->getCenterFrequency());
updateSampleRateAndFrequency();
delete message;
}
else
{
if (handleMessage(*message))
{
delete message;
}
}
}
}
void Bladerf1OutputGui::updateSampleRateAndFrequency()
{
m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate);
m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency);
ui->deviceRateLabel->setText(QString("%1k").arg(QString::number(m_sampleRate/1000.0, 'g', 5)));
}
void Bladerf1OutputGui::displaySettings()
{
ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000);
ui->sampleRate->setValue(m_settings.m_devSampleRate);
unsigned int bandwidthIndex = BladerfBandwidths::getBandwidthIndex(m_settings.m_bandwidth);
ui->bandwidth->setCurrentIndex(bandwidthIndex);
ui->interp->setCurrentIndex(m_settings.m_log2Interp);
ui->vga1Text->setText(tr("%1dB").arg(m_settings.m_vga1));
ui->vga1->setValue(m_settings.m_vga1);
ui->vga2Text->setText(tr("%1dB").arg(m_settings.m_vga2));
ui->vga2->setValue(m_settings.m_vga2);
ui->xb200->setCurrentIndex(getXb200Index(m_settings.m_xb200, m_settings.m_xb200Path, m_settings.m_xb200Filter));
}
void Bladerf1OutputGui::sendSettings()
{
if(!m_updateTimer.isActive())
m_updateTimer.start(100);
}
void Bladerf1OutputGui::on_centerFrequency_changed(quint64 value)
{
m_settings.m_centerFrequency = value * 1000;
sendSettings();
}
void Bladerf1OutputGui::on_sampleRate_changed(quint64 value)
{
m_settings.m_devSampleRate = value;
sendSettings();
}
void Bladerf1OutputGui::on_bandwidth_currentIndexChanged(int index)
{
int newbw = BladerfBandwidths::getBandwidth(index);
m_settings.m_bandwidth = newbw * 1000;
sendSettings();
}
void Bladerf1OutputGui::on_interp_currentIndexChanged(int index)
{
if ((index <0) || (index > 6))
return;
m_settings.m_log2Interp = index;
sendSettings();
}
void Bladerf1OutputGui::on_vga1_valueChanged(int value)
{
if ((value < BLADERF_TXVGA1_GAIN_MIN) || (value > BLADERF_TXVGA1_GAIN_MAX))
return;
ui->vga1Text->setText(tr("%1dB").arg(value));
m_settings.m_vga1 = value;
sendSettings();
}
void Bladerf1OutputGui::on_vga2_valueChanged(int value)
{
if ((value < BLADERF_TXVGA2_GAIN_MIN) || (value > BLADERF_TXVGA2_GAIN_MAX))
return;
ui->vga2Text->setText(tr("%1dB").arg(value));
m_settings.m_vga2 = value;
sendSettings();
}
void Bladerf1OutputGui::on_xb200_currentIndexChanged(int index)
{
if (index == 1) // bypass
{
m_settings.m_xb200 = true;
m_settings.m_xb200Path = BLADERF_XB200_BYPASS;
}
else if (index == 2) // Auto 1dB
{
m_settings.m_xb200 = true;
m_settings.m_xb200Path = BLADERF_XB200_MIX;
m_settings.m_xb200Filter = BLADERF_XB200_AUTO_1DB;
}
else if (index == 3) // Auto 3dB
{
m_settings.m_xb200 = true;
m_settings.m_xb200Path = BLADERF_XB200_MIX;
m_settings.m_xb200Filter = BLADERF_XB200_AUTO_3DB;
}
else if (index == 4) // Custom
{
m_settings.m_xb200 = true;
m_settings.m_xb200Path = BLADERF_XB200_MIX;
m_settings.m_xb200Filter = BLADERF_XB200_CUSTOM;
}
else if (index == 5) // 50 MHz
{
m_settings.m_xb200 = true;
m_settings.m_xb200Path = BLADERF_XB200_MIX;
m_settings.m_xb200Filter = BLADERF_XB200_50M;
}
else if (index == 6) // 144 MHz
{
m_settings.m_xb200 = true;
m_settings.m_xb200Path = BLADERF_XB200_MIX;
m_settings.m_xb200Filter = BLADERF_XB200_144M;
}
else if (index == 7) // 222 MHz
{
m_settings.m_xb200 = true;
m_settings.m_xb200Path = BLADERF_XB200_MIX;
m_settings.m_xb200Filter = BLADERF_XB200_222M;
}
else // no xb200
{
m_settings.m_xb200 = false;
}
if (m_settings.m_xb200)
{
ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN_XB200/1000, BLADERF_FREQUENCY_MAX/1000);
}
else
{
ui->centerFrequency->setValueRange(7, BLADERF_FREQUENCY_MIN/1000, BLADERF_FREQUENCY_MAX/1000);
}
sendSettings();
}
void Bladerf1OutputGui::on_startStop_toggled(bool checked)
{
if (m_doApplySettings)
{
Bladerf1Output::MsgStartStop *message = Bladerf1Output::MsgStartStop::create(checked);
m_deviceSampleSink->getInputMessageQueue()->push(message);
}
}
void Bladerf1OutputGui::updateHardware()
{
qDebug() << "BladerfGui::updateHardware";
Bladerf1Output::MsgConfigureBladerf1* message = Bladerf1Output::MsgConfigureBladerf1::create( m_settings, m_forceSettings);
m_deviceSampleSink->getInputMessageQueue()->push(message);
m_forceSettings = false;
m_updateTimer.stop();
}
void Bladerf1OutputGui::updateStatus()
{
int state = m_deviceUISet->m_deviceSinkAPI->state();
if(m_lastEngineState != state)
{
switch(state)
{
case DSPDeviceSinkEngine::StNotStarted:
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
break;
case DSPDeviceSinkEngine::StIdle:
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
break;
case DSPDeviceSinkEngine::StRunning:
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
break;
case DSPDeviceSinkEngine::StError:
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSinkAPI->errorMessage());
break;
default:
break;
}
m_lastEngineState = state;
}
}
unsigned int Bladerf1OutputGui::getXb200Index(bool xb_200, bladerf_xb200_path xb200Path, bladerf_xb200_filter xb200Filter)
{
if (xb_200)
{
if (xb200Path == BLADERF_XB200_BYPASS)
{
return 1;
}
else
{
if (xb200Filter == BLADERF_XB200_AUTO_1DB)
{
return 2;
}
else if (xb200Filter == BLADERF_XB200_AUTO_3DB)
{
return 3;
}
else if (xb200Filter == BLADERF_XB200_CUSTOM)
{
return 4;
}
else if (xb200Filter == BLADERF_XB200_50M)
{
return 5;
}
else if (xb200Filter == BLADERF_XB200_144M)
{
return 6;
}
else if (xb200Filter == BLADERF_XB200_222M)
{
return 7;
}
else
{
return 0;
}
}
}
else
{
return 0;
}
}
@@ -0,0 +1,89 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 INCLUDE_BLADERFOUTPUTGUI_H
#define INCLUDE_BLADERFOUTPUTGUI_H
#include <plugin/plugininstancegui.h>
#include <QTimer>
#include <QWidget>
#include "util/messagequeue.h"
#include "bladerf1output.h"
class DeviceSampleSink;
class DeviceUISet;
namespace Ui {
class Bladerf1OutputGui;
}
class Bladerf1OutputGui : public QWidget, public PluginInstanceGUI {
Q_OBJECT
public:
explicit Bladerf1OutputGui(DeviceUISet *deviceUISet, QWidget* parent = 0);
virtual ~Bladerf1OutputGui();
virtual void destroy();
void setName(const QString& name);
QString getName() const;
void resetToDefaults();
virtual qint64 getCenterFrequency() const;
virtual void setCenterFrequency(qint64 centerFrequency);
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
virtual bool handleMessage(const Message& message);
private:
Ui::Bladerf1OutputGui* ui;
DeviceUISet* m_deviceUISet;
bool m_doApplySettings;
bool m_forceSettings;
BladeRF1OutputSettings m_settings;
QTimer m_updateTimer;
QTimer m_statusTimer;
DeviceSampleSink* m_deviceSampleSink;
int m_sampleRate;
quint64 m_deviceCenterFrequency; //!< Center frequency in device
int m_lastEngineState;
MessageQueue m_inputMessageQueue;
void blockApplySettings(bool block) { m_doApplySettings = !block; }
void displaySettings();
void sendSettings();
unsigned int getXb200Index(bool xb_200, bladerf_xb200_path xb200Path, bladerf_xb200_filter xb200Filter);
void updateSampleRateAndFrequency();
private slots:
void handleInputMessages();
void on_centerFrequency_changed(quint64 value);
void on_sampleRate_changed(quint64 value);
void on_bandwidth_currentIndexChanged(int index);
void on_interp_currentIndexChanged(int index);
void on_vga1_valueChanged(int value);
void on_vga2_valueChanged(int value);
void on_xb200_currentIndexChanged(int index);
void on_startStop_toggled(bool checked);
void updateHardware();
void updateStatus();
};
#endif // INCLUDE_BLADERFOUTPUTGUI_H
@@ -0,0 +1,579 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Bladerf1OutputGui</class>
<widget class="QWidget" name="Bladerf1OutputGui">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>310</width>
<height>250</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>BladeRF1</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>2</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>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="deviceRateLayout">
<item>
<widget class="QLabel" name="deviceRateLabel">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<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="4">
<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="5">
<widget class="QLabel" name="xb200Label">
<property name="text">
<string>xb200</string>
</property>
</widget>
</item>
<item row="0" column="6">
<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>
<item row="0" column="1">
<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="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="3">
<widget class="QLabel" name="bandwidthUnit">
<property name="text">
<string>kHz</string>
</property>
</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>4</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>
<property name="toolTip">
<string>Device sample rate (S/s)</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="samplerateUnit">
<property name="text">
<string>S/s</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<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_interp">
<property name="text">
<string>Int</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="interp">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Interpolation 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>
</widget>
</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>-35</number>
</property>
<property name="maximum">
<number>-4</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>25</number>
</property>
<property name="singleStep">
<number>1</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="vga2Text">
<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>
</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>
@@ -0,0 +1,149 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 <QtPlugin>
#include <libbladeRF.h>
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include <device/devicesourceapi.h>
#include "bladerf1outputplugin.h"
#ifdef SERVER_MODE
#include "bladerf1output.h"
#else
#include "bladerf1outputgui.h"
#endif
const PluginDescriptor Bladerf1OutputPlugin::m_pluginDescriptor = {
QString("BladeRF1 Output"),
QString("4.2.0"),
QString("(c) Edouard Griffiths, F4EXB"),
QString("https://github.com/f4exb/sdrangel"),
true,
QString("https://github.com/f4exb/sdrangel")
};
const QString Bladerf1OutputPlugin::m_hardwareID = "BladeRF1";
const QString Bladerf1OutputPlugin::m_deviceTypeID = BLADERF1OUTPUT_DEVICE_TYPE_ID;
Bladerf1OutputPlugin::Bladerf1OutputPlugin(QObject* parent) :
QObject(parent)
{
}
const PluginDescriptor& Bladerf1OutputPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void Bladerf1OutputPlugin::initPlugin(PluginAPI* pluginAPI)
{
pluginAPI->registerSampleSink(m_deviceTypeID, this);
}
PluginInterface::SamplingDevices Bladerf1OutputPlugin::enumSampleSinks()
{
SamplingDevices result;
struct bladerf_devinfo *devinfo = 0;
int count = bladerf_get_device_list(&devinfo);
if (devinfo)
{
for(int i = 0; i < count; i++)
{
struct bladerf *dev;
int status = bladerf_open_with_devinfo(&dev, &devinfo[i]);
if (status == BLADERF_ERR_NODEV)
{
qCritical("BladerfOutputPlugin::enumSampleSinks: No device at index %d", i);
continue;
}
else if (status != 0)
{
qCritical("BladerfOutputPlugin::enumSampleSinks: Failed to open device at index %d", i);
continue;
}
const char *boardName = bladerf_get_board_name(dev);
if (strcmp(boardName, "bladerf1") == 0)
{
QString displayedName(QString("BladeRF1[%1] %2").arg(devinfo[i].instance).arg(devinfo[i].serial));
result.append(SamplingDevice(displayedName,
m_hardwareID,
m_deviceTypeID,
QString(devinfo[i].serial),
i,
PluginInterface::SamplingDevice::PhysicalDevice,
false,
1,
0));
}
bladerf_close(dev);
}
bladerf_free_device_list(devinfo); // Valgrind memcheck
}
return result;
}
#ifdef SERVER_MODE
PluginInstanceGUI* Bladerf1OutputPlugin::createSampleSinkPluginInstanceGUI(
const QString& sinkId __attribute__((unused)),
QWidget **widget __attribute__((unused)),
DeviceUISet *deviceUISet __attribute__((unused)))
{
return 0;
}
#else
PluginInstanceGUI* Bladerf1OutputPlugin::createSampleSinkPluginInstanceGUI(
const QString& sinkId,
QWidget **widget,
DeviceUISet *deviceUISet)
{
if(sinkId == m_deviceTypeID)
{
Bladerf1OutputGui* gui = new Bladerf1OutputGui(deviceUISet);
*widget = gui;
return gui;
}
else
{
return 0;
}
}
#endif
DeviceSampleSink* Bladerf1OutputPlugin::createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI)
{
if(sinkId == m_deviceTypeID)
{
Bladerf1Output* output = new Bladerf1Output(deviceAPI);
return output;
}
else
{
return 0;
}
}
@@ -0,0 +1,54 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 INCLUDE_BLADERFOUTPUTPLUGIN_H
#define INCLUDE_BLADERFOUTPUTPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class PluginAPI;
#define BLADERF1OUTPUT_DEVICE_TYPE_ID "sdrangel.samplesource.bladerf1output"
class Bladerf1OutputPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID BLADERF1OUTPUT_DEVICE_TYPE_ID)
public:
explicit Bladerf1OutputPlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual SamplingDevices enumSampleSinks();
virtual PluginInstanceGUI* createSampleSinkPluginInstanceGUI(
const QString& sinkId,
QWidget **widget,
DeviceUISet *deviceUISet);
virtual DeviceSampleSink* createSampleSinkPluginInstanceOutput(const QString& sinkId, DeviceSinkAPI *deviceAPI);
static const QString m_hardwareID;
static const QString m_deviceTypeID;
private:
static const PluginDescriptor m_pluginDescriptor;
};
#endif // INCLUDE_BLADERFOUTPUTPLUGIN_H
@@ -0,0 +1,89 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 "bladerf1outputsettings.h"
#include <QtGlobal>
#include "util/simpleserializer.h"
BladeRF1OutputSettings::BladeRF1OutputSettings()
{
resetToDefaults();
}
void BladeRF1OutputSettings::resetToDefaults()
{
m_centerFrequency = 435000*1000;
m_devSampleRate = 3072000;
m_vga1 = -20;
m_vga2 = 20;
m_bandwidth = 1500000;
m_log2Interp = 0;
m_xb200 = false;
m_xb200Path = BLADERF_XB200_MIX;
m_xb200Filter = BLADERF_XB200_AUTO_1DB;
}
QByteArray BladeRF1OutputSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_devSampleRate);
s.writeS32(2, m_vga1);
s.writeS32(3, m_vga2);
s.writeS32(4, m_bandwidth);
s.writeU32(5, m_log2Interp);
s.writeBool(6, m_xb200);
s.writeS32(7, (int) m_xb200Path);
s.writeS32(8, (int) m_xb200Filter);
return s.final();
}
bool BladeRF1OutputSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid())
{
resetToDefaults();
return false;
}
if (d.getVersion() == 1)
{
int intval;
d.readS32(1, &m_devSampleRate);
d.readS32(2, &m_vga1);
d.readS32(3, &m_vga2);
d.readS32(4, &m_bandwidth);
d.readU32(5, &m_log2Interp);
d.readBool(6, &m_xb200);
d.readS32(7, &intval);
m_xb200Path = (bladerf_xb200_path) intval;
d.readS32(8, &intval);
m_xb200Filter = (bladerf_xb200_filter) intval;
return true;
}
else
{
resetToDefaults();
return false;
}
}
@@ -0,0 +1,40 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 _BLADERF_BLADERFOUTPUTSETTINGS_H_
#define _BLADERF_BLADERFOUTPUTSETTINGS_H_
#include <QtGlobal>
#include <libbladeRF.h>
struct BladeRF1OutputSettings {
quint64 m_centerFrequency;
qint32 m_devSampleRate;
qint32 m_vga1;
qint32 m_vga2;
qint32 m_bandwidth;
quint32 m_log2Interp;
bool m_xb200;
bladerf_xb200_path m_xb200Path;
bladerf_xb200_filter m_xb200Filter;
BladeRF1OutputSettings();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* _BLADERF_BLADERFOUTPUTSETTINGS_H_ */
@@ -0,0 +1,118 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 "bladerf1outputthread.h"
#include <stdio.h>
#include <errno.h>
#include <algorithm>
Bladerf1OutputThread::Bladerf1OutputThread(struct bladerf* dev, SampleSourceFifo* sampleFifo, QObject* parent) :
QThread(parent),
m_running(false),
m_dev(dev),
m_sampleFifo(sampleFifo),
m_log2Interp(0)
{
std::fill(m_buf, m_buf + 2*BLADERFOUTPUT_BLOCKSIZE, 0);
}
Bladerf1OutputThread::~Bladerf1OutputThread()
{
stopWork();
}
void Bladerf1OutputThread::startWork()
{
m_startWaitMutex.lock();
start();
while(!m_running)
m_startWaiter.wait(&m_startWaitMutex, 100);
m_startWaitMutex.unlock();
}
void Bladerf1OutputThread::stopWork()
{
m_running = false;
wait();
}
void Bladerf1OutputThread::setLog2Interpolation(unsigned int log2_interp)
{
m_log2Interp = log2_interp;
}
void Bladerf1OutputThread::run()
{
int res;
m_running = true;
m_startWaiter.wakeAll();
while (m_running)
{
callback(m_buf, BLADERFOUTPUT_BLOCKSIZE);
if((res = bladerf_sync_tx(m_dev, m_buf, BLADERFOUTPUT_BLOCKSIZE, NULL, 10000)) < 0)
{
qCritical("BladerdOutputThread:run: sync error: %s", strerror(errno));
break;
}
}
m_running = false;
}
// Interpolate according to specified log2 (ex: log2=4 => decim=16)
void Bladerf1OutputThread::callback(qint16* buf, qint32 len)
{
SampleVector::iterator beginRead;
m_sampleFifo->readAdvance(beginRead, len/(1<<m_log2Interp));
beginRead -= len;
if (m_log2Interp == 0)
{
m_interpolators.interpolate1(&beginRead, buf, len*2);
}
else
{
switch (m_log2Interp)
{
case 1:
m_interpolators.interpolate2_cen(&beginRead, buf, len*2);
break;
case 2:
m_interpolators.interpolate4_cen(&beginRead, buf, len*2);
break;
case 3:
m_interpolators.interpolate8_cen(&beginRead, buf, len*2);
break;
case 4:
m_interpolators.interpolate16_cen(&beginRead, buf, len*2);
break;
case 5:
m_interpolators.interpolate32_cen(&beginRead, buf, len*2);
break;
case 6:
m_interpolators.interpolate64_cen(&beginRead, buf, len*2);
break;
default:
break;
}
}
}
@@ -0,0 +1,59 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 INCLUDE_BLADERFOUTPUTTHREAD_H
#define INCLUDE_BLADERFOUTPUTTHREAD_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <libbladeRF.h>
#include "dsp/samplesourcefifo.h"
#include "dsp/interpolators.h"
#define BLADERFOUTPUT_BLOCKSIZE (1<<16)
class Bladerf1OutputThread : public QThread {
Q_OBJECT
public:
Bladerf1OutputThread(struct bladerf* dev, SampleSourceFifo* sampleFifo, QObject* parent = NULL);
~Bladerf1OutputThread();
void startWork();
void stopWork();
void setLog2Interpolation(unsigned int log2_interp);
void setFcPos(int fcPos);
bool isRunning() const { return m_running; }
private:
QMutex m_startWaitMutex;
QWaitCondition m_startWaiter;
bool m_running;
struct bladerf* m_dev;
qint16 m_buf[2*BLADERFOUTPUT_BLOCKSIZE];
SampleSourceFifo* m_sampleFifo;
unsigned int m_log2Interp;
Interpolators<qint16, SDR_TX_SAMP_SZ, 12> m_interpolators;
void run();
void callback(qint16* buf, qint32 len);
};
#endif // INCLUDE_BLADERFOUTPUTTHREAD_H
@@ -0,0 +1,53 @@
#--------------------------------------------
#
# Pro file for Windows builds with Qt Creator
#
#--------------------------------------------
TEMPLATE = lib
CONFIG += plugin
QT += core gui widgets multimedia opengl
TARGET = outputbladerf1
DEFINES += USE_SSE2=1
QMAKE_CXXFLAGS += -msse2
DEFINES += USE_SSE4_1=1
QMAKE_CXXFLAGS += -msse4.1
QMAKE_CXXFLAGS += -std=c++11
CONFIG(MINGW32):LIBBLADERFSRC = "C:\softs\bladeRF\host\libraries\libbladeRF\include"
CONFIG(MINGW64):LIBBLADERFSRC = "C:\softs\bladeRF\host\libraries\libbladeRF\include"
INCLUDEPATH += $$PWD
INCLUDEPATH += ../../../exports
INCLUDEPATH += ../../../sdrbase
INCLUDEPATH += ../../../sdrgui
INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client
INCLUDEPATH += ../../../devices
INCLUDEPATH += $$LIBBLADERFSRC
CONFIG(Release):build_subdir = release
CONFIG(Debug):build_subdir = debug
SOURCES += bladerf1outputgui.cpp\
bladerf1output.cpp\
bladerf1outputplugin.cpp\
bladerf1outputsettings.cpp\
bladerf1outputthread.cpp
HEADERS += bladerf1outputgui.h\
bladerf1output.h\
bladerf1outputplugin.h\
bladerf1outputsettings.h\
bladerf1outputthread.h
FORMS += bladerf1outputgui.ui
LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase
LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui
LIBS += -L../../../swagger/$${build_subdir} -lswagger
LIBS += -L../../../libbladerf/$${build_subdir} -llibbladerf
LIBS += -L../../../devices/$${build_subdir} -ldevices
RESOURCES = ../../../sdrgui/resources/res.qrc
@@ -0,0 +1,87 @@
<h1>BladeRF output plugin</h1>
<h2>Introduction</h2>
This output sample sink plugin sends its samples to a [BladeRF device](https://www.nuand.com/).
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.
<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.
<h2>Interface</h2>
![BladeRF output plugin GUI](../../../doc/img/BladeRFOutput_plugin.png)
<h3>1: Start/Stop</h3>
Device start / stop button.
- Blue triangle icon: device is ready and can be started
- Red square icon: device is running and can be stopped
- Magenta (or pink) square icon: an error occurred. In the case the device was accidentally disconnected you may click on the icon, plug back in and start again.
<h3>2: Baseband sample rate</h3>
This is the baseband sample rate in kS/s before interpolation (4) to produce the final stream that is sent to the BladeRF device. Thus this is the device sample rate (6) divided by the interpolation factor (4).
Transmission latency depends essentially in the delay in the sample FIFO. The FIFO size is calculated as follows:
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)
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)
<h3>3: Frequency</h3>
This is the center frequency of transmission in kHz.
<h3>4: Interpolation factor</h3>
The baseband stream is interpolated by this value before being sent to the BladeRF device. Possible values are:
- **1**: no interpolation
- **2**: multiply baseband stream sample rate by 2
- **4**: multiply baseband stream sample rate by 4
- **8**: multiply baseband stream sample rate by 8
- **16**: multiply baseband stream sample rate by 16
- **32**: multiply baseband stream sample rate by 32
The main samples buffer is based on the baseband sample rate and will introduce ~500ms delay for interpolation by 16 or lower and ~1s for interpolation by 32.
<h3>5: XB-200 add-on control</h3>
This controls the optional XB-200 add-on when it is fitted to the BladeRF main board. These controls have no effect if the XB-200 board is absent. Options are:
- **None**: XB-200 is ignored
- **Bypass**: XB-200 is passed through
- **Auto 1dB**: The 50, 144 and 220 MHz filters are switched on automatically according to the frequency of reception when it is within the -1 dB passband of the filters
- **Auto 3dB**: The 50, 144 and 220 MHz filters are switched on automatically according to the frequency of reception when it is within the -3 dB passband of the filters
- **Custom**: The signal is routed through a custom filter
- **50M**: The signal is routed through the 50 MHz filter
- **144M**: The signal is routed through the 144 MHz filter
- **222M**: The signal is routed through the 222 MHz filter
<h3>6: Device sample rate</h3>
This is the BladeRF device DAC sample rate in S/s.
Use the wheels to adjust the sample rate. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2.
<h3>7: Tx filter bandwidth</h3>
This is the Tx filter bandwidth in kHz in the LMS6002D device. Possible values are: 1500, 1750, 2500, 2750, 3000, 3840, 5000, 5500, 6000, 7000, 8750, 10000, 12000, 14000, 20000, 28000 kHz.
<h3>8: Variable gain amplifier #1 gain</h3>
The VGA1 (relative) gain can be adjusted from -35 dB to -4 dB in 1 dB steps. The VGA1 is inside the LMS6002D chip and is placed between the baseband filter and the RF mixer.
<h3>9: Variable gain amplifier #2 gain</h3>
The VGA2 gain can be adjusted from 0 dB to 25 dB in 1 dB steps. The VGA2 is inside the LMS6002D chip and is placed after the RF mixer. It can be considered as the PA (Power Amplifier). The maximum output power when both VGA1 and VGA2 are at their maximum is about 4 mW (6 dBm).