diff --git a/plugins/samplemimo/CMakeLists.txt b/plugins/samplemimo/CMakeLists.txt index acc069928..b5099ce54 100644 --- a/plugins/samplemimo/CMakeLists.txt +++ b/plugins/samplemimo/CMakeLists.txt @@ -1,3 +1,7 @@ project(samplemimo) +if(ENABLE_BLADERF AND LIBBLADERF_FOUND) + add_subdirectory(bladerf2mimo) +endif() + add_subdirectory(testmi) diff --git a/plugins/samplemimo/bladerf2mimo/CMakeLists.txt b/plugins/samplemimo/bladerf2mimo/CMakeLists.txt new file mode 100644 index 000000000..6377ecde2 --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/CMakeLists.txt @@ -0,0 +1,66 @@ +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} +) + +if(LIBBLADERF_EXTERNAL) + add_dependencies(${TARGET_NAME} bladerf) +endif() + +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..c6fb40cb5 --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimo.cpp @@ -0,0 +1,1407 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "SWGDeviceReport.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 "bladerf2/devicebladerf2.h" + +#include "bladerf2mithread.h" +#include "bladerf2mothread.h" +#include "bladerf2mimo.h" + +MESSAGE_CLASS_DEFINITION(BladeRF2MIMO::MsgConfigureBladeRF2MIMO, Message) +MESSAGE_CLASS_DEFINITION(BladeRF2MIMO::MsgStartStop, Message) + +BladeRF2MIMO::BladeRF2MIMO(DeviceAPI *deviceAPI) : + m_deviceAPI(deviceAPI), + m_settings(), + m_sourceThread(nullptr), + m_sinkThread(nullptr), + m_deviceDescription("BladeRF2MIMO"), + m_runningRx(false), + m_runningTx(false), + m_dev(nullptr), + m_open(false) +{ + m_open = openDevice(); + + if (m_dev) + { + const bladerf_gain_modes *modes = nullptr; + 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_sampleMIFifo.init(2, 4096 * 64); + m_sampleMOFifo.init(2, 4096 * 64); + m_deviceAPI->setNbSourceStreams(2); + m_deviceAPI->setNbSinkStreams(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; + closeDevice(); +} + +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) { + stopRx(); + } + + if (m_runningTx) { + stopTx(); + } + + m_dev->close(); + delete m_dev; + m_dev = nullptr; + m_open = false; +} + +void BladeRF2MIMO::init() +{ + applySettings(m_settings, true); +} + +bool BladeRF2MIMO::startRx() +{ + qDebug("BladeRF2MIMO::startRx"); + + if (!m_open) + { + qCritical("BladeRF2MIMO::startRx: device was not opened"); + return false; + } + + QMutexLocker mutexLocker(&m_mutex); + + if (m_runningRx) { + stopRx(); + } + + m_sourceThread = new BladeRF2MIThread(m_dev->getDev()); + m_sampleMIFifo.reset(); + m_sourceThread->setFifo(&m_sampleMIFifo); + m_sourceThread->setFcPos(m_settings.m_fcPosRx); + m_sourceThread->setLog2Decimation(m_settings.m_log2Decim); + m_sourceThread->setIQOrder(m_settings.m_iqOrder); + + for (int i = 0; i < 2; i++) + { + if (!m_dev->openRx(i)) { + qCritical("BladeRF2MIMO::startRx: Rx channel %u cannot be enabled", i); + } + } + + m_sourceThread->startWork(); + mutexLocker.unlock(); + m_runningRx = true; + + return true; +} + +bool BladeRF2MIMO::startTx() +{ + qDebug("BladeRF2MIMO::startTx"); + + if (!m_open) + { + qCritical("BladeRF2MIMO::startRx: device was not opened"); + return false; + } + + QMutexLocker mutexLocker(&m_mutex); + + if (m_runningTx) { + stopTx(); + } + + m_sinkThread = new BladeRF2MOThread(m_dev->getDev()); + m_sampleMOFifo.reset(); + m_sinkThread->setFifo(&m_sampleMOFifo); + m_sinkThread->setFcPos(m_settings.m_fcPosTx); + m_sinkThread->setLog2Interpolation(m_settings.m_log2Interp); + + for (int i = 0; i < 2; i++) + { + if (!m_dev->openTx(i)) { + qCritical("BladeRF2MIMO::startTx: Tx channel %u cannot be enabled", i); + } + } + + m_sinkThread->startWork(); + mutexLocker.unlock(); + m_runningTx = true; + + return true; +} + +void BladeRF2MIMO::stopRx() +{ + qDebug("BladeRF2MIMO::stopRx"); + + 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() +{ + qDebug("BladeRF2MIMO::stopTx"); + + if (!m_sinkThread) { + return; + } + + QMutexLocker mutexLocker(&m_mutex); + + m_sinkThread->stopWork(); + delete m_sinkThread; + m_sinkThread = nullptr; + m_runningTx = false; + + for (int i = 0; i < 2; i++) { + m_dev->closeTx(i); + } +} + +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 (MsgStartStop::match(message)) + { + MsgStartStop& cmd = (MsgStartStop&) message; + qDebug() << "BladeRF2MIMO::handleMessage: " + << " " << (cmd.getRxElseTx() ? "Rx" : "Tx") + << " MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop"); + + bool startStopRxElseTx = cmd.getRxElseTx(); + + if (cmd.getStartStop()) + { + if (m_deviceAPI->initDeviceEngine(startStopRxElseTx ? 0 : 1)) { + m_deviceAPI->startDeviceEngine(startStopRxElseTx ? 0 : 1); + } + } + else + { + m_deviceAPI->stopDeviceEngine(startStopRxElseTx ? 0 : 1); + } + + 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_iqOrder: " << settings.m_iqOrder + << " m_fcPosRx: " << settings.m_fcPosRx + << " 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_fcPosTx: " << settings.m_fcPosTx + << " 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_useReverseAPI: " << settings.m_useReverseAPI + << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress + << " m_reverseAPIPort: " << settings.m_reverseAPIPort + << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex + << " force: " << force; + + 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_fcPosRx != settings.m_fcPosRx) || force) + { + reverseAPIKeys.append("fcPosRx"); + + if (m_sourceThread) + { + m_sourceThread->setFcPos((int) settings.m_fcPosRx); + qDebug() << "BladeRF2MIMO::applySettings: set Rx fc pos (enum) to " << (int) settings.m_fcPosRx; + } + } + + 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<setIQOrder(settings.m_iqOrder); + qDebug() << "BladeRF2MIMO::applySettings: set IQ order to " << (settings.m_iqOrder ? "IQ" : "QI"); + } + } + + if ((m_settings.m_fcPosTx != settings.m_fcPosTx) || force) + { + reverseAPIKeys.append("fcPosTx"); + + if (m_sourceThread) + { + m_sourceThread->setFcPos((int) settings.m_fcPosTx); + qDebug() << "BladeRF2MIMO::applySettings: set Tx fc pos (enum) to " << (int) settings.m_fcPosTx; + } + } + + if ((m_settings.m_log2Interp != settings.m_log2Interp) || force) + { + reverseAPIKeys.append("log2Interp"); + + if (m_sinkThread) + { + m_sinkThread->setLog2Interpolation(settings.m_log2Interp); + qDebug() << "BladeRF2Input::applySettings: set interpolation to " << (1<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_fcPosTx != settings.m_fcPosTx) + || (m_settings.m_log2Interp != settings.m_log2Interp) + || (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) settings.m_fcPosTx, + settings.m_devSampleRate, + settings.m_txTransverterMode); + setTxDeviceCenterFrequency(dev, deviceCenterFrequency, settings.m_LOppmTenths); + } + + 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<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_txCenterFrequency, 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::setRxDeviceCenterFrequency: RX0: bladerf_set_frequency(%lld) failed: %s", + freq_hz, bladerf_strerror(status)); + return false; + } + else + { + qDebug("BladeRF2MIMO::setRxDeviceCenterFrequency: RX0: bladerf_set_frequency(%lld)", freq_hz); + } + + status = bladerf_set_frequency(dev, BLADERF_CHANNEL_RX(1), freq_hz); + + if (status < 0) + { + qWarning("BladeRF2MIMO::setRxDeviceCenterFrequency: RX1: bladerf_set_frequency(%lld) failed: %s", + freq_hz, bladerf_strerror(status)); + return false; + } + else + { + qDebug("BladeRF2MIMO::setRxDeviceCenterFrequency: 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::setTxDeviceCenterFrequency: TX0: bladerf_set_frequency(%lld) failed: %s", + freq_hz, bladerf_strerror(status)); + return false; + } + else + { + qDebug("BladeRF2Output::setTxDeviceCenterFrequency: TX0: bladerf_set_frequency(%lld)", freq_hz); + } + + status = bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(1), freq_hz); + + if (status < 0) { + qWarning("BladeRF2Output::setTxDeviceCenterFrequency: TX1: bladerf_set_frequency(%lld) failed: %s", + freq_hz, bladerf_strerror(status)); + return false; + } + else + { + qDebug("BladeRF2Output::setTxDeviceCenterFrequency: TX1: bladerf_set_frequency(%lld)", freq_hz); + } + + return true; +} + +void BladeRF2MIMO::getRxFrequencyRange(uint64_t& min, uint64_t& max, int& step, float& scale) +{ + if (m_dev) { + m_dev->getFrequencyRangeRx(min, max, step, scale); + } +} + +void BladeRF2MIMO::getRxSampleRateRange(int& min, int& max, int& step, float& scale) +{ + if (m_dev) { + m_dev->getSampleRateRangeRx(min, max, step, scale); + } +} + +void BladeRF2MIMO::getRxBandwidthRange(int& min, int& max, int& step, float& scale) +{ + if (m_dev) { + m_dev->getBandwidthRangeRx(min, max, step, scale); + } +} + +void BladeRF2MIMO::getRxGlobalGainRange(int& min, int& max, int& step, float& scale) +{ + if (m_dev) { + m_dev->getGlobalGainRangeRx(min, max, step, scale); + } +} + +void BladeRF2MIMO::getTxFrequencyRange(uint64_t& min, uint64_t& max, int& step, float& scale) +{ + if (m_dev) { + m_dev->getFrequencyRangeTx(min, max, step, scale); + } +} + +void BladeRF2MIMO::getTxSampleRateRange(int& min, int& max, int& step, float& scale) +{ + if (m_dev) { + m_dev->getSampleRateRangeTx(min, max, step, scale); + } +} + +void BladeRF2MIMO::getTxBandwidthRange(int& min, int& max, int& step, float& scale) +{ + if (m_dev) { + m_dev->getBandwidthRangeTx(min, max, step, scale); + } +} + +void BladeRF2MIMO::getTxGlobalGainRange(int& min, int& max, int& step, float& scale) +{ + if (m_dev) { + m_dev->getGlobalGainRangeTx(min, max, step, scale); + } +} + +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("iqOrder")) { + settings.m_iqOrder = response.getBladeRf2MimoSettings()->getIqOrder() != 0; + } + if (deviceSettingsKeys.contains("fcPosRx")) { + settings.m_fcPosRx = static_cast(response.getBladeRf2MimoSettings()->getFcPosRx()); + } + 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("fcPosTx")) { + settings.m_fcPosRx = static_cast(response.getBladeRf2MimoSettings()->getFcPosTx()); + } + 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("useReverseAPI")) { + settings.m_useReverseAPI = response.getBladeRf2MimoSettings()->getUseReverseApi() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getBladeRf2MimoSettings()->getReverseApiAddress(); + } + if (deviceSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getBladeRf2MimoSettings()->getReverseApiPort(); + } + if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getBladeRf2MimoSettings()->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()->setIqOrder(settings.m_iqOrder ? 1 : 0); + response.getBladeRf2MimoSettings()->setFcPosRx((int) settings.m_fcPosRx); + 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()->setFcPosTx((int) settings.m_fcPosTx); + 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); + + response.getBladeRf2MimoSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getBladeRf2MimoSettings()->getReverseApiAddress()) { + *response.getBladeRf2MimoSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getBladeRf2MimoSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getBladeRf2MimoSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getBladeRf2MimoSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); +} + +int BladeRF2MIMO::webapiRunGet( + int subsystemIndex, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + if ((subsystemIndex == 0) || (subsystemIndex == 1)) + { + m_deviceAPI->getDeviceEngineStateStr(*response.getState(), subsystemIndex); + return 200; + } + else + { + errorMessage = QString("Subsystem invalid: must be 0 (Rx) or 1 (Tx)"); + return 404; + } + +} + +int BladeRF2MIMO::webapiRun( + bool run, + int subsystemIndex, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + if ((subsystemIndex == 0) || (subsystemIndex == 1)) + { + m_deviceAPI->getDeviceEngineStateStr(*response.getState(), subsystemIndex); + MsgStartStop *message = MsgStartStop::create(run, subsystemIndex == 0); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgStartStop *msgToGUI = MsgStartStop::create(run, subsystemIndex == 0); + m_guiMessageQueue->push(msgToGUI); + } + + return 200; + } + else + { + errorMessage = QString("Subsystem invalid: must be 0 (Rx) or 1 (Tx)"); + return 404; + } +} + +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("iqOrder") || force) { + swgBladeRF2MIMOSettings->setIqOrder(settings.m_iqOrder ? 1 : 0); + } + if (deviceSettingsKeys.contains("fcPosRx") || force) { + swgBladeRF2MIMOSettings->setFcPosRx((int) settings.m_fcPosRx); + } + 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("fcPosTx") || force) { + swgBladeRF2MIMOSettings->setFcPosTx((int) settings.m_fcPosTx); + } + 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 + QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + buffer->setParent(reply); + + 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); + QNetworkReply *reply; + + if (start) { + reply = m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer); + } else { + reply = m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer); + } + + buffer->setParent(reply); + delete swgDeviceSettings; +} + +int BladeRF2MIMO::webapiReportGet(SWGSDRangel::SWGDeviceReport& response, QString& errorMessage) +{ + (void) errorMessage; + response.setBladeRf2MimoReport(new SWGSDRangel::SWGBladeRF2MIMOReport()); + response.getBladeRf2MimoReport()->init(); + webapiFormatDeviceReport(response); + return 200; +} + +void BladeRF2MIMO::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response) +{ + if (m_dev) + { + int min, max, step; + float scale; + uint64_t f_min, f_max; + + m_dev->getBandwidthRangeRx(min, max, step, scale); + + response.getBladeRf2MimoReport()->setBandwidthRangeRx(new SWGSDRangel::SWGRange); + response.getBladeRf2MimoReport()->getBandwidthRangeRx()->setMin(min); + response.getBladeRf2MimoReport()->getBandwidthRangeRx()->setMax(max); + response.getBladeRf2MimoReport()->getBandwidthRangeRx()->setStep(step); + response.getBladeRf2MimoReport()->getBandwidthRangeRx()->setScale(scale); + + m_dev->getFrequencyRangeRx(f_min, f_max, step, scale); + + response.getBladeRf2MimoReport()->setFrequencyRangeRx(new SWGSDRangel::SWGFrequencyRange); + response.getBladeRf2MimoReport()->getFrequencyRangeRx()->setMin(f_min); + response.getBladeRf2MimoReport()->getFrequencyRangeRx()->setMax(f_max); + response.getBladeRf2MimoReport()->getFrequencyRangeRx()->setStep(step); + response.getBladeRf2MimoReport()->getFrequencyRangeRx()->setScale(scale); + + m_dev->getGlobalGainRangeRx(min, max, step, scale); + + response.getBladeRf2MimoReport()->setGlobalGainRangeRx(new SWGSDRangel::SWGRange); + response.getBladeRf2MimoReport()->getGlobalGainRangeRx()->setMin(min); + response.getBladeRf2MimoReport()->getGlobalGainRangeRx()->setMax(max); + response.getBladeRf2MimoReport()->getGlobalGainRangeRx()->setStep(step); + response.getBladeRf2MimoReport()->getGlobalGainRangeRx()->setScale(scale); + + m_dev->getSampleRateRangeRx(min, max, step, scale); + + response.getBladeRf2MimoReport()->setSampleRateRangeRx(new SWGSDRangel::SWGRange); + response.getBladeRf2MimoReport()->getSampleRateRangeRx()->setMin(min); + response.getBladeRf2MimoReport()->getSampleRateRangeRx()->setMax(max); + response.getBladeRf2MimoReport()->getSampleRateRangeRx()->setStep(step); + response.getBladeRf2MimoReport()->getSampleRateRangeRx()->setScale(scale); + + m_dev->getBandwidthRangeTx(min, max, step, scale); + + response.getBladeRf2MimoReport()->setBandwidthRangeTx(new SWGSDRangel::SWGRange); + response.getBladeRf2MimoReport()->getBandwidthRangeTx()->setMin(min); + response.getBladeRf2MimoReport()->getBandwidthRangeTx()->setMax(max); + response.getBladeRf2MimoReport()->getBandwidthRangeTx()->setStep(step); + response.getBladeRf2MimoReport()->getBandwidthRangeTx()->setScale(scale); + + m_dev->getFrequencyRangeTx(f_min, f_max, step, scale); + + response.getBladeRf2MimoReport()->setFrequencyRangeTx(new SWGSDRangel::SWGFrequencyRange); + response.getBladeRf2MimoReport()->getFrequencyRangeTx()->setMin(f_min); + response.getBladeRf2MimoReport()->getFrequencyRangeTx()->setMax(f_max); + response.getBladeRf2MimoReport()->getFrequencyRangeTx()->setStep(step); + response.getBladeRf2MimoReport()->getFrequencyRangeTx()->setScale(scale); + + m_dev->getGlobalGainRangeTx(min, max, step, scale); + + response.getBladeRf2MimoReport()->setGlobalGainRangeTx(new SWGSDRangel::SWGRange); + response.getBladeRf2MimoReport()->getGlobalGainRangeTx()->setMin(min); + response.getBladeRf2MimoReport()->getGlobalGainRangeTx()->setMax(max); + response.getBladeRf2MimoReport()->getGlobalGainRangeTx()->setStep(step); + response.getBladeRf2MimoReport()->getGlobalGainRangeTx()->setScale(scale); + + m_dev->getSampleRateRangeTx(min, max, step, scale); + + response.getBladeRf2MimoReport()->setSampleRateRangeTx(new SWGSDRangel::SWGRange); + response.getBladeRf2MimoReport()->getSampleRateRangeTx()->setMin(min); + response.getBladeRf2MimoReport()->getSampleRateRangeTx()->setMax(max); + response.getBladeRf2MimoReport()->getSampleRateRangeTx()->setStep(step); + response.getBladeRf2MimoReport()->getSampleRateRangeTx()->setScale(scale); + } +} + +void BladeRF2MIMO::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "BladeRF2MIMO::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("BladeRF2MIMO::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimo.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimo.h new file mode 100644 index 000000000..9cbf613cf --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimo.h @@ -0,0 +1,206 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 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 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; + }; + + BladeRF2MIMO(DeviceAPI *deviceAPI); + virtual ~BladeRF2MIMO(); + virtual void destroy(); + + virtual void init(); + virtual bool startRx(); + virtual void stopRx(); + virtual bool startTx(); + virtual void stopTx(); + + 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 unsigned int getMIMOSampleRate() const { return getSourceSampleRate(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 webapiReportGet( + SWGSDRangel::SWGDeviceReport& response, + QString& errorMessage); + + virtual int webapiRunGet( + int subsystemIndex, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + virtual int webapiRun( + bool run, + int subsystemIndex, + 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) istream; return false; } + + void getRxFrequencyRange(uint64_t& min, uint64_t& max, int& step, float& scale); + void getRxSampleRateRange(int& min, int& max, int& step, float& scale); + void getRxBandwidthRange(int& min, int& max, int& step, float& scale); + void getRxGlobalGainRange(int& min, int& max, int& step, float& scale); + const std::vector& getRxGainModes() { return m_rxGainModes; } + + void getTxFrequencyRange(uint64_t& min, uint64_t& max, int& step, float& scale); + void getTxSampleRateRange(int& min, int& max, int& step, float& scale); + void getTxBandwidthRange(int& min, int& max, int& step, float& scale); + void getTxGlobalGainRange(int& min, int& max, int& step, float& scale); + + bool getRxRunning() const { return m_runningRx; } + bool getTxRunning() const { return m_runningTx; } + +private: + DeviceAPI *m_deviceAPI; + QMutex m_mutex; + BladeRF2MIMOSettings m_settings; + BladeRF2MIThread* m_sourceThread; + BladeRF2MOThread* m_sinkThread; + QString m_deviceDescription; + 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(); + + 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); + void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + +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..ecbc3ab34 --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.cpp @@ -0,0 +1,823 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "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) : + DeviceGUI(parent), + ui(new Ui::BladeRF2MIMOGui), + m_deviceUISet(deviceUISet), + m_settings(), + m_rxElseTx(true), + m_streamIndex(0), + m_spectrumRxElseTx(true), + m_spectrumStreamIndex(0), + m_gainLock(false), + m_doApplySettings(true), + m_forceSettings(true), + m_sampleMIMO(nullptr), + m_tickCount(0), + m_rxBasebandSampleRate(3072000), + m_txBasebandSampleRate(3072000), + m_rxDeviceCenterFrequency(435000*1000), + m_txDeviceCenterFrequency(435000*1000), + m_lastRxEngineState(DeviceAPI::StNotStarted), + m_lastTxEngineState(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_fScaleRx); + m_sampleMIMO->getTxFrequencyRange(m_fMinTx, m_fMaxTx, m_fStepTx, m_fScaleTx); + m_sampleMIMO->getRxBandwidthRange(m_bwMinRx, m_bwMaxRx, m_bwStepRx, m_bwScaleRx); + m_sampleMIMO->getTxBandwidthRange(m_bwMinTx, m_bwMaxTx, m_bwStepTx, m_bwScaleTx); + + 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_srScaleRx); + m_sampleMIMO->getTxSampleRateRange(minTx, maxTx, stepTx, m_srScaleTx); + m_srMin = std::max(minRx, minTx); + m_srMax = std::min(maxRx, maxTx); + m_sampleMIMO->getRxGlobalGainRange(m_gainMinRx, m_gainMaxRx, m_gainStepRx, m_gainScaleRx); + m_sampleMIMO->getTxGlobalGainRange(m_gainMinTx, m_gainMaxTx, m_gainStepTx, m_gainScaleTx); + + 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->startStopRx); + connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &))); + + sendSettings(); +} + +BladeRF2MIMOGui::~BladeRF2MIMOGui() +{ + delete ui; +} + +void BladeRF2MIMOGui::destroy() +{ + delete this; +} + +void BladeRF2MIMOGui::resetToDefaults() +{ + m_settings.resetToDefaults(); + 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() +{ + updateFrequencyLimits(); + + if (m_rxElseTx) + { + ui->transverter->setDeltaFrequency(m_settings.m_rxTransverterDeltaFrequency); + ui->transverter->setDeltaFrequencyActive(m_settings.m_rxTransverterMode); + ui->transverter->setIQOrder(m_settings.m_iqOrder); + 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); + 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); + ui->fcPos->setCurrentIndex((int) m_settings.m_fcPosRx); + + if (m_streamIndex == 0) { + ui->gainMode->setCurrentIndex(m_settings.m_rx0GainMode); + } else if (m_streamIndex == 1) { + ui->gainMode->setCurrentIndex(m_settings.m_rx1GainMode); + } + } + else + { + ui->transverter->setDeltaFrequency(m_settings.m_txTransverterDeltaFrequency); + ui->transverter->setDeltaFrequencyActive(m_settings.m_txTransverterMode); + ui->transverter->setIQOrder(m_settings.m_iqOrder); + 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); + 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); + ui->fcPos->setCurrentIndex((int) m_settings.m_fcPosTx); + } + + displayGain(); + + 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))); + + 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_fcPosRx, + m_settings.m_devSampleRate, + DeviceSampleStatic::FrequencyShiftScheme::FSHIFT_STD + ); + } + else + { + fShift = DeviceSampleStatic::calculateSinkFrequencyShift( + m_settings.m_log2Interp, + (DeviceSampleStatic::fcPos_t) m_settings.m_fcPosTx, + 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->addItem("automatic"); + } + + ui->gainMode->blockSignals(false); +} + +void BladeRF2MIMOGui::displayGain() +{ + int min, max, step, gainDB; + float scale; + + if (m_rxElseTx) + { + m_sampleMIMO->getRxGlobalGainRange(min, max, step, scale); + + if (m_streamIndex == 0) { + gainDB = m_settings.m_rx0GlobalGain; + } else { + gainDB = m_settings.m_rx1GlobalGain; + } + } + else + { + m_sampleMIMO->getTxGlobalGainRange(min, max, step, scale); + + if (m_streamIndex == 0) { + gainDB = m_settings.m_tx0GlobalGain; + } else { + gainDB = m_settings.m_tx1GlobalGain; + } + } + + ui->gain->setMinimum(min/step); + ui->gain->setMaximum(max/step); + ui->gain->setSingleStep(1); + ui->gain->setPageStep(1); + ui->gain->setValue(getGainValue(gainDB, min, max, step, scale)); + ui->gainText->setText(tr("%1 dB").arg(QString::number(gainDB, 'f', 2))); +} + +bool BladeRF2MIMOGui::handleMessage(const Message& message) +{ + if (DSPMIMOSignalNotification::match(message)) + { + const DSPMIMOSignalNotification& notif = (const DSPMIMOSignalNotification&) message; + int istream = notif.getIndex(); + bool sourceOrSink = notif.getSourceOrSink(); + + if (sourceOrSink) + { + m_rxBasebandSampleRate = notif.getSampleRate(); + m_rxDeviceCenterFrequency = notif.getCenterFrequency(); + } + else + { + m_txBasebandSampleRate = notif.getSampleRate(); + 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; + } + else if (BladeRF2MIMO::MsgConfigureBladeRF2MIMO::match(message)) + { + const BladeRF2MIMO::MsgConfigureBladeRF2MIMO& notif = (const BladeRF2MIMO::MsgConfigureBladeRF2MIMO&) message; + m_settings = notif.getSettings(); + displaySettings(); + + 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() +{ + if (m_spectrumRxElseTx) + { + m_deviceUISet->getSpectrum()->setSampleRate(m_rxBasebandSampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_rxDeviceCenterFrequency); + } + else + { + m_deviceUISet->getSpectrum()->setSampleRate(m_txBasebandSampleRate); + m_deviceUISet->getSpectrum()->setCenterFrequency(m_txDeviceCenterFrequency); + } +} + +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); + m_deviceUISet->m_spectrum->setDisplayedStream(m_spectrumRxElseTx, m_spectrumStreamIndex); + m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(m_spectrumRxElseTx, m_spectrumStreamIndex); + m_deviceUISet->setSpectrumScalingFactor(m_spectrumRxElseTx ? SDR_RX_SCALEF : SDR_TX_SCALEF); + updateSampleRateAndFrequency(); +} + +void BladeRF2MIMOGui::on_spectrumIndex_currentIndexChanged(int index) +{ + m_spectrumStreamIndex = index < 0 ? 0 : index > 1 ? 1 : index; + m_deviceUISet->m_spectrum->setDisplayedStream(m_spectrumRxElseTx, m_spectrumStreamIndex); + m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(m_spectrumRxElseTx, m_spectrumStreamIndex); + updateSampleRateAndFrequency(); +} + +void BladeRF2MIMOGui::on_startStopRx_toggled(bool checked) +{ + if (m_doApplySettings) + { + BladeRF2MIMO::MsgStartStop *message = BladeRF2MIMO::MsgStartStop::create(checked, true); + m_sampleMIMO->getInputMessageQueue()->push(message); + } +} + +void BladeRF2MIMOGui::on_startStopTx_toggled(bool checked) +{ + if (m_doApplySettings) + { + BladeRF2MIMO::MsgStartStop *message = BladeRF2MIMO::MsgStartStop::create(checked, false); + 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); + } + } + + displaySampleRate(); + displayFcTooltip(); + sendSettings(); +} + +void BladeRF2MIMOGui::on_fcPos_currentIndexChanged(int index) +{ + if (m_rxElseTx) { + m_settings.m_fcPosRx = (BladeRF2MIMOSettings::fcPos_t) (index < 0 ? 0 : index > 2 ? 2 : index); + } else { + m_settings.m_fcPosTx = (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_gainLock_toggled(bool checked) +{ + if (!m_gainLock && checked) + { + m_settings.m_rx1GlobalGain = m_settings.m_rx0GlobalGain; + m_settings.m_rx1GainMode = m_settings.m_rx0GainMode; + m_settings.m_tx1GlobalGain = m_settings.m_tx0GlobalGain; + sendSettings(); + } + + m_gainLock = checked; +} + +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 || m_gainLock) + { + if (m_settings.m_rx0GainMode != mode.m_value) + { + if (mode.m_value == BLADERF_GAIN_MANUAL) + { + setGainFromValue(ui->gain->value()); + ui->gain->setEnabled(true); + } else { + ui->gain->setEnabled(false); + } + } + + m_settings.m_rx0GainMode = mode.m_value; + } + + if (m_streamIndex == 1 || m_gainLock) + { + if (m_settings.m_rx1GainMode != mode.m_value) + { + if (mode.m_value == BLADERF_GAIN_MANUAL) + { + setGainFromValue(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) +{ + float gainDB = setGainFromValue(value); + ui->gainText->setText(tr("%1 dB").arg(QString::number(gainDB, 'f', 2))); + sendSettings(); +} + +float BladeRF2MIMOGui::setGainFromValue(int value) +{ + int min, max, step; + float scale, gainDB; + + if (m_rxElseTx) + { + m_sampleMIMO->getRxGlobalGainRange(min, max, step, scale); + gainDB = getGainDB(value, min, max, step, scale); + + if (m_streamIndex == 0 || m_gainLock) { + m_settings.m_rx0GlobalGain = (int) gainDB; + } + if (m_streamIndex == 1 || m_gainLock) { + m_settings.m_rx1GlobalGain = (int) gainDB; + } + } + else + { + m_sampleMIMO->getTxGlobalGainRange(min, max, step, scale); + gainDB = getGainDB(value, min, max, step, scale); + + if (m_streamIndex == 0 || m_gainLock) { + m_settings.m_tx0GlobalGain = (int) gainDB; + } + if (m_streamIndex == 1 || m_gainLock) { + m_settings.m_tx1GlobalGain = (int) gainDB; + } + } + + return gainDB; +} + +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(); + m_settings.m_iqOrder = ui->transverter->getIQOrder(); + 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; + float scale; + + if (m_rxElseTx) + { + qint64 deltaFrequency = m_settings.m_rxTransverterMode ? m_settings.m_rxTransverterDeltaFrequency/1000 : 0; + m_sampleMIMO->getRxFrequencyRange(f_min, f_max, step, scale); + 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, scale); + 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 stateRx = m_deviceUISet->m_deviceAPI->state(0); + int stateTx = m_deviceUISet->m_deviceAPI->state(1); + + if (m_lastRxEngineState != stateRx) + { + qDebug("BladeRF2MIMOGui::updateStatus: stateRx: %d", (int) stateRx); + switch(stateRx) + { + case DeviceAPI::StNotStarted: + ui->startStopRx->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + break; + case DeviceAPI::StIdle: + ui->startStopRx->setStyleSheet("QToolButton { background-color : blue; }"); + break; + case DeviceAPI::StRunning: + ui->startStopRx->setStyleSheet("QToolButton { background-color : green; }"); + break; + case DeviceAPI::StError: + ui->startStopRx->setStyleSheet("QToolButton { background-color : red; }"); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage(0)); + break; + default: + break; + } + + m_lastRxEngineState = stateRx; + } + + if (m_lastTxEngineState != stateTx) + { + qDebug("BladeRF2MIMOGui::updateStatus: stateTx: %d", (int) stateTx); + switch(stateTx) + { + case DeviceAPI::StNotStarted: + ui->startStopTx->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + break; + case DeviceAPI::StIdle: + ui->startStopTx->setStyleSheet("QToolButton { background-color : blue; }"); + break; + case DeviceAPI::StRunning: + ui->startStopTx->setStyleSheet("QToolButton { background-color : green; }"); + break; + case DeviceAPI::StError: + ui->startStopTx->setStyleSheet("QToolButton { background-color : red; }"); + QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage(1)); + break; + default: + break; + } + + m_lastTxEngineState = stateTx; + } +} + +void BladeRF2MIMOGui::openDeviceSettingsDialog(const QPoint& p) +{ + BasicDeviceSettingsDialog dialog(this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + + sendSettings(); +} + +float BladeRF2MIMOGui::getGainDB(int gainValue, int gainMin, int gainMax, int gainStep, float gainScale) +{ + float gain = gainValue * gainStep * gainScale; + qDebug("BladeRF2MIMOGui::getGainDB: gainValue: %d gainMin: %d gainMax: %d gainStep: %d gainScale: %f gain: %f", + gainValue, gainMin, gainMax, gainStep, gainScale, gain); + return gain; +} + +int BladeRF2MIMOGui::getGainValue(float gainDB, int gainMin, int gainMax, int gainStep, float gainScale) +{ + int gain = (gainDB/gainScale) / gainStep; + qDebug("BladeRF2MIMOGui::getGainValue: gainDB: %f m_gainMin: %d m_gainMax: %d m_gainStep: %d gainScale: %f gain: %d", + gainDB, gainMin, gainMax, gainStep, gainScale, gain); + return gain; +} diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h new file mode 100644 index 000000000..23101e88d --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.h @@ -0,0 +1,127 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "device/devicegui.h" + +#include "bladerf2mimosettings.h" + +class DeviceUISet; +class BladeRF2MIMO; + +namespace Ui { + class BladeRF2MIMOGui; +} + +class BladeRF2MIMOGui : public DeviceGUI { + Q_OBJECT +public: + explicit BladeRF2MIMOGui(DeviceUISet *deviceUISet, QWidget* parent = nullptr); + virtual ~BladeRF2MIMOGui(); + virtual void destroy(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + +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 + bool m_gainLock; //!< Lock Rx or Tx channel gains (set channel gains to gain of channel 0 when engaged) + QTimer m_updateTimer; + QTimer m_statusTimer; + bool m_doApplySettings; + bool m_forceSettings; + BladeRF2MIMO* m_sampleMIMO; + std::size_t m_tickCount; + int m_rxBasebandSampleRate; + int m_txBasebandSampleRate; + quint64 m_rxDeviceCenterFrequency; //!< Center frequency in Rx device + quint64 m_txDeviceCenterFrequency; //!< Center frequency in Tx device + int m_lastRxEngineState; + int m_lastTxEngineState; + 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; + int m_gainMinRx, m_gainMaxRx, m_gainStepRx; + int m_gainMinTx, m_gainMaxTx, m_gainStepTx; + float m_fScaleRx, m_fScaleTx; + float m_srScaleRx, m_srScaleTx; + float m_bwScaleRx, m_bwScaleTx; + float m_gainScaleRx, m_gainScaleTx; + + void blockApplySettings(bool block) { m_doApplySettings = !block; } + void displaySettings(); + void displaySampleRate(); + void displayFcTooltip(); + void displayGainModes(); + void displayGain(); + void sendSettings(); + void updateSampleRateAndFrequency(); + void updateFrequencyLimits(); + void setCenterFrequencySetting(uint64_t kHzValue); + float getGainDB(int gainValue, int gainMin, int gainMax, int gainStep, float gainScale); + int getGainValue(float gainDB, int gainMin, int gainMax, int gainStep, float gainScale); + float setGainFromValue(int value); + bool handleMessage(const Message& message); + +private slots: + void handleInputMessages(); + void updateHardware(); + void updateStatus(); + 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_startStopRx_toggled(bool checked); + void on_startStopTx_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_gainLock_toggled(bool checked); + void on_gainMode_currentIndexChanged(int index); + void on_gain_valueChanged(int value); + void on_biasTee_toggled(bool checked); + void on_transverter_clicked(); + void openDeviceSettingsDialog(const QPoint& p); +}; + +#endif // _BLADERF2MIMO_BLADERF2MIMOGUI_H_ diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui new file mode 100644 index 000000000..8b862d61e --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimogui.ui @@ -0,0 +1,812 @@ + + + BladeRF2MIMOGui + + + + 0 + 0 + 366 + 228 + + + + + 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::Vertical + + + + + + + Rx + + + + + + + start/stop acquisition (Rx) + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + Tx + + + + + + + start/stop generation (Tx) + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 4 + + + + + + + + + + + + + 50 + 0 + + + + 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 + + + 1 + + + Qt::Horizontal + + + + + + + Gain mode + + + + + + + Gain + + + + + + + + 45 + 0 + + + + 000 dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Bias Tee + + + BT + + + + + + + Lock both channels gain + + + + + + + :/unlocked.png + :/locked.png:/unlocked.png + + + true + + + + + + + + + Qt::Horizontal + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + ValueDial + QWidget +
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + TransverterButton + QPushButton +
gui/transverterbutton.h
+
+
+ + + + +
diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimoplugin.cpp b/plugins/samplemimo/bladerf2mimo/bladerf2mimoplugin.cpp new file mode 100644 index 000000000..01c41a6d3 --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimoplugin.cpp @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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" + +#ifndef SERVER_MODE +#include "bladerf2mimogui.h" +#endif +#include "bladerf2mimo.h" +#include "bladerf2mimoplugin.h" +#include "bladerf2mimowebapiadapter.h" + +const PluginDescriptor BladeRF2MIMOPlugin::m_pluginDescriptor = { + QString("BladeRF2"), + QString("BladeRF2 MIMO"), + QString("5.13.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 +DeviceGUI* BladeRF2MIMOPlugin::createSampleMIMOPluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + (void) sourceId; + (void) widget; + (void) deviceUISet; + return nullptr; +} +#else +DeviceGUI* 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 +{ + return new BladeRF2MIMOWebAPIAdapter(); +} diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mimoplugin.h b/plugins/samplemimo/bladerf2mimo/bladerf2mimoplugin.h new file mode 100644 index 000000000..e31779a27 --- /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 DeviceGUI* 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..650dd62f1 --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.cpp @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_fcPosRx = 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_iqOrder = true; + + m_txCenterFrequency = 435000*1000; + m_log2Interp = 0; + m_fcPosTx = FC_POS_CENTER; + m_txBandwidth = 1500000; + m_tx0GlobalGain = -3; + m_tx1GlobalGain = -3; + m_txBiasTee = false; + m_txTransverterMode = false; + m_txTransverterDeltaFrequency = 0; + + 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_fcPosRx); + 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.writeBool(23, m_iqOrder); + + 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.writeS32(38, (int) m_fcPosTx); + + 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, 0); + + d.readU64(10, &m_rxCenterFrequency, 435000*1000); + d.readU32(11, &m_log2Decim); + d.readS32(12, &intval, 0); + m_fcPosRx = (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.readBool(23, &m_iqOrder, true); + + 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.readS32(38, &intval, 2); + m_fcPosTx = (fcPos_t) intval; + + 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..2f2104630 --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mimosettings.h @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_fcPosRx; + 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; + bool m_iqOrder; //!< true: IQ - false: QI + + quint64 m_txCenterFrequency; + quint32 m_log2Interp; + fcPos_t m_fcPosTx; + qint32 m_txBandwidth; + int m_tx0GlobalGain; + int m_tx1GlobalGain; + bool m_txBiasTee; + bool m_txTransverterMode; + qint64 m_txTransverterDeltaFrequency; + + 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..8117f7469 --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mithread.cpp @@ -0,0 +1,343 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "bladerf2/devicebladerf2shared.h" +#include "dsp/samplemififo.h" + +#include "bladerf2mithread.h" + +BladeRF2MIThread::BladeRF2MIThread(struct bladerf* dev, QObject* parent) : + QThread(parent), + m_running(false), + m_dev(dev), + m_sampleFifo(nullptr), + m_iqOrder(true) +{ + 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::run() +{ + int res; + + m_running = true; + m_startWaiter.wakeAll(); + + int status = bladerf_sync_config(m_dev, BLADERF_RX_X2, BLADERF_FORMAT_SC16_Q11, 64, DeviceBladeRF2::blockSize, 32, 1500); + + 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; + } + + std::vector vbegin; + int lengths[2]; + + for (unsigned int channel = 0; channel < 2; channel++) + { + if (m_iqOrder) { + lengths[channel] = channelCallbackIQ(&buf[2*samplesPerChannel*channel], 2*samplesPerChannel, channel); + } else { + lengths[channel] = channelCallbackQI(&buf[2*samplesPerChannel*channel], 2*samplesPerChannel, channel); + } + + vbegin.push_back(m_convertBuffer[channel].begin()); + } + + if (lengths[0] == lengths[1]) + { + m_sampleFifo->writeSync(vbegin, lengths[0]); + } + else + { + qWarning("BladeRF2MIThread::callback: unequal channel lengths: [0]=%d [1]=%d", lengths[0], lengths[1]); + m_sampleFifo->writeSync(vbegin, (std::min)(lengths[0], lengths[1])); + } +} + +int BladeRF2MIThread::channelCallbackIQ(const qint16* buf, qint32 len, int channel) +{ + SampleVector::iterator it = m_convertBuffer[channel].begin(); + + if (m_log2Decim == 0) + { + m_decimatorsIQ[channel].decimate1(&it, buf, len); + } + else + { + if (m_fcPos == 0) // Infra + { + switch (m_log2Decim) + { + case 1: + m_decimatorsIQ[channel].decimate2_inf(&it, buf, len); + break; + case 2: + m_decimatorsIQ[channel].decimate4_inf(&it, buf, len); + break; + case 3: + m_decimatorsIQ[channel].decimate8_inf(&it, buf, len); + break; + case 4: + m_decimatorsIQ[channel].decimate16_inf(&it, buf, len); + break; + case 5: + m_decimatorsIQ[channel].decimate32_inf(&it, buf, len); + break; + case 6: + m_decimatorsIQ[channel].decimate64_inf(&it, buf, len); + break; + default: + break; + } + } + else if (m_fcPos == 1) // Supra + { + switch (m_log2Decim) + { + case 1: + m_decimatorsIQ[channel].decimate2_sup(&it, buf, len); + break; + case 2: + m_decimatorsIQ[channel].decimate4_sup(&it, buf, len); + break; + case 3: + m_decimatorsIQ[channel].decimate8_sup(&it, buf, len); + break; + case 4: + m_decimatorsIQ[channel].decimate16_sup(&it, buf, len); + break; + case 5: + m_decimatorsIQ[channel].decimate32_sup(&it, buf, len); + break; + case 6: + m_decimatorsIQ[channel].decimate64_sup(&it, buf, len); + break; + default: + break; + } + } + else if (m_fcPos == 2) // Center + { + switch (m_log2Decim) + { + case 1: + m_decimatorsIQ[channel].decimate2_cen(&it, buf, len); + break; + case 2: + m_decimatorsIQ[channel].decimate4_cen(&it, buf, len); + break; + case 3: + m_decimatorsIQ[channel].decimate8_cen(&it, buf, len); + break; + case 4: + m_decimatorsIQ[channel].decimate16_cen(&it, buf, len); + break; + case 5: + m_decimatorsIQ[channel].decimate32_cen(&it, buf, len); + break; + case 6: + m_decimatorsIQ[channel].decimate64_cen(&it, buf, len); + break; + default: + break; + } + } + } + + return it - m_convertBuffer[channel].begin(); +} + +int BladeRF2MIThread::channelCallbackQI(const qint16* buf, qint32 len, int channel) +{ + SampleVector::iterator it = m_convertBuffer[channel].begin(); + + if (m_log2Decim == 0) + { + m_decimatorsQI[channel].decimate1(&it, buf, len); + } + else + { + if (m_fcPos == 0) // Infra + { + switch (m_log2Decim) + { + case 1: + m_decimatorsQI[channel].decimate2_inf(&it, buf, len); + break; + case 2: + m_decimatorsQI[channel].decimate4_inf(&it, buf, len); + break; + case 3: + m_decimatorsQI[channel].decimate8_inf(&it, buf, len); + break; + case 4: + m_decimatorsQI[channel].decimate16_inf(&it, buf, len); + break; + case 5: + m_decimatorsQI[channel].decimate32_inf(&it, buf, len); + break; + case 6: + m_decimatorsQI[channel].decimate64_inf(&it, buf, len); + break; + default: + break; + } + } + else if (m_fcPos == 1) // Supra + { + switch (m_log2Decim) + { + case 1: + m_decimatorsQI[channel].decimate2_sup(&it, buf, len); + break; + case 2: + m_decimatorsQI[channel].decimate4_sup(&it, buf, len); + break; + case 3: + m_decimatorsQI[channel].decimate8_sup(&it, buf, len); + break; + case 4: + m_decimatorsQI[channel].decimate16_sup(&it, buf, len); + break; + case 5: + m_decimatorsQI[channel].decimate32_sup(&it, buf, len); + break; + case 6: + m_decimatorsQI[channel].decimate64_sup(&it, buf, len); + break; + default: + break; + } + } + else if (m_fcPos == 2) // Center + { + switch (m_log2Decim) + { + case 1: + m_decimatorsQI[channel].decimate2_cen(&it, buf, len); + break; + case 2: + m_decimatorsQI[channel].decimate4_cen(&it, buf, len); + break; + case 3: + m_decimatorsQI[channel].decimate8_cen(&it, buf, len); + break; + case 4: + m_decimatorsQI[channel].decimate16_cen(&it, buf, len); + break; + case 5: + m_decimatorsQI[channel].decimate32_cen(&it, buf, len); + break; + case 6: + m_decimatorsQI[channel].decimate64_cen(&it, buf, len); + break; + default: + break; + } + } + } + + return it - m_convertBuffer[channel].begin(); +} \ 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..b25babf7a --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mithread.h @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 SampleMIFifo; + +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(SampleMIFifo *sampleFifo) { m_sampleFifo = sampleFifo; } + SampleMIFifo *getFifo() { return m_sampleFifo; } + void setIQOrder(bool iqOrder) { m_iqOrder = iqOrder; } + +private: + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + struct bladerf* m_dev; + + qint16 *m_buf; + SampleVector m_convertBuffer[2]; + SampleMIFifo* m_sampleFifo; + Decimators m_decimatorsIQ[2]; + Decimators m_decimatorsQI[2]; + unsigned int m_log2Decim; + int m_fcPos; + bool m_iqOrder; + + void run(); + void callback(const qint16* buf, qint32 samplesPerChannel); + int channelCallbackIQ(const qint16* buf, qint32 len, int channel); + int channelCallbackQI(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..362d3425c --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mothread.cpp @@ -0,0 +1,238 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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/samplemofifo.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 log2Interp) +{ + qDebug("BladeRF2MOThread::setLog2Interpolation: %u", log2Interp); + m_log2Interp = log2Interp; +} + +unsigned int BladeRF2MOThread::getLog2Interpolation() const +{ + return m_log2Interp; +} + +void BladeRF2MOThread::setFcPos(int fcPos) +{ + m_fcPos = fcPos; +} + +int BladeRF2MOThread::getFcPos() const +{ + return m_fcPos; +} + +void BladeRF2MOThread::callback(qint16* buf, qint32 samplesPerChannel) +{ + unsigned int iPart1Begin, iPart1End, iPart2Begin, iPart2End; + m_sampleFifo->readSync(samplesPerChannel/(1< decim=16). len is a number of samples (not a number of I or Q) +void BladeRF2MOThread::callbackPart(qint16* buf, qint32 nSamples, int iBegin) +{ + for (unsigned int channel = 0; channel < 2; channel++) + { + SampleVector::iterator begin = m_sampleFifo->getData(channel).begin() + iBegin; + + if (m_log2Interp == 0) + { + m_interpolators[channel].interpolate1(&begin, &buf[channel*2*nSamples], 2*nSamples); + } + else + { + if (m_fcPos == 0) // Infra + { + switch (m_log2Interp) + { + case 1: + m_interpolators[channel].interpolate2_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 2: + m_interpolators[channel].interpolate4_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 3: + m_interpolators[channel].interpolate8_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 4: + m_interpolators[channel].interpolate16_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 5: + m_interpolators[channel].interpolate32_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 6: + m_interpolators[channel].interpolate64_inf(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + default: + break; + } + } + else if (m_fcPos == 1) // Supra + { + switch (m_log2Interp) + { + case 1: + m_interpolators[channel].interpolate2_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 2: + m_interpolators[channel].interpolate4_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 3: + m_interpolators[channel].interpolate8_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 4: + m_interpolators[channel].interpolate16_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 5: + m_interpolators[channel].interpolate32_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 6: + m_interpolators[channel].interpolate64_sup(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + default: + break; + } + } + else if (m_fcPos == 2) // Center + { + switch (m_log2Interp) + { + case 1: + m_interpolators[channel].interpolate2_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 2: + m_interpolators[channel].interpolate4_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 3: + m_interpolators[channel].interpolate8_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 4: + m_interpolators[channel].interpolate16_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 5: + m_interpolators[channel].interpolate32_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + case 6: + m_interpolators[channel].interpolate64_cen(&begin, &buf[channel*2*nSamples], 2*nSamples); + break; + default: + break; + } + } + } + } +} diff --git a/plugins/samplemimo/bladerf2mimo/bladerf2mothread.h b/plugins/samplemimo/bladerf2mimo/bladerf2mothread.h new file mode 100644 index 000000000..5644bc1af --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/bladerf2mothread.h @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_BLADERF2MOTHREAD_H_ +#define PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MOTHREAD_H_ + +#include +#include +#include +#include + +#include "dsp/interpolators.h" + +class SampleMOFifo; + +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 log2Interp); + unsigned int getLog2Interpolation() const; + void setFcPos(int fcPos); + int getFcPos() const; + void setFifo(SampleMOFifo *sampleFifo) { m_sampleFifo = sampleFifo; } + SampleMOFifo *getFifo() { return m_sampleFifo; } + +private: + QMutex m_startWaitMutex; + QWaitCondition m_startWaiter; + bool m_running; + struct bladerf* m_dev; + + qint16 *m_buf; //!< Full buffer for SISO or MIMO operation + SampleMOFifo* m_sampleFifo; + Interpolators m_interpolators[2]; + unsigned int m_log2Interp; + int m_fcPos; + + void run(); + unsigned int getNbFifos(); + void callbackPart(qint16* buf, qint32 nSamples, int iBegin); + void callback(qint16* buf, qint32 samplesPerChannel); +}; + + + +#endif /* PLUGINS_SAMPLEMIMO_BLADERF2MIMO_BLADERF2MOTHREAD_H_ */ diff --git a/plugins/samplemimo/bladerf2mimo/readme.md b/plugins/samplemimo/bladerf2mimo/readme.md new file mode 100644 index 000000000..aedd9ea84 --- /dev/null +++ b/plugins/samplemimo/bladerf2mimo/readme.md @@ -0,0 +1,133 @@ +

BladeRF 2.0 micro (v2) MIMO plugin

+ +

Introduction

+ +This is a v5 only plugin. + +This MIMO plugin sends and receives its samples to/from a [BladeRF2 device](https://www.nuand.com/bladerf-2). It handles both input (Rx) and output (Tx) streams synchronously. There is no option to synchronize Rx with Tx streams. + +

Build

+ +As with other BladeRF plugins this plugin will be built only if the [BladeRF host library](https://github.com/Nuand/bladeRF) is installed in your system. If you build it from source and install it in a custom location say: `/opt/install/libbladeRF` you will have to add `-DBLADERF_DIR=/opt/install/libbladeRF/include` to the cmake command line. + +Note that libbladeRF v2 with git tag 2018.10-rc1 should be used (official release) thus: + + - The FX3 firmware version should be v2.3.1 + - The FPGA image version should be v0.9.0 + +The FPGA .rbf file should be copied to the folder where the `sdrangel` binary resides. You can download FPGA images from [here](https://www.nuand.com/fpga_images/) + +The BladeRF Host library is also provided by many Linux distributions (check its version) and is built in the SDRangel binary releases. + +

Interface

+ +![BladeRF2 MIMO plugin GUI](../../../doc/img/BladeRF2MIMO_plugin.png) + +

1. Rx/Tx settings selection

+ +Use this combo to target UI to Rx or Tx streams for Rx/Tx specific items. + +

2. Stream settings selection

+ +Use this combo to target UI to stream 0 or stream 1 for stream specific items. + +

3. Rx/Tx spectrum display selection

+ +Use this combo to select Rx or Tx side for main spectrum display. + +

4. Stream spectrum display selection

+ +Use this combo to select stream 0 or stream 1 for main spectrum display. + +

5. Start/Stop Rx

+ +This button controls the start/stop of the Rx subsystem. + +

6. Start/Stop Tx

+ +This button controls the start/stop of the Tx subsystem. + +

7. Record button

+ + - Left click: record baseband I/Q stream toggle button (inactive: waiting for synchronous streams recording) + - Right click: choose record file + +

8. Center frequency

+ +This controls the center frequency of Rx or Tx subsystems in kHz depending on the Rx/Tx settings selection (1). This frequency can effectively be different for Rx and Tx but is the same for both Rx or both Tx streams. + +

9. LO ppm correction

+ +Use this slider to adjust LO correction in ppm. It can be varied from -20.0 to 20.0 in 0.1 steps and is applied in software. This applies to the oscillator that controls both the Rx and Tx frequency therefore it is not dependent on Rx/Tx or stream selection. + +

10. DC auto correction options

+ +This button controls the local DSP DC auto remove DC component. + +

11. IQ auto correction options

+ + This button controls the local DSP auto make I/Q balance. The DC correction must be enabled for this to be effective. + +

12. Analog filter bandwidth

+ +This is the analog filter bandwidth in kHz that applies to Rx or Tx streams. Minimum and maximum values are adjusted automatically and are the same for Rx and Tx subsystems ranging from 200 kHz to 56 MHz. + +

13. Transverter mode open dialog

+ +This button opens a dialog to set the transverter mode frequency translation options. The details about this dialog can be found [here](../../../sdrgui/gui/transverterdialog.md) + +

14. Device sample rate / Baseband sample rate input toggle

+ +Use this toggle button to switch the sample rate input next (15) between device sample rate and baseband sample rate input. The button shows the current mode: + + - **SR**: device sample rate input mode. The baseband sample rate (7A) is the device sample rate (15) divided by the decimation or multiplied by the interpolation factor (17). + - **BB**: baseband sample rate input mode. The device sample rate (7A) is the baseband sample rate (15) divided by the decimation or multiplied by the interpolation factor (17). + +

15. Host/Device sample rate

+ +This controls the sample rate between Host and Device in both directions. Effectively ADC and DAC run on the same sample rate. + +

16. Baseband center frequency position relative the the BladeRF center frequency

+ +Possible values are: + + - **Cen**: the decimation operation takes place around the BladeRF Rx center frequency Fs + - **Inf**: the decimation operation takes place around Fs - Fc. + - **Sup**: the decimation operation takes place around Fs + Fc. + +With SR as the sample rate before decimation Fc is calculated as: + + - if decimation n is 4 or lower: Fc = SR/2^(log2(n)-1). The device center frequency is on the side of the baseband. You need a RF filter bandwidth at least twice the baseband. + - if decimation n is 8 or higher: Fc = SR/n. The device center frequency is half the baseband away from the side of the baseband. You need a RF filter bandwidth at least 3 times the baseband. + +

17. Decimation (Rx) or Interpolation (Tx) factor

+ +For Rx streams the I/Q stream from the BladeRF ADC is downsampled by a power of two before being sent to the passband. + +For Tx strams the baseband stream is interpolated by this value before being sent to the BladeRF device. + +Possible values are increasing powers of two: 1 (no decimation or interpolation), 2, 4, 8, 16, 32, 64. + +

18. Lock both streams gains

+ +When engaged this applies the same gain with value control (20) to both streams (Rx or Tx) + +

19. Gain mode

+ +This is the gain mode control that differs between Rx and Tx streams: + - **Rx**: + - **automatic**: AGC with default behavior + - **manual**: Manual. Use control (20) to adjust gain + - **fast**: fast AGC + - **slow**: slow AGC + - **hybrid**: hybrid AGC + - **Tx:**: + - **automatic**: Manual actually. Use control (20) to adjust gain + +

20. Manual gain setting

+ +This sets the gain manually per stream or for both streams if (18) is engaged. + +

21. Bias Tee

+ +This sets the bias tee on all Rx or Tx ports. \ No newline at end of file diff --git a/sdrbase/device/deviceapi.cpp b/sdrbase/device/deviceapi.cpp index 9e2ce4cfd..c8a1448f0 100644 --- a/sdrbase/device/deviceapi.cpp +++ b/sdrbase/device/deviceapi.cpp @@ -488,7 +488,7 @@ void DeviceAPI::loadSamplingDeviceSettings(const Preset* preset) } // set center frequency anyway - if (m_deviceSourceEngine->getSource() != 0) // Server flavor + if (m_deviceSourceEngine->getSource()) { m_deviceSourceEngine->getSource()->setCenterFrequency(centerFrequency); } @@ -510,7 +510,7 @@ void DeviceAPI::loadSamplingDeviceSettings(const Preset* preset) qDebug("DeviceAPI::loadSamplingDeviceSettings: deserializing sink %s[%d]: %s", qPrintable(m_samplingDeviceId), m_samplingDeviceSequence, qPrintable(m_samplingDeviceSerial)); - if (m_deviceSinkEngine->getSink() != 0) // Server flavor + if (m_deviceSinkEngine->getSink()) { m_deviceSinkEngine->getSink()->deserialize(*sinkConfig); m_deviceSinkEngine->getSink()->setCenterFrequency(centerFrequency); @@ -539,10 +539,11 @@ void DeviceAPI::loadSamplingDeviceSettings(const Preset* preset) qDebug("DeviceAPI::loadSamplingDeviceSettings: deserializing MIMO %s[%d]: %s", qPrintable(m_samplingDeviceId), m_samplingDeviceSequence, qPrintable(m_samplingDeviceSerial)); - if (m_deviceSinkEngine->getSink() != 0) // Server flavor + if (m_deviceMIMOEngine->getMIMO()) { - m_deviceSinkEngine->getSink()->deserialize(*mimoConfig); - m_deviceSinkEngine->getSink()->setCenterFrequency(centerFrequency); + m_deviceMIMOEngine->getMIMO()->deserialize(*mimoConfig); + m_deviceMIMOEngine->getMIMO()->setSourceCenterFrequency(centerFrequency, 0); + m_deviceMIMOEngine->getMIMO()->setSinkCenterFrequency(centerFrequency, 0); } else {