diff --git a/plugins/samplemimo/xtrxmimo/CMakeLists.txt b/plugins/samplemimo/xtrxmimo/CMakeLists.txt
new file mode 100644
index 000000000..0215dc376
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/CMakeLists.txt
@@ -0,0 +1,62 @@
+project(xtrxmimo)
+
+set(xtrxmimo_SOURCES
+ xtrxmimo.cpp
+ xtrxmimoplugin.cpp
+ xtrxmithread.cpp
+ xtrxmothread.cpp
+ xtrxmimosettings.cpp
+ xtrxmimowebapiadapter.cpp
+)
+
+set(xtrxmimo_HEADERS
+ xtrxmimo.h
+ xtrxmimoplugin.h
+ xtrxmithread.h
+ xtrxmothread.h
+ xtrxmimosettings.h
+ xtrxmimowebapiadapter.h
+)
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
+ ${CMAKE_SOURCE_DIR}/devices
+ ${LIBXTRX_INCLUDE_DIRS}
+)
+
+if (NOT SERVER_MODE)
+ set (xtrxmimo_SOURCES
+ ${xtrxmimo_SOURCES}
+ xtrxmimogui.cpp
+ xtrxmimogui.ui
+ )
+ set(xtrxmimo_HEADERS
+ ${xtrxmimo_HEADERS}
+ xtrxmimogui.h
+ )
+ set(TARGET_NAME mimoxtrx)
+ set(TARGET_LIB "Qt5::Widgets")
+ set(TARGET_LIB_GUI "sdrgui")
+ set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
+else()
+ set(TARGET_NAME mimoxtrxsrv)
+ set(TARGET_LIB "")
+ set(TARGET_LIB_GUI "")
+ set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
+endif()
+
+add_library(${TARGET_NAME} SHARED
+ ${xtrxmimo_SOURCES}
+)
+
+target_link_libraries(${TARGET_NAME}
+ Qt5::Core
+ ${TARGET_LIB}
+ sdrbase
+ ${TARGET_LIB_GUI}
+ swagger
+ ${LIBXTRX_LIBRARY}
+ xtrxdevice
+)
+
+install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimo.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimo.cpp
new file mode 100644
index 000000000..8fac703b1
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmimo.cpp
@@ -0,0 +1,1306 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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 "device/deviceapi.h"
+#include "dsp/dspcommands.h"
+#include "dsp/dspengine.h"
+#include "dsp/dspdevicemimoengine.h"
+#include "dsp/devicesamplesource.h"
+#include "dsp/devicesamplesink.h"
+#include "dsp/filerecord.h"
+#include "xtrx/devicextrxparam.h"
+#include "xtrx/devicextrxshared.h"
+#include "xtrx/devicextrx.h"
+
+#include "xtrxmithread.h"
+#include "xtrxmothread.h"
+#include "xtrxmimo.h"
+
+MESSAGE_CLASS_DEFINITION(XTRXMIMO::MsgConfigureXTRXMIMO, Message)
+MESSAGE_CLASS_DEFINITION(XTRXMIMO::MsgGetStreamInfo, Message)
+MESSAGE_CLASS_DEFINITION(XTRXMIMO::MsgGetDeviceInfo, Message)
+MESSAGE_CLASS_DEFINITION(XTRXMIMO::MsgReportClockGenChange, Message)
+MESSAGE_CLASS_DEFINITION(XTRXMIMO::MsgReportStreamInfo, Message)
+MESSAGE_CLASS_DEFINITION(XTRXMIMO::MsgFileRecord, Message)
+MESSAGE_CLASS_DEFINITION(XTRXMIMO::MsgStartStop, Message)
+
+XTRXMIMO::XTRXMIMO(DeviceAPI *deviceAPI) :
+ m_deviceAPI(deviceAPI),
+ m_settings(),
+ m_sourceThread(nullptr),
+ m_sinkThread(nullptr),
+ m_deviceDescription("XTRXMIMO"),
+ m_runningRx(false),
+ m_runningTx(false),
+ m_open(false)
+{
+ m_open = openDevice();
+ 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*)));
+}
+
+XTRXMIMO::~XTRXMIMO()
+{
+ disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
+ delete m_networkManager;
+ closeDevice();
+}
+
+bool XTRXMIMO::openDevice()
+{
+ m_deviceShared.m_dev = new DeviceXTRX();
+ char serial[256];
+ strcpy(serial, qPrintable(m_deviceAPI->getSamplingDeviceSerial()));
+
+ if (!m_deviceShared.m_dev->open(serial))
+ {
+ qCritical("XTRXMIMO::openDevice: cannot open XTRX device");
+ return false;
+ }
+
+ return true;
+}
+
+void XTRXMIMO::closeDevice()
+{
+ if (m_runningRx) {
+ stopRx();
+ }
+
+ if (m_runningTx) {
+ stopTx();
+ }
+
+ m_deviceShared.m_dev->close();
+ delete m_deviceShared.m_dev;
+ m_deviceShared.m_dev = nullptr;
+}
+
+void XTRXMIMO::destroy()
+{
+ delete this;
+}
+
+void XTRXMIMO::init()
+{
+ applySettings(m_settings, true);
+}
+
+bool XTRXMIMO::startRx()
+{
+ qDebug("XTRXMIMO::startRx");
+
+ if (!m_open)
+ {
+ qCritical("XTRXMIMO::startRx: device was not opened");
+ return false;
+ }
+
+ QMutexLocker mutexLocker(&m_mutex);
+
+ if (m_runningRx) {
+ stopRx();
+ }
+
+ m_sourceThread = new XTRXMIThread(m_deviceShared.m_dev->getDevice());
+ m_sampleMIFifo.reset();
+ m_sourceThread->setFifo(&m_sampleMIFifo);
+ m_sourceThread->setLog2Decimation(m_settings.m_log2SoftDecim);
+ m_sourceThread->startWork();
+ mutexLocker.unlock();
+ m_runningRx = true;
+
+ return true;
+}
+
+bool XTRXMIMO::startTx()
+{
+ qDebug("XTRXMIMO::startTx");
+
+ if (!m_open)
+ {
+ qCritical("XTRXMIMO::startTx: device was not opened");
+ return false;
+ }
+
+ QMutexLocker mutexLocker(&m_mutex);
+
+ if (m_runningRx) {
+ stopRx();
+ }
+
+ m_sinkThread = new XTRXMOThread(m_deviceShared.m_dev->getDevice());
+ m_sampleMOFifo.reset();
+ m_sinkThread->setFifo(&m_sampleMOFifo);
+ m_sinkThread->setLog2Interpolation(m_settings.m_log2SoftInterp);
+ m_sinkThread->startWork();
+ mutexLocker.unlock();
+ m_runningTx = true;
+
+ return true;
+}
+
+void XTRXMIMO::stopRx()
+{
+ qDebug("XTRXMIMO::stopRx");
+
+ if (!m_sourceThread) {
+ return;
+ }
+
+ QMutexLocker mutexLocker(&m_mutex);
+
+ m_sourceThread->stopWork();
+ delete m_sourceThread;
+ m_sourceThread = nullptr;
+ m_runningRx = false;
+}
+
+void XTRXMIMO::stopTx()
+{
+ qDebug("XTRXMIMO::stopTx");
+
+ if (!m_sinkThread) {
+ return;
+ }
+
+ QMutexLocker mutexLocker(&m_mutex);
+
+ m_sinkThread->stopWork();
+ delete m_sinkThread;
+ m_sinkThread = nullptr;
+ m_runningTx = false;
+}
+
+QByteArray XTRXMIMO::serialize() const
+{
+ return m_settings.serialize();
+}
+
+bool XTRXMIMO::deserialize(const QByteArray& data)
+{
+ bool success = true;
+
+ if (!m_settings.deserialize(data))
+ {
+ m_settings.resetToDefaults();
+ success = false;
+ }
+
+ MsgConfigureXTRXMIMO* message = MsgConfigureXTRXMIMO::create(m_settings, true);
+ m_inputMessageQueue.push(message);
+
+ if (m_guiMessageQueue)
+ {
+ MsgConfigureXTRXMIMO* messageToGUI = MsgConfigureXTRXMIMO::create(m_settings, true);
+ m_guiMessageQueue->push(messageToGUI);
+ }
+
+ return success;
+}
+
+const QString& XTRXMIMO::getDeviceDescription() const
+{
+ return m_deviceDescription;
+}
+
+int XTRXMIMO::getSourceSampleRate(int index) const
+{
+ (void) index;
+ int rate = m_settings.m_devSampleRate;
+ return (rate / (1<push(messageToGUI);
+ }
+}
+
+quint64 XTRXMIMO::getSinkCenterFrequency(int index) const
+{
+ (void) index;
+ return m_settings.m_txCenterFrequency;
+}
+
+void XTRXMIMO::setSinkCenterFrequency(qint64 centerFrequency, int index)
+{
+ (void) index;
+ XTRXMIMOSettings settings = m_settings;
+ settings.m_txCenterFrequency = centerFrequency;
+
+ MsgConfigureXTRXMIMO* message = MsgConfigureXTRXMIMO::create(settings, false);
+ m_inputMessageQueue.push(message);
+
+ if (m_guiMessageQueue)
+ {
+ MsgConfigureXTRXMIMO* messageToGUI = MsgConfigureXTRXMIMO::create(settings, false);
+ m_guiMessageQueue->push(messageToGUI);
+ }
+}
+
+uint32_t XTRXMIMO::getDevSampleRate() const
+{
+ if (m_deviceShared.m_dev) {
+ return m_deviceShared.m_dev->getActualInputRate();
+ } else {
+ return m_settings.m_devSampleRate;
+ }
+}
+
+uint32_t XTRXMIMO::getLog2HardDecim() const
+{
+ if (m_deviceShared.m_dev && (m_deviceShared.m_dev->getActualInputRate() != 0.0)) {
+ return log2(m_deviceShared.m_dev->getClockGen() / m_deviceShared.m_dev->getActualInputRate() / 4);
+ } else {
+ return m_settings.m_log2HardDecim;
+ }
+}
+
+uint32_t XTRXMIMO::getLog2HardInterp() const
+{
+ if (m_deviceShared.m_dev && (m_deviceShared.m_dev->getActualOutputRate() != 0.0)) {
+ return log2(m_deviceShared.m_dev->getClockGen() / m_deviceShared.m_dev->getActualOutputRate() / 4);
+ } else {
+ return m_settings.m_log2HardInterp;
+ }
+}
+
+double XTRXMIMO::getClockGen() const
+{
+ if (m_deviceShared.m_dev) {
+ return m_deviceShared.m_dev->getClockGen();
+ } else {
+ return 0.0;
+ }
+}
+
+bool XTRXMIMO::handleMessage(const Message& message)
+{
+ if (MsgConfigureXTRXMIMO::match(message))
+ {
+ MsgConfigureXTRXMIMO& conf = (MsgConfigureXTRXMIMO&) message;
+ qDebug() << "XTRXMIMO::handleMessage: MsgConfigureXTRXMIMO";
+
+ bool success = applySettings(conf.getSettings(), conf.getForce());
+
+ if (!success) {
+ qDebug("XTRXMIMO::handleMessage: config error");
+ }
+
+ return true;
+ }
+ else if (MsgFileRecord::match(message))
+ {
+ // TODO
+ // MsgFileRecord& conf = (MsgFileRecord&) message;
+ // qDebug() << "XTRXMIMO::handleMessage: MsgFileRecord: " << conf.getStartStop();
+ // int istream = conf.getStreamIndex();
+
+ // if (conf.getStartStop())
+ // {
+ // if (m_settings.m_fileRecordName.size() != 0) {
+ // m_fileSinks[istream]->setFileName(m_settings.m_fileRecordName + "_0.sdriq");
+ // } else {
+ // m_fileSinks[istream]->genUniqueFileName(m_deviceAPI->getDeviceUID(), istream);
+ // }
+
+ // m_fileSinks[istream]->startRecording();
+ // }
+ // else
+ // {
+ // m_fileSinks[istream]->stopRecording();
+ // }
+
+ return true;
+ }
+ else if (MsgGetStreamInfo::match(message))
+ {
+ if (getMessageQueueToGUI() && m_deviceShared.m_dev && m_deviceShared.m_dev->getDevice())
+ {
+ uint64_t fifolevelRx = 0;
+ uint64_t fifolevelTx = 0;
+
+ xtrx_val_get(m_deviceShared.m_dev->getDevice(), XTRX_RX, XTRX_CH_AB, XTRX_PERF_LLFIFO, &fifolevelRx);
+ xtrx_val_get(m_deviceShared.m_dev->getDevice(), XTRX_TX, XTRX_CH_AB, XTRX_PERF_LLFIFO, &fifolevelTx);
+
+ MsgReportStreamInfo *report = MsgReportStreamInfo::create(
+ true,
+ true,
+ fifolevelRx,
+ fifolevelTx,
+ 65536);
+
+ getMessageQueueToGUI()->push(report);
+ }
+
+ return true;
+ }
+ else if (MsgGetDeviceInfo::match(message))
+ {
+ double board_temp = 0.0;
+ bool gps_locked = false;
+
+ if (!m_deviceShared.m_dev->getDevice() || ((board_temp = m_deviceShared.get_board_temperature() / 256.0) == 0.0)) {
+ qDebug("XTRXMIMO::handleMessage: MsgGetDeviceInfo: cannot get board temperature");
+ }
+
+ if (!m_deviceShared.m_dev->getDevice()) {
+ qDebug("XTRXMIMO::handleMessage: MsgGetDeviceInfo: cannot get GPS lock status");
+ } else {
+ gps_locked = m_deviceShared.get_gps_status();
+ }
+
+ // send to oneself
+ if (getMessageQueueToGUI())
+ {
+ DeviceXTRXShared::MsgReportDeviceInfo *report = DeviceXTRXShared::MsgReportDeviceInfo::create(board_temp, gps_locked);
+ getMessageQueueToGUI()->push(report);
+ }
+
+ return true;
+ }
+ else if (MsgStartStop::match(message))
+ {
+ MsgStartStop& cmd = (MsgStartStop&) message;
+ qDebug() << "XTRXMIMO::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 XTRXMIMO::applySettings(const XTRXMIMOSettings& settings, bool force)
+{
+ QList reverseAPIKeys;
+ bool rxThreadWasRunning = false;
+ bool txThreadWasRunning = false;
+ bool doRxLPCalibration = false;
+ bool doRxChangeSampleRate = false;
+ bool doRxChangeFreq = false;
+ bool doTxLPCalibration = false;
+ bool doTxChangeSampleRate = false;
+ bool doTxChangeFreq = false;
+ bool forceNCOFrequencyRx = false;
+ bool forceNCOFrequencyTx = false;
+ bool forwardChangeRxDSP = false;
+ bool forwardChangeTxDSP = false;
+
+ qint64 rxXlatedDeviceCenterFrequency = settings.m_rxCenterFrequency;
+ qint64 txXlatedDeviceCenterFrequency = settings.m_txCenterFrequency;
+
+ qDebug() << "XTRXMIMO::applySettings: common:"
+ << " m_devSampleRate: " << settings.m_devSampleRate
+ << " m_extClock: " << settings.m_extClock
+ << " m_extClockFreq: " << settings.m_extClockFreq
+ << " force: " << force;
+ qDebug() << "XTRXMIMO::applySettings: Rx:"
+ << " m_rxCenterFrequency: " << settings.m_txCenterFrequency
+ << " m_log2HardDecim: " << settings.m_log2HardDecim
+ << " m_log2SoftDecim: " << settings.m_log2SoftDecim
+ << " m_dcBlock: " << settings.m_dcBlock
+ << " m_iqCorrection: " << settings.m_iqCorrection
+ << " m_ncoEnableRx: " << settings.m_ncoEnableRx
+ << " m_ncoFrequencyRx: " << settings.m_ncoFrequencyRx
+ << " m_antennaPathRx: " << settings.m_antennaPathRx;
+ qDebug() << "XTRXMIMO::applySettings: Rx0:"
+ << " m_gainRx0: " << settings.m_gainRx0
+ << " m_lpfBWRx0: " << settings.m_lpfBWRx0
+ << " m_pwrmodeRx0: " << settings.m_pwrmodeRx0;
+ qDebug() << "XTRXMIMO::applySettings: Rx1:"
+ << " m_gainRx1: " << settings.m_gainRx1
+ << " m_lpfBWRx1: " << settings.m_lpfBWRx1
+ << " m_pwrmodeRx1: " << settings.m_pwrmodeRx1;
+ qDebug() << "XTRXMIMO::applySettings: Tx:"
+ << " m_txCenterFrequency: " << settings.m_txCenterFrequency
+ << " m_log2HardInterp: " << settings.m_log2HardInterp
+ << " m_log2SoftInterp: " << settings.m_log2SoftInterp
+ << " m_ncoEnableTx0: " << settings.m_ncoEnableTx
+ << " m_ncoFrequencyTx: " << settings.m_ncoFrequencyTx
+ << " m_antennaPathTx: " << settings.m_antennaPathTx;
+ qDebug() << "XTRXMIMO::applySettings: Tx0:"
+ << " m_gainTx0: " << settings.m_gainTx0
+ << " m_lpfBWTx0: " << settings.m_lpfBWTx0
+ << " m_pwrmodeTx0: " << settings.m_pwrmodeTx0;
+ qDebug() << "XTRXMIMO::applySettings: Tx0:"
+ << " m_gainTx1: " << settings.m_gainTx1
+ << " m_lpfBWTx1: " << settings.m_lpfBWTx1
+ << " m_pwrmodeTx1: " << settings.m_pwrmodeTx1;
+
+ // common
+
+ if ((m_settings.m_extClock != settings.m_extClock) || force) {
+ reverseAPIKeys.append("extClock");
+ }
+ if ((m_settings.m_extClockFreq != settings.m_extClockFreq) || force) {
+ reverseAPIKeys.append("extClockFreq");
+ }
+
+ if ((m_settings.m_extClock != settings.m_extClock)
+ || (settings.m_extClock && (m_settings.m_extClockFreq != settings.m_extClockFreq)) || force)
+ {
+ if (m_deviceShared.m_dev->getDevice() != 0)
+ {
+ xtrx_set_ref_clk(m_deviceShared.m_dev->getDevice(),
+ (settings.m_extClock) ? settings.m_extClockFreq : 0,
+ (settings.m_extClock) ? XTRX_CLKSRC_EXT : XTRX_CLKSRC_INT);
+ {
+ doRxChangeSampleRate = true;
+ doTxChangeSampleRate = true;
+ doRxChangeFreq = true;
+ doTxChangeFreq = true;
+ qDebug("XTRXMIMO::applySettings: clock set to %s (Ext: %d Hz)",
+ settings.m_extClock ? "external" : "internal",
+ settings.m_extClockFreq);
+ }
+ }
+ }
+
+ if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) {
+ reverseAPIKeys.append("devSampleRate");
+ }
+
+ // Rx
+
+ if ((m_settings.m_dcBlock != settings.m_dcBlock) || force)
+ {
+ reverseAPIKeys.append("dcBlock");
+ m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection);
+ }
+
+ if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force)
+ {
+ reverseAPIKeys.append("iqCorrection");
+ m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection);
+ }
+
+ if ((m_settings.m_log2HardDecim != settings.m_log2HardDecim) || force) {
+ reverseAPIKeys.append("log2HardDecim");
+ }
+
+ if ((m_settings.m_devSampleRate != settings.m_devSampleRate)
+ || (m_settings.m_log2HardDecim != settings.m_log2HardDecim) || force)
+ {
+ forwardChangeRxDSP = true;
+
+ if (m_deviceShared.m_dev->getDevice()) {
+ doTxChangeSampleRate = true;
+ }
+ }
+
+ if ((m_settings.m_log2SoftDecim != settings.m_log2SoftDecim) || force)
+ {
+ reverseAPIKeys.append("log2SoftDecim");
+ forwardChangeRxDSP = true;
+
+ if (m_sourceThread)
+ {
+ m_sourceThread->setLog2Decimation(settings.m_log2SoftDecim);
+ qDebug() << "XTRXMIMO::applySettings: set soft decimation to " << (1<getDevice())
+ {
+ if (xtrx_set_antenna(m_deviceShared.m_dev->getDevice(), toXTRXAntennaRx(settings.m_antennaPathRx)) < 0) {
+ qCritical("XTRXMIMO::applySettings: could not set antenna path of Rx to %d", (int) settings.m_antennaPathRx);
+ } else {
+ qDebug("XTRXMIMO::applySettings: set Rx antenna path to %d", (int) settings.m_antennaPathRx);
+ }
+ }
+ }
+
+ if ((m_settings.m_rxCenterFrequency != settings.m_rxCenterFrequency) || force)
+ {
+ reverseAPIKeys.append("rxCenterFrequency");
+ doRxChangeFreq = true;
+ }
+
+ // Rx0/1
+
+ if ((m_settings.m_pwrmodeRx0 != settings.m_pwrmodeRx0))
+ {
+ reverseAPIKeys.append("pwrmodeRx0");
+
+ if (m_deviceShared.m_dev->getDevice())
+ {
+ if (xtrx_val_set(m_deviceShared.m_dev->getDevice(),
+ XTRX_TRX,
+ XTRX_CH_A,
+ XTRX_LMS7_PWR_MODE,
+ settings.m_pwrmodeRx0) < 0) {
+ qCritical("XTRXMIMO::applySettings: could not set Rx0 power mode %d", settings.m_pwrmodeRx0);
+ }
+ }
+ }
+
+ if ((m_settings.m_pwrmodeRx1 != settings.m_pwrmodeRx1))
+ {
+ reverseAPIKeys.append("pwrmodeRx1");
+
+ if (m_deviceShared.m_dev->getDevice())
+ {
+ if (xtrx_val_set(m_deviceShared.m_dev->getDevice(),
+ XTRX_TRX,
+ XTRX_CH_B,
+ XTRX_LMS7_PWR_MODE,
+ settings.m_pwrmodeRx1) < 0) {
+ qCritical("XTRXMIMO::applySettings: could not set Rx1 power mode %d", settings.m_pwrmodeRx0);
+ }
+ }
+ }
+
+ if ((m_settings.m_gainModeRx0 != settings.m_gainModeRx0) || force) {
+ reverseAPIKeys.append("gainModeRx0");
+ }
+ if ((m_settings.m_gainRx0 != settings.m_gainRx0) || force) {
+ reverseAPIKeys.append("gainRx0");
+ }
+ if ((m_settings.m_lnaGainRx0 != settings.m_lnaGainRx0) || force) {
+ reverseAPIKeys.append("lnaGainRx0");
+ }
+ if ((m_settings.m_tiaGainRx0 != settings.m_tiaGainRx0) || force) {
+ reverseAPIKeys.append("tiaGainRx0");
+ }
+ if ((m_settings.m_pgaGainRx0 != settings.m_pgaGainRx0) || force) {
+ reverseAPIKeys.append("pgaGainRx0");
+ }
+
+ if ((m_settings.m_gainModeRx1 != settings.m_gainModeRx1) || force) {
+ reverseAPIKeys.append("gainModeRx1");
+ }
+ if ((m_settings.m_gainRx1 != settings.m_gainRx1) || force) {
+ reverseAPIKeys.append("gainRx1");
+ }
+ if ((m_settings.m_lnaGainRx1 != settings.m_lnaGainRx1) || force) {
+ reverseAPIKeys.append("lnaGainRx1");
+ }
+ if ((m_settings.m_tiaGainRx1 != settings.m_tiaGainRx1) || force) {
+ reverseAPIKeys.append("tiaGainRx1");
+ }
+ if ((m_settings.m_pgaGainRx1 != settings.m_pgaGainRx1) || force) {
+ reverseAPIKeys.append("pgaGainRx1");
+ }
+
+ if (m_deviceShared.m_dev->getDevice())
+ {
+ bool doGainAuto = false;
+ bool doGainLna = false;
+ bool doGainTia = false;
+ bool doGainPga = false;
+
+ if ((m_settings.m_gainModeRx0 != settings.m_gainModeRx0) || force)
+ {
+ if (settings.m_gainModeRx0 == XTRXMIMOSettings::GAIN_AUTO)
+ {
+ doGainAuto = true;
+ }
+ else
+ {
+ doGainLna = true;
+ doGainTia = true;
+ doGainPga = true;
+ }
+ }
+ else if (m_settings.m_gainModeRx0 == XTRXMIMOSettings::GAIN_AUTO)
+ {
+ if (m_settings.m_gainRx0 != settings.m_gainRx0) {
+ doGainAuto = true;
+ }
+ }
+ else if (m_settings.m_gainModeRx0 == XTRXMIMOSettings::GAIN_MANUAL)
+ {
+ if (m_settings.m_lnaGainRx0 != settings.m_lnaGainRx0) {
+ doGainLna = true;
+ }
+ if (m_settings.m_tiaGainRx0 != settings.m_tiaGainRx0) {
+ doGainTia = true;
+ }
+ if (m_settings.m_pgaGainRx0 != settings.m_pgaGainRx0) {
+ doGainPga = true;
+ }
+ }
+
+ if (doGainAuto) {
+ applyGainAuto(0, m_settings.m_gainRx0);
+ }
+ if (doGainLna) {
+ applyGainLNA(0, m_settings.m_lnaGainRx0);
+ }
+ if (doGainTia) {
+ applyGainTIA(0, tiaToDB(m_settings.m_tiaGainRx0));
+ }
+ if (doGainPga) {
+ applyGainPGA(0, m_settings.m_pgaGainRx0);
+ }
+
+ doGainAuto = false;
+ doGainLna = false;
+ doGainTia = false;
+ doGainPga = false;
+
+ if ((m_settings.m_gainModeRx1 != settings.m_gainModeRx1) || force)
+ {
+ if (settings.m_gainModeRx1 == XTRXMIMOSettings::GAIN_AUTO)
+ {
+ doGainAuto = true;
+ }
+ else
+ {
+ doGainLna = true;
+ doGainTia = true;
+ doGainPga = true;
+ }
+ }
+ else if (m_settings.m_gainModeRx1 == XTRXMIMOSettings::GAIN_AUTO)
+ {
+ if (m_settings.m_gainRx1 != settings.m_gainRx1) {
+ doGainAuto = true;
+ }
+ }
+ else if (m_settings.m_gainModeRx1 == XTRXMIMOSettings::GAIN_MANUAL)
+ {
+ if (m_settings.m_lnaGainRx1 != settings.m_lnaGainRx1) {
+ doGainLna = true;
+ }
+ if (m_settings.m_tiaGainRx1 != settings.m_tiaGainRx1) {
+ doGainTia = true;
+ }
+ if (m_settings.m_pgaGainRx1 != settings.m_pgaGainRx1) {
+ doGainPga = true;
+ }
+ }
+
+ if (doGainAuto) {
+ applyGainAuto(1, m_settings.m_gainRx1);
+ }
+ if (doGainLna) {
+ applyGainLNA(1, m_settings.m_lnaGainRx1);
+ }
+ if (doGainTia) {
+ applyGainTIA(1, tiaToDB(m_settings.m_tiaGainRx1));
+ }
+ if (doGainPga) {
+ applyGainPGA(1, m_settings.m_pgaGainRx1);
+ }
+ }
+
+ if ((m_settings.m_lpfBWRx0 != settings.m_lpfBWRx0) || force)
+ {
+ reverseAPIKeys.append("lpfBWRx0");
+
+ if (m_deviceShared.m_dev->getDevice()) {
+ doRxLPCalibration = true;
+ }
+ }
+
+ if ((m_settings.m_lpfBWRx1 != settings.m_lpfBWRx1) || force)
+ {
+ reverseAPIKeys.append("lpfBWRx1");
+
+ if (m_deviceShared.m_dev->getDevice()) {
+ doRxLPCalibration = true;
+ }
+ }
+
+ // Tx
+
+ if ((m_settings.m_log2HardInterp != settings.m_log2HardInterp) || force) {
+ reverseAPIKeys.append("log2HardInterp");
+ }
+
+ if ((m_settings.m_devSampleRate != settings.m_devSampleRate)
+ || (m_settings.m_log2HardInterp != settings.m_log2HardInterp) || force)
+ {
+ forwardChangeTxDSP = true;
+
+ if (m_deviceShared.m_dev->getDevice()) {
+ doTxChangeSampleRate = true;
+ }
+ }
+
+ if ((m_settings.m_log2SoftInterp != settings.m_log2SoftInterp) || force)
+ {
+ reverseAPIKeys.append("log2SoftInterp");
+ forwardChangeTxDSP = true;
+
+ if (m_sinkThread)
+ {
+ m_sinkThread->setLog2Interpolation(settings.m_log2SoftInterp);
+ qDebug("XTRXMIMO::applySettings: set soft interpolation to %u", (1<getDevice())
+ {
+ if (xtrx_set_antenna(m_deviceShared.m_dev->getDevice(), toXTRXAntennaTx(settings.m_antennaPathTx)) < 0) {
+ qCritical("XTRXMIMO::applySettings: could not set Tx antenna path to %d", (int) settings.m_antennaPathTx);
+ } else {
+ qDebug("XTRXMIMO::applySettings: set Tx antenna path to %d", (int) settings.m_antennaPathTx);
+ }
+ }
+ }
+
+ if ((m_settings.m_txCenterFrequency != settings.m_txCenterFrequency) || force)
+ {
+ reverseAPIKeys.append("txCenterFrequency");
+ doTxChangeFreq = true;
+ }
+
+ // Tx0
+
+ if ((m_settings.m_pwrmodeTx0 != settings.m_pwrmodeTx0))
+ {
+ reverseAPIKeys.append("pwrmodeTx0");
+
+ if (m_deviceShared.m_dev->getDevice())
+ {
+ if (xtrx_val_set(m_deviceShared.m_dev->getDevice(),
+ XTRX_TRX,
+ XTRX_CH_A,
+ XTRX_LMS7_PWR_MODE,
+ settings.m_pwrmodeTx0) < 0) {
+ qCritical("XTRXMIMO::applySettings: could not set Tx0 power mode %d", settings.m_pwrmodeTx0);
+ }
+ }
+ }
+
+ if ((m_settings.m_gainTx0 != settings.m_gainTx0) || force)
+ {
+ reverseAPIKeys.append("gainTx0");
+
+ if (m_deviceShared.m_dev->getDevice())
+ {
+ if (xtrx_set_gain(m_deviceShared.m_dev->getDevice(),
+ XTRX_CH_A,
+ XTRX_TX_PAD_GAIN,
+ settings.m_gainTx0,
+ 0) < 0) {
+ qDebug("XTRXMIMO::applySettings: Tx0 gain (PAD) set to %u failed", settings.m_gainTx0);
+ } else {
+ qDebug("XTRXMIMO::applySettings: Tx0 gain (PAD) set to %u", settings.m_gainTx0);
+ }
+ }
+ }
+
+ if ((m_settings.m_lpfBWTx0 != settings.m_lpfBWTx0) || force)
+ {
+ reverseAPIKeys.append("lpfBWTx0");
+
+ if (m_deviceShared.m_dev->getDevice()) {
+ doTxLPCalibration = true;
+ }
+ }
+
+ // Reverse API
+
+ 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);
+ }
+
+ // Post Rx
+
+ if (doRxChangeSampleRate && (settings.m_devSampleRate != 0))
+ {
+ // if (m_sourceThread && m_sourceThread->isRunning())
+ // {
+ // m_sourceThread->stopWork();
+ // rxThreadWasRunning = true;
+ // }
+
+ double master = (settings.m_log2HardDecim == 0) ? 0 : (settings.m_devSampleRate * 4 * (1 << settings.m_log2HardDecim));
+
+ if (m_deviceShared.m_dev->setSamplerate(settings.m_devSampleRate,
+ master, //(settings.m_devSampleRate<getActualInputRate(),
+ 1 << getLog2HardDecim());
+ }
+
+ // if (rxThreadWasRunning) {
+ // m_sourceThread->startWork();
+ // }
+ }
+
+ if (doRxLPCalibration)
+ {
+ if (xtrx_tune_rx_bandwidth(m_deviceShared.m_dev->getDevice(),
+ XTRX_CH_A,
+ settings.m_lpfBWRx0,
+ 0) < 0) {
+ qCritical("XTRXMIMO::applySettings: could not set Rx0 LPF to %f Hz", settings.m_lpfBWRx0);
+ } else {
+ qDebug("XTRXMIMO::applySettings: Rx0 LPF set to %f Hz", settings.m_lpfBWRx0);
+ }
+
+ if (xtrx_tune_rx_bandwidth(m_deviceShared.m_dev->getDevice(),
+ XTRX_CH_B,
+ settings.m_lpfBWRx1,
+ 0) < 0) {
+ qCritical("XTRXMIMO::applySettings: could not set Rx1 LPF to %f Hz", settings.m_lpfBWRx1);
+ } else {
+ qDebug("XTRXMIMO::applySettings: Rx1 LPF set to %f Hz", settings.m_lpfBWRx1);
+ }
+ }
+
+ if (doRxChangeFreq)
+ {
+ forwardChangeRxDSP = true;
+
+ if (m_deviceShared.m_dev->getDevice())
+ {
+ qint64 deviceCenterFrequency = DeviceSampleSource::calculateDeviceCenterFrequency(
+ rxXlatedDeviceCenterFrequency,
+ 0,
+ settings.m_log2SoftDecim,
+ DeviceSampleSource::FC_POS_CENTER,
+ settings.m_devSampleRate,
+ DeviceSampleSource::FrequencyShiftScheme::FSHIFT_STD,
+ false);
+ setRxDeviceCenterFrequency(m_deviceShared.m_dev->getDevice(), deviceCenterFrequency, 0);
+ }
+ }
+
+ if (forceNCOFrequencyRx)
+ {
+ forwardChangeRxDSP = true;
+
+ if (m_deviceShared.m_dev->getDevice())
+ {
+ if (xtrx_tune_ex(m_deviceShared.m_dev->getDevice(),
+ XTRX_TUNE_BB_RX,
+ XTRX_CH_AB,
+ (settings.m_ncoEnableRx) ? settings.m_ncoFrequencyRx : 0,
+ nullptr) < 0)
+ {
+ qCritical("XTRXMIMO::applySettings: could not %s and set Rx NCO to %d Hz",
+ settings.m_ncoEnableRx ? "enable" : "disable",
+ settings.m_ncoFrequencyRx);
+ }
+ else
+ {
+ qDebug("XTRXMIMO::applySettings: %sd and set NCO Rx to %d Hz",
+ settings.m_ncoEnableRx ? "enable" : "disable",
+ settings.m_ncoFrequencyRx);
+ }
+ }
+ }
+
+ // Post Tx
+
+ if (doTxChangeSampleRate && (settings.m_devSampleRate != 0))
+ {
+ // if (m_sinkThread && m_sinkThread->isRunning())
+ // {
+ // m_sinkThread->stopWork();
+ // txThreadWasRunning = true;
+ // }
+
+ double master = (settings.m_log2HardInterp == 0) ? 0 : (settings.m_devSampleRate * 4 * (1 << settings.m_log2HardInterp));
+
+ if (m_deviceShared.m_dev->setSamplerate(settings.m_devSampleRate,
+ master, //(settings.m_devSampleRate<getActualOutputRate(),
+ 1 << getLog2HardInterp());
+ }
+
+ // if (txThreadWasRunning) {
+ // m_sinkThread->startWork();
+ // }
+ }
+
+ if (doTxLPCalibration)
+ {
+ if (xtrx_tune_tx_bandwidth(m_deviceShared.m_dev->getDevice(),
+ XTRX_CH_A,
+ settings.m_lpfBWTx0,
+ 0) < 0) {
+ qCritical("XTRXMIMO::applySettings: could not set Tx0 LPF to %f Hz", settings.m_lpfBWTx0);
+ } else {
+ qDebug("XTRXMIMO::applySettings: Tx0 LPF set to %f Hz", settings.m_lpfBWTx0);
+ }
+
+ if (xtrx_tune_tx_bandwidth(m_deviceShared.m_dev->getDevice(),
+ XTRX_CH_B,
+ settings.m_lpfBWTx1,
+ 0) < 0) {
+ qCritical("XTRXMIMO::applySettings: could not set Tx1 LPF to %f Hz", settings.m_lpfBWTx1);
+ } else {
+ qDebug("XTRXMIMO::applySettings: Tx1 LPF set to %f Hz", settings.m_lpfBWTx1);
+ }
+ }
+
+ if (doTxChangeFreq)
+ {
+ forwardChangeTxDSP = true;
+
+ if (m_deviceShared.m_dev->getDevice())
+ {
+ qint64 deviceCenterFrequency = DeviceSampleSink::calculateDeviceCenterFrequency(
+ settings.m_txCenterFrequency,
+ 0,
+ settings.m_log2SoftInterp,
+ DeviceSampleSink::FC_POS_CENTER,
+ settings.m_devSampleRate,
+ false);
+ setTxDeviceCenterFrequency(m_deviceShared.m_dev->getDevice(), deviceCenterFrequency, 0);
+ }
+ }
+
+ if (forceNCOFrequencyTx)
+ {
+ forwardChangeTxDSP = true;
+
+ if (m_deviceShared.m_dev->getDevice())
+ {
+ if (xtrx_tune_ex(m_deviceShared.m_dev->getDevice(),
+ XTRX_TUNE_BB_TX,
+ XTRX_CH_AB,
+ (settings.m_ncoEnableTx) ? settings.m_ncoFrequencyTx : 0,
+ nullptr) < 0)
+ {
+ qCritical("XTRXMIMO::applySettings: could not %s and set Tx NCO to %d Hz",
+ settings.m_ncoEnableTx ? "enable" : "disable",
+ settings.m_ncoFrequencyTx);
+ }
+ else
+ {
+ qDebug("XTRXMIMO::applySettings: %sd and set Tx NCO to %d Hz",
+ settings.m_ncoEnableTx ? "enable" : "disable",
+ settings.m_ncoFrequencyTx);
+ }
+ }
+ }
+
+ // forward changes
+
+ if (forwardChangeRxDSP || forwardChangeTxDSP)
+ {
+ if (getMessageQueueToGUI())
+ {
+ MsgReportClockGenChange *report = MsgReportClockGenChange::create();
+ getMessageQueueToGUI()->push(report);
+ }
+ }
+
+ if (forwardChangeRxDSP)
+ {
+ int sampleRate = settings.m_devSampleRate/(1<getDeviceEngineInputMessageQueue()->push(notif0);
+ DSPMIMOSignalNotification *notif1 = new DSPMIMOSignalNotification(sampleRate, settings.m_rxCenterFrequency + ncoShift, 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 + ncoShift, false, 1);
+ m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif1);
+ }
+
+ m_settings = settings;
+ return true;
+}
+
+void XTRXMIMO::applyGainAuto(unsigned int channel, uint32_t gain)
+{
+ uint32_t lna, tia, pga;
+
+ DeviceXTRX::getAutoGains(gain, lna, tia, pga);
+
+ applyGainLNA(channel, lna);
+ applyGainTIA(channel, tiaToDB(tia));
+ applyGainPGA(channel, pga);
+}
+
+void XTRXMIMO::applyGainLNA(unsigned int channel, double gain)
+{
+ if (xtrx_set_gain(m_deviceShared.m_dev->getDevice(),
+ channel == 0 ? XTRX_CH_A : XTRX_CH_B,
+ XTRX_RX_LNA_GAIN,
+ gain,
+ 0) < 0) {
+ qDebug("XTRXMIMO::applyGainLNA: set Rx%u gain (LNA) to %f failed", channel, gain);
+ } else {
+ qDebug("XTRXMIMO::applyGainLNA: Rx%u gain (LNA) set to %f", channel, gain);
+ }
+}
+
+void XTRXMIMO::applyGainTIA(unsigned int channel, double gain)
+{
+ if (xtrx_set_gain(m_deviceShared.m_dev->getDevice(),
+ channel == 0 ? XTRX_CH_A : XTRX_CH_B,
+ XTRX_RX_TIA_GAIN,
+ gain,
+ 0) < 0) {
+ qDebug("XTRXMIMO::applyGainTIA: set Rx%u gain (TIA) to %f failed", channel, gain);
+ } else {
+ qDebug("XTRXMIMO::applyGainTIA: Rx%u gain (TIA) set to %f", channel, gain);
+ }
+}
+
+void XTRXMIMO::applyGainPGA(unsigned int channel, double gain)
+{
+ if (xtrx_set_gain(m_deviceShared.m_dev->getDevice(),
+ channel == 0 ? XTRX_CH_A : XTRX_CH_B,
+ XTRX_RX_PGA_GAIN,
+ gain,
+ 0) < 0)
+ {
+ qDebug("XTRXMIMO::applyGainPGA: set Rx%u gain (PGA) to %f failed", channel, gain);
+ }
+ else
+ {
+ qDebug("XTRXMIMO::applyGainPGA: Rx%u gain (PGA) set to %f", channel, gain);
+ }
+}
+
+double XTRXMIMO::tiaToDB(unsigned idx)
+{
+ switch (idx) {
+ case 1: return 12;
+ case 2: return 9;
+ default: return 0;
+ }
+}
+
+xtrx_antenna_t XTRXMIMO::toXTRXAntennaRx(XTRXMIMOSettings::RxAntenna antennaPath)
+{
+ switch (antennaPath) {
+ case XTRXMIMOSettings::RXANT_LO: return XTRX_RX_L;
+ case XTRXMIMOSettings::RXANT_HI: return XTRX_RX_H;
+ default: return XTRX_RX_W;
+ }
+}
+
+xtrx_antenna_t XTRXMIMO::toXTRXAntennaTx(XTRXMIMOSettings::TxAntenna antennaPath)
+{
+ switch (antennaPath) {
+ case XTRXMIMOSettings::TXANT_HI: return XTRX_TX_H;
+ default: return XTRX_TX_W;
+ }
+}
+
+void XTRXMIMO::getLORange(float& minF, float& maxF, float& stepF) const
+{
+ minF = 29e6;
+ maxF = 3840e6;
+ stepF = 10;
+ qDebug("XTRXMIMO::getLORange: min: %f max: %f step: %f",
+ minF, maxF, stepF);
+}
+
+void XTRXMIMO::getSRRange(float& minF, float& maxF, float& stepF) const
+{
+ minF = 100e3;
+ maxF = 120e6;
+ stepF = 10;
+ qDebug("XTRXMIMO::getSRRange: min: %f max: %f step: %f",
+ minF, maxF, stepF);
+}
+
+void XTRXMIMO::getLPRange(float& minF, float& maxF, float& stepF) const
+{
+ minF = 500e3;
+ maxF = 130e6;
+ stepF = 10;
+ qDebug("XTRXMIMO::getLPRange: min: %f max: %f step: %f",
+ minF, maxF, stepF);
+}
+
+void XTRXMIMO::setRxDeviceCenterFrequency(xtrx_dev *dev, quint64 freq_hz, int loPpmTenths)
+{
+ if (dev)
+ {
+ if (xtrx_tune(dev,
+ XTRX_TUNE_RX_FDD,
+ freq_hz,
+ 0) < 0) {
+ qCritical("XTRXMIMO::setRxDeviceCenterFrequency: could not set Rx frequency to %llu", freq_hz);
+ } else {
+ qDebug("XTRXMIMO::setRxDeviceCenterFrequency: Rx frequency set to %llu", freq_hz);
+ }
+ }
+}
+
+void XTRXMIMO::setTxDeviceCenterFrequency(xtrx_dev *dev, quint64 freq_hz, int loPpmTenths)
+{
+ if (dev)
+ {
+ if (xtrx_tune(dev,
+ XTRX_TUNE_TX_FDD,
+ freq_hz,
+ 0) < 0) {
+ qCritical("XTRXMIMO::setTxDeviceCenterFrequency: could not set Tx frequency to %llu", freq_hz);
+ } else {
+ qDebug("XTRXMIMO::setTxDeviceCenterFrequency: Tx frequency set to %llu", freq_hz);
+ }
+ }
+}
+
+void XTRXMIMO::webapiFormatDeviceSettings(
+ SWGSDRangel::SWGDeviceSettings& response,
+ const XTRXMIMOSettings& settings)
+{
+ // TODO
+ (void) response;
+ (void) settings;
+}
+
+void XTRXMIMO::webapiUpdateDeviceSettings(
+ XTRXMIMOSettings& settings,
+ const QStringList& deviceSettingsKeys,
+ SWGSDRangel::SWGDeviceSettings& response)
+{
+ // TODO
+ (void) settings;
+ (void) deviceSettingsKeys;
+ (void) response;
+}
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimo.h b/plugins/samplemimo/xtrxmimo/xtrxmimo.h
new file mode 100644
index 000000000..48033907f
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmimo.h
@@ -0,0 +1,318 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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_XTRXMIMO_XTRXMIMO_H_
+#define PLUGINS_SAMPLEMIMO_XTRXMIMO_XTRXMIMO_H_
+
+#include
+
+#include
+#include
+#include
+
+#include "dsp/devicesamplemimo.h"
+#include "xtrx/devicextrxshared.h"
+#include "xtrxmimosettings.h"
+
+class QNetworkAccessManager;
+class QNetworkReply;
+class DeviceAPI;
+class XTRXMIThread;
+class XTRXMOThread;
+class DeviceLimeSDRParams;
+
+#endif // PLUGINS_SAMPLEMIMO_XTRXMIMO_XTRXMIMO_H_
+
+class XTRXMIMO : public DeviceSampleMIMO {
+ Q_OBJECT
+
+public:
+ class MsgConfigureXTRXMIMO : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ const XTRXMIMOSettings& getSettings() const { return m_settings; }
+ bool getForce() const { return m_force; }
+
+ static MsgConfigureXTRXMIMO* create(const XTRXMIMOSettings& settings, bool force)
+ {
+ return new MsgConfigureXTRXMIMO(settings, force);
+ }
+
+ private:
+ XTRXMIMOSettings m_settings;
+ bool m_force;
+
+ MsgConfigureXTRXMIMO(const XTRXMIMOSettings& settings, bool force) :
+ Message(),
+ m_settings(settings),
+ m_force(force)
+ { }
+ };
+
+ class MsgGetStreamInfo : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgGetStreamInfo* create()
+ {
+ return new MsgGetStreamInfo();
+ }
+
+ private:
+ MsgGetStreamInfo() :
+ Message()
+ { }
+ };
+
+ class MsgGetDeviceInfo : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgGetDeviceInfo* create()
+ {
+ return new MsgGetDeviceInfo();
+ }
+
+ private:
+ MsgGetDeviceInfo() :
+ Message()
+ { }
+ };
+
+ class MsgReportClockGenChange : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgReportClockGenChange* create()
+ {
+ return new MsgReportClockGenChange();
+ }
+
+ private:
+ MsgReportClockGenChange() :
+ Message()
+ { }
+ };
+
+ class MsgReportStreamInfo : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ bool getSuccess() const { return m_success; }
+ bool getActive() const { return m_active; }
+ uint32_t getFifoFilledCountRx() const { return m_fifoFilledCountRx; }
+ uint32_t getFifoFilledCountTx() const { return m_fifoFilledCountTx; }
+ uint32_t getFifoSize() const { return m_fifoSize; }
+
+ static MsgReportStreamInfo* create(
+ bool success,
+ bool active,
+ uint32_t fifoFilledCountRx,
+ uint32_t fifoFilledCountTx,
+ uint32_t fifoSize
+ )
+ {
+ return new MsgReportStreamInfo(
+ success,
+ active,
+ fifoFilledCountRx,
+ fifoFilledCountTx,
+ fifoSize
+ );
+ }
+
+ private:
+ bool m_success;
+ // everything from lms_stream_status_t
+ bool m_active; //!< Indicates whether the stream is currently active
+ uint32_t m_fifoFilledCountRx; //!< Number of samples in FIFO buffer (Rx)
+ uint32_t m_fifoFilledCountTx; //!< Number of samples in FIFO buffer (Tx)
+ uint32_t m_fifoSize; //!< Size of FIFO buffer
+
+ MsgReportStreamInfo(
+ bool success,
+ bool active,
+ uint32_t fifoFilledCountRx,
+ uint32_t fifoFilledCountTx,
+ uint32_t fifoSize
+ ) :
+ Message(),
+ m_success(success),
+ m_active(active),
+ m_fifoFilledCountRx(fifoFilledCountRx),
+ m_fifoFilledCountTx(fifoFilledCountTx),
+ m_fifoSize(fifoSize)
+ { }
+ };
+
+ 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)
+ { }
+ };
+
+ class MsgFileRecord : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ bool getStartStop() const { return m_startStop; }
+ int getStreamIndex() const { return m_streamIndex; }
+
+ static MsgFileRecord* create(bool startStop, int streamIndex) {
+ return new MsgFileRecord(startStop, streamIndex);
+ }
+
+ protected:
+ bool m_startStop;
+ int m_streamIndex;
+
+ MsgFileRecord(bool startStop, int streamIndex) :
+ Message(),
+ m_startStop(startStop),
+ m_streamIndex(streamIndex)
+ { }
+ };
+
+ XTRXMIMO(DeviceAPI *deviceAPI);
+ virtual ~XTRXMIMO();
+ 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);
+
+ uint32_t getDevSampleRate() const;
+ uint32_t getLog2HardDecim() const;
+ uint32_t getLog2HardInterp() const;
+ double getClockGen() const;
+
+ // TODO
+ // virtual int webapiSettingsGet(
+ // SWGSDRangel::SWGDeviceSettings& response,
+ // QString& errorMessage);
+
+ // virtual int webapiSettingsPutPatch(
+ // bool force,
+ // const QStringList& deviceSettingsKeys,
+ // SWGSDRangel::SWGDeviceSettings& response, // query + response
+ // QString& errorMessage);
+
+ // virtual int webapiRunGet(
+ // 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 XTRXMIMOSettings& settings);
+
+ static void webapiUpdateDeviceSettings(
+ XTRXMIMOSettings& settings,
+ const QStringList& deviceSettingsKeys,
+ SWGSDRangel::SWGDeviceSettings& response);
+
+ bool isRecording(unsigned int istream) const { (void) istream; return false; }
+
+ bool getRxRunning() const { return m_runningRx; }
+ bool getTxRunning() const { return m_runningTx; }
+
+ void getLORange(float& minF, float& maxF, float& stepF) const;
+ void getSRRange(float& minF, float& maxF, float& stepF) const;
+ void getLPRange(float& minF, float& maxF, float& stepF) const;
+
+private:
+ DeviceAPI *m_deviceAPI;
+ QMutex m_mutex;
+ XTRXMIMOSettings m_settings;
+ XTRXMIThread* m_sourceThread;
+ XTRXMOThread* m_sinkThread;
+ QString m_deviceDescription;
+ bool m_runningRx;
+ bool m_runningTx;
+ QNetworkAccessManager *m_networkManager;
+ QNetworkRequest m_networkRequest;
+
+ bool m_open;
+ DeviceXTRXShared m_deviceShared;
+
+ bool openDevice();
+ void closeDevice();
+
+ bool applySettings(const XTRXMIMOSettings& settings, bool force);
+ void applyGainAuto(unsigned int channel, uint32_t gain);
+ void applyGainLNA(unsigned int channel, double gain);
+ void applyGainTIA(unsigned int channel, double gain);
+ void applyGainPGA(unsigned int channel, double gain);
+ void setRxDeviceCenterFrequency(xtrx_dev *dev, quint64 freq_hz, int loPpmTenths);
+ void setTxDeviceCenterFrequency(xtrx_dev *dev, quint64 freq_hz, int loPpmTenths);
+
+ void webapiReverseSendSettings(QList& deviceSettingsKeys, const XTRXMIMOSettings& settings, bool force);
+ void webapiReverseSendStartStop(bool start);
+
+ static xtrx_antenna_t toXTRXAntennaRx(XTRXMIMOSettings::RxAntenna antennaPath);
+ static xtrx_antenna_t toXTRXAntennaTx(XTRXMIMOSettings::TxAntenna antennaPath);
+ static double tiaToDB(unsigned idx);
+
+private slots:
+ void networkManagerFinished(QNetworkReply *reply);
+};
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp
new file mode 100644
index 000000000..3af743488
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp
@@ -0,0 +1,1087 @@
+// Copyright (C) 2020 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 "xtrx/devicextrxshared.h"
+
+#include "mainwindow.h"
+
+#include "xtrxmimo.h"
+#include "ui_xtrxmimogui.h"
+#include "xtrxmimogui.h"
+
+XTRXMIMOGUI::XTRXMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent) :
+ QWidget(parent),
+ ui(new Ui::XTRXMIMOGUI),
+ m_deviceUISet(deviceUISet),
+ m_settings(),
+ m_rxElseTx(true),
+ m_streamIndex(0),
+ m_spectrumRxElseTx(true),
+ m_spectrumStreamIndex(0),
+ m_doApplySettings(true),
+ m_forceSettings(true),
+ m_xtrxMIMO(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),
+ m_statusCounter(0),
+ m_deviceStatusCounter(0)
+{
+ qDebug("XTRXMIMOGUI::XTRXMIMOGUI");
+ ui->setupUi(this);
+ m_xtrxMIMO = (XTRXMIMO*) m_deviceUISet->m_deviceAPI->getSampleMIMO();
+
+ float minF, maxF, stepF;
+
+ m_xtrxMIMO->getLORange(minF, maxF, stepF);
+ ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
+ ui->centerFrequency->setValueRange(7, ((uint32_t) minF)/1000, ((uint32_t) maxF)/1000); // frequency dial is in kHz
+
+ m_xtrxMIMO->getSRRange(minF, maxF, stepF);
+ ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
+ ui->sampleRate->setValueRange(8, (uint32_t) minF, (uint32_t) maxF);
+
+ m_xtrxMIMO->getLPRange(minF, maxF, stepF);
+ ui->lpf->setColorMapper(ColorMapper(ColorMapper::GrayYellow));
+ ui->lpf->setValueRange(6, (minF/1000)+1, maxF/1000);
+
+ ui->ncoFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
+
+ 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_xtrxMIMO->setMessageQueueToGUI(&m_inputMessageQueue);
+
+ CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStopRx);
+ connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &)));
+
+ CRightClickEnabler *fileRecordRightClickEnabler = new CRightClickEnabler(ui->record);
+ connect(fileRecordRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openFileRecordDialog(const QPoint &)));
+
+ sendSettings();
+}
+
+XTRXMIMOGUI::~XTRXMIMOGUI()
+{
+ delete ui;
+}
+
+void XTRXMIMOGUI::destroy()
+{
+ delete this;
+}
+
+void XTRXMIMOGUI::setName(const QString& name)
+{
+ setObjectName(name);
+}
+
+QString XTRXMIMOGUI::getName() const
+{
+ return objectName();
+}
+
+void XTRXMIMOGUI::resetToDefaults()
+{
+ m_settings.resetToDefaults();
+ displaySettings();
+ sendSettings();
+}
+
+qint64 XTRXMIMOGUI::getCenterFrequency() const
+{
+ return m_settings.m_rxCenterFrequency;
+}
+
+void XTRXMIMOGUI::setCenterFrequency(qint64 centerFrequency)
+{
+ m_settings.m_rxCenterFrequency = centerFrequency;
+ displaySettings();
+ sendSettings();
+}
+
+QByteArray XTRXMIMOGUI::serialize() const
+{
+ return m_settings.serialize();
+}
+
+bool XTRXMIMOGUI::deserialize(const QByteArray& data)
+{
+ if (m_settings.deserialize(data))
+ {
+ displaySettings();
+ m_forceSettings = true;
+ sendSettings();
+ return true;
+ }
+ else
+ {
+ resetToDefaults();
+ return false;
+ }
+}
+
+void XTRXMIMOGUI::handleInputMessages()
+{
+ Message* message;
+
+ while ((message = m_inputMessageQueue.pop()) != 0)
+ {
+ if (handleMessage(*message)) {
+ delete message;
+ } else {
+ qDebug("LimeSDRMIMOGUI::handleInputMessages: unhandled message: %s", message->getIdentifier());
+ }
+ }
+}
+
+bool XTRXMIMOGUI::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("XTRXMIMOGUI::handleInputMessages: DSPMIMOSignalNotification: %s stream: %d SampleRate:%d, CenterFrequency:%llu",
+ sourceOrSink ? "source" : "sink",
+ istream,
+ notif.getSampleRate(),
+ notif.getCenterFrequency());
+
+ updateSampleRateAndFrequency();
+
+ return true;
+ }
+ else if (XTRXMIMO::MsgConfigureXTRXMIMO::match(message))
+ {
+ const XTRXMIMO::MsgConfigureXTRXMIMO& notif = (const XTRXMIMO::MsgConfigureXTRXMIMO&) message;
+ m_settings = notif.getSettings();
+ blockApplySettings(true);
+ displaySettings();
+ blockApplySettings(false);
+
+ return true;
+ }
+ else if (XTRXMIMO::MsgReportClockGenChange::match(message))
+ {
+ m_settings.m_devSampleRate = m_xtrxMIMO->getDevSampleRate();
+
+ blockApplySettings(true);
+ displaySettings();
+ blockApplySettings(false);
+
+ return true;
+ }
+ else if (XTRXMIMO::MsgReportStreamInfo::match(message))
+ {
+ XTRXMIMO::MsgReportStreamInfo& report = (XTRXMIMO::MsgReportStreamInfo&) message;
+
+ if (report.getSuccess())
+ {
+ if (report.getActive()) {
+ ui->streamStatusLabel->setStyleSheet("QLabel { background-color : green; }");
+ } else {
+ ui->streamStatusLabel->setStyleSheet("QLabel { background-color : blue; }");
+ }
+
+ ui->fifoBarRx->setMaximum(report.getFifoSize());
+ ui->fifoBarRx->setValue(report.getFifoFilledCountRx());
+ ui->fifoBarRx->setToolTip(tr("Rx FIFO fill %1/%2 samples")
+ .arg(QString::number(report.getFifoFilledCountRx()))
+ .arg(QString::number(report.getFifoSize())));
+
+ ui->fifoBarTx->setMaximum(report.getFifoSize());
+ ui->fifoBarTx->setValue(report.getFifoFilledCountTx());
+ ui->fifoBarTx->setToolTip(tr("Tx FIFO fill %1/%2 samples")
+ .arg(QString::number(report.getFifoFilledCountTx()))
+ .arg(QString::number(report.getFifoSize())));
+ }
+ else
+ {
+ ui->streamStatusLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }");
+ }
+
+ return true;
+ }
+ else if (DeviceXTRXShared::MsgReportDeviceInfo::match(message))
+ {
+ DeviceXTRXShared::MsgReportDeviceInfo& report = (DeviceXTRXShared::MsgReportDeviceInfo&) message;
+ ui->temperatureText->setText(tr("%1C").arg(QString::number(report.getTemperature(), 'f', 0)));
+
+ if (report.getGPSLocked()) {
+ ui->gpsStatusLabel->setStyleSheet("QLabel { background-color : green; }");
+ } else {
+ ui->gpsStatusLabel->setStyleSheet("QLabel { background:rgb(48,48,48); }");
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void XTRXMIMOGUI::displaySettings()
+{
+ ui->extClock->setExternalClockFrequency(m_settings.m_extClockFreq);
+ ui->extClock->setExternalClockActive(m_settings.m_extClock);
+ displaySampleRate();
+
+ if (m_rxElseTx)
+ {
+ setRxCenterFrequencyDisplay();
+
+ ui->dcOffset->setChecked(m_settings.m_dcBlock);
+ ui->iqImbalance->setChecked(m_settings.m_iqCorrection);
+ ui->swDecim->setCurrentIndex(m_settings.m_log2SoftDecim);
+ ui->hwDecim->setCurrentIndex(m_settings.m_log2HardDecim);
+ ui->antenna->setCurrentIndex((int) m_settings.m_antennaPathRx);
+ ui->ncoEnable->setChecked(m_settings.m_ncoEnableRx);
+
+ setNCODisplay();
+ updateADCRate();
+
+ if (m_streamIndex == 0)
+ {
+ ui->lpf->setValue(m_settings.m_lpfBWRx0 / 1000);
+ ui->pwrmode->setCurrentIndex(m_settings.m_pwrmodeRx0);
+ ui->gain->setValue(m_settings.m_gainRx0);
+ ui->gainText->setText(tr("%1").arg(m_settings.m_gainRx0));
+ ui->gainMode->setCurrentIndex((int) m_settings.m_gainModeRx0);
+ ui->lnaGain->setValue(m_settings.m_lnaGainRx0);
+ ui->tiaGain->setCurrentIndex(m_settings.m_tiaGainRx0 - 1);
+ ui->pgaGain->setValue(m_settings.m_pgaGainRx0);
+
+ if (m_settings.m_gainModeRx0 == XTRXMIMOSettings::GAIN_AUTO)
+ {
+ ui->gain->setEnabled(true);
+ ui->lnaGain->setEnabled(false);
+ ui->tiaGain->setEnabled(false);
+ ui->pgaGain->setEnabled(false);
+ }
+ else
+ {
+ ui->gain->setEnabled(false);
+ ui->lnaGain->setEnabled(true);
+ ui->tiaGain->setEnabled(true);
+ ui->pgaGain->setEnabled(true);
+ }
+ }
+ else
+ {
+ ui->lpf->setValue(m_settings.m_lpfBWRx1 / 1000);
+ ui->pwrmode->setCurrentIndex(m_settings.m_pwrmodeRx1);
+ ui->gain->setValue(m_settings.m_gainRx1);
+ ui->gainText->setText(tr("%1").arg(m_settings.m_gainRx1));
+ ui->gainMode->setCurrentIndex((int) m_settings.m_gainModeRx1);
+ ui->lnaGain->setValue(m_settings.m_lnaGainRx1);
+ ui->tiaGain->setCurrentIndex(m_settings.m_tiaGainRx1 - 1);
+ ui->pgaGain->setValue(m_settings.m_pgaGainRx1);
+
+ if (m_settings.m_gainModeRx1 == XTRXMIMOSettings::GAIN_AUTO)
+ {
+ ui->gain->setEnabled(true);
+ ui->lnaGain->setEnabled(false);
+ ui->tiaGain->setEnabled(false);
+ ui->pgaGain->setEnabled(false);
+ }
+ else
+ {
+ ui->gain->setEnabled(false);
+ ui->lnaGain->setEnabled(true);
+ ui->tiaGain->setEnabled(true);
+ ui->pgaGain->setEnabled(true);
+ }
+ }
+ }
+ else
+ {
+ setTxCenterFrequencyDisplay();
+
+ ui->swDecim->setCurrentIndex(m_settings.m_log2SoftInterp);
+ ui->hwDecim->setCurrentIndex(m_settings.m_log2HardInterp);
+ ui->antenna->setCurrentIndex((int) m_settings.m_antennaPathTx);
+ ui->ncoEnable->setChecked(m_settings.m_ncoEnableTx);
+
+ updateDACRate();
+ setNCODisplay();
+
+ if (m_streamIndex == 0)
+ {
+ ui->lpf->setValue(m_settings.m_lpfBWTx0 / 1000);
+ ui->pwrmode->setCurrentIndex(m_settings.m_pwrmodeTx0);
+ ui->gain->setValue(m_settings.m_gainTx0);
+ ui->gainText->setText(tr("%1").arg(m_settings.m_gainTx0));
+ }
+ else
+ {
+ ui->lpf->setValue(m_settings.m_lpfBWTx1 / 1000);
+ ui->pwrmode->setCurrentIndex(m_settings.m_pwrmodeTx1);
+ ui->gain->setValue(m_settings.m_gainTx1);
+ ui->gainText->setText(tr("%1").arg(m_settings.m_gainTx1));
+ }
+ }
+}
+
+void XTRXMIMOGUI::displaySampleRate()
+{
+ float minF, maxF, stepF;
+ m_xtrxMIMO->getSRRange(minF, maxF, stepF);
+
+ ui->sampleRate->blockSignals(true);
+
+ if (m_sampleRateMode)
+ {
+ ui->sampleRateMode->setStyleSheet("QToolButton { background:rgb(60,60,60); }");
+ ui->sampleRateMode->setText("SR");
+ ui->sampleRate->setValueRange(8, (uint32_t) minF, (uint32_t) maxF);
+ 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");
+ ui->sampleRate->setValueRange(8, (uint32_t) minF/(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 XTRXMIMOGUI::setNCODisplay()
+{
+ ui->ncoFrequency->blockSignals(true);
+
+ if (m_rxElseTx)
+ {
+ int ncoHalfRange = (m_settings.m_devSampleRate * (1<<(m_settings.m_log2HardDecim)))/2;
+ ui->ncoFrequency->setValueRange(
+ false,
+ 8,
+ -ncoHalfRange,
+ ncoHalfRange);
+ ui->ncoFrequency->setToolTip(QString("NCO frequency shift in Hz (Range: +/- %1 kHz)").arg(ncoHalfRange/1000));
+ ui->ncoFrequency->setValue(m_settings.m_ncoFrequencyRx);
+ ui->ncoEnable->setChecked(m_settings.m_ncoEnableRx);
+ }
+ else
+ {
+ int ncoHalfRange = (m_settings.m_devSampleRate * (1<<(m_settings.m_log2HardInterp)))/2;
+ ui->ncoFrequency->setValueRange(
+ false,
+ 8,
+ -ncoHalfRange,
+ ncoHalfRange);
+ ui->ncoFrequency->setToolTip(QString("NCO frequency shift in Hz (Range: +/- %1 kHz)").arg(ncoHalfRange/1000));
+ ui->ncoFrequency->setValue(m_settings.m_ncoFrequencyTx);
+ ui->ncoEnable->setChecked(m_settings.m_ncoEnableTx);
+ }
+
+ ui->ncoFrequency->blockSignals(false);
+}
+
+void XTRXMIMOGUI::setRxCenterFrequencyDisplay()
+{
+ int64_t centerFrequency = m_settings.m_rxCenterFrequency;
+ ui->centerFrequency->setToolTip(QString("Main center frequency in kHz (LO: %1 kHz)").arg(centerFrequency/1000));
+
+ if (m_settings.m_ncoEnableRx) {
+ centerFrequency += m_settings.m_ncoFrequencyRx;
+ }
+
+ ui->centerFrequency->blockSignals(true);
+ ui->centerFrequency->setValue(centerFrequency < 0 ? 0 : (uint64_t) centerFrequency/1000); // kHz
+ ui->centerFrequency->blockSignals(false);
+}
+
+void XTRXMIMOGUI::setRxCenterFrequencySetting(uint64_t kHzValue)
+{
+ int64_t centerFrequency = kHzValue*1000;
+
+ if (m_settings.m_ncoEnableRx) {
+ centerFrequency -= m_settings.m_ncoFrequencyRx;
+ }
+
+ m_settings.m_rxCenterFrequency = centerFrequency < 0 ? 0 : (uint64_t) centerFrequency;
+ ui->centerFrequency->setToolTip(QString("Main center frequency in kHz (LO: %1 kHz)").arg(centerFrequency/1000));
+}
+
+void XTRXMIMOGUI::setTxCenterFrequencyDisplay()
+{
+ int64_t centerFrequency = m_settings.m_txCenterFrequency;
+ ui->centerFrequency->setToolTip(QString("Main center frequency in kHz (LO: %1 kHz)").arg(centerFrequency/1000));
+
+ if (m_settings.m_ncoEnableTx) {
+ centerFrequency += m_settings.m_ncoFrequencyTx;
+ }
+
+ ui->centerFrequency->blockSignals(true);
+ ui->centerFrequency->setValue(centerFrequency < 0 ? 0 : (uint64_t) centerFrequency/1000); // kHz
+ ui->centerFrequency->blockSignals(false);
+}
+
+void XTRXMIMOGUI::setTxCenterFrequencySetting(uint64_t kHzValue)
+{
+ int64_t centerFrequency = kHzValue*1000;
+
+ if (m_settings.m_ncoEnableTx) {
+ centerFrequency -= m_settings.m_ncoFrequencyTx;
+ }
+
+ 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 XTRXMIMOGUI::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 XTRXMIMOGUI::updateADCRate()
+{
+ uint32_t adcRate = m_xtrxMIMO->getClockGen() / 4;
+ uint32_t log2HardDecim = m_xtrxMIMO->getLog2HardDecim();
+
+ if (adcRate < 100000000) {
+ ui->adcRateLabel->setText(tr("%1k").arg(QString::number(adcRate / 1000.0f, 'g', 5)));
+ } else {
+ ui->adcRateLabel->setText(tr("%1M").arg(QString::number(adcRate / 1000000.0f, 'g', 5)));
+ }
+
+ if (ui->hwDecim->currentIndex() != 0)
+ {
+ ui->hwDecim->blockSignals(true);
+ ui->hwDecim->setCurrentIndex(log2HardDecim);
+ ui->hwDecim->blockSignals(false);
+ }
+}
+
+void XTRXMIMOGUI::updateDACRate()
+{
+ uint32_t dacRate = m_xtrxMIMO->getClockGen() / 4;
+ uint32_t log2HardInterp = m_xtrxMIMO->getLog2HardInterp();
+
+ if (dacRate < 100000000) {
+ ui->adcRateLabel->setText(tr("%1k").arg(QString::number(dacRate / 1000.0f, 'g', 5)));
+ } else {
+ ui->adcRateLabel->setText(tr("%1M").arg(QString::number(dacRate / 1000000.0f, 'g', 5)));
+ }
+
+ if (ui->hwDecim->currentIndex() != 0)
+ {
+ ui->hwDecim->blockSignals(true);
+ ui->hwDecim->setCurrentIndex(log2HardInterp);
+ ui->hwDecim->blockSignals(false);
+ }
+}
+
+void XTRXMIMOGUI::sendSettings()
+{
+ if (!m_updateTimer.isActive()) {
+ m_updateTimer.start(100);
+ }
+}
+
+void XTRXMIMOGUI::updateHardware()
+{
+ if (m_doApplySettings)
+ {
+ XTRXMIMO::MsgConfigureXTRXMIMO* message = XTRXMIMO::MsgConfigureXTRXMIMO::create(m_settings, m_forceSettings);
+ m_xtrxMIMO->getInputMessageQueue()->push(message);
+ m_forceSettings = false;
+ m_updateTimer.stop();
+ }
+}
+
+void XTRXMIMOGUI::updateStatus()
+{
+ int stateRx = m_deviceUISet->m_deviceAPI->state(0);
+ int stateTx = m_deviceUISet->m_deviceAPI->state(1);
+
+ if (m_lastRxEngineState != 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());
+ break;
+ default:
+ break;
+ }
+
+ m_lastRxEngineState = stateRx;
+ }
+
+ if (m_lastTxEngineState != 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;
+ }
+
+ if (m_statusCounter < 1)
+ {
+ m_statusCounter++;
+ }
+ else
+ {
+ XTRXMIMO::MsgGetStreamInfo* message = XTRXMIMO::MsgGetStreamInfo::create();
+ m_xtrxMIMO->getInputMessageQueue()->push(message);
+ m_statusCounter = 0;
+ }
+
+ if (m_deviceStatusCounter < 10)
+ {
+ m_deviceStatusCounter++;
+ }
+ else
+ {
+ XTRXMIMO::MsgGetDeviceInfo* message = XTRXMIMO::MsgGetDeviceInfo::create();
+ m_xtrxMIMO->getInputMessageQueue()->push(message);
+ m_deviceStatusCounter = 0;
+ }
+}
+
+void XTRXMIMOGUI::on_streamSide_currentIndexChanged(int index)
+{
+ m_rxElseTx = index == 0;
+ ui->gainMode->setEnabled(m_rxElseTx);
+ ui->lnaGain->setEnabled(m_rxElseTx);
+ ui->tiaGain->setEnabled(m_rxElseTx);
+ ui->pgaGain->setEnabled(m_rxElseTx);
+
+ ui->antenna->blockSignals(true);
+ ui->antenna->clear();
+
+ if (m_rxElseTx)
+ {
+ ui->antenna->addItem("Lo");
+ ui->antenna->addItem("Wide");
+ ui->antenna->addItem("Hi");
+ }
+ else
+ {
+ ui->antenna->addItem("Hi");
+ ui->antenna->addItem("Wide");
+ }
+
+ ui->antenna->blockSignals(false);
+ displaySettings();
+}
+
+void XTRXMIMOGUI::on_streamIndex_currentIndexChanged(int index)
+{
+ m_streamIndex = index < 0 ? 0 : index > 1 ? 1 : index;
+ displaySettings();
+}
+
+void XTRXMIMOGUI::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 XTRXMIMOGUI::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 XTRXMIMOGUI::on_startStopRx_toggled(bool checked)
+{
+ if (m_doApplySettings)
+ {
+ XTRXMIMO::MsgStartStop *message = XTRXMIMO::MsgStartStop::create(checked, true);
+ m_xtrxMIMO->getInputMessageQueue()->push(message);
+ }
+}
+
+void XTRXMIMOGUI::on_startStopTx_toggled(bool checked)
+{
+ if (m_doApplySettings)
+ {
+ XTRXMIMO::MsgStartStop *message = XTRXMIMO::MsgStartStop::create(checked, false);
+ m_xtrxMIMO->getInputMessageQueue()->push(message);
+ }
+}
+
+void XTRXMIMOGUI::on_record_toggled(bool checked)
+{
+ if (checked) {
+ ui->record->setStyleSheet("QToolButton { background-color : red; }");
+ } else {
+ ui->record->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
+ }
+
+ XTRXMIMO::MsgFileRecord* message = XTRXMIMO::MsgFileRecord::create(checked, m_streamIndex);
+ m_xtrxMIMO->getInputMessageQueue()->push(message);
+}
+
+void XTRXMIMOGUI::on_centerFrequency_changed(quint64 value)
+{
+ if (m_rxElseTx) {
+ setRxCenterFrequencySetting(value);
+ } else {
+ setTxCenterFrequencySetting(value);
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_ncoEnable_toggled(bool checked)
+{
+ if (m_rxElseTx)
+ {
+ m_settings.m_ncoEnableRx = checked;
+ setRxCenterFrequencyDisplay();
+ }
+ else
+ {
+ m_settings.m_ncoEnableTx = checked;
+ setTxCenterFrequencyDisplay();
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_ncoFrequency_changed(qint64 value)
+{
+ if (m_rxElseTx)
+ {
+ m_settings.m_ncoFrequencyRx = value;
+ setRxCenterFrequencyDisplay();
+ }
+ else
+ {
+ m_settings.m_ncoFrequencyTx = value;
+ setTxCenterFrequencyDisplay();
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_dcOffset_toggled(bool checked)
+{
+ m_settings.m_dcBlock = checked;
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_iqImbalance_toggled(bool checked)
+{
+ m_settings.m_iqCorrection = checked;
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_extClock_clicked()
+{
+ m_settings.m_extClock = ui->extClock->getExternalClockActive();
+ m_settings.m_extClockFreq = ui->extClock->getExternalClockFrequency();
+ qDebug("XTRXMIMOGUI::on_extClock_clicked: %u Hz %s", m_settings.m_extClockFreq, m_settings.m_extClock ? "on" : "off");
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_hwDecim_currentIndexChanged(int index)
+{
+ if ((index <0) || (index > 5)) {
+ return;
+ }
+
+
+ if (m_rxElseTx)
+ {
+ m_settings.m_log2HardDecim = index;
+ updateADCRate();
+ }
+ else
+ {
+ m_settings.m_log2HardInterp = index;
+ updateDACRate();
+ }
+
+ setNCODisplay();
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_swDecim_currentIndexChanged(int index)
+{
+ if ((index <0) || (index > 6)) {
+ return;
+ }
+
+ displaySampleRate();
+
+ if (m_rxElseTx)
+ {
+ m_settings.m_log2SoftDecim = index;
+
+ if (m_sampleRateMode) {
+ m_settings.m_devSampleRate = ui->sampleRate->getValueNew();
+ } else {
+ m_settings.m_devSampleRate = ui->sampleRate->getValueNew() * (1 << m_settings.m_log2SoftDecim);
+ }
+ }
+ else
+ {
+ m_settings.m_log2SoftInterp = index;
+
+ if (m_sampleRateMode) {
+ m_settings.m_devSampleRate = ui->sampleRate->getValueNew();
+ } else {
+ m_settings.m_devSampleRate = ui->sampleRate->getValueNew() * (1 << m_settings.m_log2SoftInterp);
+ }
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_sampleRateMode_toggled(bool checked)
+{
+ m_sampleRateMode = checked;
+ displaySampleRate();
+}
+
+void XTRXMIMOGUI::on_sampleRate_changed(quint64 value)
+{
+ if (m_rxElseTx)
+ {
+ if (m_sampleRateMode) {
+ m_settings.m_devSampleRate = value;
+ } else {
+ m_settings.m_devSampleRate = value * (1 << m_settings.m_log2SoftDecim);
+ }
+
+ updateADCRate();
+ }
+ else
+ {
+ if (m_sampleRateMode) {
+ m_settings.m_devSampleRate = value;
+ } else {
+ m_settings.m_devSampleRate = value * (1 << m_settings.m_log2SoftInterp);
+ }
+
+ updateDACRate();
+ }
+
+ setNCODisplay();
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_lpf_changed(quint64 value)
+{
+ if (m_rxElseTx)
+ {
+ if (m_streamIndex == 0) {
+ m_settings.m_lpfBWRx0 = value * 1000;
+ } else if (m_streamIndex == 1) {
+ m_settings.m_lpfBWRx1 = value * 1000;
+ }
+ }
+ else
+ {
+ if (m_streamIndex == 0) {
+ m_settings.m_lpfBWTx0 = value * 1000;
+ } else if (m_streamIndex == 1) {
+ m_settings.m_lpfBWTx1 = value * 1000;
+ }
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_pwrmode_currentIndexChanged(int index)
+{
+ if (m_rxElseTx)
+ {
+ if (m_streamIndex == 0) {
+ m_settings.m_pwrmodeRx0 = index;
+ } else if (m_streamIndex == 1) {
+ m_settings.m_pwrmodeRx1 = index;
+ }
+ }
+ else
+ {
+ if (m_streamIndex == 0) {
+ m_settings.m_pwrmodeTx0 = index;
+ } else if (m_streamIndex == 1) {
+ m_settings.m_pwrmodeTx1 = index;
+ }
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_gainMode_currentIndexChanged(int index)
+{
+ if (!m_rxElseTx) {
+ return;
+ }
+
+ if (m_streamIndex == 0) {
+ m_settings.m_gainModeRx0 = (XTRXMIMOSettings::GainMode) index;
+ } else if (m_streamIndex == 1) {
+ m_settings.m_gainModeRx1 = (XTRXMIMOSettings::GainMode) index;
+ }
+
+ if (index == 0)
+ {
+ ui->gain->setEnabled(true);
+ ui->lnaGain->setEnabled(false);
+ ui->tiaGain->setEnabled(false);
+ ui->pgaGain->setEnabled(false);
+ }
+ else
+ {
+ ui->gain->setEnabled(false);
+ ui->lnaGain->setEnabled(true);
+ ui->tiaGain->setEnabled(true);
+ ui->pgaGain->setEnabled(true);
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_gain_valueChanged(int value)
+{
+ if (m_rxElseTx)
+ {
+ if (m_streamIndex == 0)
+ {
+ m_settings.m_gainRx0 = value;
+ ui->gainText->setText(tr("%1").arg(m_settings.m_gainRx0));
+ }
+ else if (m_streamIndex == 1)
+ {
+ m_settings.m_gainRx1 = value;
+ ui->gainText->setText(tr("%1").arg(m_settings.m_gainRx1));
+ }
+ }
+ else
+ {
+ if (m_streamIndex == 0)
+ {
+ m_settings.m_gainTx0 = value;
+ ui->gainText->setText(tr("%1").arg(m_settings.m_gainTx0));
+ }
+ else if (m_streamIndex == 1)
+ {
+ m_settings.m_gainTx1 = value;
+ ui->gainText->setText(tr("%1").arg(m_settings.m_gainTx1));
+ }
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_lnaGain_valueChanged(int value)
+{
+ if (!m_rxElseTx) {
+ return;
+ }
+
+ if (m_streamIndex == 0)
+ {
+ m_settings.m_lnaGainRx0 = value;
+ ui->lnaGainText->setText(tr("%1").arg(m_settings.m_lnaGainRx0));
+ }
+ else if (m_streamIndex == 1)
+ {
+ m_settings.m_lnaGainRx1 = value;
+ ui->lnaGainText->setText(tr("%1").arg(m_settings.m_lnaGainRx1));
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_tiaGain_currentIndexChanged(int index)
+{
+ if (!m_rxElseTx) {
+ return;
+ }
+
+ if (m_streamIndex == 0) {
+ m_settings.m_tiaGainRx0 = index + 1;
+ } else {
+ m_settings.m_tiaGainRx1 = index + 1;
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_pgaGain_valueChanged(int value)
+{
+ if (!m_rxElseTx) {
+ return;
+ }
+
+ if (m_streamIndex == 0)
+ {
+ m_settings.m_pgaGainRx0 = value;
+ ui->pgaGainText->setText(tr("%1").arg(m_settings.m_pgaGainRx0));
+ }
+ else
+ {
+ m_settings.m_pgaGainRx1 = value;
+ ui->pgaGainText->setText(tr("%1").arg(m_settings.m_pgaGainRx1));
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::on_antenna_currentIndexChanged(int index)
+{
+ if (m_rxElseTx) {
+ m_settings.m_antennaPathRx = (XTRXMIMOSettings::RxAntenna) index;
+ } else {
+ m_settings.m_antennaPathTx = (XTRXMIMOSettings::TxAntenna) index;
+ }
+
+ sendSettings();
+}
+
+void XTRXMIMOGUI::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();
+}
+
+void XTRXMIMOGUI::openFileRecordDialog(const QPoint& p)
+{
+ QFileDialog fileDialog(
+ this,
+ tr("Save I/Q record file"),
+ m_settings.m_fileRecordName,
+ tr("SDR I/Q Files (*.sdriq)")
+ );
+
+ fileDialog.setOptions(QFileDialog::DontUseNativeDialog);
+ fileDialog.setFileMode(QFileDialog::AnyFile);
+ fileDialog.move(p);
+ QStringList fileNames;
+
+ if (fileDialog.exec())
+ {
+ fileNames = fileDialog.selectedFiles();
+
+ if (fileNames.size() > 0)
+ {
+ m_settings.m_fileRecordName = fileNames.at(0);
+ sendSettings();
+ }
+ }
+}
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.h b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h
new file mode 100644
index 000000000..470d5a5be
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h
@@ -0,0 +1,127 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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_XTRXMIMO_XTRXMIMOGUI_H_
+#define PLUGINS_SAMPLEMIMO_XTRXMIMO_XTRXMIMOGUI_H_
+
+#include
+#include
+
+#include "util/messagequeue.h"
+#include "plugin/plugininstancegui.h"
+
+#include "xtrxmimosettings.h"
+
+class DeviceUISet;
+class XTRXMIMO;
+
+namespace Ui {
+ class XTRXMIMOGUI;
+}
+
+class XTRXMIMOGUI : public QWidget, public PluginInstanceGUI {
+ Q_OBJECT
+public:
+ explicit XTRXMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent = nullptr);
+ virtual ~XTRXMIMOGUI();
+ virtual void destroy();
+
+ void setName(const QString& name);
+ QString getName() const;
+
+ void resetToDefaults();
+ virtual qint64 getCenterFrequency() const;
+ virtual void setCenterFrequency(qint64 centerFrequency);
+ QByteArray serialize() const;
+ bool deserialize(const QByteArray& data);
+ virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
+ virtual bool handleMessage(const Message& message);
+
+private:
+ Ui::XTRXMIMOGUI* ui;
+
+ DeviceUISet* m_deviceUISet;
+ XTRXMIMOSettings m_settings;
+ bool m_rxElseTx; //!< Which side is being dealt with
+ int m_streamIndex; //!< Current stream index being dealt with
+ bool m_spectrumRxElseTx;
+ int m_spectrumStreamIndex; //!< Index of the stream displayed on main spectrum
+ QTimer m_updateTimer;
+ QTimer m_statusTimer;
+ bool m_doApplySettings;
+ bool m_forceSettings;
+ XTRXMIMO* m_xtrxMIMO;
+ 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;
+ int m_statusCounter;
+ int m_deviceStatusCounter;
+ MessageQueue m_inputMessageQueue;
+
+ bool m_sampleRateMode;
+
+ void blockApplySettings(bool block) { m_doApplySettings = !block; }
+ void displaySettings();
+ void displaySampleRate();
+ void setNCODisplay();
+ void setRxCenterFrequencyDisplay();
+ void setRxCenterFrequencySetting(uint64_t kHzValue);
+ void setTxCenterFrequencyDisplay();
+ void setTxCenterFrequencySetting(uint64_t kHzValue);
+ void sendSettings();
+ void updateSampleRateAndFrequency();
+ void updateADCRate();
+ void updateDACRate();
+
+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_record_toggled(bool checked);
+ void on_centerFrequency_changed(quint64 value);
+ void on_ncoEnable_toggled(bool checked);
+ void on_ncoFrequency_changed(qint64 value);
+ void on_dcOffset_toggled(bool checked);
+ void on_iqImbalance_toggled(bool checked);
+ void on_extClock_clicked();
+ void on_hwDecim_currentIndexChanged(int index);
+ void on_swDecim_currentIndexChanged(int index);
+ void on_sampleRateMode_toggled(bool checked);
+ void on_sampleRate_changed(quint64 value);
+ void on_lpf_changed(quint64 value);
+ void on_pwrmode_currentIndexChanged(int index);
+ void on_gainMode_currentIndexChanged(int index);
+ void on_gain_valueChanged(int value);
+ void on_lnaGain_valueChanged(int value);
+ void on_tiaGain_currentIndexChanged(int index);
+ void on_pgaGain_valueChanged(int value);
+ void on_antenna_currentIndexChanged(int index);
+ void openDeviceSettingsDialog(const QPoint& p);
+ void openFileRecordDialog(const QPoint& p);
+};
+
+#endif // PLUGINS_SAMPLEMIMO_XTRXMIMO_XTRXMIMOGUI_H_
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui b/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui
new file mode 100644
index 000000000..978e8877e
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui
@@ -0,0 +1,1345 @@
+
+
+ XTRXMIMOGUI
+
+
+
+ 0
+ 0
+ 370
+ 290
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 360
+ 290
+
+
+
+
+ Liberation Sans
+ 9
+
+
+
+ XTRX MIMO
+
+
+
+ 3
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+ -
+
+
-
+
+
+
+ 16777215
+ 22
+
+
+
+
+
+
+ :/antenna.png
+
+
+
+ -
+
+
+
+ 45
+ 0
+
+
+
+
+ 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
+
+
-
+
+
-
+
+
-
+
+
+ Left: toggle record I/Q samples from device - Right: select output file
+
+
+
+
+
+
+ :/record_off.png
+ :/record_on.png:/record_off.png
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 54
+ 0
+
+
+
+ ADC rate before hardware downsampling (k or MS/s)
+
+
+ 00000k
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 16
+
+
+
+
+ Liberation Mono
+ 20
+ 50
+ false
+
+
+
+ PointingHandCursor
+
+
+ Qt::StrongFocus
+
+
+ Main center frequency in kHz
+
+
+
+ -
+
+
+ 6
+
+
+ 6
+
+
-
+
+
-
+
+
+ kHz
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 54
+ 0
+
+
+
+ Baseband I/Q sample rate kS/s
+
+
+ 00000k
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 2
+
+
-
+
+
+ Enable the TSP NCO
+
+
+ NCO
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 16
+
+
+
+
+ Liberation Mono
+ 12
+ 50
+ false
+
+
+
+ PointingHandCursor
+
+
+ Center frequency with NCO engaged (kHz)
+
+
+
+ -
+
+
+ kHz
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Automatic DC offset removal
+
+
+ DC
+
+
+
+ -
+
+
+ Automatic IQ imbalance correction
+
+
+ IQ
+
+
+
+ -
+
+
+ External clock dialog
+
+
+
+
+
+
+ :/clocksource.png:/clocksource.png
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 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
+ 50
+ false
+
+
+
+ PointingHandCursor
+
+
+ Device to host sample rate
+
+
+
+ -
+
+
+ S/s
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Hw
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ TSP hardware decimation factor
+
+
+ 0
+
+
-
+
+ A
+
+
+ -
+
+ 2
+
+
+ -
+
+ 4
+
+
+ -
+
+ 8
+
+
+ -
+
+ 16
+
+
+ -
+
+ 32
+
+
+
+
+ -
+
+
+ Sw
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ Software decimation factor
+
+
+ 0
+
+
-
+
+ 1
+
+
+ -
+
+ 2
+
+
+ -
+
+ 4
+
+
+ -
+
+ 8
+
+
+ -
+
+ 16
+
+
+ -
+
+ 32
+
+
+ -
+
+ 64
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 2
+
+
+ 2
+
+
-
+
+
+ LP
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 16
+
+
+
+
+ Liberation Mono
+ 12
+ 50
+ false
+
+
+
+ PointingHandCursor
+
+
+ Analog lowpass filer bandwidth (kHz)
+
+
+
+ -
+
+
+ kHz
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ LMS Pwr
+
+
+
+ -
+
+
+
+ 50
+ 0
+
+
+
+ Power mode
+
+
+ 4
+
+
-
+
+ 0 - Save Max
+
+
+ -
+
+ 1
+
+
+ -
+
+ 2
+
+
+ -
+
+ 3 - Economy
+
+
+ -
+
+ 4 - Optimal
+
+
+ -
+
+ 5
+
+
+ -
+
+ 6
+
+
+ -
+
+ 7 - Perf Max
+
+
+
+
+
+
+ -
+
+
+ 2
+
+
+ 2
+
+
-
+
+
+
+ 54
+ 16777215
+
+
+
+ Automatic or Manual gain selection
+
+
-
+
+ Aut
+
+
+ -
+
+ Man
+
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Automatic global gain (dB)
+
+
+ 70
+
+
+ 1
+
+
+ 20
+
+
+
+ -
+
+
+
+ 18
+ 0
+
+
+
+
+ 18
+ 16777215
+
+
+
+ Automatic global gain
+
+
+ 20
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Manual LNA gain
+
+
+ 1
+
+
+ 30
+
+
+ 1
+
+
+ 15
+
+
+
+ -
+
+
+
+ 18
+ 0
+
+
+
+
+ 18
+ 16777215
+
+
+
+ Manual LNA gain (dB)
+
+
+ 15
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 40
+ 16777215
+
+
+
+ Manual TIA gain (dB)
+
+
+ 1
+
+
-
+
+ 1
+
+
+ -
+
+ 2
+
+
+ -
+
+ 3
+
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Manual PGA gain
+
+
+ 32
+
+
+ 1
+
+
+ 16
+
+
+
+ -
+
+
+
+ 18
+ 0
+
+
+
+
+ 18
+ 16777215
+
+
+
+ Manual PGA gain (dB)
+
+
+ 16
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ :/antenna.png
+
+
+
+ -
+
+
+
+ 50
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+ Antenna select: No: none, Lo: 700:900M, Hi: 2:2.6G, Wi: wideband, T1: Tx1 LB, T2: Tx2 LB
+
+
-
+
+ Lo
+
+
+ -
+
+ Wide
+
+
+ -
+
+ Hi
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 2
+
+
+ 2
+
+
-
+
+
+
+ 24
+ 24
+
+
+
+
+ 24
+ 24
+
+
+
+ Green when stream is reporting data
+
+
+
+
+
+ :/stream.png
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+
+ 24
+ 24
+
+
+
+ Green when GPS is locked
+
+
+
+
+
+ :/gps.png
+
+
+
+ -
+
+
+ Rx
+
+
+
+ -
+
+
+
+ 80
+ 16
+
+
+
+
+ 8
+
+
+
+ Rx FIFO fill status
+
+
+ QProgressBar{border: 2px solid rgb(79, 79, 79); text-align: center;}
+QToolTip{background-color: white; color: black;}
+
+
+ 0
+
+
+
+ -
+
+
+ Tx
+
+
+
+ -
+
+
+
+ 80
+ 16
+
+
+
+
+ Ubuntu
+ 8
+
+
+
+ Tx FIFO fill status
+
+
+ QProgressBar{border: 2px solid rgb(79, 79, 79); text-align: center;}
+QToolTip{background-color: white; color: black;}
+
+
+ 0
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 24
+ 0
+
+
+
+ Board temperature (degrees C)
+
+
+ 00C
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+ ValueDial
+ QWidget
+
+ 1
+
+
+ ButtonSwitch
+ QToolButton
+
+
+
+ ExternalClockButton
+ QToolButton
+ gui/externalclockbutton.h
+
+
+ ValueDialZ
+ QWidget
+
+ 1
+
+
+
+
+
+
+
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimoplugin.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimoplugin.cpp
new file mode 100644
index 000000000..c32a2e01b
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmimoplugin.cpp
@@ -0,0 +1,142 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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"
+#include "xtrx/devicextrx.h"
+
+#ifndef SERVER_MODE
+#include "xtrxmimogui.h"
+#endif
+#include "xtrxmimo.h"
+#include "xtrxmimoplugin.h"
+#include "xtrxmimowebapiadapter.h"
+
+const PluginDescriptor XTRXMIMOPlugin::m_pluginDescriptor = {
+ QString("XTRX"),
+ QString("XTRX MIMO"),
+ QString("5.4.0"),
+ QString("(c) Edouard Griffiths, F4EXB"),
+ QString("https://github.com/f4exb/sdrangel"),
+ true,
+ QString("https://github.com/f4exb/sdrangel")
+};
+
+const QString XTRXMIMOPlugin::m_hardwareID = "XTRX";
+const QString XTRXMIMOPlugin::m_deviceTypeID = XTRXMIMO_DEVICE_TYPE_ID;
+
+XTRXMIMOPlugin::XTRXMIMOPlugin(QObject* parent) :
+ QObject(parent)
+{
+}
+
+const PluginDescriptor& XTRXMIMOPlugin::getPluginDescriptor() const
+{
+ return m_pluginDescriptor;
+}
+
+void XTRXMIMOPlugin::initPlugin(PluginAPI* pluginAPI)
+{
+ pluginAPI->registerSampleMIMO(m_deviceTypeID, this);
+}
+
+void XTRXMIMOPlugin::enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices)
+{
+ if (listedHwIds.contains(m_hardwareID)) { // check if it was done
+ return;
+ }
+
+ DeviceXTRX::enumOriginDevices(m_hardwareID, originDevices);
+ listedHwIds.append(m_hardwareID);
+}
+
+PluginInterface::SamplingDevices XTRXMIMOPlugin::enumSampleMIMO(const OriginDevices& originDevices)
+{
+ SamplingDevices result;
+
+ for (OriginDevices::const_iterator it = originDevices.begin(); it != originDevices.end(); ++it)
+ {
+ if (it->hardwareId == m_hardwareID)
+ {
+ QString displayedName = it->displayableName;
+ displayedName.replace(QString(":$1]"), QString("]"));
+ result.append(SamplingDevice(
+ displayedName,
+ m_hardwareID,
+ m_deviceTypeID,
+ it->serial,
+ it->sequence,
+ PluginInterface::SamplingDevice::PhysicalDevice,
+ PluginInterface::SamplingDevice::StreamMIMO,
+ 1,
+ 0
+ ));
+ }
+ }
+
+ return result;
+}
+
+#ifdef SERVER_MODE
+PluginInstanceGUI* XTRXMIMOPlugin::createSampleMIMOPluginInstanceGUI(
+ const QString& sourceId,
+ QWidget **widget,
+ DeviceUISet *deviceUISet)
+{
+ (void) sourceId;
+ (void) widget;
+ (void) deviceUISet;
+ return nullptr;
+}
+#else
+PluginInstanceGUI* XTRXMIMOPlugin::createSampleMIMOPluginInstanceGUI(
+ const QString& sourceId,
+ QWidget **widget,
+ DeviceUISet *deviceUISet)
+{
+ if (sourceId == m_deviceTypeID)
+ {
+ XTRXMIMOGUI* gui = new XTRXMIMOGUI(deviceUISet);
+ *widget = gui;
+ return gui;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+#endif
+
+DeviceSampleMIMO *XTRXMIMOPlugin::createSampleMIMOPluginInstance(const QString& mimoId, DeviceAPI *deviceAPI)
+{
+ if (mimoId == m_deviceTypeID)
+ {
+ XTRXMIMO* input = new XTRXMIMO(deviceAPI);
+ return input;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+DeviceWebAPIAdapter *XTRXMIMOPlugin::createDeviceWebAPIAdapter() const
+{
+ return new XTRXMIMOWebAPIAdapter();
+}
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimoplugin.h b/plugins/samplemimo/xtrxmimo/xtrxmimoplugin.h
new file mode 100644
index 000000000..e0278cba8
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmimoplugin.h
@@ -0,0 +1,56 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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 _XTRXMIMO_XTRXMIMOPLUGIN_H
+#define _XTRXMIMO_XTRXMIMOPLUGIN_H
+
+#include
+#include "plugin/plugininterface.h"
+
+class PluginAPI;
+
+#define XTRXMIMO_DEVICE_TYPE_ID "sdrangel.samplemimo.xtrxmimo"
+
+class XTRXMIMOPlugin : public QObject, public PluginInterface {
+ Q_OBJECT
+ Q_INTERFACES(PluginInterface)
+ Q_PLUGIN_METADATA(IID XTRXMIMO_DEVICE_TYPE_ID)
+
+public:
+ explicit XTRXMIMOPlugin(QObject* parent = nullptr);
+
+ const PluginDescriptor& getPluginDescriptor() const;
+ void initPlugin(PluginAPI* pluginAPI);
+
+ virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices);
+ virtual SamplingDevices enumSampleMIMO(const OriginDevices& originDevices);
+
+ virtual PluginInstanceGUI* createSampleMIMOPluginInstanceGUI(
+ const QString& sourceId,
+ QWidget **widget,
+ DeviceUISet *deviceUISet);
+ virtual DeviceSampleMIMO* createSampleMIMOPluginInstance(const QString& sourceId, DeviceAPI *deviceAPI);
+ virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const;
+
+ static const QString m_hardwareID;
+ static const QString m_deviceTypeID;
+
+private:
+ static const PluginDescriptor m_pluginDescriptor;
+};
+
+#endif // _XTRXMIMO_XTRXMIMOPLUGIN_H
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp
new file mode 100644
index 000000000..5d66e70ca
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp
@@ -0,0 +1,215 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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 "util/simpleserializer.h"
+#include "xtrxmimosettings.h"
+
+XTRXMIMOSettings::XTRXMIMOSettings()
+{
+ resetToDefaults();
+}
+
+void XTRXMIMOSettings::resetToDefaults()
+{
+ // common
+ m_devSampleRate = 5e6;
+ m_extClock = false;
+ m_extClockFreq = 0; // Auto
+ m_fileRecordName = "";
+ m_useReverseAPI = false;
+ m_reverseAPIAddress = "127.0.0.1";
+ m_reverseAPIPort = 8888;
+ m_reverseAPIDeviceIndex = 0;
+ // Rx
+ m_rxCenterFrequency = 435000*1000;
+ m_log2HardDecim = 2;
+ m_dcBlock = false;
+ m_iqCorrection = false;
+ m_log2SoftDecim = 0;
+ m_ncoEnableRx = false;
+ m_ncoFrequencyRx = 0;
+ m_antennaPathRx = RXANT_LO;
+ // Rx0
+ m_lpfBWRx0 = 4.5e6f;
+ m_gainRx0 = 50;
+ m_gainModeRx0 = GAIN_AUTO;
+ m_lnaGainRx0 = 15;
+ m_tiaGainRx0 = 2;
+ m_pgaGainRx0 = 16;
+ m_pwrmodeRx0 = 4;
+ // Rx1
+ m_lpfBWRx1 = 4.5e6f;
+ m_gainRx1 = 50;
+ m_gainModeRx1 = GAIN_AUTO;
+ m_lnaGainRx1 = 15;
+ m_tiaGainRx1 = 2;
+ m_pgaGainRx1 = 16;
+ m_pwrmodeRx1 = 4;
+ // Tx
+ m_txCenterFrequency = 435000*1000;
+ m_log2HardInterp = 2;
+ m_log2SoftInterp = 4;
+ m_ncoEnableTx = true;
+ m_ncoFrequencyTx = 500000;
+ m_antennaPathTx = TXANT_WI;
+ // Tx0
+ m_lpfBWTx0 = 4.5e6f;
+ m_gainTx0 = 20;
+ m_pwrmodeTx0 = 4;
+ // Tx1
+ m_lpfBWTx1 = 4.5e6f;
+ m_gainTx1 = 20;
+ m_pwrmodeTx1 = 4;
+}
+
+QByteArray XTRXMIMOSettings::serialize() const
+{
+ SimpleSerializer s(1);
+
+ // common
+ s.writeDouble(1, m_devSampleRate);
+ s.writeBool(2, m_extClock);
+ s.writeU32(3, m_extClockFreq);
+ s.writeString(4, m_fileRecordName);
+ s.writeBool(5, m_useReverseAPI);
+ s.writeString(6, m_reverseAPIAddress);
+ s.writeU32(7, m_reverseAPIPort);
+ s.writeU32(8, m_reverseAPIDeviceIndex);
+ // Rx
+ s.writeU32(20, m_log2HardDecim);
+ s.writeU32(21, m_log2SoftDecim);
+ s.writeBool(22, m_dcBlock);
+ s.writeBool(23, m_iqCorrection);
+ s.writeBool(24, m_ncoEnableRx);
+ s.writeS32(25, m_ncoFrequencyRx);
+ s.writeS32(26, (int) m_antennaPathRx);
+ // Rx0
+ s.writeFloat(30, m_lpfBWRx0);
+ s.writeU32(31, m_gainRx0);
+ s.writeS32(34, (int) m_gainModeRx0);
+ s.writeU32(35, m_lnaGainRx0);
+ s.writeU32(36, m_tiaGainRx0);
+ s.writeU32(37, m_pgaGainRx0);
+ s.writeU32(38, m_pwrmodeRx0);
+ // Rx1
+ s.writeFloat(50, m_lpfBWRx0);
+ s.writeU32(51, m_gainRx0);
+ s.writeS32(54, (int) m_gainModeRx0);
+ s.writeU32(55, m_lnaGainRx0);
+ s.writeU32(56, m_tiaGainRx0);
+ s.writeU32(57, m_pgaGainRx0);
+ s.writeU32(58, m_pwrmodeRx0);
+ // Tx
+ s.writeU32(70, m_log2HardInterp);
+ s.writeU32(71, m_log2SoftInterp);
+ s.writeBool(72, m_ncoEnableTx);
+ s.writeS32(73, m_ncoFrequencyTx);
+ s.writeS32(74, (int) m_antennaPathTx);
+ // Tx0
+ s.writeFloat(80, m_lpfBWTx0);
+ s.writeU32(81, m_gainTx0);
+ s.writeU32(82, m_pwrmodeTx0);
+ // Tx1
+ s.writeFloat(90, m_lpfBWTx1);
+ s.writeU32(91, m_gainTx1);
+ s.writeU32(92, m_pwrmodeTx1);
+
+ return s.final();
+}
+
+bool XTRXMIMOSettings::deserialize(const QByteArray& data)
+{
+ SimpleDeserializer d(data);
+
+ if (!d.isValid())
+ {
+ resetToDefaults();
+ return false;
+ }
+
+ if (d.getVersion() == 1)
+ {
+ int intval;
+ uint32_t uintval;
+
+ // common
+ d.readDouble(1, &m_devSampleRate, 5e6);
+ d.readBool(2, &m_extClock, false);
+ d.readU32(3, &m_extClockFreq, 0);
+ d.readString(4, &m_fileRecordName, "");
+ d.readBool(5, &m_useReverseAPI, false);
+ d.readString(6, &m_reverseAPIAddress, "127.0.0.1");
+ d.readU32(7, &uintval, 0);
+ if ((uintval > 1023) && (uintval < 65535)) {
+ m_reverseAPIPort = uintval;
+ } else {
+ m_reverseAPIPort = 8888;
+ }
+ d.readU32(8, &uintval, 0);
+ m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
+ // Rx
+ d.readU32(20, &m_log2HardDecim, 1);
+ d.readU32(21, &m_log2SoftDecim, 0);
+ d.readBool(22, &m_dcBlock, false);
+ d.readBool(23, &m_iqCorrection, false);
+ d.readBool(32, &m_ncoEnableRx, false);
+ d.readS32(33, &m_ncoFrequencyRx, 0);
+ d.readS32(24, &intval, 0);
+ m_antennaPathRx = (RxAntenna) intval;
+ // Rx0
+ d.readFloat(30, &m_lpfBWRx0, 1.5e6);
+ d.readU32(31, &m_gainRx0, 50);
+ d.readS32(34, &intval, 0);
+ m_gainModeRx0 = (GainMode) intval;
+ d.readU32(35, &m_lnaGainRx0, 15);
+ d.readU32(36, &m_tiaGainRx0, 2);
+ d.readU32(37, &m_pgaGainRx0, 16);
+ d.readU32(38, &m_pwrmodeRx0, 4);
+ // Rx1
+ d.readFloat(50, &m_lpfBWRx1, 1.5e6);
+ d.readU32(51, &m_gainRx1, 50);
+ d.readS32(54, &intval, 0);
+ m_gainModeRx1 = (GainMode) intval;
+ d.readU32(55, &m_lnaGainRx1, 15);
+ d.readU32(56, &m_tiaGainRx1, 2);
+ d.readU32(57, &m_pgaGainRx1, 16);
+ d.readU32(58, &m_pwrmodeRx1, 4);
+ // Tx
+ d.readU32(20, &m_log2HardInterp, 2);
+ d.readU32(71, &m_log2SoftInterp, 0);
+ d.readS32(72, &intval, 0);
+ d.readBool(82, &m_ncoEnableTx, true);
+ d.readS32(83, &m_ncoFrequencyTx, 500000);
+ m_antennaPathTx = (TxAntenna) intval;
+ // Tx0
+ d.readFloat(80, &m_lpfBWTx0, 1.5e6);
+ d.readU32(81, &m_gainTx0, 20);
+ d.readU32(84, &m_pwrmodeTx0, 4);
+ // Tx1
+ d.readFloat(90, &m_lpfBWTx1, 1.5e6);
+ d.readU32(91, &m_gainTx1, 20);
+ d.readU32(94, &m_pwrmodeTx1, 4);
+
+ return true;
+ }
+ else
+ {
+ resetToDefaults();
+ return false;
+ }
+
+}
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h
new file mode 100644
index 000000000..6735e5303
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h
@@ -0,0 +1,103 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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_XTRXMIMO_XTRXMIMOSETTINGS_H_
+#define PLUGINS_SAMPLEMIMO_XTRXMIMO_XTRXMIMOSETTINGS_H_
+
+#include
+
+#include
+#include
+
+struct XTRXMIMOSettings
+{
+ typedef enum {
+ GAIN_AUTO,
+ GAIN_MANUAL
+ } GainMode;
+
+ typedef enum {
+ RXANT_LO,
+ RXANT_WI,
+ RXANT_HI
+ } RxAntenna;
+
+ typedef enum {
+ TXANT_HI,
+ TXANT_WI
+ } TxAntenna;
+
+ // common
+ double m_devSampleRate;
+ bool m_extClock; //!< True if external clock source
+ uint32_t m_extClockFreq; //!< Frequency (Hz) of external clock source
+ QString m_fileRecordName;
+ uint8_t m_gpioDir; //!< GPIO pin direction LSB first; 0 input, 1 output
+ uint8_t m_gpioPins; //!< GPIO pins to write; LSB first
+ bool m_useReverseAPI;
+ QString m_reverseAPIAddress;
+ uint16_t m_reverseAPIPort;
+ uint16_t m_reverseAPIDeviceIndex;
+ // Rx
+ uint64_t m_rxCenterFrequency;
+ uint32_t m_log2HardDecim;
+ uint32_t m_log2SoftDecim;
+ bool m_dcBlock;
+ bool m_iqCorrection;
+ bool m_ncoEnableRx; //!< Enable TSP NCO and mixing
+ int m_ncoFrequencyRx; //!< Actual NCO frequency (the resulting frequency with mixing is displayed)
+ RxAntenna m_antennaPathRx;
+ // Rx0
+ float m_lpfBWRx0; //!< LMS analog lowpass filter bandwidth (Hz)
+ uint32_t m_gainRx0; //!< Optimally distributed gain (dB)
+ GainMode m_gainModeRx0; //!< Gain mode: auto or manual
+ uint32_t m_lnaGainRx0; //!< Manual LNA gain
+ uint32_t m_tiaGainRx0; //!< Manual TIA gain
+ uint32_t m_pgaGainRx0; //!< Manual PGA gain
+ uint32_t m_pwrmodeRx0;
+ // Rx1
+ float m_lpfBWRx1; //!< LMS analog lowpass filter bandwidth (Hz)
+ uint32_t m_gainRx1; //!< Optimally distributed gain (dB)
+ GainMode m_gainModeRx1; //!< Gain mode: auto or manual
+ uint32_t m_lnaGainRx1; //!< Manual LNA gain
+ uint32_t m_tiaGainRx1; //!< Manual TIA gain
+ uint32_t m_pgaGainRx1; //!< Manual PGA gain
+ uint32_t m_pwrmodeRx1;
+ // Tx
+ uint64_t m_txCenterFrequency;
+ uint32_t m_log2HardInterp;
+ uint32_t m_log2SoftInterp;
+ bool m_ncoEnableTx; //!< Enable TSP NCO and mixing
+ int m_ncoFrequencyTx; //!< Actual NCO frequency (the resulting frequency with mixing is displayed)
+ TxAntenna m_antennaPathTx;
+ // Tx0
+ float m_lpfBWTx0; //!< LMS analog lowpass filter bandwidth (Hz)
+ uint32_t m_gainTx0; //!< Optimally distributed gain (dB)
+ uint32_t m_pwrmodeTx0;
+ // Tx1
+ float m_lpfBWTx1; //!< LMS analog lowpass filter bandwidth (Hz)
+ uint32_t m_gainTx1; //!< Optimally distributed gain (dB)
+ uint32_t m_pwrmodeTx1;
+
+ XTRXMIMOSettings();
+ void resetToDefaults();
+ QByteArray serialize() const;
+ bool deserialize(const QByteArray& data);
+};
+
+
+#endif // PLUGINS_SAMPLEMIMO_XTRXMIMO_XTRXMIMOSETTINGS_H_
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimowebapiadapter.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimowebapiadapter.cpp
new file mode 100644
index 000000000..081089057
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmimowebapiadapter.cpp
@@ -0,0 +1,50 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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 "xtrxmimowebapiadapter.h"
+
+XTRXMIMOWebAPIAdapter::XTRXMIMOWebAPIAdapter()
+{}
+
+XTRXMIMOWebAPIAdapter::~XTRXMIMOWebAPIAdapter()
+{}
+
+int XTRXMIMOWebAPIAdapter::webapiSettingsGet(
+ SWGSDRangel::SWGDeviceSettings& response,
+ QString& errorMessage)
+{
+ (void) errorMessage;
+ (void) response;
+ return 501;
+}
+
+int XTRXMIMOWebAPIAdapter::webapiSettingsPutPatch(
+ bool force,
+ const QStringList& deviceSettingsKeys,
+ SWGSDRangel::SWGDeviceSettings& response, // query + response
+ QString& errorMessage)
+{
+ (void) errorMessage;
+ (void) force;
+ (void) deviceSettingsKeys;
+ (void) response;
+ return 501;
+}
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimowebapiadapter.h b/plugins/samplemimo/xtrxmimo/xtrxmimowebapiadapter.h
new file mode 100644
index 000000000..8f6efce82
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmimowebapiadapter.h
@@ -0,0 +1,44 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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 "xtrxmimosettings.h"
+
+class XTRXMIMOWebAPIAdapter : public DeviceWebAPIAdapter
+{
+public:
+ XTRXMIMOWebAPIAdapter();
+ virtual ~XTRXMIMOWebAPIAdapter();
+ 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:
+ XTRXMIMOSettings m_settings;
+};
\ No newline at end of file
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmithread.cpp b/plugins/samplemimo/xtrxmimo/xtrxmithread.cpp
new file mode 100644
index 000000000..91f322e0c
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmithread.cpp
@@ -0,0 +1,213 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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 "dsp/samplemififo.h"
+
+#include "xtrxmithread.h"
+
+XTRXMIThread::XTRXMIThread(struct xtrx_dev *dev, QObject* parent) :
+ QThread(parent),
+ m_running(false),
+ m_dev(dev),
+ m_sampleFifo(nullptr)
+{
+ qDebug("XTRXMIThread::XTRXMIThread");
+
+ for (unsigned int i = 0; i < 2; i++) {
+ m_channels[i].m_convertBuffer.resize(DeviceXTRX::blockSize, Sample{0,0});
+ }
+
+ m_vBegin.push_back(m_channels[0].m_convertBuffer.begin());
+ m_vBegin.push_back(m_channels[1].m_convertBuffer.begin());
+}
+
+XTRXMIThread::~XTRXMIThread()
+{
+ qDebug("XTRXMIThread::~XTRXMIThread");
+
+ if (m_running) {
+ stopWork();
+ }
+}
+
+void XTRXMIThread::startWork()
+{
+ if (m_running) {
+ return; // return if running already
+ }
+
+ m_startWaitMutex.lock();
+ start();
+
+ while (!m_running) {
+ m_startWaiter.wait(&m_startWaitMutex, 100);
+ }
+
+ m_startWaitMutex.unlock();
+}
+
+void XTRXMIThread::stopWork()
+{
+ if (!m_running) {
+ return; // return if not running
+ }
+
+ m_running = false;
+ wait();
+}
+
+void XTRXMIThread::run()
+{
+ int res;
+ int lengths[2];
+
+ m_running = true;
+ m_startWaiter.wakeAll();
+
+ xtrx_run_params params;
+ xtrx_run_params_init(¶ms);
+
+ params.dir = XTRX_RX;
+ params.rx.chs = XTRX_CH_AB;
+ params.rx.wfmt = XTRX_WF_16;
+ params.rx.hfmt = XTRX_IQ_INT16;
+ params.rx_stream_start = 2*DeviceXTRX::blockSize; // was 2*8192
+ params.rx.paketsize = 2*DeviceXTRX::blockSize;
+
+ res = xtrx_run_ex(m_dev, ¶ms);
+
+ if (res != 0)
+ {
+ qCritical("XTRXInputThread::run: could not start stream err:%d", res);
+ m_running = false;
+ }
+ else
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ qDebug("XTRXInputThread::run: stream started");
+ }
+
+ const unsigned int elemSize = 4; // XTRX uses 4 byte I+Q samples
+ std::vector> buffMem(2, std::vector(elemSize*DeviceXTRX::blockSize));
+ std::vector buffs(2);
+
+ for (std::size_t i = 0; i < 2; i++) {
+ buffs[i] = buffMem[i].data();
+ }
+
+ xtrx_recv_ex_info_t nfo;
+ nfo.samples = DeviceXTRX::blockSize;
+ nfo.buffer_count = 2;
+ nfo.buffers = (void* const*) buffs.data();
+ nfo.flags = RCVEX_DONT_INSER_ZEROS | RCVEX_DROP_OLD_ON_OVERFLOW;
+
+ while (m_running)
+ {
+ res = xtrx_recv_sync_ex(m_dev, &nfo);
+
+ if (res < 0)
+ {
+ qCritical("XTRXInputThread::run read error: %d", res);
+ qDebug("XTRXInputThread::run: out_samples: %u out_events: %u", nfo.out_samples, nfo.out_events);
+ break;
+ }
+
+ if (nfo.out_events & RCVEX_EVENT_OVERFLOW) {
+ qDebug("XTRXInputThread::run: overflow");
+ }
+
+ lengths[0] = callbackSI(0, (const qint16*) buffs[0], 2*nfo.out_samples);
+ lengths[1] = callbackSI(1, (const qint16*) buffs[1], 2*nfo.out_samples);
+
+ if (lengths[0] == lengths[1])
+ {
+ m_sampleFifo->writeSync(m_vBegin, lengths[0]);
+ }
+ else
+ {
+ qWarning("XTRXMIThread::run: unequal channel lengths: [0]=%d [1]=%d", lengths[0], lengths[1]);
+ m_sampleFifo->writeSync(m_vBegin, (std::min)(lengths[0], lengths[1]));
+ }
+ }
+
+ res = xtrx_stop(m_dev, XTRX_RX);
+
+ if (res != 0)
+ {
+ qCritical("XTRXInputThread::run: could not stop stream");
+ }
+ else
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ qDebug("XTRXInputThread::run: stream stopped");
+ }
+
+ m_running = false;
+}
+
+void XTRXMIThread::setLog2Decimation(unsigned int log2_decim)
+{
+ m_log2Decim = log2_decim;
+}
+
+unsigned int XTRXMIThread::getLog2Decimation() const
+{
+ return m_log2Decim;
+}
+
+int XTRXMIThread::callbackSI(unsigned int channel, const qint16* buf, qint32 len)
+{
+ SampleVector::iterator it = m_channels[channel].m_convertBuffer.begin();
+
+ if (m_log2Decim == 0)
+ {
+ m_channels[channel].m_decimators.decimate1(&it, buf, len);
+ }
+ else
+ {
+ switch (m_log2Decim)
+ {
+ case 1:
+ m_channels[channel].m_decimators.decimate2_cen(&it, buf, len);
+ break;
+ case 2:
+ m_channels[channel].m_decimators.decimate4_cen(&it, buf, len);
+ break;
+ case 3:
+ m_channels[channel].m_decimators.decimate8_cen(&it, buf, len);
+ break;
+ case 4:
+ m_channels[channel].m_decimators.decimate16_cen(&it, buf, len);
+ break;
+ case 5:
+ m_channels[channel].m_decimators.decimate32_cen(&it, buf, len);
+ break;
+ case 6:
+ m_channels[channel].m_decimators.decimate64_cen(&it, buf, len);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return it - m_channels[channel].m_convertBuffer.begin();
+}
+
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmithread.h b/plugins/samplemimo/xtrxmimo/xtrxmithread.h
new file mode 100644
index 000000000..acadc85d5
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmithread.h
@@ -0,0 +1,69 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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_XTRXMIMO_XTRXMITHREAD_H_
+#define PLUGINS_SAMPLEMIMO_XTRXMIMO_XTRXMITHREAD_H_
+
+#include
+#include
+#include
+
+#include "xtrx_api.h"
+
+#include "xtrx/devicextrx.h"
+#include "dsp/decimators.h"
+
+class SampleMIFifo;
+
+class XTRXMIThread : public QThread {
+ Q_OBJECT
+
+public:
+ XTRXMIThread(struct xtrx_dev *dev, QObject* parent = nullptr);
+ ~XTRXMIThread();
+
+ void startWork();
+ void stopWork();
+ bool isRunning() const { return m_running; }
+ void setLog2Decimation(unsigned int log2_decim);
+ unsigned int getLog2Decimation() const;
+ void setFifo(SampleMIFifo *sampleFifo) { m_sampleFifo = sampleFifo; }
+ SampleMIFifo *getFifo() { return m_sampleFifo; }
+
+private:
+ struct Channel
+ {
+ SampleVector m_convertBuffer;
+ Decimators m_decimators;
+ };
+
+ QMutex m_startWaitMutex;
+ QWaitCondition m_startWaiter;
+ bool m_running;
+
+ struct xtrx_dev* m_dev;
+ qint16 m_buf[2][2*DeviceXTRX::blockSize];
+ Channel m_channels[2];
+ std::vector m_vBegin;
+ SampleMIFifo* m_sampleFifo;
+ unsigned int m_log2Decim;
+
+ void run();
+ int callbackSI(unsigned int channel, const qint16* buf, qint32 len);
+};
+
+#endif // PLUGINS_SAMPLEMIMO_XTRXMIMO_XTRXMITHREAD_H_
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmothread.cpp b/plugins/samplemimo/xtrxmimo/xtrxmothread.cpp
new file mode 100644
index 000000000..2dfbfffef
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmothread.cpp
@@ -0,0 +1,240 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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 "dsp/samplemofifo.h"
+
+#include "xtrxmothread.h"
+
+XTRXMOThread::XTRXMOThread(struct xtrx_dev *dev, QObject* parent) :
+ QThread(parent),
+ m_running(false),
+ m_dev(dev),
+ m_sampleFifo(nullptr)
+{
+ qDebug("XTRXMOThread::XTRXMOThread");
+ m_buf = new qint16[2*DeviceXTRX::blockSize*2];
+ std::fill(m_buf, m_buf + 2*DeviceXTRX::blockSize*2, 0);
+}
+
+XTRXMOThread::~XTRXMOThread()
+{
+ qDebug("XTRXMOThread::~XTRXMOThread");
+
+ if (m_running) {
+ stopWork();
+ }
+
+ delete[] m_buf;
+}
+
+void XTRXMOThread::startWork()
+{
+ m_startWaitMutex.lock();
+ start();
+
+ while(!m_running) {
+ m_startWaiter.wait(&m_startWaitMutex, 100);
+ }
+
+ m_startWaitMutex.unlock();
+}
+
+void XTRXMOThread::stopWork()
+{
+ m_running = false;
+ wait();
+}
+
+void XTRXMOThread::setLog2Interpolation(unsigned int log2Interp)
+{
+ qDebug("XTRXMOThread::setLog2Interpolation: %u", log2Interp);
+ m_log2Interp = log2Interp;
+}
+
+unsigned int XTRXMOThread::getLog2Interpolation() const
+{
+ return m_log2Interp;
+}
+
+void XTRXMOThread::run()
+{
+ int res;
+
+ m_running = true;
+ m_startWaiter.wakeAll();
+
+ xtrx_run_params params;
+ xtrx_run_params_init(¶ms);
+
+ params.dir = XTRX_TX;
+ params.tx_repeat_buf = 0;
+ params.tx.paketsize = 2*DeviceXTRX::blockSize;
+ params.tx.chs = XTRX_CH_AB;
+ params.tx.wfmt = XTRX_WF_16;
+ params.tx.hfmt = XTRX_IQ_INT16;
+ params.tx.flags |= XTRX_RSP_SWAP_IQ;
+
+ res = xtrx_run_ex(m_dev, ¶ms);
+
+ if (res != 0)
+ {
+ qCritical("XTRXMOThread::run: could not start stream err:%d", res);
+ m_running = false;
+ }
+ else
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ qDebug("XTRXMOThread::run: stream started");
+ }
+
+ const unsigned int elemSize = 4; // XTRX uses 4 byte I+Q samples
+ std::vector> buffMem(2, std::vector(elemSize*DeviceXTRX::blockSize));
+ std::vector buffs(2);
+ master_ts ts = 4096*1024;
+
+ for (std::size_t i = 0; i < 2; i++) {
+ buffs[i] = buffMem[i].data();
+ }
+
+ xtrx_send_ex_info_t nfo;
+ nfo.samples = DeviceXTRX::blockSize;
+ nfo.buffer_count = 2;
+ nfo.buffers = (void* const*) buffs.data();
+ nfo.flags = XTRX_TX_DONT_BUFFER; // | XTRX_TX_SEND_ZEROS;
+ nfo.timeout = 0;
+ nfo.out_txlatets = 0;
+ nfo.ts = ts;
+
+ while (m_running)
+ {
+ callback((qint16*) buffs[0], (qint16*) buffs[1], nfo.samples);
+ res = xtrx_send_sync_ex(m_dev, &nfo);
+
+ if (res < 0)
+ {
+ qCritical("XTRXMOThread::run send error: %d", res);
+ qDebug("XTRXMOThread::run: out_samples: %u out_flags: %u", nfo.out_samples, nfo.out_flags);
+ break;
+ }
+
+ if (nfo.out_flags & XTRX_TX_DISCARDED_TO) {
+ qDebug("XTRXMOThread::run: underrun");
+ }
+
+ if (nfo.out_txlatets) {
+ qDebug("XTRXMOThread::run: out_txlatets: %lu", nfo.out_txlatets);
+ }
+
+ nfo.ts += DeviceXTRX::blockSize;
+ }
+
+ res = xtrx_stop(m_dev, XTRX_TX);
+
+ if (res != 0)
+ {
+ qCritical("XTRXMOThread::run: could not stop stream");
+ }
+ else
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ qDebug("XTRXMOThread::run: stream stopped");
+ }
+
+ m_running = false;
+}
+
+void XTRXMOThread::callback(qint16* buf0, qint16* buf1, 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 XTRXMOThread::callbackPart(qint16* buf0, qint16* buf1, 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,
+ channel == 0 ? &buf0[2*nSamples] : &buf1[2*nSamples],
+ 2*nSamples);
+ }
+ else
+ {
+ switch (m_log2Interp)
+ {
+ case 1:
+ m_interpolators[channel].interpolate2_cen(
+ &begin,
+ channel == 0 ? &buf0[2*nSamples] : &buf1[2*nSamples],
+ 2*nSamples);
+ break;
+ case 2:
+ m_interpolators[channel].interpolate4_cen(
+ &begin,
+ channel == 0 ? &buf0[2*nSamples] : &buf1[2*nSamples],
+ 2*nSamples);
+ break;
+ case 3:
+ m_interpolators[channel].interpolate8_cen(
+ &begin,
+ channel == 0 ? &buf0[2*nSamples] : &buf1[2*nSamples],
+ 2*nSamples);
+ break;
+ case 4:
+ m_interpolators[channel].interpolate16_cen(
+ &begin,
+ channel == 0 ? &buf0[2*nSamples] : &buf1[2*nSamples],
+ 2*nSamples);
+ break;
+ case 5:
+ m_interpolators[channel].interpolate32_cen(
+ &begin,
+ channel == 0 ? &buf0[2*nSamples] : &buf1[2*nSamples],
+ 2*nSamples);
+ break;
+ case 6:
+ m_interpolators[channel].interpolate64_cen(
+ &begin,
+ channel == 0 ? &buf0[2*nSamples] : &buf1[2*nSamples],
+ 2*nSamples);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/plugins/samplemimo/xtrxmimo/xtrxmothread.h b/plugins/samplemimo/xtrxmimo/xtrxmothread.h
new file mode 100644
index 000000000..8ee51cfc1
--- /dev/null
+++ b/plugins/samplemimo/xtrxmimo/xtrxmothread.h
@@ -0,0 +1,63 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2020 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_XTRXMIMO_XTRXMOTHREAD_H_
+#define PLUGINS_SAMPLEMIMO_XTRXMIMO_XTRXMOTHREAD_H_
+
+#include
+#include
+#include
+
+#include "xtrx_api.h"
+
+#include "xtrx/devicextrx.h"
+#include "dsp/interpolators.h"
+
+class SampleMOFifo;
+
+class XTRXMOThread : public QThread {
+ Q_OBJECT
+
+public:
+ XTRXMOThread(struct xtrx_dev *dev, QObject* parent = nullptr);
+ ~XTRXMOThread();
+
+ void startWork();
+ void stopWork();
+ bool isRunning() const { return m_running; }
+ void setLog2Interpolation(unsigned int log2Interp);
+ unsigned int getLog2Interpolation() const;
+ void setFifo(SampleMOFifo *sampleFifo) { m_sampleFifo = sampleFifo; }
+ SampleMOFifo *getFifo() { return m_sampleFifo; }
+
+private:
+ QMutex m_startWaitMutex;
+ QWaitCondition m_startWaiter;
+ bool m_running;
+ struct xtrx_dev* m_dev;
+
+ qint16 *m_buf; //!< Full buffer for SISO or MIMO operation
+ SampleMOFifo* m_sampleFifo;
+ Interpolators m_interpolators[2];
+ unsigned int m_log2Interp;
+
+ void run();
+ void callback(qint16* buf0, qint16* buf1, qint32 samplesPerChannel);
+ void callbackPart(qint16* buf0, qint16* buf1, qint32 nSamples, int iBegin);
+};
+
+#endif // PLUGINS_SAMPLEMIMO_XTRXMIMO_XTRXMOTHREAD_H_
\ No newline at end of file