diff --git a/plugins/samplemimo/CMakeLists.txt b/plugins/samplemimo/CMakeLists.txt
index acc069928..07c626536 100644
--- a/plugins/samplemimo/CMakeLists.txt
+++ b/plugins/samplemimo/CMakeLists.txt
@@ -1,3 +1,4 @@
project(samplemimo)
+add_subdirectory(bladerf2mimo)
add_subdirectory(testmi)
diff --git a/plugins/samplemimo/bladerf2mimo/CMakeLists.txt b/plugins/samplemimo/bladerf2mimo/CMakeLists.txt
new file mode 100644
index 000000000..1f658b28d
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/CMakeLists.txt
@@ -0,0 +1,62 @@
+project(bladerf2mimo)
+
+set(bladerf2mimo_SOURCES
+ bladerf2mimo.cpp
+ bladerf2mimoplugin.cpp
+ bladerf2mithread.cpp
+ bladerf2mothread.cpp
+ bladerf2mimosettings.cpp
+ bladerf2mimowebapiadapter.cpp
+)
+
+set(bladerf2mimo_HEADERS
+ bladerf2mimo.h
+ bladerf2mimoplugin.h
+ bladerf2mithread.h
+ bladerf2mothread.h
+ bladerf2mimosettings.h
+ bladerf2mimowebapiadapter.h
+)
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
+ ${CMAKE_SOURCE_DIR}/devices
+ ${LIBBLADERF_INCLUDE_DIRS}
+)
+
+if (NOT SERVER_MODE)
+ set (bladerf2mimo_SOURCES
+ ${bladerf2mimo_SOURCES}
+ bladerf2mimogui.cpp
+ bladerf2mimogui.ui
+ )
+ set(bladerf2mimo_HEADERS
+ ${bladerf2mimo_HEADERS}
+ bladerf2mimogui.h
+ )
+ set(TARGET_NAME mimobladerf2)
+ set(TARGET_LIB "Qt5::Widgets")
+ set(TARGET_LIB_GUI "sdrgui")
+ set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
+else()
+ set(TARGET_NAME mimobladerf2srv)
+ set(TARGET_LIB "")
+ set(TARGET_LIB_GUI "")
+ set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
+endif()
+
+add_library(${TARGET_NAME} SHARED
+ ${bladerf2mimo_SOURCES}
+)
+
+target_link_libraries(${TARGET_NAME}
+ Qt5::Core
+ ${TARGET_LIB}
+ sdrbase
+ ${TARGET_LIB_GUI}
+ swagger
+ ${LIBBLADERF_LIBRARIES}
+ bladerf2device
+)
+
+install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimo.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimo.cpp
new file mode 100644
index 000000000..cb71106b8
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimo.cpp
@@ -0,0 +1,1308 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "SWGDeviceSettings.h"
+#include "SWGDeviceState.h"
+#include "SWGTestMISettings.h"
+
+#include "device/deviceapi.h"
+#include "dsp/dspcommands.h"
+#include "dsp/dspengine.h"
+#include "dsp/dspdevicemimoengine.h"
+#include "dsp/devicesamplesource.h"
+#include "dsp/devicesamplesink.h"
+#include "dsp/filerecord.h"
+#include "bladerf2/devicebladerf2.h"
+
+#include "bladerf2mithread.h"
+#include "bladerf2mothread.h"
+#include "bladerf2mimo.h"
+
+MESSAGE_CLASS_DEFINITION(BladeRF2MIMO::MsgConfigureBladeRF2MIMO, Message)
+MESSAGE_CLASS_DEFINITION(BladeRF2MIMO::MsgFileRecord, Message)
+MESSAGE_CLASS_DEFINITION(BladeRF2MIMO::MsgStartStop, Message)
+MESSAGE_CLASS_DEFINITION(BladeRF2MIMO::MsgReportGainRange, Message)
+
+BladeRF2MIMO::BladeRF2MIMO(DeviceAPI *deviceAPI) :
+ m_deviceAPI(deviceAPI),
+ m_settings(),
+ m_sourceThread(nullptr),
+ m_sinkThread(nullptr),
+ m_deviceDescription("BladeRF2MIMO"),
+ m_rxElseTx(true),
+ m_runningRx(false),
+ m_runningTx(false),
+ m_dev(nullptr),
+ m_open(false)
+{
+ m_open = openDevice();
+
+ if (m_dev)
+ {
+ const bladerf_gain_modes *modes = 0;
+ int nbModes = m_dev->getGainModesRx(&modes);
+
+ if (modes)
+ {
+ for (int i = 0; i < nbModes; i++) {
+ m_rxGainModes.push_back(GainMode{QString(modes[i].name), modes[i].mode});
+ }
+ }
+ }
+
+ m_mimoType = MIMOHalfSynchronous;
+ m_sampleSinkFifos.push_back(SampleSinkFifo(96000 * 4));
+ m_sampleSinkFifos.push_back(SampleSinkFifo(96000 * 4));
+ m_deviceAPI->setNbSourceStreams(2);
+ m_networkManager = new QNetworkAccessManager();
+ connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
+}
+
+BladeRF2MIMO::~BladeRF2MIMO()
+{
+ disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
+ delete m_networkManager;
+
+ if (m_runningRx) {
+ stop();
+ }
+
+ std::vector::iterator it = m_fileSinks.begin();
+ int istream = 0;
+
+ for (; it != m_fileSinks.end(); ++it, istream++)
+ {
+ m_deviceAPI->removeAncillarySink(*it, istream);
+ delete *it;
+ }
+
+ m_deviceAPI->removeLastSourceStream(); // Remove the last source stream data set in the engine
+ m_deviceAPI->removeLastSourceStream(); // Remove the last source stream data set in the engine
+}
+
+void BladeRF2MIMO::destroy()
+{
+ delete this;
+}
+
+bool BladeRF2MIMO::openDevice()
+{
+ m_dev = new DeviceBladeRF2();
+ char serial[256];
+ strcpy(serial, qPrintable(m_deviceAPI->getSamplingDeviceSerial()));
+
+ if (!m_dev->open(serial))
+ {
+ qCritical("BladeRF2MIMO::openDevice: cannot open BladeRF2 device");
+ return false;
+ }
+ else
+ {
+ qDebug("BladeRF2MIMO::openDevice: device opened");
+ return true;
+ }
+}
+
+void BladeRF2MIMO::closeDevice()
+{
+ if (m_dev == nullptr) { // was never open
+ return;
+ }
+
+ if (m_runningRx) {
+ stop();
+ }
+
+ m_dev->close();
+ delete m_dev;
+ m_dev = nullptr;
+}
+
+void BladeRF2MIMO::init()
+{
+ m_fileSinks.push_back(new FileRecord(QString("test_0_%1.sdriq").arg(m_deviceAPI->getDeviceUID())));
+ m_fileSinks.push_back(new FileRecord(QString("test_1_%1.sdriq").arg(m_deviceAPI->getDeviceUID())));
+ m_deviceAPI->addAncillarySink(m_fileSinks[0], 0);
+ m_deviceAPI->addAncillarySink(m_fileSinks[1], 1);
+
+ applySettings(m_settings, true);
+}
+
+bool BladeRF2MIMO::start()
+{
+ if (!m_open)
+ {
+ qCritical("BladeRF2MIMO::start: device could not be opened");
+ return false;
+ }
+
+ if (m_rxElseTx) {
+ startRx();
+ } else {
+ startTx();
+ }
+
+ return true;
+}
+
+void BladeRF2MIMO::startRx()
+{
+ qDebug("BladeRF2MIMO::start");
+ QMutexLocker mutexLocker(&m_mutex);
+
+ if (m_runningRx) {
+ stop();
+ }
+
+ m_sourceThread = new BladeRF2MIThread(m_dev->getDev());
+ m_sourceThread->setFifo(0, &m_sampleSinkFifos[0]);
+ m_sourceThread->setFifo(1, &m_sampleSinkFifos[1]);
+ m_sourceThread->setFcPos(m_settings.m_fcPos);
+ m_sourceThread->setLog2Decimation(m_settings.m_log2Decim);
+
+ for (int i = 0; i < 2; i++)
+ {
+ if (!m_dev->openRx(i)) {
+ qCritical("BladeRF2MIMO::start: Rx channel %u cannot be enabled", i);
+ }
+ }
+
+ m_sourceThread->startWork();
+
+ mutexLocker.unlock();
+
+ applySettings(m_settings, true);
+ m_runningRx = true;
+}
+
+void BladeRF2MIMO::startTx()
+{
+ // TODO
+}
+
+void BladeRF2MIMO::stop()
+{
+ if (m_rxElseTx) {
+ stopRx();
+ } else {
+ stopTx();
+ }
+}
+
+void BladeRF2MIMO::stopRx()
+{
+ qDebug("BladeRF2MIMO::stop");
+
+ if (!m_sourceThread) {
+ return;
+ }
+
+ QMutexLocker mutexLocker(&m_mutex);
+
+ m_sourceThread->stopWork();
+ delete m_sourceThread;
+ m_sourceThread = nullptr;
+ m_runningRx = false;
+
+ for (int i = 0; i < 2; i++) {
+ m_dev->closeRx(i);
+ }
+}
+
+void BladeRF2MIMO::stopTx()
+{
+ // TODO
+}
+
+QByteArray BladeRF2MIMO::serialize() const
+{
+ return m_settings.serialize();
+}
+
+bool BladeRF2MIMO::deserialize(const QByteArray& data)
+{
+ bool success = true;
+
+ if (!m_settings.deserialize(data))
+ {
+ m_settings.resetToDefaults();
+ success = false;
+ }
+
+ MsgConfigureBladeRF2MIMO* message = MsgConfigureBladeRF2MIMO::create(m_settings, true);
+ m_inputMessageQueue.push(message);
+
+ if (m_guiMessageQueue)
+ {
+ MsgConfigureBladeRF2MIMO* messageToGUI = MsgConfigureBladeRF2MIMO::create(m_settings, true);
+ m_guiMessageQueue->push(messageToGUI);
+ }
+
+ return success;
+}
+
+const QString& BladeRF2MIMO::getDeviceDescription() const
+{
+ return m_deviceDescription;
+}
+
+int BladeRF2MIMO::getSourceSampleRate(int index) const
+{
+ (void) index;
+ int rate = m_settings.m_devSampleRate;
+ return (rate / (1<push(messageToGUI);
+ }
+}
+
+quint64 BladeRF2MIMO::getSinkCenterFrequency(int index) const
+{
+ (void) index;
+ return m_settings.m_txCenterFrequency;
+}
+
+void BladeRF2MIMO::setSinkCenterFrequency(qint64 centerFrequency, int index)
+{
+ (void) index;
+ BladeRF2MIMOSettings settings = m_settings;
+ settings.m_txCenterFrequency = centerFrequency;
+
+ MsgConfigureBladeRF2MIMO* message = MsgConfigureBladeRF2MIMO::create(settings, false);
+ m_inputMessageQueue.push(message);
+
+ if (m_guiMessageQueue)
+ {
+ MsgConfigureBladeRF2MIMO* messageToGUI = MsgConfigureBladeRF2MIMO::create(settings, false);
+ m_guiMessageQueue->push(messageToGUI);
+ }
+}
+
+bool BladeRF2MIMO::handleMessage(const Message& message)
+{
+ if (MsgConfigureBladeRF2MIMO::match(message))
+ {
+ MsgConfigureBladeRF2MIMO& conf = (MsgConfigureBladeRF2MIMO&) message;
+ qDebug() << "BladeRF2MIMO::handleMessage: MsgConfigureBladeRF2MIMO";
+
+ bool success = applySettings(conf.getSettings(), conf.getForce());
+
+ if (!success) {
+ qDebug("BladeRF2MIMO::handleMessage: config error");
+ }
+
+ return true;
+ }
+ else if (MsgFileRecord::match(message))
+ {
+ MsgFileRecord& conf = (MsgFileRecord&) message;
+ qDebug() << "BladeRF2MIMO::handleMessage: MsgFileRecord: " << conf.getStartStop();
+ int istream = conf.getStreamIndex();
+
+ if (conf.getStartStop())
+ {
+ if (m_settings.m_fileRecordName.size() != 0) {
+ m_fileSinks[istream]->setFileName(m_settings.m_fileRecordName + "_0.sdriq");
+ } else {
+ m_fileSinks[istream]->genUniqueFileName(m_deviceAPI->getDeviceUID(), istream);
+ }
+
+ m_fileSinks[istream]->startRecording();
+ }
+ else
+ {
+ m_fileSinks[istream]->stopRecording();
+ }
+
+ return true;
+ }
+ else if (MsgStartStop::match(message))
+ {
+ MsgStartStop& cmd = (MsgStartStop&) message;
+ qDebug() << "BladeRF2MIMO::handleMessage: "
+ << " " << (cmd.getRxElseTx() ? "Rx" : "Tx")
+ << " MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop");
+
+ m_rxElseTx = cmd.getRxElseTx();
+
+ if (cmd.getStartStop())
+ {
+ if (m_deviceAPI->initDeviceEngine()) {
+ m_deviceAPI->startDeviceEngine();
+ }
+ }
+ else
+ {
+ m_deviceAPI->stopDeviceEngine();
+ }
+
+ if (m_settings.m_useReverseAPI) {
+ webapiReverseSendStartStop(cmd.getStartStop());
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool BladeRF2MIMO::applySettings(const BladeRF2MIMOSettings& settings, bool force)
+{
+ QList reverseAPIKeys;
+ bool forwardChangeRxDSP = false;
+ bool forwardChangeTxDSP = false;
+
+ qDebug() << "BladeRF2MIMO::applySettings: common: "
+ << " m_devSampleRate: " << settings.m_devSampleRate
+ << " m_LOppmTenths: " << settings.m_LOppmTenths
+ << " m_rxCenterFrequency: " << settings.m_rxCenterFrequency
+ << " m_log2Decim: " << settings.m_log2Decim
+ << " m_fcPos: " << settings.m_fcPos
+ << " m_rxBandwidth: " << settings.m_rxBandwidth
+ << " m_rx0GainMode: " << settings.m_rx0GainMode
+ << " m_rx0GlobalGain: " << settings.m_rx0GlobalGain
+ << " m_rx1GainMode: " << settings.m_rx1GainMode
+ << " m_rx1GlobalGain: " << settings.m_rx1GlobalGain
+ << " m_rxBiasTee: " << settings.m_rxBiasTee
+ << " m_dcBlock: " << settings.m_dcBlock
+ << " m_iqCorrection: " << settings.m_iqCorrection
+ << " m_rxTransverterMode: " << settings.m_rxTransverterMode
+ << " m_rxTransverterDeltaFrequency: " << settings.m_rxTransverterDeltaFrequency
+ << " m_txCenterFrequency: " << settings.m_txCenterFrequency
+ << " m_log2Interp: " << settings.m_log2Interp
+ << " m_txBandwidth: " << settings.m_txBandwidth
+ << " m_tx0GlobalGain: " << settings.m_tx0GlobalGain
+ << " m_tx1GlobalGain: " << settings.m_tx1GlobalGain
+ << " m_txBiasTee: " << settings.m_txBiasTee
+ << " m_txTransverterMode: " << settings.m_txTransverterMode
+ << " m_txTransverterDeltaFrequency: " << settings.m_txTransverterDeltaFrequency
+ << " m_fileRecordName: " << settings.m_fileRecordName
+ << " m_useReverseAPI: " << settings.m_useReverseAPI
+ << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
+ << " m_reverseAPIPort: " << settings.m_reverseAPIPort
+ << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex;
+
+ struct bladerf *dev = m_dev ? m_dev->getDev() : nullptr;
+
+ qint64 rxXlatedDeviceCenterFrequency = settings.m_rxCenterFrequency;
+ rxXlatedDeviceCenterFrequency -= settings.m_rxTransverterMode ? settings.m_rxTransverterDeltaFrequency : 0;
+ rxXlatedDeviceCenterFrequency = rxXlatedDeviceCenterFrequency < 0 ? 0 : rxXlatedDeviceCenterFrequency;
+
+
+ qint64 txXlatedDeviceCenterFrequency = settings.m_txCenterFrequency;
+ txXlatedDeviceCenterFrequency -= settings.m_txTransverterMode ? settings.m_txTransverterDeltaFrequency : 0;
+ txXlatedDeviceCenterFrequency = txXlatedDeviceCenterFrequency < 0 ? 0 : txXlatedDeviceCenterFrequency;
+
+ if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force)
+ {
+ reverseAPIKeys.append("devSampleRate");
+
+ if (dev)
+ {
+ unsigned int actualSamplerate;
+ int status = bladerf_set_sample_rate(dev, BLADERF_CHANNEL_RX(0), settings.m_devSampleRate, &actualSamplerate);
+
+ if (status < 0)
+ {
+ qCritical("BladeRF2MIMO::applySettings: could not set sample rate: %d: %s",
+ settings.m_devSampleRate, bladerf_strerror(status));
+ }
+ else
+ {
+ qDebug() << "BladeRF2MIMO::applySettings: bladerf_set_sample_rate: actual sample rate is " << actualSamplerate;
+ }
+
+ }
+ }
+
+ // Rx settings
+
+ if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) {
+ reverseAPIKeys.append("dcBlock");
+ }
+ if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) {
+ reverseAPIKeys.append("iqCorrection");
+ }
+
+ if ((m_settings.m_dcBlock != settings.m_dcBlock) ||
+ (m_settings.m_iqCorrection != settings.m_iqCorrection) || force)
+ {
+ m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection, 0);
+ m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection, 1);
+ }
+
+ if ((m_settings.m_rxBandwidth != settings.m_rxBandwidth) || force)
+ {
+ reverseAPIKeys.append("rxBandwidth");
+
+ if (dev)
+ {
+ unsigned int actualBandwidth;
+ int status = bladerf_set_bandwidth(dev, BLADERF_CHANNEL_RX(0), settings.m_rxBandwidth, &actualBandwidth);
+
+ if(status < 0) {
+ qCritical("BladeRF2MIMO::applySettings: could not set RX0 bandwidth: %d: %s", settings.m_rxBandwidth, bladerf_strerror(status));
+ } else {
+ qDebug() << "BladeRF2MIMO::applySettings: RX0: bladerf_set_bandwidth: actual bandwidth is " << actualBandwidth;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_CHANNEL_RX(1), settings.m_rxBandwidth, &actualBandwidth);
+
+ if(status < 0) {
+ qCritical("BladeRF2MIMO::applySettings: could not set RX1 bandwidth: %d: %s", settings.m_rxBandwidth, bladerf_strerror(status));
+ } else {
+ qDebug() << "BladeRF2MIMO::applySettings: RX1: bladerf_set_bandwidth: actual bandwidth is " << actualBandwidth;
+ }
+ }
+ }
+
+ if ((m_settings.m_fcPos != settings.m_fcPos) || force)
+ {
+ reverseAPIKeys.append("fcPos");
+
+ if (m_sourceThread)
+ {
+ m_sourceThread->setFcPos((int) settings.m_fcPos);
+ qDebug() << "BladeRF2MIMO::applySettings: set fc pos (enum) to " << (int) settings.m_fcPos;
+ }
+ }
+
+ if ((m_settings.m_log2Decim != settings.m_log2Decim) || force)
+ {
+ reverseAPIKeys.append("log2Decim");
+
+ if (m_sourceThread)
+ {
+ m_sourceThread->setLog2Decimation(settings.m_log2Decim);
+ qDebug() << "BladeRF2MIMO::applySettings: set decimation to " << (1<setLog2Interpolation(settings.m_log2Interp);
+ qDebug() << "BladeRF2Input::applySettings: set interpolation to " << (1<push(msg);
+ }
+
+ }
+ }
+
+ forwardChangeRxDSP = true;
+ }
+
+ if ((m_settings.m_rxBiasTee != settings.m_rxBiasTee) || force)
+ {
+ reverseAPIKeys.append("rxBiasTee");
+
+ if (m_dev) {
+ m_dev->setBiasTeeRx(settings.m_rxBiasTee);
+ }
+ }
+
+ if ((m_settings.m_rx0GainMode != settings.m_rx0GainMode) || force)
+ {
+ reverseAPIKeys.append("rx0GainMode");
+
+ if (dev)
+ {
+ int status = bladerf_set_gain_mode(dev, BLADERF_CHANNEL_RX(0), (bladerf_gain_mode) settings.m_rx0GainMode);
+
+ if (status < 0) {
+ qWarning("BladeRF2MIMO::applySettings: RX0: bladerf_set_gain_mode(%d) failed: %s",
+ settings.m_rx0GainMode, bladerf_strerror(status));
+ } else {
+ qDebug("BladeRF2MIMO::applySettings: RX0: bladerf_set_gain_mode(%d)", settings.m_rx0GainMode);
+ }
+ }
+ }
+
+ if ((m_settings.m_rx1GainMode != settings.m_rx1GainMode) || force)
+ {
+ reverseAPIKeys.append("rx1GainMode");
+
+ if (dev)
+ {
+ int status = bladerf_set_gain_mode(dev, BLADERF_CHANNEL_RX(1), (bladerf_gain_mode) settings.m_rx1GainMode);
+
+ if (status < 0) {
+ qWarning("BladeRF2MIMO::applySettings: RX1: bladerf_set_gain_mode(%d) failed: %s",
+ settings.m_rx1GainMode, bladerf_strerror(status));
+ } else {
+ qDebug("BladeRF2MIMO::applySettings: RX1: bladerf_set_gain_mode(%d)", settings.m_rx1GainMode);
+ }
+ }
+ }
+
+ if ((m_settings.m_rx0GlobalGain != settings.m_rx0GlobalGain) || force) {
+ reverseAPIKeys.append("rx0GlobalGain");
+ }
+ if ((m_settings.m_rx1GlobalGain != settings.m_rx1GlobalGain) || force) {
+ reverseAPIKeys.append("rx1GlobalGain");
+ }
+
+ if ((m_settings.m_rx0GlobalGain != settings.m_rx0GlobalGain)
+ || ((m_settings.m_rx0GlobalGain != settings.m_rx0GlobalGain) && (settings.m_rx0GlobalGain == BLADERF_GAIN_MANUAL)) || force)
+ {
+ if (dev)
+ {
+ int status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(0), settings.m_rx0GlobalGain);
+
+ if (status < 0) {
+ qWarning("BladeRF2MIMO::applySettings: RX0: bladerf_set_gain(%d) failed: %s",
+ settings.m_rx0GlobalGain, bladerf_strerror(status));
+ } else {
+ qDebug("BladeRF2MIMO::applySettings: RX0: bladerf_set_gain(%d)", settings.m_rx0GlobalGain);
+ }
+ }
+ }
+
+ if ((m_settings.m_rx1GlobalGain != settings.m_rx1GlobalGain)
+ || ((m_settings.m_rx1GlobalGain != settings.m_rx1GlobalGain) && (settings.m_rx1GlobalGain == BLADERF_GAIN_MANUAL)) || force)
+ {
+ if (dev)
+ {
+ int status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(1), settings.m_rx1GlobalGain);
+
+ if (status < 0) {
+ qWarning("BladeRF2MIMO::applySettings: RX1: bladerf_set_gain(%d) failed: %s",
+ settings.m_rx1GlobalGain, bladerf_strerror(status));
+ } else {
+ qDebug("BladeRF2MIMO::applySettings: RX1: bladerf_set_gain(%d)", settings.m_rx1GlobalGain);
+ }
+ }
+ }
+
+ // Tx settings
+
+ if ((m_settings.m_txCenterFrequency != settings.m_txCenterFrequency) || force) {
+ reverseAPIKeys.append("txCenterFrequency");
+ }
+ if ((m_settings.m_txTransverterMode != settings.m_txTransverterMode) || force) {
+ reverseAPIKeys.append("txTransverterMode");
+ }
+ if ((m_settings.m_txTransverterDeltaFrequency != settings.m_txTransverterDeltaFrequency) || force) {
+ reverseAPIKeys.append("txTransverterDeltaFrequency");
+ }
+
+ if ((m_settings.m_txCenterFrequency != settings.m_txCenterFrequency)
+ || (m_settings.m_txTransverterMode != settings.m_txTransverterMode)
+ || (m_settings.m_txTransverterDeltaFrequency != settings.m_txTransverterDeltaFrequency)
+ || (m_settings.m_LOppmTenths != settings.m_LOppmTenths)
+ || (m_settings.m_devSampleRate != settings.m_devSampleRate) || force)
+ {
+ if (dev)
+ {
+ qint64 deviceCenterFrequency = DeviceSampleSink::calculateDeviceCenterFrequency(
+ settings.m_txCenterFrequency,
+ settings.m_txTransverterDeltaFrequency,
+ settings.m_log2Interp,
+ (DeviceSampleSink::fcPos_t) DeviceSampleSink::FC_POS_CENTER,
+ settings.m_devSampleRate,
+ settings.m_txTransverterMode);
+
+ if (setTxDeviceCenterFrequency(dev, deviceCenterFrequency, settings.m_LOppmTenths))
+ {
+ if (getMessageQueueToGUI())
+ {
+ int min, max, step;
+ getTxGlobalGainRange(min, max, step);
+ MsgReportGainRange *msg = MsgReportGainRange::create(min, max, step, false);
+ getMessageQueueToGUI()->push(msg);
+ }
+ }
+ }
+
+ forwardChangeTxDSP = true;
+ }
+
+ if ((m_settings.m_txBandwidth != settings.m_txBandwidth) || force)
+ {
+ reverseAPIKeys.append("txBandwidth");
+
+ if (dev)
+ {
+ unsigned int actualBandwidth;
+ int status = bladerf_set_bandwidth(dev, BLADERF_CHANNEL_TX(0), settings.m_txBandwidth, &actualBandwidth);
+
+ if (status < 0)
+ {
+ qCritical("BladeRF2MIMO::applySettings: TX0: could not set bandwidth: %d: %s",
+ settings.m_txBandwidth, bladerf_strerror(status));
+ }
+ else
+ {
+ qDebug() << "BladeRF2MIMO::applySettings: TX0: bladerf_set_bandwidth: actual bandwidth is " << actualBandwidth;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_CHANNEL_TX(0), settings.m_txBandwidth, &actualBandwidth);
+
+ if (status < 0)
+ {
+ qCritical("BladeRF2MIMO::applySettings: TX1: could not set bandwidth: %d: %s",
+ settings.m_txBandwidth, bladerf_strerror(status));
+ }
+ else
+ {
+ qDebug() << "BladeRF2MIMO::applySettings: TX1: bladerf_set_bandwidth: actual bandwidth is " << actualBandwidth;
+ }
+ }
+ }
+
+ if ((m_settings.m_log2Interp != settings.m_log2Interp) || force)
+ {
+ reverseAPIKeys.append("log2Interp");
+
+ if (m_sinkThread)
+ {
+ m_sinkThread->setLog2Interpolation(settings.m_log2Interp);
+ qDebug() << "BladeRF2MIMO::applySettings: set interpolation to " << (1<setBiasTeeTx(settings.m_txBiasTee);
+ }
+ }
+
+ if ((m_settings.m_tx0GlobalGain != settings.m_tx0GlobalGain) || force)
+ {
+ reverseAPIKeys.append("tx0GlobalGain");
+
+ if (dev)
+ {
+ int status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(0), settings.m_tx0GlobalGain);
+
+ if (status < 0) {
+ qWarning("BladeRF2MIMO::applySettings: TX0: bladerf_set_gain(%d) failed: %s",
+ settings.m_tx0GlobalGain, bladerf_strerror(status));
+ } else {
+ qDebug("BladeRF2MIMO::applySettings: TX0: bladerf_set_gain(%d)", settings.m_tx0GlobalGain);
+ }
+ }
+ }
+
+ if ((m_settings.m_tx1GlobalGain != settings.m_tx1GlobalGain) || force)
+ {
+ reverseAPIKeys.append("tx1GlobalGain");
+
+ if (dev)
+ {
+ int status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(1), settings.m_tx1GlobalGain);
+
+ if (status < 0) {
+ qWarning("BladeRF2MIMO::applySettings: TX1: bladerf_set_gain(%d) failed: %s",
+ settings.m_tx1GlobalGain, bladerf_strerror(status));
+ } else {
+ qDebug("BladeRF2MIMO::applySettings: TX1: bladerf_set_gain(%d)", settings.m_tx1GlobalGain);
+ }
+ }
+ }
+
+ if (forwardChangeRxDSP)
+ {
+ int sampleRate = settings.m_devSampleRate/(1<handleMessage(*notifFileSink0); // forward to file sinks
+ DSPSignalNotification *notifFileSink1 = new DSPSignalNotification(sampleRate, settings.m_rxCenterFrequency);
+ m_fileSinks[1]->handleMessage(*notifFileSink0); // forward to file sinks
+ DSPMIMOSignalNotification *notif0 = new DSPMIMOSignalNotification(sampleRate, settings.m_rxCenterFrequency, true, 0);
+ m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif0);
+ DSPMIMOSignalNotification *notif1 = new DSPMIMOSignalNotification(sampleRate, settings.m_rxCenterFrequency, true, 1);
+ m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif1);
+ }
+
+ if (forwardChangeTxDSP)
+ {
+ int sampleRate = settings.m_devSampleRate/(1<getDeviceEngineInputMessageQueue()->push(notif0);
+ DSPMIMOSignalNotification *notif1 = new DSPMIMOSignalNotification(sampleRate, settings.m_rxCenterFrequency, false, 1);
+ m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif1);
+ }
+
+ // Reverse API settings
+
+ if (settings.m_useReverseAPI)
+ {
+ bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
+ (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
+ (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
+ (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex);
+ webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
+ }
+
+ m_settings = settings;
+ return true;
+}
+
+bool BladeRF2MIMO::setRxDeviceCenterFrequency(struct bladerf *dev, quint64 freq_hz, int loPpmTenths)
+{
+ qint64 df = ((qint64)freq_hz * loPpmTenths) / 10000000LL;
+ freq_hz += df;
+
+ int status = bladerf_set_frequency(dev, BLADERF_CHANNEL_RX(0), freq_hz);
+
+ if (status < 0)
+ {
+ qWarning("BladeRF2MIMO::setDeviceCenterFrequency: RX0: bladerf_set_frequency(%lld) failed: %s",
+ freq_hz, bladerf_strerror(status));
+ return false;
+ }
+ else
+ {
+ qDebug("BladeRF2MIMO::setDeviceCenterFrequency: RX0: bladerf_set_frequency(%lld)", freq_hz);
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_CHANNEL_RX(1), freq_hz);
+
+ if (status < 0)
+ {
+ qWarning("BladeRF2MIMO::setDeviceCenterFrequency: RX1: bladerf_set_frequency(%lld) failed: %s",
+ freq_hz, bladerf_strerror(status));
+ return false;
+ }
+ else
+ {
+ qDebug("BladeRF2MIMO::setDeviceCenterFrequency: RX1: bladerf_set_frequency(%lld)", freq_hz);
+ }
+
+ return true;
+}
+
+bool BladeRF2MIMO::setTxDeviceCenterFrequency(struct bladerf *dev, quint64 freq_hz, int loPpmTenths)
+{
+ qint64 df = ((qint64)freq_hz * loPpmTenths) / 10000000LL;
+ freq_hz += df;
+
+ int status = bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(0), freq_hz);
+
+ if (status < 0) {
+ qWarning("BladeRF2Output::setDeviceCenterFrequency: TX0: bladerf_set_frequency(%lld) failed: %s",
+ freq_hz, bladerf_strerror(status));
+ return false;
+ }
+ else
+ {
+ qDebug("BladeRF2Output::setDeviceCenterFrequency: TX0: bladerf_set_frequency(%lld)", freq_hz);
+ return true;
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(1), freq_hz);
+
+ if (status < 0) {
+ qWarning("BladeRF2Output::setDeviceCenterFrequency: TX1: bladerf_set_frequency(%lld) failed: %s",
+ freq_hz, bladerf_strerror(status));
+ return false;
+ }
+ else
+ {
+ qDebug("BladeRF2Output::setDeviceCenterFrequency: TX1: bladerf_set_frequency(%lld)", freq_hz);
+ return true;
+ }
+}
+
+void BladeRF2MIMO::getRxFrequencyRange(uint64_t& min, uint64_t& max, int& step)
+{
+ if (m_dev) {
+ m_dev->getFrequencyRangeRx(min, max, step);
+ }
+}
+
+void BladeRF2MIMO::getRxSampleRateRange(int& min, int& max, int& step)
+{
+ if (m_dev) {
+ m_dev->getSampleRateRangeRx(min, max, step);
+ }
+}
+
+void BladeRF2MIMO::getRxBandwidthRange(int& min, int& max, int& step)
+{
+ if (m_dev) {
+ m_dev->getBandwidthRangeRx(min, max, step);
+ }
+}
+
+void BladeRF2MIMO::getRxGlobalGainRange(int& min, int& max, int& step)
+{
+ if (m_dev) {
+ m_dev->getGlobalGainRangeRx(min, max, step);
+ }
+}
+
+void BladeRF2MIMO::getTxFrequencyRange(uint64_t& min, uint64_t& max, int& step)
+{
+ if (m_dev) {
+ m_dev->getFrequencyRangeTx(min, max, step);
+ }
+}
+
+void BladeRF2MIMO::getTxSampleRateRange(int& min, int& max, int& step)
+{
+ if (m_dev) {
+ m_dev->getSampleRateRangeTx(min, max, step);
+ }
+}
+
+void BladeRF2MIMO::getTxBandwidthRange(int& min, int& max, int& step)
+{
+ if (m_dev) {
+ m_dev->getBandwidthRangeTx(min, max, step);
+ }
+}
+
+void BladeRF2MIMO::getTxGlobalGainRange(int& min, int& max, int& step)
+{
+ if (m_dev) {
+ m_dev->getGlobalGainRangeTx(min, max, step);
+ }
+}
+
+int BladeRF2MIMO::webapiSettingsGet(
+ SWGSDRangel::SWGDeviceSettings& response,
+ QString& errorMessage)
+{
+ (void) errorMessage;
+ response.setBladeRf2MimoSettings(new SWGSDRangel::SWGBladeRF2MIMOSettings());
+ response.getBladeRf2MimoSettings()->init();
+ webapiFormatDeviceSettings(response, m_settings);
+ return 200;
+}
+
+int BladeRF2MIMO::webapiSettingsPutPatch(
+ bool force,
+ const QStringList& deviceSettingsKeys,
+ SWGSDRangel::SWGDeviceSettings& response, // query + response
+ QString& errorMessage)
+{
+ (void) errorMessage;
+ BladeRF2MIMOSettings settings = m_settings;
+ webapiUpdateDeviceSettings(settings, deviceSettingsKeys, response);
+
+ MsgConfigureBladeRF2MIMO *msg = MsgConfigureBladeRF2MIMO::create(settings, force);
+ m_inputMessageQueue.push(msg);
+
+ if (m_guiMessageQueue) // forward to GUI if any
+ {
+ MsgConfigureBladeRF2MIMO *msgToGUI = MsgConfigureBladeRF2MIMO::create(settings, force);
+ m_guiMessageQueue->push(msgToGUI);
+ }
+
+ webapiFormatDeviceSettings(response, settings);
+ return 200;
+}
+
+void BladeRF2MIMO::webapiUpdateDeviceSettings(
+ BladeRF2MIMOSettings& settings,
+ const QStringList& deviceSettingsKeys,
+ SWGSDRangel::SWGDeviceSettings& response)
+{
+ if (deviceSettingsKeys.contains("devSampleRate")) {
+ settings.m_devSampleRate = response.getBladeRf2MimoSettings()->getDevSampleRate();
+ }
+ if (deviceSettingsKeys.contains("LOppmTenths")) {
+ settings.m_LOppmTenths = response.getBladeRf2MimoSettings()->getLOppmTenths();
+ }
+
+ if (deviceSettingsKeys.contains("rxCenterFrequency")) {
+ settings.m_rxCenterFrequency = response.getBladeRf2MimoSettings()->getRxCenterFrequency();
+ }
+ if (deviceSettingsKeys.contains("log2Decim")) {
+ settings.m_log2Decim = response.getBladeRf2MimoSettings()->getLog2Decim();
+ }
+ if (deviceSettingsKeys.contains("fcPos")) {
+ settings.m_fcPos = static_cast(response.getBladeRf2MimoSettings()->getFcPos());
+ }
+ if (deviceSettingsKeys.contains("rxBandwidth")) {
+ settings.m_rxBandwidth = response.getBladeRf2MimoSettings()->getRxBandwidth();
+ }
+ if (deviceSettingsKeys.contains("rx0GainMode")) {
+ settings.m_rx0GainMode = response.getBladeRf2MimoSettings()->getRx0GainMode();
+ }
+ if (deviceSettingsKeys.contains("rx0GlobalGain")) {
+ settings.m_rx0GlobalGain = response.getBladeRf2MimoSettings()->getRx0GlobalGain();
+ }
+ if (deviceSettingsKeys.contains("rx1GainMode")) {
+ settings.m_rx1GainMode = response.getBladeRf2MimoSettings()->getRx1GainMode();
+ }
+ if (deviceSettingsKeys.contains("rx1GlobalGain")) {
+ settings.m_rx1GlobalGain = response.getBladeRf2MimoSettings()->getRx1GlobalGain();
+ }
+ if (deviceSettingsKeys.contains("rxBiasTee")) {
+ settings.m_rxBiasTee = response.getBladeRf2MimoSettings()->getRxBiasTee() != 0;
+ }
+ if (deviceSettingsKeys.contains("dcBlock")) {
+ settings.m_dcBlock = response.getBladeRf2MimoSettings()->getDcBlock() != 0;
+ }
+ if (deviceSettingsKeys.contains("iqCorrection")) {
+ settings.m_iqCorrection = response.getBladeRf2MimoSettings()->getIqCorrection() != 0;
+ }
+ if (deviceSettingsKeys.contains("rxTransverterDeltaFrequency")) {
+ settings.m_rxTransverterDeltaFrequency = response.getBladeRf2MimoSettings()->getRxTransverterDeltaFrequency();
+ }
+ if (deviceSettingsKeys.contains("rxTransverterMode")) {
+ settings.m_rxTransverterMode = response.getBladeRf2MimoSettings()->getRxTransverterMode() != 0;
+ }
+
+ if (deviceSettingsKeys.contains("txCenterFrequency")) {
+ settings.m_txCenterFrequency = response.getBladeRf2MimoSettings()->getTxCenterFrequency();
+ }
+ if (deviceSettingsKeys.contains("log2Interp")) {
+ settings.m_log2Interp = response.getBladeRf2MimoSettings()->getLog2Interp();
+ }
+ if (deviceSettingsKeys.contains("txBandwidth")) {
+ settings.m_txBandwidth = response.getBladeRf2MimoSettings()->getTxBandwidth();
+ }
+ if (deviceSettingsKeys.contains("tx0GlobalGain")) {
+ settings.m_tx0GlobalGain = response.getBladeRf2MimoSettings()->getTx0GlobalGain();
+ }
+ if (deviceSettingsKeys.contains("tx1GlobalGain")) {
+ settings.m_tx1GlobalGain = response.getBladeRf2MimoSettings()->getTx1GlobalGain();
+ }
+ if (deviceSettingsKeys.contains("txBiasTee")) {
+ settings.m_txBiasTee = response.getBladeRf2MimoSettings()->getTxBiasTee() != 0;
+ }
+ if (deviceSettingsKeys.contains("txTransverterMode")) {
+ settings.m_txTransverterMode = response.getBladeRf2MimoSettings()->getTxTransverterMode() != 0;
+ }
+ if (deviceSettingsKeys.contains("txTransverterDeltaFrequency")) {
+ settings.m_txTransverterDeltaFrequency = response.getBladeRf2MimoSettings()->getTxTransverterDeltaFrequency();
+ }
+
+ if (deviceSettingsKeys.contains("fileRecordName")) {
+ settings.m_fileRecordName = *response.getBladeRf2MimoSettings()->getFileRecordName();
+ }
+ if (deviceSettingsKeys.contains("useReverseAPI")) {
+ settings.m_useReverseAPI = response.getBladeRf2OutputSettings()->getUseReverseApi() != 0;
+ }
+ if (deviceSettingsKeys.contains("reverseAPIAddress")) {
+ settings.m_reverseAPIAddress = *response.getBladeRf2OutputSettings()->getReverseApiAddress();
+ }
+ if (deviceSettingsKeys.contains("reverseAPIPort")) {
+ settings.m_reverseAPIPort = response.getBladeRf2OutputSettings()->getReverseApiPort();
+ }
+ if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) {
+ settings.m_reverseAPIDeviceIndex = response.getBladeRf2OutputSettings()->getReverseApiDeviceIndex();
+ }
+}
+
+void BladeRF2MIMO::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const BladeRF2MIMOSettings& settings)
+{
+ response.getBladeRf2MimoSettings()->setDevSampleRate(settings.m_devSampleRate);
+ response.getBladeRf2MimoSettings()->setLOppmTenths(settings.m_LOppmTenths);
+
+ response.getBladeRf2MimoSettings()->setRxCenterFrequency(settings.m_rxCenterFrequency);
+ response.getBladeRf2MimoSettings()->setLog2Decim(settings.m_log2Decim);
+ response.getBladeRf2MimoSettings()->setFcPos((int) settings.m_fcPos);
+ response.getBladeRf2MimoSettings()->setRxBandwidth(settings.m_rxBandwidth);
+ response.getBladeRf2MimoSettings()->setRx0GainMode(settings.m_rx0GainMode);
+ response.getBladeRf2MimoSettings()->setRx0GlobalGain(settings.m_rx0GlobalGain);
+ response.getBladeRf2MimoSettings()->setRx1GainMode(settings.m_rx1GainMode);
+ response.getBladeRf2MimoSettings()->setRx1GlobalGain(settings.m_rx1GlobalGain);
+ response.getBladeRf2MimoSettings()->setRxBiasTee(settings.m_rxBiasTee ? 1 : 0);
+ response.getBladeRf2MimoSettings()->setDcBlock(settings.m_dcBlock ? 1 : 0);
+ response.getBladeRf2MimoSettings()->setIqCorrection(settings.m_iqCorrection ? 1 : 0);
+ response.getBladeRf2MimoSettings()->setRxTransverterDeltaFrequency(settings.m_rxTransverterDeltaFrequency);
+ response.getBladeRf2MimoSettings()->setRxTransverterMode(settings.m_rxTransverterMode ? 1 : 0);
+
+ response.getBladeRf2MimoSettings()->setTxCenterFrequency(settings.m_txCenterFrequency);
+ response.getBladeRf2MimoSettings()->setLog2Interp(settings.m_log2Interp);
+ response.getBladeRf2MimoSettings()->setTxBandwidth(settings.m_txBandwidth);
+ response.getBladeRf2MimoSettings()->setTx0GlobalGain(settings.m_tx0GlobalGain);
+ response.getBladeRf2MimoSettings()->setTx1GlobalGain(settings.m_tx1GlobalGain);
+ response.getBladeRf2MimoSettings()->setTxBiasTee(settings.m_txBiasTee ? 1 : 0);
+ response.getBladeRf2MimoSettings()->setTxTransverterDeltaFrequency(settings.m_txTransverterDeltaFrequency);
+ response.getBladeRf2MimoSettings()->setTxTransverterMode(settings.m_txTransverterMode ? 1 : 0);
+
+ if (response.getBladeRf2MimoSettings()->getFileRecordName()) {
+ *response.getBladeRf2MimoSettings()->getFileRecordName() = settings.m_fileRecordName;
+ } else {
+ response.getBladeRf2MimoSettings()->setFileRecordName(new QString(settings.m_fileRecordName));
+ }
+
+ response.getBladeRf2OutputSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
+
+ if (response.getBladeRf2OutputSettings()->getReverseApiAddress()) {
+ *response.getBladeRf2OutputSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
+ } else {
+ response.getBladeRf2OutputSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
+ }
+
+ response.getBladeRf2OutputSettings()->setReverseApiPort(settings.m_reverseAPIPort);
+ response.getBladeRf2OutputSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
+}
+
+int BladeRF2MIMO::webapiRunGet(
+ SWGSDRangel::SWGDeviceState& response,
+ QString& errorMessage)
+{
+ (void) errorMessage;
+ m_deviceAPI->getDeviceEngineStateStr(*response.getState());
+ return 200;
+}
+
+int BladeRF2MIMO::webapiRun(
+ bool run,
+ SWGSDRangel::SWGDeviceState& response,
+ QString& errorMessage)
+{
+ (void) errorMessage;
+ m_deviceAPI->getDeviceEngineStateStr(*response.getState());
+ MsgStartStop *message = MsgStartStop::create(run, true); // TODO: Tx support
+ m_inputMessageQueue.push(message);
+
+ if (m_guiMessageQueue) // forward to GUI if any
+ {
+ MsgStartStop *msgToGUI = MsgStartStop::create(run, true); // TODO: Tx support
+ m_guiMessageQueue->push(msgToGUI);
+ }
+
+ return 200;
+}
+
+void BladeRF2MIMO::webapiReverseSendSettings(QList& deviceSettingsKeys, const BladeRF2MIMOSettings& settings, bool force)
+{
+ SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
+ swgDeviceSettings->setDirection(2); // MIMO
+ swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
+ swgDeviceSettings->setDeviceHwType(new QString("BladeRF2"));
+ swgDeviceSettings->setBladeRf2MimoSettings(new SWGSDRangel::SWGBladeRF2MIMOSettings());
+ SWGSDRangel::SWGBladeRF2MIMOSettings *swgBladeRF2MIMOSettings = swgDeviceSettings->getBladeRf2MimoSettings();
+
+ // transfer data that has been modified. When force is on transfer all data except reverse API data
+
+ if (deviceSettingsKeys.contains("devSampleRate") || force) {
+ swgBladeRF2MIMOSettings->setDevSampleRate(settings.m_devSampleRate);
+ }
+ if (deviceSettingsKeys.contains("LOppmTenths") || force) {
+ swgBladeRF2MIMOSettings->setLOppmTenths(settings.m_LOppmTenths);
+ }
+
+ if (deviceSettingsKeys.contains("rxCenterFrequency") || force) {
+ swgBladeRF2MIMOSettings->setRxCenterFrequency(settings.m_rxCenterFrequency);
+ }
+ if (deviceSettingsKeys.contains("log2Decim") || force) {
+ swgBladeRF2MIMOSettings->setLog2Decim(settings.m_log2Decim);
+ }
+ if (deviceSettingsKeys.contains("fcPos") || force) {
+ swgBladeRF2MIMOSettings->setFcPos((int) settings.m_fcPos);
+ }
+ if (deviceSettingsKeys.contains("rxBandwidth") || force) {
+ swgBladeRF2MIMOSettings->setRxBandwidth(settings.m_rxBandwidth);
+ }
+ if (deviceSettingsKeys.contains("rx0GainMode")) {
+ swgBladeRF2MIMOSettings->setRx0GainMode(settings.m_rx0GainMode);
+ }
+ if (deviceSettingsKeys.contains("rx0GlobalGain")) {
+ swgBladeRF2MIMOSettings->setRx0GlobalGain(settings.m_rx0GlobalGain);
+ }
+ if (deviceSettingsKeys.contains("rx1GainMode")) {
+ swgBladeRF2MIMOSettings->setRx1GainMode(settings.m_rx1GainMode);
+ }
+ if (deviceSettingsKeys.contains("rx1GlobalGain")) {
+ swgBladeRF2MIMOSettings->setRx1GlobalGain(settings.m_rx1GlobalGain);
+ }
+ if (deviceSettingsKeys.contains("rxBiasTee") || force) {
+ swgBladeRF2MIMOSettings->setRxBiasTee(settings.m_rxBiasTee ? 1 : 0);
+ }
+ if (deviceSettingsKeys.contains("dcBlock") || force) {
+ swgBladeRF2MIMOSettings->setDcBlock(settings.m_dcBlock ? 1 : 0);
+ }
+ if (deviceSettingsKeys.contains("iqCorrection") || force) {
+ swgBladeRF2MIMOSettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0);
+ }
+ if (deviceSettingsKeys.contains("rxTransverterDeltaFrequency") || force) {
+ swgBladeRF2MIMOSettings->setRxTransverterDeltaFrequency(settings.m_rxTransverterDeltaFrequency);
+ }
+ if (deviceSettingsKeys.contains("rxTransverterMode") || force) {
+ swgBladeRF2MIMOSettings->setRxTransverterMode(settings.m_rxTransverterMode ? 1 : 0);
+ }
+
+ if (deviceSettingsKeys.contains("txCenterFrequency") || force) {
+ swgBladeRF2MIMOSettings->setTxCenterFrequency(settings.m_txCenterFrequency);
+ }
+ if (deviceSettingsKeys.contains("log2Interp") || force) {
+ swgBladeRF2MIMOSettings->setLog2Interp(settings.m_log2Interp);
+ }
+ if (deviceSettingsKeys.contains("txBandwidth") || force) {
+ swgBladeRF2MIMOSettings->setTxBandwidth(settings.m_txBandwidth);
+ }
+ if (deviceSettingsKeys.contains("tx0GlobalGain") || force) {
+ swgBladeRF2MIMOSettings->setTx0GlobalGain(settings.m_tx0GlobalGain);
+ }
+ if (deviceSettingsKeys.contains("tx1GlobalGain") || force) {
+ swgBladeRF2MIMOSettings->setTx1GlobalGain(settings.m_tx1GlobalGain);
+ }
+ if (deviceSettingsKeys.contains("txBiasTee") || force) {
+ swgBladeRF2MIMOSettings->setTxBiasTee(settings.m_txBiasTee ? 1 : 0);
+ }
+ if (deviceSettingsKeys.contains("txTransverterDeltaFrequency") || force) {
+ swgBladeRF2MIMOSettings->setTxTransverterDeltaFrequency(settings.m_txTransverterDeltaFrequency);
+ }
+ if (deviceSettingsKeys.contains("txTransverterMode") || force) {
+ swgBladeRF2MIMOSettings->setTxTransverterMode(settings.m_txTransverterMode ? 1 : 0);
+ }
+
+ QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings")
+ .arg(settings.m_reverseAPIAddress)
+ .arg(settings.m_reverseAPIPort)
+ .arg(settings.m_reverseAPIDeviceIndex);
+ m_networkRequest.setUrl(QUrl(deviceSettingsURL));
+ m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+
+ QBuffer *buffer=new QBuffer();
+ buffer->open((QBuffer::ReadWrite));
+ buffer->write(swgDeviceSettings->asJson().toUtf8());
+ buffer->seek(0);
+
+ // Always use PATCH to avoid passing reverse API settings
+ m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
+
+ delete swgDeviceSettings;
+}
+
+void BladeRF2MIMO::webapiReverseSendStartStop(bool start)
+{
+ SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
+ swgDeviceSettings->setDirection(2); // MIMO
+ swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
+ swgDeviceSettings->setDeviceHwType(new QString("BladeRF2"));
+
+ QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run")
+ .arg(m_settings.m_reverseAPIAddress)
+ .arg(m_settings.m_reverseAPIPort)
+ .arg(m_settings.m_reverseAPIDeviceIndex);
+ m_networkRequest.setUrl(QUrl(deviceSettingsURL));
+ m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+
+ QBuffer *buffer=new QBuffer();
+ buffer->open((QBuffer::ReadWrite));
+ buffer->write(swgDeviceSettings->asJson().toUtf8());
+ buffer->seek(0);
+
+ if (start) {
+ m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer);
+ } else {
+ m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer);
+ }
+
+ delete swgDeviceSettings;
+}
+
+void BladeRF2MIMO::networkManagerFinished(QNetworkReply *reply)
+{
+ QNetworkReply::NetworkError replyError = reply->error();
+
+ if (replyError)
+ {
+ qWarning() << "BladeRF2MIMO::networkManagerFinished:"
+ << " error(" << (int) replyError
+ << "): " << replyError
+ << ": " << reply->errorString();
+ return;
+ }
+
+ QString answer = reply->readAll();
+ answer.chop(1); // remove last \n
+ qDebug("BladeRF2MIMO::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
+}
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimo.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimo.h
new file mode 100644
index 000000000..212831012
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimo.h
@@ -0,0 +1,250 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMO_H_
+#define PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMO_H_
+
+#include
+
+#include
+#include
+#include
+
+#include "dsp/devicesamplemimo.h"
+#include "bladerf2/devicebladerf2shared.h"
+#include "bladerf2mimosettings.h"
+
+class QNetworkAccessManager;
+class QNetworkReply;
+class DeviceAPI;
+class BladeRF2MIThread;
+class BladeRF2MOThread;
+class FileRecord;
+class DeviceBladeRF2;
+struct bladerf_gain_modes;
+struct bladerf;
+
+class BladeRF2MIMO : public DeviceSampleMIMO {
+ Q_OBJECT
+
+public:
+ class MsgConfigureBladeRF2MIMO : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ const BladeRF2MIMOSettings& getSettings() const { return m_settings; }
+ bool getForce() const { return m_force; }
+
+ static MsgConfigureBladeRF2MIMO* create(const BladeRF2MIMOSettings& settings, bool force)
+ {
+ return new MsgConfigureBladeRF2MIMO(settings, force);
+ }
+
+ private:
+ BladeRF2MIMOSettings m_settings;
+ bool m_force;
+
+ MsgConfigureBladeRF2MIMO(const BladeRF2MIMOSettings& settings, bool force) :
+ Message(),
+ m_settings(settings),
+ m_force(force)
+ { }
+ };
+
+ class MsgFileRecord : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ bool getStartStop() const { return m_startStop; }
+ int getStreamIndex() const { return m_streamIndex; }
+
+ static MsgFileRecord* create(bool startStop, int streamIndex) {
+ return new MsgFileRecord(startStop, streamIndex);
+ }
+
+ protected:
+ bool m_startStop;
+ int m_streamIndex;
+
+ MsgFileRecord(bool startStop, int streamIndex) :
+ Message(),
+ m_startStop(startStop),
+ m_streamIndex(streamIndex)
+ { }
+ };
+
+ class MsgStartStop : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ bool getStartStop() const { return m_startStop; }
+ bool getRxElseTx() const { return m_rxElseTx; }
+
+ static MsgStartStop* create(bool startStop, bool rxElseTx) {
+ return new MsgStartStop(startStop, rxElseTx);
+ }
+
+ protected:
+ bool m_startStop;
+ bool m_rxElseTx;
+
+ MsgStartStop(bool startStop, bool rxElseTx) :
+ Message(),
+ m_startStop(startStop),
+ m_rxElseTx(rxElseTx)
+ { }
+ };
+
+ struct GainMode
+ {
+ QString m_name;
+ int m_value;
+ };
+
+ class MsgReportGainRange : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ int getMin() const { return m_min; }
+ int getMax() const { return m_max; }
+ int getStep() const { return m_step; }
+ bool getRxElseTx() const { return m_rxElseTx; }
+
+ static MsgReportGainRange* create(int min, int max, int step, bool rxElseTx) {
+ return new MsgReportGainRange(min, max, step, rxElseTx);
+ }
+
+ protected:
+ int m_min;
+ int m_max;
+ int m_step;
+ bool m_rxElseTx;
+
+ MsgReportGainRange(int min, int max, int step, bool rxElseTx) :
+ Message(),
+ m_min(min),
+ m_max(max),
+ m_step(step),
+ m_rxElseTx(rxElseTx)
+ {}
+ };
+
+ BladeRF2MIMO(DeviceAPI *deviceAPI);
+ virtual ~BladeRF2MIMO();
+ 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 getSourceSampleRate(int index) const;
+ virtual void setSourceSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; }
+ virtual quint64 getSourceCenterFrequency(int index) const;
+ virtual void setSourceCenterFrequency(qint64 centerFrequency, int index);
+
+ virtual int getSinkSampleRate(int index) const;
+ virtual void setSinkSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; }
+ virtual quint64 getSinkCenterFrequency(int index) const;
+ virtual void setSinkCenterFrequency(qint64 centerFrequency, int index);
+
+ virtual quint64 getMIMOCenterFrequency() const { return getSourceCenterFrequency(0); }
+
+ 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);
+
+ static void webapiFormatDeviceSettings(
+ SWGSDRangel::SWGDeviceSettings& response,
+ const BladeRF2MIMOSettings& settings);
+
+ static void webapiUpdateDeviceSettings(
+ BladeRF2MIMOSettings& settings,
+ const QStringList& deviceSettingsKeys,
+ SWGSDRangel::SWGDeviceSettings& response);
+
+ bool isRecording(unsigned int istream) const;
+
+ void getRxFrequencyRange(uint64_t& min, uint64_t& max, int& step);
+ void getRxSampleRateRange(int& min, int& max, int& step);
+ void getRxBandwidthRange(int& min, int& max, int& step);
+ void getRxGlobalGainRange(int& min, int& max, int& step);
+ const std::vector& getRxGainModes() { return m_rxGainModes; }
+
+ void getTxFrequencyRange(uint64_t& min, uint64_t& max, int& step);
+ void getTxSampleRateRange(int& min, int& max, int& step);
+ void getTxBandwidthRange(int& min, int& max, int& step);
+ void getTxGlobalGainRange(int& min, int& max, int& step);
+
+private:
+ DeviceAPI *m_deviceAPI;
+ std::vector m_fileSinks; //!< File sinks to record device I/Q output
+ QMutex m_mutex;
+ BladeRF2MIMOSettings m_settings;
+ BladeRF2MIThread* m_sourceThread;
+ BladeRF2MOThread* m_sinkThread;
+ QString m_deviceDescription;
+ bool m_rxElseTx;
+ bool m_runningRx;
+ bool m_runningTx;
+ QNetworkAccessManager *m_networkManager;
+ QNetworkRequest m_networkRequest;
+
+ DeviceBladeRF2 *m_dev;
+ bool m_open;
+ std::vector m_rxGainModes;
+
+ bool openDevice();
+ void closeDevice();
+ void startRx();
+ void stopRx();
+ void startTx();
+ void stopTx();
+
+ bool applySettings(const BladeRF2MIMOSettings& settings, bool force);
+ bool setRxDeviceCenterFrequency(struct bladerf *dev, quint64 freq_hz, int loPpmTenths);
+ bool setTxDeviceCenterFrequency(struct bladerf *dev, quint64 freq_hz, int loPpmTenths);
+ void webapiReverseSendSettings(QList& deviceSettingsKeys, const BladeRF2MIMOSettings& settings, bool force);
+ void webapiReverseSendStartStop(bool start);
+
+private slots:
+ void networkManagerFinished(QNetworkReply *reply);
+};
+
+#endif // PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMO_H_
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp
new file mode 100644
index 000000000..103ff5f48
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp
@@ -0,0 +1,706 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include "plugin/pluginapi.h"
+#include "device/deviceapi.h"
+#include "device/deviceuiset.h"
+#include "gui/colormapper.h"
+#include "gui/glspectrum.h"
+#include "gui/crightclickenabler.h"
+#include "gui/basicdevicesettingsdialog.h"
+#include "dsp/dspengine.h"
+#include "dsp/dspdevicemimoengine.h"
+#include "dsp/dspcommands.h"
+#include "dsp/devicesamplestatic.h"
+#include "util/db.h"
+
+#include "mainwindow.h"
+
+#include "bladerf2mimo.h"
+#include "ui_bladerf2mimogui.h"
+#include "bladerf2mimogui.h"
+
+BladeRF2MIMOGui::BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent) :
+ QWidget(parent),
+ ui(new Ui::BladeRF2MIMOGui),
+ m_deviceUISet(deviceUISet),
+ m_settings(),
+ m_rxElseTx(true),
+ m_streamIndex(0),
+ m_spectrumRxElseTx(true),
+ m_spectrumStreamIndex(0),
+ m_doApplySettings(true),
+ m_forceSettings(true),
+ m_sampleMIMO(nullptr),
+ m_tickCount(0),
+ m_deviceSampleRate(3072000),
+ m_rxDeviceCenterFrequency(435000*1000),
+ m_txDeviceCenterFrequency(435000*1000),
+ m_lastEngineState(DeviceAPI::StNotStarted),
+ m_sampleRateMode(true)
+{
+ qDebug("BladeRF2MIMOGui::BladeRF2MIMOGui");
+ ui->setupUi(this);
+ m_sampleMIMO = (BladeRF2MIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO();
+
+ m_sampleMIMO->getRxFrequencyRange(m_fMinRx, m_fMaxRx, m_fStepRx);
+ m_sampleMIMO->getTxFrequencyRange(m_fMinTx, m_fMaxTx, m_fStepTx);
+ m_sampleMIMO->getRxBandwidthRange(m_bwMinRx, m_bwMaxRx, m_bwStepRx);
+ m_sampleMIMO->getTxBandwidthRange(m_bwMinTx, m_bwMaxTx, m_bwStepTx);
+
+ ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
+ ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
+ ui->bandwidth->setColorMapper(ColorMapper(ColorMapper::GrayYellow));
+
+ int minRx, maxRx, stepRx, minTx, maxTx, stepTx;
+ m_sampleMIMO->getRxSampleRateRange(minRx, maxRx, stepRx);
+ m_sampleMIMO->getTxSampleRateRange(minTx, maxTx, stepTx);
+ m_srMin = std::max(minRx, minTx);
+ m_srMax = std::min(maxRx, maxTx);
+
+ displayGainModes();
+ displaySettings();
+
+ connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
+ connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
+ m_statusTimer.start(500);
+
+ connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
+ m_sampleMIMO->setMessageQueueToGUI(&m_inputMessageQueue);
+
+ CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop);
+ connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &)));
+}
+
+BladeRF2MIMOGui::~BladeRF2MIMOGui()
+{
+ delete ui;
+}
+
+void BladeRF2MIMOGui::destroy()
+{
+ delete this;
+}
+
+void BladeRF2MIMOGui::setName(const QString& name)
+{
+ setObjectName(name);
+}
+
+QString BladeRF2MIMOGui::getName() const
+{
+ return objectName();
+}
+
+void BladeRF2MIMOGui::resetToDefaults()
+{
+ m_settings.resetToDefaults();
+ displaySettings();
+ sendSettings();
+}
+
+qint64 BladeRF2MIMOGui::getCenterFrequency() const
+{
+ return m_settings.m_rxCenterFrequency;
+}
+
+void BladeRF2MIMOGui::setCenterFrequency(qint64 centerFrequency)
+{
+ m_settings.m_rxCenterFrequency = centerFrequency;
+ displaySettings();
+ sendSettings();
+}
+
+QByteArray BladeRF2MIMOGui::serialize() const
+{
+ return m_settings.serialize();
+}
+
+bool BladeRF2MIMOGui::deserialize(const QByteArray& data)
+{
+ if(m_settings.deserialize(data)) {
+ displaySettings();
+ m_forceSettings = true;
+ sendSettings();
+ return true;
+ } else {
+ resetToDefaults();
+ return false;
+ }
+}
+
+void BladeRF2MIMOGui::displaySettings()
+{
+ if (m_rxElseTx)
+ {
+ ui->transverter->setDeltaFrequency(m_settings.m_rxTransverterDeltaFrequency);
+ ui->transverter->setDeltaFrequencyActive(m_settings.m_rxTransverterMode);
+ ui->centerFrequency->setValueRange(7, m_fMinRx / 1000, m_fMaxRx / 1000);
+ ui->centerFrequency->setValue(m_settings.m_rxCenterFrequency / 1000);
+ ui->bandwidth->setValueRange(5, m_bwMinRx / 1000, m_bwMaxRx / 1000);
+ ui->bandwidth->setValue(m_settings.m_rxBandwidth / 1000);
+ ui->record->setEnabled(true);
+ uint32_t basebandSampleRate = m_settings.m_devSampleRate/(1<deviceRateText->setText(tr("%1k").arg(QString::number(basebandSampleRate / 1000.0f, 'g', 5)));
+ ui->dcOffset->setEnabled(true);
+ ui->dcOffset->setChecked(m_settings.m_dcBlock);
+ ui->iqImbalance->setEnabled(true);
+ ui->iqImbalance->setChecked(m_settings.m_iqCorrection);
+ ui->biasTee->setChecked(m_settings.m_rxBiasTee);
+ ui->decim->setCurrentIndex(m_settings.m_log2Decim);
+ ui->label_decim->setText(QString("Dec"));
+ ui->decim->setToolTip(QString("Decimation factor"));
+ ui->gainMode->setEnabled(true);
+
+ if (m_streamIndex == 0)
+ {
+ ui->gainMode->setCurrentIndex(m_settings.m_rx0GainMode);
+ ui->gainText->setText(tr("%1 dB").arg(m_settings.m_rx0GlobalGain));
+ ui->gain->setValue(m_settings.m_rx0GlobalGain);
+ }
+ else if (m_streamIndex == 1)
+ {
+ ui->gainMode->setCurrentIndex(m_settings.m_rx1GainMode);
+ ui->gainText->setText(tr("%1 dB").arg(m_settings.m_rx1GlobalGain));
+ ui->gain->setValue(m_settings.m_rx1GlobalGain);
+ }
+ }
+ else
+ {
+ ui->transverter->setDeltaFrequency(m_settings.m_txTransverterDeltaFrequency);
+ ui->transverter->setDeltaFrequencyActive(m_settings.m_txTransverterMode);
+ ui->centerFrequency->setValueRange(7, m_fMinTx / 1000, m_fMaxTx / 1000);
+ ui->centerFrequency->setValue(m_settings.m_txCenterFrequency / 1000);
+ ui->bandwidth->setValueRange(5, m_bwMinTx / 1000, m_bwMaxTx / 1000);
+ ui->bandwidth->setValue(m_settings.m_txBandwidth / 1000);
+ ui->record->setEnabled(false);
+ uint32_t basebandSampleRate = m_settings.m_devSampleRate/(1<deviceRateText->setText(tr("%1k").arg(QString::number(basebandSampleRate / 1000.0f, 'g', 5)));
+ ui->dcOffset->setEnabled(false);
+ ui->iqImbalance->setEnabled(false);
+ ui->biasTee->setChecked(m_settings.m_txBiasTee);
+ ui->decim->setCurrentIndex(m_settings.m_log2Interp);
+ ui->label_decim->setText(QString("Int"));
+ ui->decim->setToolTip(QString("Interpolation factor"));
+ ui->gainMode->setEnabled(false);
+
+ if (m_streamIndex == 0)
+ {
+ ui->gainText->setText(tr("%1 dB").arg(m_settings.m_tx0GlobalGain));
+ ui->gain->setValue(m_settings.m_tx0GlobalGain);
+ }
+ else if (m_streamIndex == 1)
+ {
+ ui->gainText->setText(tr("%1 dB").arg(m_settings.m_tx1GlobalGain));
+ ui->gain->setValue(m_settings.m_tx1GlobalGain);
+ }
+ }
+
+ ui->sampleRate->setValue(m_settings.m_devSampleRate);
+ ui->LOppm->setValue(m_settings.m_LOppmTenths);
+ ui->LOppmText->setText(QString("%1").arg(QString::number(m_settings.m_LOppmTenths/10.0, 'f', 1)));
+ ui->fcPos->setCurrentIndex((int) m_settings.m_fcPos);
+
+ displaySampleRate();
+}
+
+void BladeRF2MIMOGui::displaySampleRate()
+{
+ ui->sampleRate->blockSignals(true);
+ displayFcTooltip();
+ quint32 log2Factor = m_rxElseTx ? m_settings.m_log2Decim : m_settings.m_log2Interp;
+
+ if (m_sampleRateMode)
+ {
+ ui->sampleRateMode->setStyleSheet("QToolButton { background:rgb(60,60,60); }");
+ ui->sampleRateMode->setText("SR");
+ // 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, m_srMin, m_srMax);
+ ui->sampleRate->setValue(m_settings.m_devSampleRate);
+ ui->sampleRate->setToolTip("Device to host sample rate (S/s)");
+ ui->deviceRateText->setToolTip("Baseband sample rate (S/s)");
+ uint32_t basebandSampleRate = m_settings.m_devSampleRate/(1<deviceRateText->setText(tr("%1k").arg(QString::number(basebandSampleRate / 1000.0f, 'g', 5)));
+ }
+ else
+ {
+ ui->sampleRateMode->setStyleSheet("QToolButton { background:rgb(50,50,50); }");
+ ui->sampleRateMode->setText("BB");
+ // 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, m_srMin/(1<sampleRate->setValue(m_settings.m_devSampleRate/(1<sampleRate->setToolTip("Baseband sample rate (S/s)");
+ ui->deviceRateText->setToolTip("Device to host sample rate (S/s)");
+ ui->deviceRateText->setText(tr("%1k").arg(QString::number(m_settings.m_devSampleRate / 1000.0f, 'g', 5)));
+ }
+
+ ui->sampleRate->blockSignals(false);
+}
+
+void BladeRF2MIMOGui::displayFcTooltip()
+{
+ int32_t fShift;
+
+ if (m_rxElseTx)
+ {
+ fShift = DeviceSampleStatic::calculateSourceFrequencyShift(
+ m_settings.m_log2Decim,
+ (DeviceSampleStatic::fcPos_t) m_settings.m_fcPos,
+ m_settings.m_devSampleRate,
+ DeviceSampleStatic::FrequencyShiftScheme::FSHIFT_STD
+ );
+ }
+ else
+ {
+ fShift = DeviceSampleStatic::calculateSinkFrequencyShift(
+ m_settings.m_log2Decim,
+ (DeviceSampleStatic::fcPos_t) m_settings.m_fcPos,
+ m_settings.m_devSampleRate
+ );
+ }
+
+ ui->fcPos->setToolTip(tr("Relative position of device center frequency: %1 kHz").arg(QString::number(fShift / 1000.0f, 'g', 5)));
+}
+
+void BladeRF2MIMOGui::displayGainModes()
+{
+ ui->gainMode->blockSignals(true);
+
+ if (m_rxElseTx)
+ {
+ const std::vector& modes = m_sampleMIMO->getRxGainModes();
+ std::vector::const_iterator it = modes.begin();
+
+ for (; it != modes.end(); ++it) {
+ ui->gainMode->addItem(it->m_name);
+ }
+ }
+ else
+ {
+ ui->gainMode->clear();
+ }
+
+ ui->gainMode->blockSignals(false);
+}
+
+bool BladeRF2MIMOGui::handleMessage(const Message& message)
+{
+ if (DSPMIMOSignalNotification::match(message))
+ {
+ const DSPMIMOSignalNotification& notif = (const DSPMIMOSignalNotification&) message;
+ int istream = notif.getIndex();
+ bool sourceOrSink = notif.getSourceOrSink();
+ m_deviceSampleRate = notif.getSampleRate();
+
+ if (sourceOrSink) {
+ m_rxDeviceCenterFrequency = notif.getCenterFrequency();
+ } else {
+ m_txDeviceCenterFrequency = notif.getCenterFrequency();
+ }
+
+ qDebug("BladeRF2MIMOGui::handleInputMessages: DSPMIMOSignalNotification: %s stream: %d SampleRate:%d, CenterFrequency:%llu",
+ sourceOrSink ? "source" : "sink",
+ istream,
+ notif.getSampleRate(),
+ notif.getCenterFrequency());
+
+ updateSampleRateAndFrequency();
+
+ return true;
+ }
+
+ return false;
+}
+
+void BladeRF2MIMOGui::handleInputMessages()
+{
+ Message* message;
+
+ while ((message = m_inputMessageQueue.pop()) != 0)
+ {
+ if (handleMessage(*message)) {
+ delete message;
+ } else {
+ qDebug("BladeRF2MIMOGui::handleInputMessages: unhandled message: %s", message->getIdentifier());
+ }
+ }
+}
+
+void BladeRF2MIMOGui::sendSettings()
+{
+ if(!m_updateTimer.isActive()) {
+ m_updateTimer.start(100);
+ }
+}
+
+void BladeRF2MIMOGui::updateHardware()
+{
+ if (m_doApplySettings)
+ {
+ BladeRF2MIMO::MsgConfigureBladeRF2MIMO* message = BladeRF2MIMO::MsgConfigureBladeRF2MIMO::create(m_settings, m_forceSettings);
+ m_sampleMIMO->getInputMessageQueue()->push(message);
+ m_forceSettings = false;
+ m_updateTimer.stop();
+ }
+}
+
+void BladeRF2MIMOGui::updateSampleRateAndFrequency()
+{
+ m_deviceUISet->getSpectrum()->setSampleRate(m_deviceSampleRate);
+
+ if (m_rxElseTx) {
+ m_deviceUISet->getSpectrum()->setCenterFrequency(m_rxDeviceCenterFrequency);
+ } else {
+ m_deviceUISet->getSpectrum()->setCenterFrequency(m_txDeviceCenterFrequency);
+ }
+}
+
+void BladeRF2MIMOGui::updateFileRecordStatus()
+{
+ if (m_sampleMIMO->isRecording(m_streamIndex)) {
+ ui->record->setStyleSheet("QToolButton { background-color : red; }");
+ } else {
+ ui->record->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
+ }
+}
+
+void BladeRF2MIMOGui::on_streamSide_currentIndexChanged(int index)
+{
+ m_rxElseTx = index == 0;
+ displayGainModes();
+ displaySettings();
+}
+
+void BladeRF2MIMOGui::on_streamIndex_currentIndexChanged(int index)
+{
+ m_streamIndex = index < 0 ? 0 : index > 1 ? 1 : index;
+ displaySettings();
+}
+
+void BladeRF2MIMOGui::on_spectrumSide_currentIndexChanged(int index)
+{
+ m_spectrumRxElseTx = index == 0;
+ // TODO
+}
+
+void BladeRF2MIMOGui::on_spectrumIndex_currentIndexChanged(int index)
+{
+ m_spectrumStreamIndex = index < 0 ? 0 : index > 1 ? 1 : index;
+ m_deviceUISet->m_spectrum->setDisplayedStream(true, m_spectrumStreamIndex);
+ m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(true, m_spectrumStreamIndex);
+ updateSampleRateAndFrequency();
+}
+
+void BladeRF2MIMOGui::on_startStop_toggled(bool checked)
+{
+ if (m_doApplySettings)
+ {
+ BladeRF2MIMO::MsgStartStop *message = BladeRF2MIMO::MsgStartStop::create(checked, m_rxElseTx);
+ m_sampleMIMO->getInputMessageQueue()->push(message);
+ }
+}
+
+void BladeRF2MIMOGui::on_record_toggled(bool checked)
+{
+ if (checked) {
+ ui->record->setStyleSheet("QToolButton { background-color : red; }");
+ } else {
+ ui->record->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
+ }
+
+ BladeRF2MIMO::MsgFileRecord* message = BladeRF2MIMO::MsgFileRecord::create(checked, m_streamIndex);
+ m_sampleMIMO->getInputMessageQueue()->push(message);
+}
+
+void BladeRF2MIMOGui::on_centerFrequency_changed(quint64 value)
+{
+ if (m_rxElseTx) {
+ m_settings.m_rxCenterFrequency = value * 1000;
+ } else {
+ m_settings.m_txCenterFrequency = value * 1000;
+ }
+
+ sendSettings();
+}
+
+void BladeRF2MIMOGui::on_LOppm_valueChanged(int value)
+{
+ ui->LOppmText->setText(QString("%1").arg(QString::number(value/10.0, 'f', 1)));
+ m_settings.m_LOppmTenths = value;
+ sendSettings();
+}
+
+void BladeRF2MIMOGui::on_dcOffset_toggled(bool checked)
+{
+ m_settings.m_dcBlock = checked;
+ sendSettings();
+}
+
+void BladeRF2MIMOGui::on_iqImbalance_toggled(bool checked)
+{
+ m_settings.m_iqCorrection = checked;
+ sendSettings();
+}
+
+void BladeRF2MIMOGui::on_bandwidth_changed(quint64 value)
+{
+ if (m_rxElseTx) {
+ m_settings.m_rxBandwidth = value * 1000;
+ } else {
+ m_settings.m_txBandwidth = value * 1000;
+ }
+
+ sendSettings();
+}
+
+void BladeRF2MIMOGui::on_sampleRate_changed(quint64 value)
+{
+ if (m_sampleRateMode)
+ {
+ m_settings.m_devSampleRate = value;
+ }
+ else
+ {
+ if (m_rxElseTx) {
+ m_settings.m_devSampleRate = value * (1 << m_settings.m_log2Decim);
+ } else {
+ m_settings.m_devSampleRate = value * (1 << m_settings.m_log2Interp);
+ }
+ }
+
+ displayFcTooltip();
+ sendSettings();
+}
+
+void BladeRF2MIMOGui::on_fcPos_currentIndexChanged(int index)
+{
+ m_settings.m_fcPos = (BladeRF2MIMOSettings::fcPos_t) (index < 0 ? 0 : index > 2 ? 2 : index);
+ displayFcTooltip();
+ sendSettings();
+}
+
+void BladeRF2MIMOGui::on_decim_currentIndexChanged(int index)
+{
+ if ((index <0) || (index > 6)) {
+ return;
+ }
+
+ if (m_rxElseTx) {
+ m_settings.m_log2Decim = index;
+ } else {
+ m_settings.m_log2Interp = index;
+ }
+
+ displaySampleRate();
+
+ if (m_sampleRateMode) {
+ m_settings.m_devSampleRate = ui->sampleRate->getValueNew();
+ } else {
+ m_settings.m_devSampleRate = ui->sampleRate->getValueNew() * (1 << (m_rxElseTx ? m_settings.m_log2Decim : m_settings.m_log2Interp));
+ }
+
+ sendSettings();
+}
+
+void BladeRF2MIMOGui::on_gainMode_currentIndexChanged(int index)
+{
+ if (!m_rxElseTx) { // not for Tx
+ return;
+ }
+
+ const std::vector& modes = m_sampleMIMO->getRxGainModes();
+ unsigned int uindex = index < 0 ? 0 : (unsigned int) index;
+
+ if (uindex < modes.size())
+ {
+ BladeRF2MIMO::GainMode mode = modes[index];
+
+ if (m_streamIndex == 0)
+ {
+ if (m_settings.m_rx0GainMode != mode.m_value)
+ {
+ if (mode.m_value == BLADERF_GAIN_MANUAL)
+ {
+ m_settings.m_rx0GlobalGain = ui->gain->value();
+ ui->gain->setEnabled(true);
+ } else {
+ ui->gain->setEnabled(false);
+ }
+ }
+
+ m_settings.m_rx0GainMode = mode.m_value;
+ }
+ else if (m_streamIndex == 1)
+ {
+ if (m_settings.m_rx1GainMode != mode.m_value)
+ {
+ if (mode.m_value == BLADERF_GAIN_MANUAL)
+ {
+ m_settings.m_rx1GlobalGain = ui->gain->value();
+ ui->gain->setEnabled(true);
+ } else {
+ ui->gain->setEnabled(false);
+ }
+ }
+
+ m_settings.m_rx1GainMode = mode.m_value;
+ }
+
+ sendSettings();
+ }
+}
+
+void BladeRF2MIMOGui::on_gain_valueChanged(int value)
+{
+ ui->gainText->setText(tr("%1 dB").arg(value));
+
+ if (m_rxElseTx)
+ {
+ if (m_streamIndex == 0) {
+ m_settings.m_rx0GlobalGain = value;
+ } else {
+ m_settings.m_rx1GlobalGain = value;
+ }
+ }
+ else
+ {
+ if (m_streamIndex == 0) {
+ m_settings.m_tx0GlobalGain = value;
+ } else {
+ m_settings.m_tx1GlobalGain = value;
+ }
+ }
+
+ sendSettings();
+}
+
+void BladeRF2MIMOGui::on_biasTee_toggled(bool checked)
+{
+ if (m_rxElseTx) {
+ m_settings.m_rxBiasTee = checked;
+ } else {
+ m_settings.m_txBiasTee = checked;
+ }
+
+ sendSettings();
+}
+
+void BladeRF2MIMOGui::on_transverter_clicked()
+{
+ if (m_rxElseTx)
+ {
+ m_settings.m_rxTransverterMode = ui->transverter->getDeltaFrequencyAcive();
+ m_settings.m_rxTransverterDeltaFrequency = ui->transverter->getDeltaFrequency();
+ qDebug("BladeRF2InputGui::on_transverter_clicked: Rx: %lld Hz %s", m_settings.m_rxTransverterDeltaFrequency, m_settings.m_rxTransverterMode ? "on" : "off");
+ }
+ else
+ {
+ m_settings.m_txTransverterMode = ui->transverter->getDeltaFrequencyAcive();
+ m_settings.m_txTransverterDeltaFrequency = ui->transverter->getDeltaFrequency();
+ qDebug("BladeRF2InputGui::on_transverter_clicked: Tx: %lld Hz %s", m_settings.m_txTransverterDeltaFrequency, m_settings.m_txTransverterMode ? "on" : "off");
+ }
+
+ updateFrequencyLimits();
+ setCenterFrequencySetting(ui->centerFrequency->getValueNew());
+ sendSettings();
+}
+
+void BladeRF2MIMOGui::updateFrequencyLimits()
+{
+ // values in kHz
+ uint64_t f_min, f_max;
+ int step;
+
+ if (m_rxElseTx)
+ {
+ qint64 deltaFrequency = m_settings.m_rxTransverterMode ? m_settings.m_rxTransverterDeltaFrequency/1000 : 0;
+ m_sampleMIMO->getRxFrequencyRange(f_min, f_max, step);
+ qint64 minLimit = f_min/1000 + deltaFrequency;
+ qint64 maxLimit = f_max/1000 + deltaFrequency;
+
+ minLimit = minLimit < 0 ? 0 : minLimit > 9999999 ? 9999999 : minLimit;
+ maxLimit = maxLimit < 0 ? 0 : maxLimit > 9999999 ? 9999999 : maxLimit;
+
+ qDebug("BladeRF2MIMOGui::updateFrequencyLimits: Rx: delta: %lld min: %lld max: %lld", deltaFrequency, minLimit, maxLimit);
+
+ ui->centerFrequency->setValueRange(7, minLimit, maxLimit);
+ }
+ else
+ {
+ qint64 deltaFrequency = m_settings.m_txTransverterMode ? m_settings.m_txTransverterDeltaFrequency/1000 : 0;
+ m_sampleMIMO->getRxFrequencyRange(f_min, f_max, step);
+ qint64 minLimit = f_min/1000 + deltaFrequency;
+ qint64 maxLimit = f_max/1000 + deltaFrequency;
+
+ minLimit = minLimit < 0 ? 0 : minLimit > 9999999 ? 9999999 : minLimit;
+ maxLimit = maxLimit < 0 ? 0 : maxLimit > 9999999 ? 9999999 : maxLimit;
+
+ qDebug("BladeRF2MIMOGui::updateFrequencyLimits: Rx: delta: %lld min: %lld max: %lld", deltaFrequency, minLimit, maxLimit);
+
+ ui->centerFrequency->setValueRange(7, minLimit, maxLimit);
+ }
+}
+
+void BladeRF2MIMOGui::setCenterFrequencySetting(uint64_t kHzValue)
+{
+ int64_t centerFrequency = kHzValue*1000;
+
+ if (m_rxElseTx) {
+ m_settings.m_rxCenterFrequency = centerFrequency < 0 ? 0 : (uint64_t) centerFrequency;
+ } else {
+ m_settings.m_txCenterFrequency = centerFrequency < 0 ? 0 : (uint64_t) centerFrequency;
+ }
+
+ ui->centerFrequency->setToolTip(QString("Main center frequency in kHz (LO: %1 kHz)").arg(centerFrequency/1000));
+}
+
+void BladeRF2MIMOGui::updateStatus()
+{
+ int state = m_deviceUISet->m_deviceAPI->state();
+
+ if(m_lastEngineState != state)
+ {
+ switch(state)
+ {
+ case DeviceAPI::StNotStarted:
+ ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
+ break;
+ case DeviceAPI::StIdle:
+ ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
+ break;
+ case DeviceAPI::StRunning:
+ ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
+ break;
+ case DeviceAPI::StError:
+ ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
+ QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage());
+ break;
+ default:
+ break;
+ }
+
+ m_lastEngineState = state;
+ }
+}
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h
new file mode 100644
index 000000000..d5465a895
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h
@@ -0,0 +1,119 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef _BLADERF2MIMO_BLADERF2MIMOGUI_H_
+#define _BLADERF2MIMO_BLADERF2MIMOGUI_H_
+
+#include
+#include
+
+#include "util/messagequeue.h"
+#include "plugin/plugininstancegui.h"
+
+#include "bladerf2mimosettings.h"
+
+class DeviceUISet;
+class BladeRF2MIMO;
+
+namespace Ui {
+ class BladeRF2MIMOGui;
+}
+
+class BladeRF2MIMOGui : public QWidget, public PluginInstanceGUI {
+ Q_OBJECT
+public:
+ explicit BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent = nullptr);
+ virtual ~BladeRF2MIMOGui();
+ 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::BladeRF2MIMOGui* ui;
+
+ DeviceUISet* m_deviceUISet;
+ BladeRF2MIMOSettings m_settings;
+ bool m_rxElseTx; //!< Which side is being dealt with
+ int m_streamIndex; //!< Current stream index being dealt with
+ bool m_spectrumRxElseTx;
+ int m_spectrumStreamIndex; //!< Index of the stream displayed on main spectrum
+ QTimer m_updateTimer;
+ QTimer m_statusTimer;
+ bool m_doApplySettings;
+ bool m_forceSettings;
+ BladeRF2MIMO* m_sampleMIMO;
+ std::size_t m_tickCount;
+ int m_deviceSampleRate;
+ quint64 m_rxDeviceCenterFrequency; //!< Center frequency in Rx device
+ quint64 m_txDeviceCenterFrequency; //!< Center frequency in Tx device
+ int m_lastEngineState;
+ MessageQueue m_inputMessageQueue;
+
+ bool m_sampleRateMode;
+ int m_srMax, m_srMin, m_srStep;
+ int m_bwMaxRx, m_bwMinRx, m_bwStepRx;
+ int m_bwMaxTx, m_bwMinTx, m_bwStepTx;
+ uint64_t m_fMinRx, m_fMaxRx;
+ uint64_t m_fMinTx, m_fMaxTx;
+ int m_fStepRx, m_fStepTx;
+
+ void blockApplySettings(bool block) { m_doApplySettings = !block; }
+ void displaySettings();
+ void displaySampleRate();
+ void displayFcTooltip();
+ void displayGainModes();
+ void sendSettings();
+ void updateSampleRateAndFrequency();
+ void updateFileRecordStatus();
+ void updateFrequencyLimits();
+ void setCenterFrequencySetting(uint64_t kHzValue);
+
+private slots:
+ void handleInputMessages();
+ void updateHardware();
+ void updateStatus();
+ void openDeviceSettingsDialog(const QPoint& p);
+ void on_streamSide_currentIndexChanged(int index);
+ void on_streamIndex_currentIndexChanged(int index);
+ void on_spectrumSide_currentIndexChanged(int index);
+ void on_spectrumIndex_currentIndexChanged(int index);
+ void on_startStop_toggled(bool checked);
+ void on_record_toggled(bool checked);
+ void on_centerFrequency_changed(quint64 value);
+ void on_LOppm_valueChanged(int value);
+ void on_dcOffset_toggled(bool checked);
+ void on_iqImbalance_toggled(bool checked);
+ void on_bandwidth_changed(quint64 value);
+ void on_sampleRate_changed(quint64 value);
+ void on_fcPos_currentIndexChanged(int index);
+ void on_decim_currentIndexChanged(int index);
+ void on_gainMode_currentIndexChanged(int index);
+ void on_gain_valueChanged(int value);
+ void on_biasTee_toggled(bool checked);
+ void on_transverter_clicked();
+};
+
+#endif // _BLADERF2MIMO_BLADERF2MIMOGUI_H_
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui
new file mode 100644
index 000000000..375271bb8
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui
@@ -0,0 +1,765 @@
+
+
+ BladeRF2MIMOGui
+
+
+
+ 0
+ 0
+ 350
+ 220
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 350
+ 220
+
+
+
+
+ Liberation Sans
+ 9
+
+
+
+ BladeRF2
+
+
+
+ 3
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+ -
+
+
+ 6
+
+
-
+
+
+
+ 16777215
+ 22
+
+
+
+
+
+
+ :/antenna.png
+
+
+
+ -
+
+
+
+ 45
+ 16777215
+
+
+
+ Select Rx or Tx settings
+
+
-
+
+ Rx
+
+
+ -
+
+ Tx
+
+
+
+
+ -
+
+
+
+ 35
+ 16777215
+
+
+
+ Select stream index to which settings apply
+
+
-
+
+ 0
+
+
+ -
+
+ 1
+
+
+
+
+ -
+
+
+
+ 16777215
+ 22
+
+
+
+
+
+
+ :/dsb.png
+
+
+
+ -
+
+
+
+ 45
+ 0
+
+
+
+
+ 45
+ 16777215
+
+
+
+ Select Rx or Tx spectrum
+
+
-
+
+ Rx
+
+
+ -
+
+ Tx
+
+
+
+
+ -
+
+
+
+ 35
+ 16777215
+
+
+
+ Select which stream index to display spectrum
+
+
-
+
+ 0
+
+
+ -
+
+ 1
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ 4
+
+
-
+
+
-
+
+
-
+
+
+ start/stop acquisition
+
+
+
+
+
+
+ :/play.png
+ :/stop.png:/play.png
+
+
+
+ -
+
+
+ Toggle record I/Q samples from device
+
+
+
+
+
+
+ :/record_off.png
+ :/record_on.png:/record_off.png
+
+
+
+
+
+ -
+
+
-
+
+
+ I/Q sample rate kS/s
+
+
+ 00000k
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 16
+
+
+
+
+ Liberation Mono
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Qt::StrongFocus
+
+
+ Tuner center frequency in kHz
+
+
+
+ -
+
+
+ kHz
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ -
+
+
-
+
+
+ LO ppm
+
+
+
+ -
+
+
+ Local Oscillator ppm correction
+
+
+ -20
+
+
+ 20
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 26
+ 0
+
+
+
+ -0.0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
+
+
-
+
+
+ Automatic IQ imbalance correction (Rx)
+
+
+ IQ
+
+
+
+ -
+
+
+ Automatic DC offset removal (Rx)
+
+
+ DC
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Auto
+
+
+
+ -
+
+
+ kHz
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ BW
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 16
+
+
+
+
+ Liberation Mono
+ 12
+
+
+
+ PointingHandCursor
+
+
+ RF bandwidth
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Transverter frequency translation dialog
+
+
+ X
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 2
+
+
+ 2
+
+
-
+
+
+
+ 24
+ 0
+
+
+
+
+ 24
+ 16777215
+
+
+
+ Toggle between device to host (SR) and base band (BB) sample rate input
+
+
+ SR
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 16
+
+
+
+
+ Liberation Mono
+ 12
+
+
+
+ PointingHandCursor
+
+
+ Device sample rate
+
+
+
+ -
+
+
+ S/s
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Fp
+
+
+
+ -
+
+
+ Relative position of device center frequency (Rx)
+
+
-
+
+ Inf
+
+
+ -
+
+ Sup
+
+
+ -
+
+ Cen
+
+
+
+
+ -
+
+
+ Dec
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ Decimation factor
+
+
+ 0
+
+
-
+
+ 1
+
+
+ -
+
+ 2
+
+
+ -
+
+ 4
+
+
+ -
+
+ 8
+
+
+ -
+
+ 16
+
+
+ -
+
+ 32
+
+
+ -
+
+ 64
+
+
+
+
+
+
+ -
+
+
+ 3
+
+
-
+
+
+ Gain value
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Gain mode
+
+
+
+ -
+
+
+ Gain
+
+
+
+ -
+
+
+
+ 45
+ 0
+
+
+
+ 000 dB
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Bias Tee
+
+
+ BT
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+ ValueDial
+ QWidget
+
+ 1
+
+
+ ButtonSwitch
+ QToolButton
+
+
+
+ TransverterButton
+ QPushButton
+
+
+
+
+
+
+
+
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimoplugin.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimoplugin.cpp
new file mode 100644
index 000000000..d162e5ef5
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimoplugin.cpp
@@ -0,0 +1,144 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+
+#include "plugin/pluginapi.h"
+#include "util/simpleserializer.h"
+
+#ifdef SERVER_MODE
+#include "bladerf2mimo.h"
+#else
+#include "bladerf2mimogui.h"
+#include "bladerf2mimo.h" // TODO
+#endif
+#include "bladerf2mimoplugin.h"
+//#include "testmiwebapiadapter.h"
+
+const PluginDescriptor BladeRF2MIMOPlugin::m_pluginDescriptor = {
+ QString("BladeRF2 MIMO"),
+ QString("4.12.0"),
+ QString("(c) Edouard Griffiths, F4EXB"),
+ QString("https://github.com/f4exb/sdrangel"),
+ true,
+ QString("https://github.com/f4exb/sdrangel")
+};
+
+const QString BladeRF2MIMOPlugin::m_hardwareID = "BladeRF2";
+const QString BladeRF2MIMOPlugin::m_deviceTypeID = BLADERF2MIMO_DEVICE_TYPE_ID;
+
+BladeRF2MIMOPlugin::BladeRF2MIMOPlugin(QObject* parent) :
+ QObject(parent)
+{
+}
+
+const PluginDescriptor& BladeRF2MIMOPlugin::getPluginDescriptor() const
+{
+ return m_pluginDescriptor;
+}
+
+void BladeRF2MIMOPlugin::initPlugin(PluginAPI* pluginAPI)
+{
+ pluginAPI->registerSampleMIMO(m_deviceTypeID, this);
+}
+
+void BladeRF2MIMOPlugin::enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices)
+{
+ if (listedHwIds.contains(m_hardwareID)) { // check if it was done
+ return;
+ }
+
+ DeviceBladeRF2::enumOriginDevices(m_hardwareID, originDevices);
+ listedHwIds.append(m_hardwareID);
+}
+
+PluginInterface::SamplingDevices BladeRF2MIMOPlugin::enumSampleMIMO(const OriginDevices& originDevices)
+{
+ SamplingDevices result;
+
+ for (OriginDevices::const_iterator it = originDevices.begin(); it != originDevices.end(); ++it)
+ {
+ if (it->hardwareId == m_hardwareID)
+ {
+ QString displayedName = it->displayableName;
+ displayedName.replace(QString(":$1]"), QString("]"));
+ result.append(SamplingDevice(
+ displayedName,
+ m_hardwareID,
+ m_deviceTypeID,
+ it->serial,
+ it->sequence,
+ PluginInterface::SamplingDevice::PhysicalDevice,
+ PluginInterface::SamplingDevice::StreamMIMO,
+ 1,
+ 0
+ ));
+ }
+ }
+
+ return result;
+}
+
+#ifdef SERVER_MODE
+PluginInstanceGUI* BladeRF2MIMOPlugin::createSampleMIMOPluginInstanceGUI(
+ const QString& sourceId,
+ QWidget **widget,
+ DeviceUISet *deviceUISet)
+{
+ (void) sourceId;
+ (void) widget;
+ (void) deviceUISet;
+ return nullptr;
+}
+#else
+PluginInstanceGUI* BladeRF2MIMOPlugin::createSampleMIMOPluginInstanceGUI(
+ const QString& sourceId,
+ QWidget **widget,
+ DeviceUISet *deviceUISet)
+{
+ if (sourceId == m_deviceTypeID)
+ {
+ BladeRF2MIMOGui* gui = new BladeRF2MIMOGui(deviceUISet);
+ *widget = gui;
+ return gui;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+#endif
+
+DeviceSampleMIMO *BladeRF2MIMOPlugin::createSampleMIMOPluginInstance(const QString& mimoId, DeviceAPI *deviceAPI)
+{
+ if (mimoId == m_deviceTypeID)
+ {
+ BladeRF2MIMO* input = new BladeRF2MIMO(deviceAPI);
+ return input;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+DeviceWebAPIAdapter *BladeRF2MIMOPlugin::createDeviceWebAPIAdapter() const
+{
+ // TODO
+ //return new BladeRF2MIMOWebAPIAdapter();
+ return nullptr;
+}
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimoplugin.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimoplugin.h
new file mode 100644
index 000000000..9bd39154f
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimoplugin.h
@@ -0,0 +1,56 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef _BLADERF2MIMO_BLADERF2MIMOPLUGIN_H
+#define _BLADERF2MIMO_BLADERF2MIMOPLUGIN_H
+
+#include
+#include "plugin/plugininterface.h"
+
+class PluginAPI;
+
+#define BLADERF2MIMO_DEVICE_TYPE_ID "sdrangel.samplemimo.bladerf2mimo"
+
+class BladeRF2MIMOPlugin : public QObject, public PluginInterface {
+ Q_OBJECT
+ Q_INTERFACES(PluginInterface)
+ Q_PLUGIN_METADATA(IID BLADERF2MIMO_DEVICE_TYPE_ID)
+
+public:
+ explicit BladeRF2MIMOPlugin(QObject* parent = nullptr);
+
+ const PluginDescriptor& getPluginDescriptor() const;
+ void initPlugin(PluginAPI* pluginAPI);
+
+ virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices);
+ virtual SamplingDevices enumSampleMIMO(const OriginDevices& originDevices);
+
+ virtual PluginInstanceGUI* createSampleMIMOPluginInstanceGUI(
+ const QString& sourceId,
+ QWidget **widget,
+ DeviceUISet *deviceUISet);
+ virtual DeviceSampleMIMO* createSampleMIMOPluginInstance(const QString& sourceId, DeviceAPI *deviceAPI);
+ virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const;
+
+ static const QString m_hardwareID;
+ static const QString m_deviceTypeID;
+
+private:
+ static const PluginDescriptor m_pluginDescriptor;
+};
+
+#endif // _BLADERF2MIMO_BLADERF2MIMOPLUGIN_H
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp
new file mode 100644
index 000000000..59902a876
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp
@@ -0,0 +1,166 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "bladerf2mimosettings.h"
+
+#include "util/simpleserializer.h"
+
+BladeRF2MIMOSettings::BladeRF2MIMOSettings()
+{
+ resetToDefaults();
+}
+
+void BladeRF2MIMOSettings::resetToDefaults()
+{
+ m_devSampleRate = 3072000;
+ m_LOppmTenths = 0;
+
+ m_rxCenterFrequency = 435000*1000;
+ m_log2Decim = 0;
+ m_fcPos = FC_POS_INFRA;
+ m_rxBandwidth = 1500000;
+ m_rx0GainMode = 0;
+ m_rx0GlobalGain = 0;
+ m_rx1GainMode = 0;
+ m_rx1GlobalGain = 0;
+ m_rxBiasTee = false;
+ m_dcBlock = false;
+ m_iqCorrection = false;
+ m_rxTransverterMode = false;
+ m_rxTransverterDeltaFrequency = 0;
+
+ m_txCenterFrequency = 435000*1000;
+ m_log2Interp = 0;
+ m_txBandwidth = 1500000;
+ m_tx0GlobalGain = -3;
+ m_tx1GlobalGain = -3;
+ m_txBiasTee = false;
+ m_txTransverterMode = false;
+ m_txTransverterDeltaFrequency = 0;
+
+ m_fileRecordName = "";
+ m_useReverseAPI = false;
+ m_reverseAPIAddress = "127.0.0.1";
+ m_reverseAPIPort = 8888;
+ m_reverseAPIDeviceIndex = 0;
+}
+
+QByteArray BladeRF2MIMOSettings::serialize() const
+{
+ SimpleSerializer s(1);
+
+ s.writeS32(1, m_devSampleRate);
+ s.writeS32(2, m_LOppmTenths);
+
+ s.writeU64(10, m_rxCenterFrequency);
+ s.writeU32(11, m_log2Decim);
+ s.writeS32(12, (int) m_fcPos);
+ s.writeS32(13, m_rxBandwidth);
+ s.writeS32(14, m_rx0GainMode);
+ s.writeS32(15, m_rx0GlobalGain);
+ s.writeS32(16, m_rx1GainMode);
+ s.writeS32(17, m_rx1GlobalGain);
+ s.writeBool(18, m_rxBiasTee);
+ s.writeBool(19, m_dcBlock);
+ s.writeBool(20, m_iqCorrection);
+ s.writeBool(21, m_rxTransverterMode);
+ s.writeS64(22, m_rxTransverterDeltaFrequency);
+
+ s.writeU64(30, m_txCenterFrequency);
+ s.writeU32(31, m_log2Interp);
+ s.writeS32(32, m_txBandwidth);
+ s.writeS32(33, m_tx0GlobalGain);
+ s.writeS32(34, m_tx1GlobalGain);
+ s.writeBool(35, m_txBiasTee);
+ s.writeBool(36, m_txTransverterMode);
+ s.writeS64(37, m_txTransverterDeltaFrequency);
+
+ s.writeString(50, m_fileRecordName);
+ s.writeBool(51, m_useReverseAPI);
+ s.writeString(52, m_reverseAPIAddress);
+ s.writeU32(53, m_reverseAPIPort);
+ s.writeU32(54, m_reverseAPIDeviceIndex);
+
+ return s.final();
+}
+
+bool BladeRF2MIMOSettings::deserialize(const QByteArray& data)
+{
+ SimpleDeserializer d(data);
+
+ if (!d.isValid())
+ {
+ resetToDefaults();
+ return false;
+ }
+
+ if (d.getVersion() == 1)
+ {
+ int intval;
+ uint32_t uintval;
+
+ d.readS32(1, &m_devSampleRate, 3072000);
+ d.readS32(2, &m_LOppmTenths);
+
+ d.readU64(10, &m_rxCenterFrequency, 435000*1000);
+ d.readU32(11, &m_log2Decim);
+ d.readS32(12, &intval);
+ m_fcPos = (fcPos_t) intval;
+ d.readS32(13, &m_rxBandwidth);
+ d.readS32(14, &m_rx0GainMode);
+ d.readS32(15, &m_rx0GlobalGain);
+ d.readS32(16, &m_rx1GainMode);
+ d.readS32(17, &m_rx1GlobalGain);
+ d.readBool(18, &m_rxBiasTee);
+ d.readBool(19, &m_dcBlock);
+ d.readBool(20, &m_iqCorrection);
+ d.readBool(21, &m_rxTransverterMode, false);
+ d.readS64(22, &m_rxTransverterDeltaFrequency, 0);
+
+ d.readU64(30, &m_txCenterFrequency, 435000*1000);
+ d.readU32(31, &m_log2Interp);
+ d.readS32(32, &m_txBandwidth);
+ d.readS32(33, &m_tx0GlobalGain);
+ d.readS32(34, &m_tx1GlobalGain);
+ d.readBool(35, &m_txBiasTee);
+ d.readBool(36, &m_txTransverterMode, false);
+ d.readS64(37, &m_txTransverterDeltaFrequency, 0);
+
+ d.readString(50, &m_fileRecordName, "");
+ d.readBool(51, &m_useReverseAPI, false);
+ d.readString(52, &m_reverseAPIAddress, "127.0.0.1");
+ d.readU32(53, &uintval, 0);
+
+ if ((uintval > 1023) && (uintval < 65535)) {
+ m_reverseAPIPort = uintval;
+ } else {
+ m_reverseAPIPort = 8888;
+ }
+
+ d.readU32(54, &uintval, 0);
+ m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
+
+ return true;
+ }
+ else
+ {
+ resetToDefaults();
+ return false;
+ }
+}
+
+
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h
new file mode 100644
index 000000000..c89e76a50
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h
@@ -0,0 +1,72 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMOSETTINGS_H_
+#define PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMOSETTINGS_H_
+
+#include
+#include
+
+struct BladeRF2MIMOSettings {
+ typedef enum {
+ FC_POS_INFRA = 0,
+ FC_POS_SUPRA,
+ FC_POS_CENTER
+ } fcPos_t;
+
+ qint32 m_devSampleRate;
+ qint32 m_LOppmTenths;
+
+ quint64 m_rxCenterFrequency;
+ quint32 m_log2Decim;
+ fcPos_t m_fcPos;
+ qint32 m_rxBandwidth;
+ int m_rx0GainMode;
+ int m_rx0GlobalGain;
+ int m_rx1GainMode;
+ int m_rx1GlobalGain;
+ bool m_rxBiasTee;
+ bool m_dcBlock;
+ bool m_iqCorrection;
+ bool m_rxTransverterMode;
+ qint64 m_rxTransverterDeltaFrequency;
+
+ quint64 m_txCenterFrequency;
+ quint32 m_log2Interp;
+ qint32 m_txBandwidth;
+ int m_tx0GlobalGain;
+ int m_tx1GlobalGain;
+ bool m_txBiasTee;
+ bool m_txTransverterMode;
+ qint64 m_txTransverterDeltaFrequency;
+
+ QString m_fileRecordName;
+ bool m_useReverseAPI;
+ QString m_reverseAPIAddress;
+ uint16_t m_reverseAPIPort;
+ uint16_t m_reverseAPIDeviceIndex;
+
+ BladeRF2MIMOSettings();
+ void resetToDefaults();
+ QByteArray serialize() const;
+ bool deserialize(const QByteArray& data);
+};
+
+
+
+
+#endif /* PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MIMOSETTINGS_H_ */
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimowebapiadapter.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimowebapiadapter.cpp
new file mode 100644
index 000000000..7fa0fe2d6
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimowebapiadapter.cpp
@@ -0,0 +1,51 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 Edouard Griffiths, F4EXB //
+// //
+// Implementation of static web API adapters used for preset serialization and //
+// deserialization //
+// //
+// 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "SWGDeviceSettings.h"
+#include "bladerf2mimo.h"
+#include "bladerf2mimowebapiadapter.h"
+
+BladeRF2MIMOWebAPIAdapter::BladeRF2MIMOWebAPIAdapter()
+{}
+
+BladeRF2MIMOWebAPIAdapter::~BladeRF2MIMOWebAPIAdapter()
+{}
+
+int BladeRF2MIMOWebAPIAdapter::webapiSettingsGet(
+ SWGSDRangel::SWGDeviceSettings& response,
+ QString& errorMessage)
+{
+ (void) errorMessage;
+ response.setBladeRf2MimoSettings(new SWGSDRangel::SWGBladeRF2MIMOSettings());
+ response.getBladeRf2MimoSettings()->init();
+ BladeRF2MIMO::webapiFormatDeviceSettings(response, m_settings);
+ return 200;
+}
+
+int BladeRF2MIMOWebAPIAdapter::webapiSettingsPutPatch(
+ bool force,
+ const QStringList& deviceSettingsKeys,
+ SWGSDRangel::SWGDeviceSettings& response, // query + response
+ QString& errorMessage)
+{
+ (void) errorMessage;
+ BladeRF2MIMO::webapiUpdateDeviceSettings(m_settings, deviceSettingsKeys, response);
+ return 200;
+}
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimowebapiadapter.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimowebapiadapter.h
new file mode 100644
index 000000000..5f40452f8
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimowebapiadapter.h
@@ -0,0 +1,44 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 Edouard Griffiths, F4EXB //
+// //
+// Implementation of static web API adapters used for preset serialization and //
+// deserialization //
+// //
+// 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "device/devicewebapiadapter.h"
+#include "bladerf2mimosettings.h"
+
+class BladeRF2MIMOWebAPIAdapter : public DeviceWebAPIAdapter
+{
+public:
+ BladeRF2MIMOWebAPIAdapter();
+ virtual ~BladeRF2MIMOWebAPIAdapter();
+ virtual QByteArray serialize() { return m_settings.serialize(); }
+ virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
+
+ virtual int webapiSettingsGet(
+ SWGSDRangel::SWGDeviceSettings& response,
+ QString& errorMessage);
+
+ virtual int webapiSettingsPutPatch(
+ bool force,
+ const QStringList& deviceSettingsKeys,
+ SWGSDRangel::SWGDeviceSettings& response, // query + response
+ QString& errorMessage);
+
+private:
+ BladeRF2MIMOSettings m_settings;
+};
\ No newline at end of file
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mithread.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mithread.cpp
new file mode 100644
index 000000000..82d542b3b
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mithread.cpp
@@ -0,0 +1,245 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "bladerf2/devicebladerf2shared.h"
+#include "dsp/samplesinkfifo.h"
+
+#include "bladerf2mithread.h"
+
+BladeRF2MIThread::BladeRF2MIThread(struct bladerf* dev, QObject* parent) :
+ QThread(parent),
+ m_running(false),
+ m_dev(dev)
+{
+ qDebug("BladeRF2MIThread::BladeRF2MIThread");
+ m_buf = new qint16[2*DeviceBladeRF2::blockSize*2];
+
+ for (unsigned int i = 0; i < 2; i++) {
+ m_convertBuffer[i].resize(DeviceBladeRF2::blockSize, Sample{0,0});
+ }
+}
+
+BladeRF2MIThread::~BladeRF2MIThread()
+{
+ qDebug("BladeRF2MIThread::~BladeRF2MIThread");
+
+ if (m_running) {
+ stopWork();
+ }
+
+ delete[] m_buf;
+}
+
+void BladeRF2MIThread::startWork()
+{
+ m_startWaitMutex.lock();
+ start();
+
+ while(!m_running) {
+ m_startWaiter.wait(&m_startWaitMutex, 100);
+ }
+
+ m_startWaitMutex.unlock();
+}
+
+void BladeRF2MIThread::stopWork()
+{
+ m_running = false;
+ wait();
+}
+
+void BladeRF2MIThread::setLog2Decimation(unsigned int log2_decim)
+{
+ m_log2Decim = log2_decim;
+}
+
+unsigned int BladeRF2MIThread::getLog2Decimation() const
+{
+ return m_log2Decim;
+}
+
+void BladeRF2MIThread::setFcPos(int fcPos)
+{
+ m_fcPos = fcPos;
+}
+
+int BladeRF2MIThread::getFcPos() const
+{
+ return m_fcPos;
+}
+
+void BladeRF2MIThread::setFifo(unsigned int channel, SampleSinkFifo *sampleFifo)
+{
+ if (channel < 2) {
+ m_sampleFifo[channel] = sampleFifo;
+ }
+}
+
+SampleSinkFifo *BladeRF2MIThread::getFifo(unsigned int channel)
+{
+ if (channel < 2) {
+ return m_sampleFifo[channel];
+ } else {
+ return nullptr;
+ }
+}
+
+void BladeRF2MIThread::run()
+{
+ int res;
+
+ m_running = true;
+ m_startWaiter.wakeAll();
+
+ int status = bladerf_sync_config(m_dev, BLADERF_RX_X2, BLADERF_FORMAT_SC16_Q11, 64, 8192, 32, 10000);
+
+ if (status < 0)
+ {
+ qCritical("BladeRF2MIThread::run: cannot configure streams: %s", bladerf_strerror(status));
+ }
+ else
+ {
+ qDebug("BladeRF2MIThread::run: start running loop");
+
+ while (m_running)
+ {
+ res = bladerf_sync_rx(m_dev, m_buf, DeviceBladeRF2::blockSize*2, nullptr, 1500);
+
+ if (res < 0)
+ {
+ qCritical("BladeRF2MIThread::run sync Rx error: %s", bladerf_strerror(res));
+ break;
+ }
+
+ callback(m_buf, DeviceBladeRF2::blockSize);
+ }
+
+ qDebug("BladeRF2MIThread::run: stop running loop");
+ m_running = false;
+ }
+}
+
+void BladeRF2MIThread::callback(const qint16* buf, qint32 samplesPerChannel)
+{
+ int status = bladerf_deinterleave_stream_buffer(BLADERF_RX_X2, BLADERF_FORMAT_SC16_Q11 , samplesPerChannel*2, (void *) buf);
+
+ if (status < 0)
+ {
+ qCritical("BladeRF2MIThread::callback: cannot de-interleave buffer: %s", bladerf_strerror(status));
+ return;
+ }
+
+ for (unsigned int channel = 0; channel < 2; channel++)
+ {
+ if (m_sampleFifo[channel]) {
+ channelCallback(&buf[2*samplesPerChannel*channel], 2*samplesPerChannel, channel);
+ }
+ }
+}
+
+void BladeRF2MIThread::channelCallback(const qint16* buf, qint32 len, int channel)
+{
+ SampleVector::iterator it = m_convertBuffer[channel].begin();
+
+ if (m_log2Decim == 0)
+ {
+ m_decimators[channel].decimate1(&it, buf, len);
+ }
+ else
+ {
+ if (m_fcPos == 0) // Infra
+ {
+ switch (m_log2Decim)
+ {
+ case 1:
+ m_decimators[channel].decimate2_inf(&it, buf, len);
+ break;
+ case 2:
+ m_decimators[channel].decimate4_inf(&it, buf, len);
+ break;
+ case 3:
+ m_decimators[channel].decimate8_inf(&it, buf, len);
+ break;
+ case 4:
+ m_decimators[channel].decimate16_inf(&it, buf, len);
+ break;
+ case 5:
+ m_decimators[channel].decimate32_inf(&it, buf, len);
+ break;
+ case 6:
+ m_decimators[channel].decimate64_inf(&it, buf, len);
+ break;
+ default:
+ break;
+ }
+ }
+ else if (m_fcPos == 1) // Supra
+ {
+ switch (m_log2Decim)
+ {
+ case 1:
+ m_decimators[channel].decimate2_sup(&it, buf, len);
+ break;
+ case 2:
+ m_decimators[channel].decimate4_sup(&it, buf, len);
+ break;
+ case 3:
+ m_decimators[channel].decimate8_sup(&it, buf, len);
+ break;
+ case 4:
+ m_decimators[channel].decimate16_sup(&it, buf, len);
+ break;
+ case 5:
+ m_decimators[channel].decimate32_sup(&it, buf, len);
+ break;
+ case 6:
+ m_decimators[channel].decimate64_sup(&it, buf, len);
+ break;
+ default:
+ break;
+ }
+ }
+ else if (m_fcPos == 2) // Center
+ {
+ switch (m_log2Decim)
+ {
+ case 1:
+ m_decimators[channel].decimate2_cen(&it, buf, len);
+ break;
+ case 2:
+ m_decimators[channel].decimate4_cen(&it, buf, len);
+ break;
+ case 3:
+ m_decimators[channel].decimate8_cen(&it, buf, len);
+ break;
+ case 4:
+ m_decimators[channel].decimate16_cen(&it, buf, len);
+ break;
+ case 5:
+ m_decimators[channel].decimate32_cen(&it, buf, len);
+ break;
+ case 6:
+ m_decimators[channel].decimate64_cen(&it, buf, len);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ m_sampleFifo[channel]->write(m_convertBuffer[channel].begin(), it);
+}
\ No newline at end of file
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mithread.h b/plugins/samplemimo/bladerf2mimo/bladerf2mithread.h
new file mode 100644
index 000000000..ff47eacd7
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mithread.h
@@ -0,0 +1,69 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MITHREAD_H_
+#define PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MITHREAD_H_
+
+// BladerRF2 is a SISO/MIMO device. It can support one or two Rx. Here ww will
+// configure two Rx
+
+#include
+#include
+#include
+
+#include
+
+#include "dsp/decimators.h"
+
+class SampleSinkFifo;
+
+class BladeRF2MIThread : public QThread {
+ Q_OBJECT
+
+public:
+ BladeRF2MIThread(struct bladerf* dev, QObject* parent = nullptr);
+ ~BladeRF2MIThread();
+
+ void startWork();
+ void stopWork();
+ bool isRunning() const { return m_running; }
+ void setLog2Decimation(unsigned int log2_decim);
+ unsigned int getLog2Decimation() const;
+ void setFcPos(int fcPos);
+ int getFcPos() const;
+ void setFifo(unsigned int channel, SampleSinkFifo *sampleFifo);
+ SampleSinkFifo *getFifo(unsigned int channel);
+
+private:
+ QMutex m_startWaitMutex;
+ QWaitCondition m_startWaiter;
+ bool m_running;
+ struct bladerf* m_dev;
+
+ qint16 *m_buf;
+ SampleVector m_convertBuffer[2];
+ SampleSinkFifo* m_sampleFifo[2];
+ Decimators m_decimators[2];
+ unsigned int m_log2Decim;
+ int m_fcPos;
+
+ void run();
+ void callback(const qint16* buf, qint32 samplesPerChannel);
+ void channelCallback(const qint16* buf, qint32 len, int channel);
+};
+
+#endif // PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MITHREAD_H_
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mothread.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mothread.cpp
new file mode 100644
index 000000000..c2e735ba9
--- /dev/null
+++ b/plugins/samplemimo/bladerf2mimo/bladerf2mothread.cpp
@@ -0,0 +1,197 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2019 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 //
+// (at your option) any later version. //
+// //
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "bladerf2/devicebladerf2shared.h"
+#include "dsp/samplesourcefifo.h"
+
+#include "bladerf2mothread.h"
+
+BladeRF2MOThread::BladeRF2MOThread(struct bladerf* dev, QObject* parent) :
+ QThread(parent),
+ m_running(false),
+ m_dev(dev),
+ m_log2Interp(0)
+{
+ qDebug("BladeRF2MOThread::BladeRF2MOThread");
+ m_buf = new qint16[2*DeviceBladeRF2::blockSize*2];
+}
+
+BladeRF2MOThread::~BladeRF2MOThread()
+{
+ qDebug("BladeRF2MOThread::~BladeRF2MOThread");
+
+ if (m_running) {
+ stopWork();
+ }
+
+ delete[] m_buf;
+}
+
+void BladeRF2MOThread::startWork()
+{
+ m_startWaitMutex.lock();
+ start();
+
+ while(!m_running) {
+ m_startWaiter.wait(&m_startWaitMutex, 100);
+ }
+
+ m_startWaitMutex.unlock();
+}
+
+void BladeRF2MOThread::stopWork()
+{
+ m_running = false;
+ wait();
+}
+
+void BladeRF2MOThread::run()
+{
+ int res;
+
+ m_running = true;
+ m_startWaiter.wakeAll();
+
+ int status;
+
+ status = bladerf_sync_config(m_dev, BLADERF_TX_X2, BLADERF_FORMAT_SC16_Q11, 128, 16384, 32, 1500);
+
+ if (status < 0)
+ {
+ qCritical("BladeRF2MOThread::run: cannot configure streams: %s", bladerf_strerror(status));
+ }
+ else
+ {
+ qDebug("BladeRF2MOThread::run: start running loop");
+
+ while (m_running)
+ {
+ callback(m_buf, DeviceBladeRF2::blockSize);
+ res = bladerf_sync_tx(m_dev, m_buf, DeviceBladeRF2::blockSize*2, 0, 1500);
+
+ if (res < 0)
+ {
+ qCritical("BladeRF2MOThread::run sync Rx error: %s", bladerf_strerror(res));
+ break;
+ }
+ }
+
+ qDebug("BladeRF2MOThread::run: stop running loop");
+ }
+
+ m_running = false;
+}
+
+void BladeRF2MOThread::setLog2Interpolation(unsigned int log2_interp)
+{
+ m_log2Interp = log2_interp;
+}
+
+unsigned int BladeRF2MOThread::getLog2Interpolation() const
+{
+ return m_log2Interp;
+}
+
+void BladeRF2MOThread::setFifo(unsigned int channel, SampleSourceFifo *sampleFifo)
+{
+ if (channel < 2) {
+ m_sampleFifo[channel] = sampleFifo;
+ }
+}
+
+SampleSourceFifo *BladeRF2MOThread::getFifo(unsigned int channel)
+{
+ if (channel < 2) {
+ return m_sampleFifo[channel];
+ } else {
+ return nullptr;
+ }
+}
+
+void BladeRF2MOThread::callback(qint16* buf, qint32 samplesPerChannel)
+{
+ for (unsigned int channel = 0; channel < 2; channel++)
+ {
+ if (m_sampleFifo[channel]) {
+ channelCallback(&buf[2*samplesPerChannel*channel], samplesPerChannel, channel);
+ } else {
+ std::fill(&buf[2*samplesPerChannel*channel], &buf[2*samplesPerChannel*channel]+2*samplesPerChannel, 0); // fill with zero samples
+ }
+ }
+
+ int status = bladerf_interleave_stream_buffer(BLADERF_TX_X2, BLADERF_FORMAT_SC16_Q11 , samplesPerChannel*2, (void *) buf);
+
+ if (status < 0)
+ {
+ qCritical("BladeRF2MOThread::callback: cannot interleave buffer: %s", bladerf_strerror(status));
+ return;
+ }
+}
+
+// Interpolate according to specified log2 (ex: log2=4 => decim=16). len is a number of samples (not a number of I or Q)
+void BladeRF2MOThread::channelCallback(qint16* buf, qint32 len, unsigned int channel)
+{
+ if (m_sampleFifo[channel])
+ {
+ float bal = m_sampleFifo[channel]->getRWBalance();
+
+ if (bal < -0.25) {
+ qDebug("BladeRF2MOThread::channelCallback: read lags: %f", bal);
+ } else if (bal > 0.25) {
+ qDebug("BladeRF2MOThread::channelCallback: read leads: %f", bal);
+ }
+
+ SampleVector::iterator beginRead;
+ m_sampleFifo[channel]->readAdvance(beginRead, len/(1<. //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MOTHREAD_H_
+#define PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MOTHREAD_H_
+
+#include
+#include
+#include
+#include
+
+#include "dsp/interpolators.h"
+
+class SampleSourceFifo;
+
+class BladeRF2MOThread : public QThread {
+ Q_OBJECT
+
+public:
+ BladeRF2MOThread(struct bladerf* dev, QObject* parent = nullptr);
+ ~BladeRF2MOThread();
+
+ void startWork();
+ void stopWork();
+ bool isRunning() const { return m_running; }
+ void setLog2Interpolation(unsigned int log2_interp);
+ unsigned int getLog2Interpolation() const;
+ void setFifo(unsigned int channel, SampleSourceFifo *sampleFifo);
+ SampleSourceFifo *getFifo(unsigned int channel);
+
+private:
+ QMutex m_startWaitMutex;
+ QWaitCondition m_startWaiter;
+ bool m_running;
+ struct bladerf* m_dev;
+
+ qint16 *m_buf; //!< Full buffer for SISO or MIMO operation
+ SampleSourceFifo* m_sampleFifo[2];
+ Interpolators m_interpolators[2];
+ unsigned int m_log2Interp;
+
+ void run();
+ unsigned int getNbFifos();
+ void channelCallback(qint16* buf, qint32 len, unsigned int channel = 0);
+ void callback(qint16* buf, qint32 samplesPerChannel);
+};
+
+
+
+#endif /* PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MOTHREAD_H_ */