1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-06-04 06:54:39 -04:00

LibbladeRF2: migrate bladerfinput to bladerf1input (2)

This commit is contained in:
f4exb
2018-09-19 13:28:14 +02:00
parent 9c5c2de56c
commit 5b31982772
17 changed files with 33 additions and 32 deletions
@@ -0,0 +1,80 @@
project(bladerf1input)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(bladerf1input_SOURCES
bladerf1inputgui.cpp
bladerf1input.cpp
bladerf1inputplugin.cpp
bladerf1inputsettings.cpp
bladerf1inputthread.cpp
)
set(bladerf1input_HEADERS
bladerf1inputgui.h
bladerf1input.h
bladerf1inputplugin.h
bladerf1inputsettings.h
bladerf1inputthread.h
)
set(bladerf1input_FORMS
bladerf1inputgui.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(bladerf1input_HEADERS_MOC ${bladerf1input_HEADERS})
qt5_wrap_ui(bladerf1input_FORMS_HEADERS ${bladerf1input_FORMS})
add_library(inputbladerf1 SHARED
${bladerf1input_SOURCES}
${bladerf1input_HEADERS_MOC}
${bladerf1input_FORMS_HEADERS}
)
if (BUILD_DEBIAN)
target_link_libraries(inputbladerf1
${QT_LIBRARIES}
bladerf
sdrbase
sdrgui
swagger
bladerf1device
)
else (BUILD_DEBIAN)
target_link_libraries(inputbladerf1
${QT_LIBRARIES}
${LIBBLADERF_LIBRARIES}
sdrbase
sdrgui
swagger
bladerf1device
)
endif (BUILD_DEBIAN)
target_link_libraries(inputbladerf1 Qt5::Core Qt5::Widgets)
install(TARGETS inputbladerf1 DESTINATION lib/plugins/samplesource)
@@ -0,0 +1,731 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "bladerf1input.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 "dsp/filerecord.h"
#include "device/devicesourceapi.h"
#include "device/devicesinkapi.h"
#include "bladerf1inputthread.h"
MESSAGE_CLASS_DEFINITION(Bladerf1Input::MsgConfigureBladerf1, Message)
MESSAGE_CLASS_DEFINITION(Bladerf1Input::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(Bladerf1Input::MsgFileRecord, Message)
Bladerf1Input::Bladerf1Input(DeviceSourceAPI *deviceAPI) :
m_deviceAPI(deviceAPI),
m_settings(),
m_dev(0),
m_bladerfThread(0),
m_deviceDescription("BladeRFInput"),
m_running(false)
{
openDevice();
m_fileSink = new FileRecord(QString("test_%1.sdriq").arg(m_deviceAPI->getDeviceUID()));
m_deviceAPI->addSink(m_fileSink);
m_deviceAPI->setBuddySharedPtr(&m_sharedParams);
}
Bladerf1Input::~Bladerf1Input()
{
if (m_running) stop();
m_deviceAPI->removeSink(m_fileSink);
delete m_fileSink;
closeDevice();
m_deviceAPI->setBuddySharedPtr(0);
}
void Bladerf1Input::destroy()
{
delete this;
}
bool Bladerf1Input::openDevice()
{
if (m_dev != 0)
{
closeDevice();
}
int res;
if (!m_sampleFifo.setSize(96000 * 4))
{
qCritical("BladerfInput::openDevice: could not allocate SampleFifo");
return false;
}
if (m_deviceAPI->getSinkBuddies().size() > 0)
{
DeviceSinkAPI *sinkBuddy = m_deviceAPI->getSinkBuddies()[0];
DeviceBladeRF1Params *buddySharedParams = (DeviceBladeRF1Params *) sinkBuddy->getBuddySharedPtr();
if (buddySharedParams == 0)
{
qCritical("BladerfInput::openDevice: could not get shared parameters from buddy");
return false;
}
if (buddySharedParams->m_dev == 0) // device is not opened by buddy
{
qCritical("BladerfInput::openDevice: 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->getSampleSourceSerial())))
{
qCritical("BladerfInput::start: could not open BladeRF %s", qPrintable(m_deviceAPI->getSampleSourceSerial()));
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_RX_X1, BLADERF_FORMAT_SC16_Q11, 64, 8192, 32, 10000)) < 0)
{
qCritical("BladerfInput::start: bladerf_sync_config with return code %d", res);
return false;
}
if ((res = bladerf_enable_module(m_dev, BLADERF_MODULE_RX, true)) < 0)
{
qCritical("BladerfInput::start: bladerf_enable_module with return code %d", res);
return false;
}
return true;
}
void Bladerf1Input::init()
{
applySettings(m_settings, true);
}
bool Bladerf1Input::start()
{
// QMutexLocker mutexLocker(&m_mutex);
if (!m_dev)
{
qDebug("BladerfInput::start: no device handle");
return false;
}
if (m_running) stop();
m_bladerfThread = new Bladerf1InputThread(m_dev, &m_sampleFifo);
m_bladerfThread->setLog2Decimation(m_settings.m_log2Decim);
m_bladerfThread->setFcPos((int) m_settings.m_fcPos);
m_bladerfThread->startWork();
// mutexLocker.unlock();
applySettings(m_settings, true);
qDebug("BladerfInput::startInput: started");
m_running = true;
return true;
}
void Bladerf1Input::closeDevice()
{
int res;
if (m_dev == 0) { // was never open
return;
}
if ((res = bladerf_enable_module(m_dev, BLADERF_MODULE_RX, false)) < 0)
{
qCritical("BladerfInput::stop: bladerf_enable_module with return code %d", res);
}
if (m_deviceAPI->getSinkBuddies().size() == 0)
{
qDebug("BladerfInput::closeDevice: closing device since Tx side is not open");
if(m_dev != 0) // close BladeRF
{
bladerf_close(m_dev);
}
}
m_sharedParams.m_dev = 0;
m_dev = 0;
}
void Bladerf1Input::stop()
{
// QMutexLocker mutexLocker(&m_mutex);
if(m_bladerfThread != 0)
{
m_bladerfThread->stopWork();
delete m_bladerfThread;
m_bladerfThread = 0;
}
m_running = false;
}
QByteArray Bladerf1Input::serialize() const
{
return m_settings.serialize();
}
bool Bladerf1Input::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& Bladerf1Input::getDeviceDescription() const
{
return m_deviceDescription;
}
int Bladerf1Input::getSampleRate() const
{
int rate = m_settings.m_devSampleRate;
return (rate / (1<<m_settings.m_log2Decim));
}
quint64 Bladerf1Input::getCenterFrequency() const
{
return m_settings.m_centerFrequency;
}
void Bladerf1Input::setCenterFrequency(qint64 centerFrequency)
{
BladeRF1InputSettings 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 Bladerf1Input::handleMessage(const Message& message)
{
if (MsgConfigureBladerf1::match(message))
{
MsgConfigureBladerf1& conf = (MsgConfigureBladerf1&) message;
qDebug() << "Bladerf1Input::handleMessage: MsgConfigureBladerf1";
if (!applySettings(conf.getSettings(), conf.getForce()))
{
qDebug("BladeRF config error");
}
return true;
}
else if (MsgFileRecord::match(message))
{
MsgFileRecord& conf = (MsgFileRecord&) message;
qDebug() << "BladerfInput::handleMessage: MsgFileRecord: " << conf.getStartStop();
if (conf.getStartStop())
{
if (m_settings.m_fileRecordName.size() != 0) {
m_fileSink->setFileName(m_settings.m_fileRecordName);
} else {
m_fileSink->genUniqueFileName(m_deviceAPI->getDeviceUID());
}
m_fileSink->startRecording();
}
else
{
m_fileSink->stopRecording();
}
return true;
}
else if (MsgStartStop::match(message))
{
MsgStartStop& cmd = (MsgStartStop&) message;
qDebug() << "BladerfInput::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop");
if (cmd.getStartStop())
{
if (m_deviceAPI->initAcquisition())
{
m_deviceAPI->startAcquisition();
}
}
else
{
m_deviceAPI->stopAcquisition();
}
return true;
}
else
{
return false;
}
}
bool Bladerf1Input::applySettings(const BladeRF1InputSettings& settings, bool force)
{
bool forwardChange = false;
// QMutexLocker mutexLocker(&m_mutex);
qDebug() << "BladerfInput::applySettings: m_dev: " << m_dev;
if ((m_settings.m_dcBlock != settings.m_dcBlock) ||
(m_settings.m_iqCorrection != settings.m_iqCorrection) || force)
{
// m_settings.m_dcBlock = settings.m_dcBlock;
// m_settings.m_iqCorrection = settings.m_iqCorrection;
m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection);
}
if ((m_settings.m_lnaGain != settings.m_lnaGain) || force)
{
// m_settings.m_lnaGain = settings.m_lnaGain;
if (m_dev != 0)
{
if(bladerf_set_lna_gain(m_dev, getLnaGain(settings.m_lnaGain)) != 0)
{
qDebug("BladerfInput::applySettings: bladerf_set_lna_gain() failed");
}
else
{
qDebug() << "BladerfInput::applySettings: LNA gain set to " << getLnaGain(settings.m_lnaGain);
}
}
}
if ((m_settings.m_vga1 != settings.m_vga1) || force)
{
// m_settings.m_vga1 = settings.m_vga1;
if (m_dev != 0)
{
if(bladerf_set_rxvga1(m_dev, settings.m_vga1) != 0)
{
qDebug("BladerfInput::applySettings: bladerf_set_rxvga1() failed");
}
else
{
qDebug() << "BladerfInput::applySettings: VGA1 gain set to " << settings.m_vga1;
}
}
}
if ((m_settings.m_vga2 != settings.m_vga2) || force)
{
// m_settings.m_vga2 = settings.m_vga2;
if(m_dev != 0)
{
if(bladerf_set_rxvga2(m_dev, settings.m_vga2) != 0)
{
qDebug("BladerfInput::applySettings: bladerf_set_rxvga2() failed");
}
else
{
qDebug() << "BladerfInput::applySettings: VGA2 gain set to " << settings.m_vga2;
}
}
}
if ((m_settings.m_xb200 != settings.m_xb200) || force)
{
// m_settings.m_xb200 = settings.m_xb200;
if (m_dev != 0)
{
bool changeSettings;
if (m_deviceAPI->getSinkBuddies().size() > 0)
{
DeviceSinkAPI *buddy = m_deviceAPI->getSinkBuddies()[0];
if (buddy->getDeviceSinkEngine()->state() == DSPDeviceSinkEngine::StRunning) // Tx side running
{
changeSettings = false;
}
else
{
changeSettings = true;
}
}
else // No Tx open
{
changeSettings = true;
}
if (changeSettings)
{
if (settings.m_xb200)
{
if (bladerf_expansion_attach(m_dev, BLADERF_XB_200) != 0)
{
qDebug("BladerfInput::applySettings: bladerf_expansion_attach(xb200) failed");
}
else
{
qDebug() << "BladerfInput::applySettings: Attach XB200";
}
}
else
{
if (bladerf_expansion_attach(m_dev, BLADERF_XB_NONE) != 0)
{
qDebug("BladerfInput::applySettings: bladerf_expansion_attach(none) failed");
}
else
{
qDebug() << "BladerfInput::applySettings: Detach XB200";
}
}
m_sharedParams.m_xb200Attached = settings.m_xb200;
}
}
}
if ((m_settings.m_xb200Path != settings.m_xb200Path) || force)
{
// m_settings.m_xb200Path = settings.m_xb200Path;
if (m_dev != 0)
{
if(bladerf_xb200_set_path(m_dev, BLADERF_MODULE_RX, settings.m_xb200Path) != 0)
{
qDebug("BladerfInput::applySettings: bladerf_xb200_set_path(BLADERF_MODULE_RX) failed");
}
else
{
qDebug() << "BladerfInput::applySettings: set xb200 path to " << settings.m_xb200Path;
}
}
}
if ((m_settings.m_xb200Filter != settings.m_xb200Filter) || force)
{
// m_settings.m_xb200Filter = settings.m_xb200Filter;
if (m_dev != 0)
{
if(bladerf_xb200_set_filterbank(m_dev, BLADERF_MODULE_RX, settings.m_xb200Filter) != 0)
{
qDebug("BladerfInput::applySettings: bladerf_xb200_set_filterbank(BLADERF_MODULE_RX) failed");
}
else
{
qDebug() << "BladerfInput::applySettings: set xb200 filter to " << settings.m_xb200Filter;
}
}
}
if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force)
{
// m_settings.m_devSampleRate = settings.m_devSampleRate;
forwardChange = true;
if (m_dev != 0)
{
unsigned int actualSamplerate;
if (bladerf_set_sample_rate(m_dev, BLADERF_MODULE_RX, settings.m_devSampleRate, &actualSamplerate) < 0)
{
qCritical("BladerfInput::applySettings: could not set sample rate: %d", settings.m_devSampleRate);
}
else
{
qDebug() << "BladerfInput::applySettings: bladerf_set_sample_rate(BLADERF_MODULE_RX) actual sample rate is " << actualSamplerate;
}
}
}
if ((m_settings.m_bandwidth != settings.m_bandwidth) || force)
{
// m_settings.m_bandwidth = settings.m_bandwidth;
if(m_dev != 0)
{
unsigned int actualBandwidth;
if( bladerf_set_bandwidth(m_dev, BLADERF_MODULE_RX, settings.m_bandwidth, &actualBandwidth) < 0)
{
qCritical("BladerfInput::applySettings: could not set bandwidth: %d", settings.m_bandwidth);
}
else
{
qDebug() << "BladerfInput::applySettings: bladerf_set_bandwidth(BLADERF_MODULE_RX) actual bandwidth is " << actualBandwidth;
}
}
}
if ((m_settings.m_fcPos != settings.m_fcPos) || force)
{
if (m_bladerfThread != 0)
{
m_bladerfThread->setFcPos((int) settings.m_fcPos);
qDebug() << "BladerfInput::applySettings: set fc pos (enum) to " << (int) settings.m_fcPos;
}
}
if ((m_settings.m_log2Decim != settings.m_log2Decim) || force)
{
// m_settings.m_log2Decim = settings.m_log2Decim;
forwardChange = true;
if (m_bladerfThread != 0)
{
m_bladerfThread->setLog2Decimation(settings.m_log2Decim);
qDebug() << "BladerfInput::applySettings: set decimation to " << (1<<settings.m_log2Decim);
}
}
if ((m_settings.m_centerFrequency != settings.m_centerFrequency)
|| (m_settings.m_devSampleRate != settings.m_devSampleRate)
|| (m_settings.m_fcPos != settings.m_fcPos)
|| (m_settings.m_log2Decim != settings.m_log2Decim) || force)
{
qint64 deviceCenterFrequency = DeviceSampleSource::calculateDeviceCenterFrequency(
settings.m_centerFrequency,
0,
settings.m_log2Decim,
(DeviceSampleSource::fcPos_t) settings.m_fcPos,
settings.m_devSampleRate);
// m_settings.m_centerFrequency = settings.m_centerFrequency;
// m_settings.m_log2Decim = settings.m_log2Decim;
// m_settings.m_fcPos = settings.m_fcPos;
forwardChange = true;
if (m_dev != 0)
{
if (bladerf_set_frequency( m_dev, BLADERF_MODULE_RX, deviceCenterFrequency ) != 0) {
qWarning("BladerfInput::applySettings: bladerf_set_frequency(%lld) failed", settings.m_centerFrequency);
} else {
qDebug("BladerfInput::applySettings: bladerf_set_frequency(%lld)", settings.m_centerFrequency);
}
}
}
if (forwardChange)
{
int sampleRate = settings.m_devSampleRate/(1<<settings.m_log2Decim);
DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, settings.m_centerFrequency);
m_fileSink->handleMessage(*notif); // forward to file sink
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
}
m_settings = settings;
qDebug() << "BladerfInput::applySettings: "
<< " m_centerFrequency: " << m_settings.m_centerFrequency << " Hz"
<< " m_bandwidth: " << m_settings.m_bandwidth
<< " m_lnaGain: " << m_settings.m_lnaGain
<< " m_vga1: " << m_settings.m_vga1
<< " m_vga2: " << m_settings.m_vga2
<< " m_log2Decim: " << m_settings.m_log2Decim
<< " m_fcPos: " << m_settings.m_fcPos
<< " m_devSampleRate: " << m_settings.m_devSampleRate
<< " m_dcBlock: " << m_settings.m_dcBlock
<< " m_iqCorrection: " << m_settings.m_iqCorrection
<< " m_xb200Filter: " << m_settings.m_xb200Filter
<< " m_xb200Path: " << m_settings.m_xb200Path
<< " m_xb200: " << m_settings.m_xb200;
return true;
}
bladerf_lna_gain Bladerf1Input::getLnaGain(int lnaGain)
{
if (lnaGain == 2)
{
return BLADERF_LNA_GAIN_MAX;
}
else if (lnaGain == 1)
{
return BLADERF_LNA_GAIN_MID;
}
else
{
return BLADERF_LNA_GAIN_BYPASS;
}
}
int Bladerf1Input::webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage __attribute__((unused)))
{
response.setBladeRf1InputSettings(new SWGSDRangel::SWGBladeRF1InputSettings());
response.getBladeRf1InputSettings()->init();
webapiFormatDeviceSettings(response, m_settings);
return 200;
}
void Bladerf1Input::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF1InputSettings& settings)
{
response.getBladeRf1InputSettings()->setCenterFrequency(settings.m_centerFrequency);
response.getBladeRf1InputSettings()->setDevSampleRate(settings.m_devSampleRate);
response.getBladeRf1InputSettings()->setLnaGain(settings.m_lnaGain);
response.getBladeRf1InputSettings()->setVga1(settings.m_vga1);
response.getBladeRf1InputSettings()->setVga2(settings.m_vga2);
response.getBladeRf1InputSettings()->setBandwidth(settings.m_bandwidth);
response.getBladeRf1InputSettings()->setLog2Decim(settings.m_log2Decim);
response.getBladeRf1InputSettings()->setFcPos((int) settings.m_fcPos);
response.getBladeRf1InputSettings()->setXb200(settings.m_xb200 ? 1 : 0);
response.getBladeRf1InputSettings()->setXb200Path((int) settings.m_xb200Path);
response.getBladeRf1InputSettings()->setXb200Filter((int) settings.m_xb200Filter);
response.getBladeRf1InputSettings()->setDcBlock(settings.m_dcBlock ? 1 : 0);
response.getBladeRf1InputSettings()->setIqCorrection(settings.m_iqCorrection ? 1 : 0);
if (response.getBladeRf1InputSettings()->getFileRecordName()) {
*response.getBladeRf1InputSettings()->getFileRecordName() = settings.m_fileRecordName;
} else {
response.getBladeRf1InputSettings()->setFileRecordName(new QString(settings.m_fileRecordName));
}
}
int Bladerf1Input::webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage __attribute__((unused)))
{
BladeRF1InputSettings settings = m_settings;
if (deviceSettingsKeys.contains("centerFrequency")) {
settings.m_centerFrequency = response.getBladeRf1InputSettings()->getCenterFrequency();
}
if (deviceSettingsKeys.contains("devSampleRate")) {
settings.m_devSampleRate = response.getBladeRf1InputSettings()->getDevSampleRate();
}
if (deviceSettingsKeys.contains("lnaGain")) {
settings.m_lnaGain = response.getBladeRf1InputSettings()->getLnaGain();
}
if (deviceSettingsKeys.contains("vga1")) {
settings.m_vga1 = response.getBladeRf1InputSettings()->getVga1();
}
if (deviceSettingsKeys.contains("vga2")) {
settings.m_vga2 = response.getBladeRf1InputSettings()->getVga2();
}
if (deviceSettingsKeys.contains("bandwidth")) {
settings.m_bandwidth = response.getBladeRf1InputSettings()->getBandwidth();
}
if (deviceSettingsKeys.contains("log2Decim")) {
settings.m_log2Decim = response.getBladeRf1InputSettings()->getLog2Decim();
}
if (deviceSettingsKeys.contains("fcPos")) {
settings.m_fcPos = static_cast<BladeRF1InputSettings::fcPos_t>(response.getBladeRf1InputSettings()->getFcPos());
}
if (deviceSettingsKeys.contains("xb200")) {
settings.m_xb200 = response.getBladeRf1InputSettings()->getXb200() == 0 ? 0 : 1;
}
if (deviceSettingsKeys.contains("xb200Path")) {
settings.m_xb200Path = static_cast<bladerf_xb200_path>(response.getBladeRf1InputSettings()->getXb200Path());
}
if (deviceSettingsKeys.contains("xb200Filter")) {
settings.m_xb200Filter = static_cast<bladerf_xb200_filter>(response.getBladeRf1InputSettings()->getXb200Filter());
}
if (deviceSettingsKeys.contains("dcBlock")) {
settings.m_dcBlock = response.getBladeRf1InputSettings()->getDcBlock() != 0;
}
if (deviceSettingsKeys.contains("iqCorrection")) {
settings.m_iqCorrection = response.getBladeRf1InputSettings()->getIqCorrection() != 0;
}
if (deviceSettingsKeys.contains("fileRecordName")) {
settings.m_fileRecordName = *response.getBladeRf1InputSettings()->getFileRecordName();
}
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 Bladerf1Input::webapiRunGet(
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage __attribute__((unused)))
{
m_deviceAPI->getDeviceEngineStateStr(*response.getState());
return 200;
}
int Bladerf1Input::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) // forward to GUI if any
{
MsgStartStop *msgToGUI = MsgStartStop::create(run);
m_guiMessageQueue->push(msgToGUI);
}
return 200;
}
@@ -0,0 +1,153 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_BLADERFINPUT_H
#define INCLUDE_BLADERFINPUT_H
#include <QString>
#include <QByteArray>
#include <libbladeRF.h>
#include <dsp/devicesamplesource.h>
#include "../../../devices/bladerf1/devicebladerf1.h"
#include "../../../devices/bladerf1/devicebladerf1param.h"
#include "bladerf1inputsettings.h"
class DeviceSourceAPI;
class Bladerf1InputThread;
class FileRecord;
class Bladerf1Input : public DeviceSampleSource {
public:
class MsgConfigureBladerf1 : public Message {
MESSAGE_CLASS_DECLARATION
public:
const BladeRF1InputSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureBladerf1* create(const BladeRF1InputSettings& settings, bool force)
{
return new MsgConfigureBladerf1(settings, force);
}
private:
BladeRF1InputSettings m_settings;
bool m_force;
MsgConfigureBladerf1(const BladeRF1InputSettings& 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)
{ }
};
Bladerf1Input(DeviceSourceAPI *deviceAPI);
virtual ~Bladerf1Input();
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 BladeRF1InputSettings& settings, bool force);
bladerf_lna_gain getLnaGain(int lnaGain);
void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF1InputSettings& settings);
DeviceSourceAPI *m_deviceAPI;
QMutex m_mutex;
BladeRF1InputSettings m_settings;
struct bladerf* m_dev;
Bladerf1InputThread* m_bladerfThread;
QString m_deviceDescription;
DeviceBladeRF1Params m_sharedParams;
bool m_running;
FileRecord *m_fileSink; //!< File sink to record device I/Q output
};
#endif // INCLUDE_BLADERFINPUT_H
@@ -0,0 +1,481 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "bladerf1inputgui.h"
#include <QDebug>
#include <QMessageBox>
#include <libbladeRF.h>
#include "ui_bladerf1inputgui.h"
#include "gui/colormapper.h"
#include "gui/glspectrum.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include <device/devicesourceapi.h>
#include "device/deviceuiset.h"
Bladerf1InputGui::Bladerf1InputGui(DeviceUISet *deviceUISet, QWidget* parent) :
QWidget(parent),
ui(new Ui::Bladerf1InputGui),
m_deviceUISet(deviceUISet),
m_forceSettings(true),
m_doApplySettings(true),
m_settings(),
m_sampleSource(NULL),
m_sampleRate(0),
m_lastEngineState(DSPDeviceSourceEngine::StNotStarted)
{
m_sampleSource = (Bladerf1Input*) m_deviceUISet->m_deviceSourceAPI->getSampleSource();
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));
// BladeRF can go as low as 80 kS/s but because of buffering in practice experience is not good below 330 kS/s
ui->sampleRate->setValueRange(8, 330000U, 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();
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue);
sendSettings();
}
Bladerf1InputGui::~Bladerf1InputGui()
{
delete ui;
}
void Bladerf1InputGui::destroy()
{
delete this;
}
void Bladerf1InputGui::setName(const QString& name)
{
setObjectName(name);
}
QString Bladerf1InputGui::getName() const
{
return objectName();
}
void Bladerf1InputGui::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
sendSettings();
}
qint64 Bladerf1InputGui::getCenterFrequency() const
{
return m_settings.m_centerFrequency;
}
void Bladerf1InputGui::setCenterFrequency(qint64 centerFrequency)
{
m_settings.m_centerFrequency = centerFrequency;
displaySettings();
sendSettings();
}
QByteArray Bladerf1InputGui::serialize() const
{
return m_settings.serialize();
}
bool Bladerf1InputGui::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
displaySettings();
m_forceSettings = true;
sendSettings();
return true;
} else {
resetToDefaults();
return false;
}
}
bool Bladerf1InputGui::handleMessage(const Message& message)
{
if (Bladerf1Input::MsgConfigureBladerf1::match(message))
{
const Bladerf1Input::MsgConfigureBladerf1& cfg = (Bladerf1Input::MsgConfigureBladerf1&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else if (Bladerf1Input::MsgStartStop::match(message))
{
Bladerf1Input::MsgStartStop& notif = (Bladerf1Input::MsgStartStop&) message;
blockApplySettings(true);
ui->startStop->setChecked(notif.getStartStop());
blockApplySettings(false);
return true;
}
else
{
return false;
}
}
void Bladerf1InputGui::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != 0)
{
qDebug("BladerfGui::handleInputMessages: message: %s", message->getIdentifier());
if (DSPSignalNotification::match(*message))
{
DSPSignalNotification* notif = (DSPSignalNotification*) message;
m_sampleRate = notif->getSampleRate();
m_deviceCenterFrequency = notif->getCenterFrequency();
qDebug("BladerfGui::handleInputMessages: DSPSignalNotification: SampleRate:%d, CenterFrequency:%llu", notif->getSampleRate(), notif->getCenterFrequency());
updateSampleRateAndFrequency();
delete message;
}
else
{
if (handleMessage(*message))
{
delete message;
}
}
}
}
void Bladerf1InputGui::updateSampleRateAndFrequency()
{
m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate);
m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency);
ui->deviceRateLabel->setText(tr("%1k").arg(QString::number(m_sampleRate / 1000.0f, 'g', 5)));
}
void Bladerf1InputGui::displaySettings()
{
blockApplySettings(true);
ui->centerFrequency->setValue(m_settings.m_centerFrequency / 1000);
ui->sampleRate->setValue(m_settings.m_devSampleRate);
ui->dcOffset->setChecked(m_settings.m_dcBlock);
ui->iqImbalance->setChecked(m_settings.m_iqCorrection);
unsigned int bandwidthIndex = BladerfBandwidths::getBandwidthIndex(m_settings.m_bandwidth);
ui->bandwidth->setCurrentIndex(bandwidthIndex);
ui->decim->setCurrentIndex(m_settings.m_log2Decim);
ui->fcPos->setCurrentIndex((int) m_settings.m_fcPos);
ui->lna->setCurrentIndex(m_settings.m_lnaGain);
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));
blockApplySettings(false);
}
void Bladerf1InputGui::sendSettings()
{
if(!m_updateTimer.isActive())
m_updateTimer.start(100);
}
void Bladerf1InputGui::on_centerFrequency_changed(quint64 value)
{
m_settings.m_centerFrequency = value * 1000;
sendSettings();
}
void Bladerf1InputGui::on_sampleRate_changed(quint64 value)
{
m_settings.m_devSampleRate = value;
sendSettings();
}
void Bladerf1InputGui::on_dcOffset_toggled(bool checked)
{
m_settings.m_dcBlock = checked;
sendSettings();
}
void Bladerf1InputGui::on_iqImbalance_toggled(bool checked)
{
m_settings.m_iqCorrection = checked;
sendSettings();
}
void Bladerf1InputGui::on_bandwidth_currentIndexChanged(int index)
{
int newbw = BladerfBandwidths::getBandwidth(index);
m_settings.m_bandwidth = newbw * 1000;
sendSettings();
}
void Bladerf1InputGui::on_decim_currentIndexChanged(int index)
{
if ((index <0) || (index > 6))
return;
m_settings.m_log2Decim = index;
sendSettings();
}
void Bladerf1InputGui::on_fcPos_currentIndexChanged(int index)
{
if (index == 0) {
m_settings.m_fcPos = BladeRF1InputSettings::FC_POS_INFRA;
sendSettings();
} else if (index == 1) {
m_settings.m_fcPos = BladeRF1InputSettings::FC_POS_SUPRA;
sendSettings();
} else if (index == 2) {
m_settings.m_fcPos = BladeRF1InputSettings::FC_POS_CENTER;
sendSettings();
}
}
void Bladerf1InputGui::on_lna_currentIndexChanged(int index)
{
qDebug() << "BladerfGui: LNA gain = " << index * 3 << " dB";
if ((index < 0) || (index > 2))
return;
m_settings.m_lnaGain = index;
sendSettings();
}
void Bladerf1InputGui::on_vga1_valueChanged(int value)
{
if ((value < BLADERF_RXVGA1_GAIN_MIN) || (value > BLADERF_RXVGA1_GAIN_MAX))
return;
ui->vga1Text->setText(tr("%1dB").arg(value));
m_settings.m_vga1 = value;
sendSettings();
}
void Bladerf1InputGui::on_vga2_valueChanged(int value)
{
if ((value < BLADERF_RXVGA2_GAIN_MIN) || (value > BLADERF_RXVGA2_GAIN_MAX))
return;
ui->vga2Text->setText(tr("%1dB").arg(value));
m_settings.m_vga2 = value;
sendSettings();
}
void Bladerf1InputGui::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 Bladerf1InputGui::on_startStop_toggled(bool checked)
{
if (m_doApplySettings)
{
Bladerf1Input::MsgStartStop *message = Bladerf1Input::MsgStartStop::create(checked);
m_sampleSource->getInputMessageQueue()->push(message);
}
}
void Bladerf1InputGui::on_record_toggled(bool checked)
{
if (checked) {
ui->record->setStyleSheet("QToolButton { background-color : red; }");
} else {
ui->record->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
}
Bladerf1Input::MsgFileRecord* message = Bladerf1Input::MsgFileRecord::create(checked);
m_sampleSource->getInputMessageQueue()->push(message);
}
void Bladerf1InputGui::updateHardware()
{
if (m_doApplySettings)
{
qDebug() << "BladerfGui::updateHardware";
Bladerf1Input::MsgConfigureBladerf1* message = Bladerf1Input::MsgConfigureBladerf1::create(m_settings, m_forceSettings);
m_sampleSource->getInputMessageQueue()->push(message);
m_forceSettings = false;
m_updateTimer.stop();
}
}
void Bladerf1InputGui::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void Bladerf1InputGui::updateStatus()
{
int state = m_deviceUISet->m_deviceSourceAPI->state();
if(m_lastEngineState != state)
{
switch(state)
{
case DSPDeviceSourceEngine::StNotStarted:
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
break;
case DSPDeviceSourceEngine::StIdle:
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
break;
case DSPDeviceSourceEngine::StRunning:
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
break;
case DSPDeviceSourceEngine::StError:
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceSourceAPI->errorMessage());
break;
default:
break;
}
m_lastEngineState = state;
}
}
unsigned int Bladerf1InputGui::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,94 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_BLADERFINPUTGUI_H
#define INCLUDE_BLADERFINPUTGUI_H
#include <plugin/plugininstancegui.h>
#include <QTimer>
#include <QWidget>
#include "util/messagequeue.h"
#include "bladerf1input.h"
class DeviceUISet;
namespace Ui {
class Bladerf1InputGui;
}
class Bladerf1InputGui : public QWidget, public PluginInstanceGUI {
Q_OBJECT
public:
explicit Bladerf1InputGui(DeviceUISet *deviceUISet, QWidget* parent = 0);
virtual ~Bladerf1InputGui();
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::Bladerf1InputGui* ui;
DeviceUISet* m_deviceUISet;
bool m_forceSettings;
bool m_doApplySettings;
BladeRF1InputSettings m_settings;
QTimer m_updateTimer;
QTimer m_statusTimer;
std::vector<int> m_gains;
DeviceSampleSource* m_sampleSource;
int m_sampleRate;
quint64 m_deviceCenterFrequency; //!< Center frequency in device
int m_lastEngineState;
MessageQueue m_inputMessageQueue;
void displaySettings();
void sendSettings();
unsigned int getXb200Index(bool xb_200, bladerf_xb200_path xb200Path, bladerf_xb200_filter xb200Filter);
void updateSampleRateAndFrequency();
void blockApplySettings(bool block);
private slots:
void handleInputMessages();
void on_centerFrequency_changed(quint64 value);
void on_sampleRate_changed(quint64 value);
void on_dcOffset_toggled(bool checked);
void on_iqImbalance_toggled(bool checked);
void on_bandwidth_currentIndexChanged(int index);
void on_decim_currentIndexChanged(int index);
void on_lna_currentIndexChanged(int index);
void on_vga1_valueChanged(int value);
void on_vga2_valueChanged(int value);
void on_xb200_currentIndexChanged(int index);
void on_fcPos_currentIndexChanged(int index);
void on_startStop_toggled(bool checked);
void on_record_toggled(bool checked);
void updateHardware();
void updateStatus();
};
#endif // INCLUDE_BLADERFINPUTGUI_H
@@ -0,0 +1,727 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Bladerf1InputGui</class>
<widget class="QWidget" name="Bladerf1InputGui">
<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>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>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>
@@ -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 "bladerf1inputplugin.h"
#include <QtPlugin>
#include <libbladeRF.h>
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include <device/devicesourceapi.h>
#ifdef SERVER_MODE
#include "bladerf1input.h"
#else
#include "bladerf1inputgui.h"
#endif
const PluginDescriptor Blderf1InputPlugin::m_pluginDescriptor = {
QString("BladeRF1 Input"),
QString("4.2.0"),
QString("(c) Edouard Griffiths, F4EXB"),
QString("https://github.com/f4exb/sdrangel"),
true,
QString("https://github.com/f4exb/sdrangel")
};
const QString Blderf1InputPlugin::m_hardwareID = "BladeRF1";
const QString Blderf1InputPlugin::m_deviceTypeID = BLADERF1INPUT_DEVICE_TYPE_ID;
Blderf1InputPlugin::Blderf1InputPlugin(QObject* parent) :
QObject(parent)
{
}
const PluginDescriptor& Blderf1InputPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void Blderf1InputPlugin::initPlugin(PluginAPI* pluginAPI)
{
pluginAPI->registerSampleSource(m_deviceTypeID, this);
}
PluginInterface::SamplingDevices Blderf1InputPlugin::enumSampleSources()
{
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("BlderfInputPlugin::enumSampleSources: No device at index %d", i);
continue;
}
else if (status != 0)
{
qCritical("BlderfInputPlugin::enumSampleSources: 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,
true,
1,
0));
}
bladerf_close(dev);
}
bladerf_free_device_list(devinfo); // Valgrind memcheck
}
return result;
}
#ifdef SERVER_MODE
PluginInstanceGUI* Blderf1InputPlugin::createSampleSourcePluginInstanceGUI(
const QString& sourceId __attribute__((unused)),
QWidget **widget __attribute__((unused)),
DeviceUISet *deviceUISet __attribute__((unused)))
{
return 0;
}
#else
PluginInstanceGUI* Blderf1InputPlugin::createSampleSourcePluginInstanceGUI(
const QString& sourceId,
QWidget **widget,
DeviceUISet *deviceUISet)
{
if(sourceId == m_deviceTypeID)
{
Bladerf1InputGui* gui = new Bladerf1InputGui(deviceUISet);
*widget = gui;
return gui;
}
else
{
return 0;
}
}
#endif
DeviceSampleSource *Blderf1InputPlugin::createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI)
{
if (sourceId == m_deviceTypeID)
{
Bladerf1Input *input = new Bladerf1Input(deviceAPI);
return input;
}
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_BLADERFINPUTPLUGIN_H
#define INCLUDE_BLADERFINPUTPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class PluginAPI;
class DeviceSourceAPI;
class DeviceUISet;
#define BLADERF1INPUT_DEVICE_TYPE_ID "sdrangel.samplesource.bladerf1input"
class Blderf1InputPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID BLADERF1INPUT_DEVICE_TYPE_ID)
public:
explicit Blderf1InputPlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual SamplingDevices enumSampleSources();
virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(
const QString& sourceId,
QWidget **widget,
DeviceUISet *deviceUISet);
virtual DeviceSampleSource* createSampleSourcePluginInstanceInput(const QString& sourceId, DeviceSourceAPI *deviceAPI);
static const QString m_hardwareID;
static const QString m_deviceTypeID;
private:
static const PluginDescriptor m_pluginDescriptor;
};
#endif // INCLUDE_BLADERFINPUTPLUGIN_H
@@ -0,0 +1,103 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "bladerf1inputsettings.h"
#include <QtGlobal>
#include "util/simpleserializer.h"
BladeRF1InputSettings::BladeRF1InputSettings()
{
resetToDefaults();
}
void BladeRF1InputSettings::resetToDefaults()
{
m_centerFrequency = 435000*1000;
m_devSampleRate = 3072000;
m_lnaGain = 0;
m_vga1 = 20;
m_vga2 = 9;
m_bandwidth = 1500000;
m_log2Decim = 0;
m_fcPos = FC_POS_INFRA;
m_xb200 = false;
m_xb200Path = BLADERF_XB200_MIX;
m_xb200Filter = BLADERF_XB200_AUTO_1DB;
m_dcBlock = false;
m_iqCorrection = false;
m_fileRecordName = "";
}
QByteArray BladeRF1InputSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_devSampleRate);
s.writeS32(2, m_lnaGain);
s.writeS32(3, m_vga1);
s.writeS32(4, m_vga2);
s.writeS32(5, m_bandwidth);
s.writeU32(6, m_log2Decim);
s.writeS32(7, (int) m_fcPos);
s.writeBool(8, m_xb200);
s.writeS32(9, (int) m_xb200Path);
s.writeS32(10, (int) m_xb200Filter);
s.writeBool(11, m_dcBlock);
s.writeBool(12, m_iqCorrection);
return s.final();
}
bool BladeRF1InputSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid())
{
resetToDefaults();
return false;
}
if (d.getVersion() == 1)
{
int intval;
d.readS32(1, &m_devSampleRate, 3072000);
d.readS32(2, &m_lnaGain);
d.readS32(3, &m_vga1);
d.readS32(4, &m_vga2);
d.readS32(5, &m_bandwidth);
d.readU32(6, &m_log2Decim);
d.readS32(7, &intval);
m_fcPos = (fcPos_t) intval;
d.readBool(8, &m_xb200);
d.readS32(9, &intval);
m_xb200Path = (bladerf_xb200_path) intval;
d.readS32(10, &intval);
m_xb200Filter = (bladerf_xb200_filter) intval;
d.readBool(11, &m_dcBlock);
d.readBool(12, &m_iqCorrection);
return true;
}
else
{
resetToDefaults();
return false;
}
}
@@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_BLADERFINPUTSETTINGS_H_
#define _BLADERF_BLADERFINPUTSETTINGS_H_
#include <QtGlobal>
#include <QString>
#include <libbladeRF.h>
struct BladeRF1InputSettings {
typedef enum {
FC_POS_INFRA = 0,
FC_POS_SUPRA,
FC_POS_CENTER
} fcPos_t;
quint64 m_centerFrequency;
qint32 m_devSampleRate;
qint32 m_lnaGain;
qint32 m_vga1;
qint32 m_vga2;
qint32 m_bandwidth;
quint32 m_log2Decim;
fcPos_t m_fcPos;
bool m_xb200;
bladerf_xb200_path m_xb200Path;
bladerf_xb200_filter m_xb200Filter;
bool m_dcBlock;
bool m_iqCorrection;
QString m_fileRecordName;
BladeRF1InputSettings();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* _BLADERF_BLADERFINPUTSETTINGS_H_ */
@@ -0,0 +1,180 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "bladerf1inputthread.h"
#include <stdio.h>
#include <errno.h>
#include <algorithm>
#include "dsp/samplesinkfifo.h"
Bladerf1InputThread::Bladerf1InputThread(struct bladerf* dev, SampleSinkFifo* sampleFifo, QObject* parent) :
QThread(parent),
m_running(false),
m_dev(dev),
m_convertBuffer(BLADERF_BLOCKSIZE),
m_sampleFifo(sampleFifo),
m_log2Decim(0),
m_fcPos(0)
{
std::fill(m_buf, m_buf + 2*BLADERF_BLOCKSIZE, 0);
}
Bladerf1InputThread::~Bladerf1InputThread()
{
stopWork();
}
void Bladerf1InputThread::startWork()
{
m_startWaitMutex.lock();
start();
while(!m_running)
m_startWaiter.wait(&m_startWaitMutex, 100);
m_startWaitMutex.unlock();
}
void Bladerf1InputThread::stopWork()
{
m_running = false;
wait();
}
void Bladerf1InputThread::setLog2Decimation(unsigned int log2_decim)
{
m_log2Decim = log2_decim;
}
void Bladerf1InputThread::setFcPos(int fcPos)
{
m_fcPos = fcPos;
}
void Bladerf1InputThread::run()
{
int res;
m_running = true;
m_startWaiter.wakeAll();
while(m_running) {
if((res = bladerf_sync_rx(m_dev, m_buf, BLADERF_BLOCKSIZE, NULL, 10000)) < 0) {
qCritical("BladerfThread: sync error: %s", strerror(errno));
break;
}
callback(m_buf, 2 * BLADERF_BLOCKSIZE);
}
m_running = false;
}
// Decimate according to specified log2 (ex: log2=4 => decim=16)
void Bladerf1InputThread::callback(const qint16* buf, qint32 len)
{
SampleVector::iterator it = m_convertBuffer.begin();
if (m_log2Decim == 0)
{
m_decimators.decimate1(&it, buf, len);
}
else
{
if (m_fcPos == 0) // Infra
{
switch (m_log2Decim)
{
case 1:
m_decimators.decimate2_inf(&it, buf, len);
break;
case 2:
m_decimators.decimate4_inf(&it, buf, len);
break;
case 3:
m_decimators.decimate8_inf(&it, buf, len);
break;
case 4:
m_decimators.decimate16_inf(&it, buf, len);
break;
case 5:
m_decimators.decimate32_inf(&it, buf, len);
break;
case 6:
m_decimators.decimate64_inf(&it, buf, len);
break;
default:
break;
}
}
else if (m_fcPos == 1) // Supra
{
switch (m_log2Decim)
{
case 1:
m_decimators.decimate2_sup(&it, buf, len);
break;
case 2:
m_decimators.decimate4_sup(&it, buf, len);
break;
case 3:
m_decimators.decimate8_sup(&it, buf, len);
break;
case 4:
m_decimators.decimate16_sup(&it, buf, len);
break;
case 5:
m_decimators.decimate32_sup(&it, buf, len);
break;
case 6:
m_decimators.decimate64_sup(&it, buf, len);
break;
default:
break;
}
}
else if (m_fcPos == 2) // Center
{
switch (m_log2Decim)
{
case 1:
m_decimators.decimate2_cen(&it, buf, len);
break;
case 2:
m_decimators.decimate4_cen(&it, buf, len);
break;
case 3:
m_decimators.decimate8_cen(&it, buf, len);
break;
case 4:
m_decimators.decimate16_cen(&it, buf, len);
break;
case 5:
m_decimators.decimate32_cen(&it, buf, len);
break;
case 6:
m_decimators.decimate64_cen(&it, buf, len);
break;
default:
break;
}
}
}
m_sampleFifo->write(m_convertBuffer.begin(), it);
}
@@ -0,0 +1,60 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_BLADERFINPUTTHREAD_H
#define INCLUDE_BLADERFINPUTTHREAD_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <libbladeRF.h>
#include "dsp/samplesinkfifo.h"
#include "dsp/decimators.h"
#define BLADERF_BLOCKSIZE (1<<14)
class Bladerf1InputThread : public QThread {
Q_OBJECT
public:
Bladerf1InputThread(struct bladerf* dev, SampleSinkFifo* sampleFifo, QObject* parent = NULL);
~Bladerf1InputThread();
void startWork();
void stopWork();
void setLog2Decimation(unsigned int log2_decim);
void setFcPos(int fcPos);
private:
QMutex m_startWaitMutex;
QWaitCondition m_startWaiter;
bool m_running;
struct bladerf* m_dev;
qint16 m_buf[2*BLADERF_BLOCKSIZE];
SampleVector m_convertBuffer;
SampleSinkFifo* m_sampleFifo;
unsigned int m_log2Decim;
int m_fcPos;
Decimators<qint32, qint16, SDR_RX_SAMP_SZ, 12> m_decimators;
void run();
void callback(const qint16* buf, qint32 len);
};
#endif // INCLUDE_BLADERFINPUTTHREAD_H
@@ -0,0 +1,53 @@
#--------------------------------------------
#
# Pro file for Windows builds with Qt Creator
#
#--------------------------------------------
TEMPLATE = lib
CONFIG += plugin
QT += core gui widgets multimedia opengl
TARGET = inputbladerf
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 += bladerf1inputgui.cpp\
bladerf1input.cpp\
bladerf1inputplugin.cpp\
bladerf1inputsettings.cpp\
bladerf1inputthread.cpp
HEADERS += bladerf1inputgui.h\
bladerf1input.h\
bladerf1inputplugin.h\
bladerf1inputsettings.h\
bladerf1inputthread.h
FORMS += bladerfinputgui.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,102 @@
<h1>BladeRF input plugin</h1>
<h2>Introduction</h2>
This input sample source plugin gets its samples from a [BladeRF device](https://www.nuand.com/).
<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 input plugin GUI](../../../doc/img/BladeRFInput_plugin.png)
<h3>1: Common stream parameters</h3>
![SDR Daemon source input stream GUI](../../../doc/img/SDRdaemonSource_plugin_01.png)
<h4>1.1: Frequency</h4>
This is the center frequency of reception in kHz.
<h4>1.2: Start/Stop</h4>
Device start / stop button.
- Blue triangle icon: device is ready and can be started
- Green 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.
<h4>1.3: Record</h4>
Record baseband I/Q stream toggle button
<h4>1.4: Stream sample rate</h4>
Baseband I/Q sample rate in kS/s. This is the device sample rate (4) divided by the decimation factor (6).
<h3>2: Auto correction options</h3>
These buttons control the local DSP auto correction options:
- **DC**: auto remove DC component
- **IQ**: auto make I/Q balance. The DC correction must be enabled for this to be effective.
<h3>3: 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>4: Device sample rate</h3>
This is the BladeRF device ADC 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>5: Decimation factor</h3>
The I/Q stream from the BladeRF ADC is downsampled by a power of two before being sent to the passband. Possible values are increasing powers of two: 1 (no decimation), 2, 4, 8, 16, 32, 64.
<h3>6: Baseband center frequency position relative the the BladeRF Rx center frequency</h3>
Possible values are:
- **Cen**: the decimation operation takes place around the BladeRF Rx center frequency Fs
- **Inf**: the decimation operation takes place around Fs - Fc.
- **Sup**: the decimation operation takes place around Fs + Fc.
With SR as the sample rate before decimation Fc is calculated as:
- if decimation n is 4 or lower: Fc = SR/2^(log2(n)-1). The device center frequency is on the side of the baseband. You need a RF filter bandwidth at least twice the baseband.
- if decimation n is 8 or higher: Fc = SR/n. The device center frequency is half the baseband away from the side of the baseband. You need a RF filter bandwidth at least 3 times the baseband.
<h3>7: Rx filter bandwidth</h3>
This is the Rx 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: LNA gain</h2>
This is the LNA gain in dB. LNA is inside the LMS6002D chip and is placed before the RF mixer. Possible values are:
- **0dB**: no gain
- **3dB**
- **6dB**
<h3>9: Variable gain amplifier #1 gain</h3>
The VGA1 gain can be adjusted from 5 dB to 30 dB in 1 dB steps. The VGA1 is inside the LMS6002D chip and is placed between the RF mixer and the baseband filter.
<h3>10: Variable gain amplifier #2 gain</h3>
The VGA2 gain can be adjusted from 0 dB to 30 dB in 3 dB steps. The VGA2 is inside the LMS6002D chip and is placed between the baseband filter and the ADC.