From 77d3cbae4447ba147727309439fad7f841fe16b1 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 16 Apr 2020 08:43:51 +0200 Subject: [PATCH] XTRX MIMO: initial implementation --- plugins/samplemimo/xtrxmimo/CMakeLists.txt | 62 + plugins/samplemimo/xtrxmimo/xtrxmimo.cpp | 1306 ++++++++++++++++ plugins/samplemimo/xtrxmimo/xtrxmimo.h | 318 ++++ plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp | 1087 +++++++++++++ plugins/samplemimo/xtrxmimo/xtrxmimogui.h | 127 ++ plugins/samplemimo/xtrxmimo/xtrxmimogui.ui | 1345 +++++++++++++++++ .../samplemimo/xtrxmimo/xtrxmimoplugin.cpp | 142 ++ plugins/samplemimo/xtrxmimo/xtrxmimoplugin.h | 56 + .../samplemimo/xtrxmimo/xtrxmimosettings.cpp | 215 +++ .../samplemimo/xtrxmimo/xtrxmimosettings.h | 103 ++ .../xtrxmimo/xtrxmimowebapiadapter.cpp | 50 + .../xtrxmimo/xtrxmimowebapiadapter.h | 44 + plugins/samplemimo/xtrxmimo/xtrxmithread.cpp | 213 +++ plugins/samplemimo/xtrxmimo/xtrxmithread.h | 69 + plugins/samplemimo/xtrxmimo/xtrxmothread.cpp | 240 +++ plugins/samplemimo/xtrxmimo/xtrxmothread.h | 63 + 16 files changed, 5440 insertions(+) create mode 100644 plugins/samplemimo/xtrxmimo/CMakeLists.txt create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmimo.cpp create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmimo.h create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmimogui.h create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmimogui.ui create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmimoplugin.cpp create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmimoplugin.h create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmimosettings.h create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmimowebapiadapter.cpp create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmimowebapiadapter.h create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmithread.cpp create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmithread.h create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmothread.cpp create mode 100644 plugins/samplemimo/xtrxmimo/xtrxmothread.h 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 +
gui/valuedial.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + ExternalClockButton + QToolButton +
gui/externalclockbutton.h
+
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 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