diff --git a/devices/xtrx/devicextrx.cpp b/devices/xtrx/devicextrx.cpp index 23d99579c..cf6ebbfb2 100644 --- a/devices/xtrx/devicextrx.cpp +++ b/devices/xtrx/devicextrx.cpp @@ -159,4 +159,52 @@ double DeviceXTRX::setSamplerate(double rate, double master, bool output) } return m_inputRate; -} \ No newline at end of file +} + +bool DeviceXTRX::setSamplerate(double rate, uint32_t log2Decim, uint32_t log2Interp, bool output) +{ + double master, rxRate, txRate; + + if (output) + { + master = (log2Interp == 0) ? 0 : (rate * 4 * (1 << log2Interp)); + rxRate = (rate * 4 * (1 << log2Interp)) / (4 * (1 << log2Decim)); + txRate = rate; + } + else + { + master = (log2Decim == 0) ? 0 : (rate * 4 * (1 << log2Decim)); + rxRate = rate; + txRate = (rate * 4 * (1 << log2Decim)) / (4 * (1 << log2Interp)); + } + + m_masterRate = master; + m_inputRate = rxRate; + m_outputRate = txRate; + + int res = xtrx_set_samplerate( + m_dev, + master, + rxRate, + txRate, + XTRX_SAMPLERATE_FORCE_UPDATE, + &m_clockGen, + &m_actualInputRate, + &m_actualOutputRate + ); + + qDebug() << "DeviceXTRX::setSamplerate: sample rate set: " + << "rate: " << rate + << "log2Decim: " << log2Decim + << "log2Interp: " << log2Interp + << "output: " << output + << "res: "<< res + << "m_masterRate: " << m_masterRate + << "m_inputRate: " << m_inputRate + << "m_outputRate: " << m_outputRate + << "m_clockGen: " << m_clockGen + << "m_actualInputRate: " << m_actualInputRate + << "m_actualOutputRate: " << m_actualOutputRate; + + return !(res < 0); +} diff --git a/devices/xtrx/devicextrx.h b/devices/xtrx/devicextrx.h index de3e2961c..936b8b533 100644 --- a/devices/xtrx/devicextrx.h +++ b/devices/xtrx/devicextrx.h @@ -37,6 +37,7 @@ public: static void enumOriginDevices(const QString& hardwareId, PluginInterface::OriginDevices& originDevices); struct xtrx_dev *getDevice() { return m_dev; } double setSamplerate(double rate, double master, bool output); + bool setSamplerate(double rate, uint32_t log2Decim, uint32_t log2Interp, bool output); double getMasterRate() const { return m_masterRate; } double getClockGen() const { return m_clockGen; } double getActualInputRate() const { return m_actualInputRate; } diff --git a/plugins/samplemimo/CMakeLists.txt b/plugins/samplemimo/CMakeLists.txt index fe588f9ef..872f83c9d 100644 --- a/plugins/samplemimo/CMakeLists.txt +++ b/plugins/samplemimo/CMakeLists.txt @@ -8,5 +8,9 @@ if(ENABLE_LIMESUITE AND LIMESUITE_FOUND) add_subdirectory(limesdrmimo) endif() +if(ENABLE_XTRX AND LIBXTRX_FOUND) + add_subdirectory(xtrxmimo) +endif() + add_subdirectory(testmi) add_subdirectory(testmosync) 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/readme.md b/plugins/samplemimo/xtrxmimo/readme.md new file mode 100644 index 000000000..e82a6eec4 --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/readme.md @@ -0,0 +1,237 @@ +

XTRX MIMO plugin

+ +

Introduction

+ +This is a v5 only plugin. + +This MIMO plugin sends and receives its samples to/from a [XTRX device](https://xtrx.io). It handles both input (Rx) and output (Tx) streams synchronously. There is no option to synchronize Rx with Tx streams. + +⚠ There are USB errors when first starting with XTRX after plugging it in. The only way to work around this is to restart SDRangel application. + +⚠ Reception may stall sometimes particularly with sample rates lower than 5 MS/s and also after changes. You may need to stop and restart the device (stop/start button) to recover. + +⚠ Right after (re)start you may need to move the main frequency dial back and forth if you notice that you are not on the right frequency. + +⚠ Simultaneous Rx and Tx is not supported. Dual Tx is not working either. + +

Build

+ +The plugin will be built only if XTRX support libraries are installed in your system. + +If libraries are installed in a custom place like `/opt/install/xtrx-images` add the following defines on `cmake` command line: + +`-DXTRX_DIR=/opt/install/xtrx-images` + +

Real time scheduling

+ +You may find in the log some info (green) messages from `libxtrx` mentioning that some task cannot be set with real time priority. While this is not an absolute necessity to make XTRX work you may want to allow your user or a specific group your user belongs to to set tasks with real time scheduling. + +In most Linux systems this is done by editing the `/etc/security/limits.conf` file (with sudo rights). In this file you may add these lines for your user (ex: `user`): + +``` +user - rtprio 99 +user - memlock unlimited +``` + +For a group the syntax is the same but the group name is prefixed with `@` like: + +``` +@realtime - rtprio 99 +@realtime - memlock unlimited +``` + +

Interface

+ +![XTRX MIMO plugin GUI](../../../doc/img/XTRXMIMO_plugin.png) + +

1. Rx/Tx settings selection

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

2. Stream settings selection

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

3. Rx/Tx spectrum display selection

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

4. Stream spectrum display selection

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

5. Start/Stop Rx

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

6. Start/Stop Tx

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

7. Record button

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

8. Center frequency

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

9. ADC/DAC sample rate

+ +This is the sample rate at which the ADC or DAC runs in kS/s (k) or MS/s (M) before hardware decimation or after hardware interpolation. Thus this is the device to host sample rate (A.7) multiplied by the hardware decimation or interpolation factor (A.8). + +

10. Baseband sample rate

+ +In device to host sample rate input mode (A.6) this is the baseband I/Q sample rate in kS/s. This is the device to host sample rate (A.7) divided by the software decimation or interpolation factor (A.9). + +In baseband sample rate input mode (A.6) this is the device to host sample rate in kS/s. This is the baseband sample rate (A.7) multiplied by the software decimation or interpolation factor (A.9) + +

A. Section

+ +![XTRX MIMO A section plugin GUI](../../../doc/img/XTRXMIMO_plugin_A.png) + +

A.1. NCO toggle

+ +The button is lit when NCO is active and dark when inactive. + +Use this button to activate/deactivate the TSP NCO. The LMS7002M chip has an independent NCO in each Rx channel that can span the bandwidth received by the ADC. This effectively allows non zero digital IF. + +

A.2. NCO frequency shift

+ +This is the frequency shift applied when the NCO is engaged thus the actual LO frequency is the center frequency of reception minus this value. Use the thumbwheels to adjust frequency as done with the LO (8). Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. The boundaries are dynamically calculated from the LO center frequency, sample rate and hardware decimation factor. + +☞ Engaging the NCO shifts the center frequency of reception or transmission by the shift amount. You have to retune the center frequency (8) to get back to the frequency before the NCO was engaged. You may also select the NCO frequency and then tune the center frequency. + +☞ In the LMS7002M TSP Rx block the NCO sits before the decimator (see Fig.14 of the [datasheet](http://www.limemicro.com/wp-content/uploads/2015/09/LMS7002M-Data-Sheet-v2.8.0.pdf) p.7) so it runs at the actual ADC rate. Hence the NCO limits are calculated as +/- half the device to host sample rate multiplied by the hardware decimation factor. For example with a 4 MS/s device to host sample rate (A.7) and a hardware decimation of 16 (A.8) you have +/- 32 MHz span around the LO for the NCO. In this example you can tune all HF frequencies with the center frequency set at its lowest (30 MHz). + +A similar logic is used on the Tx / DAC side. + +

A.3. DC auto correction options

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

A.4. IQ auto correction options

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

A.5. External clock control

+ +Use this button to open a dialog that lets you choose the external clock frequency and enable or disable it. When disabled the internal 30.72 MHz VCTCXO is used. + +![LimeSDR input plugin gain GUI](../../../doc/img/LimeSDR_plugin_extclock.png) + +
A.5.1. External clock frequency
+ +Can be varied from 5 to 300 MHz + +Use the thumbwheels to adjust frequency as done with the LO (1.1). Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. The boundaries are dynamically calculated from the LO center frequency, sample rate and hardware decimation factor. + +
A.5.2. Enable/disable external clock input
+ +Use this checkbox to enable or disable the external clock input + +
A.5.3. Confirm changes
+ +Use the "OK" button to confirm your changes + +
A.5.4. Dismiss changes
+ +Use the "Cancel" button to dismiss your changes + +

A.6. Device to host sample rate / Baseband sample rate input toggle

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

A.7. Sample rate

+ +This is the LMS7002M device to/from host stream sample rate or baseband sample rate in samples per second (S/s). The control (A.6) is used to switch between the two input modes. The device to/from host stream sample rate is the same for the Rx and Tx systems. + +The limits are adjusted automatically. In baseband input mode the limits are driven by the decimation or intepolation factor (A.9). You may need to increase this factor to be able to reach lower values. + +Use the wheels to adjust the sample rate. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. + +The LMS7002M uses the same clock for both the ADCs and DACs therefore this sample rate affects all of the 2x2 MIMO channels. + +

A.8. Hardware decimation or interpolation factor

+ +The TSP block in the LMS7002M hardware has a decimation/interpolation chain that acts on both Rx/Tx channels and therefore can achieve decimation/interpolation between 1 (no decimation/interpolation) and 64 in increasing powers of 2: 1, 2, 4, 8, 16, 32, 64. However the position "1" (no decimation/interpolation) is replaced by an automatic decimation/interpolation. + +

A.9. Software decimation or interpolation factor

+ +The I/Q stream from the LimeSDR is downsampled/upsampled by a power of two by software inside the plugin before being sent to the passband/device. Possible values are increasing powers of two: 1 (no decimation/interpolation), 2, 4, 8, 16, 32. + +

B. section

+ +![XTRX MIMO B section plugin GUI](../../../doc/img/XTRXMIMO_plugin_B.png) + +

B.1. Analog filter bandwidth

+ +This is the hardware filter bandwidth in kHz in the LMS7002M device for the given channel. Boundaries are updated automatically. Use the wheels to adjust the value. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. + +

B.2. Power mode

+ +XTRX power mode + +

B.3. Gain mode

+ +This is the gain mode control that applies to Rx only: +- **automatic**: AGC based on a global gain (B.4) +- **manual**: Manual. Use LNA, TIA and PGA controls (B.5 to B.7) to adjust gain + +

B.4. Manual gain setting

+ + - Rx: this sets the gain manually per stream if (B.3) is set to "Manual". + - Tx: this sets the gain + +

B.5. LNA gain

+ +Rx only. This is the LNA gain stage in dB + +

B.6. TIA gain

+ +Rx only. This is the TIA gain block settings in 3 increasing stages. + +

B.7. PGA gain

+ +Rx only. This is the PGA gain stage in dB + +

A.8. Antenna path select

+ +
Rx side
+ +Use this combo box to select the input path (network): + + - **Lo**: Selects the low frequency input. You should use this one and this is the default + - **Wide**: Selects the wide band frequency input. This is not connected and should not be used + - **Hi**: Selects the high frequency input. You may use this one as well with no actual difference with "Lo". + +
Tx side
+ +Use this combo box to select the output path (network): + + - **Hi**: Tx high range + - **Wi**: Tx wide range: you should use this one (default) + +

11. Stream status indicator

+ +This label turns green when status can be obtained from the current stream. Usually this means that the stream is up and running but not necessarily streaming data. The various status elements appear next on the same line (13) + +

12. GPS status

+ +This label turns green when the GPS used for the GPSDO is locked. + +

13. Rx FIFO status

+ +This is the fill percentage of the Rx FIFO in the XTRX interface. It should be zero most of the time. + +

14. Tx FIFO status

+ +This is the fill percentage of the Tx FIFO in the XTRX interface. It should be near 100% most of the time. + +

15. Board temperature

+ +This is the board temperature in degrees Celsius updated every ~5s. Before the first probe the display marks "00C" this is normal. diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimo.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimo.cpp new file mode 100644 index 000000000..0463294a7 --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/xtrxmimo.cpp @@ -0,0 +1,1860 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "SWGDeviceSettings.h" +#include "SWGXtrxMIMOSettings.h" +#include "SWGDeviceState.h" +#include "SWGDeviceReport.h" +#include "SWGDeviceActions.h" +#include "SWGXtrxMIMOReport.h" + +#include "device/deviceapi.h" +#include "dsp/dspcommands.h" +#include "dsp/dspengine.h" +#include "dsp/dspdevicemimoengine.h" +#include "dsp/devicesamplesource.h" +#include "dsp/devicesamplesink.h" +#include "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::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->setIQOrder(m_settings.m_iqOrder); + 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; + uint32_t rate = getRxDevSampleRate(); + 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::getRxDevSampleRate() const +{ + if (m_deviceShared.m_dev) { + return m_deviceShared.m_dev->getActualInputRate(); + } else { + return m_settings.m_rxDevSampleRate; + } +} + +uint32_t XTRXMIMO::getTxDevSampleRate() const +{ + if (m_deviceShared.m_dev) { + return m_deviceShared.m_dev->getActualOutputRate(); + } else { + return m_settings.m_txDevSampleRate; + } +} + +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 (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_extClock: " << settings.m_extClock + << " m_extClockFreq: " << settings.m_extClockFreq + << " force: " << force; + qDebug() << "XTRXMIMO::applySettings: Rx:" + << " m_rxDevSampleRate: " << settings.m_rxDevSampleRate + << " m_rxCenterFrequency: " << settings.m_rxCenterFrequency + << " 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_txDevSampleRate: " << settings.m_txDevSampleRate + << " 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); + } + } + } + + // 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_rxDevSampleRate != settings.m_rxDevSampleRate) || force) { + reverseAPIKeys.append("rxDevSampleRate"); + } + if ((m_settings.m_log2HardDecim != settings.m_log2HardDecim) || force) { + reverseAPIKeys.append("log2HardDecim"); + } + + if ((m_settings.m_rxDevSampleRate != settings.m_rxDevSampleRate) + || (m_settings.m_log2HardDecim != settings.m_log2HardDecim) || force) + { + forwardChangeRxDSP = true; + + if (m_deviceShared.m_dev->getDevice()) { + doRxChangeSampleRate = 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<setIQOrder(settings.m_iqOrder); + } + } + + if ((m_settings.m_ncoFrequencyRx != settings.m_ncoFrequencyRx) || force) { + reverseAPIKeys.append("ncoFrequencyRx"); + } + if ((m_settings.m_ncoEnableRx != settings.m_ncoEnableRx) || force) { + reverseAPIKeys.append("ncoEnableRx"); + } + + if ((m_settings.m_ncoFrequencyRx != settings.m_ncoFrequencyRx) + || (m_settings.m_ncoEnableRx != settings.m_ncoEnableRx) || force) + { + forceNCOFrequencyRx = true; + } + + if ((m_settings.m_antennaPathRx != settings.m_antennaPathRx) || force) + { + reverseAPIKeys.append("antennaPathRx"); + + if (m_deviceShared.m_dev->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_txDevSampleRate != settings.m_txDevSampleRate) || force) { + reverseAPIKeys.append("txDevSampleRate"); + } + if ((m_settings.m_log2HardInterp != settings.m_log2HardInterp) || force) { + reverseAPIKeys.append("log2HardInterp"); + } + + if ((m_settings.m_txDevSampleRate != settings.m_txDevSampleRate) + || (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); + } + + m_settings = settings; + + // Post Rx + + if (doRxChangeSampleRate && (m_settings.m_rxDevSampleRate != 0)) + { + // if (m_sourceThread && m_sourceThread->isRunning()) + // { + // m_sourceThread->stopWork(); + // rxThreadWasRunning = true; + // } + + bool success = m_deviceShared.m_dev->setSamplerate( + m_settings.m_rxDevSampleRate, + m_settings.m_log2HardDecim, + m_settings.m_log2HardInterp, + false + ); + + doRxChangeFreq = true; + forceNCOFrequencyRx = true; + forwardChangeRxDSP = true; + m_settings.m_rxDevSampleRate = m_deviceShared.m_dev->getActualInputRate(); + m_settings.m_txDevSampleRate = m_deviceShared.m_dev->getActualOutputRate(); + m_settings.m_log2HardDecim = getLog2HardDecim(); + m_settings.m_log2HardInterp = getLog2HardInterp(); + + qDebug("XTRXMIMO::applySettings: sample rate set %s to Rx:%f Tx:%f with decimation of %d and interpolation of %d", + success ? "unchanged" : "changed", + m_settings.m_rxDevSampleRate, + m_settings.m_txDevSampleRate, + 1 << m_settings.m_log2HardDecim, + 1 << m_settings.m_log2HardInterp); + + // if (rxThreadWasRunning) { + // m_sourceThread->startWork(); + // } + } + + if (doRxLPCalibration) + { + if (xtrx_tune_rx_bandwidth(m_deviceShared.m_dev->getDevice(), + XTRX_CH_A, + m_settings.m_lpfBWRx0, + 0) < 0) { + qCritical("XTRXMIMO::applySettings: could not set Rx0 LPF to %f Hz", m_settings.m_lpfBWRx0); + } else { + qDebug("XTRXMIMO::applySettings: Rx0 LPF set to %f Hz", m_settings.m_lpfBWRx0); + } + + if (xtrx_tune_rx_bandwidth(m_deviceShared.m_dev->getDevice(), + XTRX_CH_B, + m_settings.m_lpfBWRx1, + 0) < 0) { + qCritical("XTRXMIMO::applySettings: could not set Rx1 LPF to %f Hz", m_settings.m_lpfBWRx1); + } else { + qDebug("XTRXMIMO::applySettings: Rx1 LPF set to %f Hz", m_settings.m_lpfBWRx1); + } + } + + if (doRxChangeFreq) + { + forwardChangeRxDSP = true; + + if (m_deviceShared.m_dev->getDevice()) + { + qint64 deviceCenterFrequency = DeviceSampleSource::calculateDeviceCenterFrequency( + rxXlatedDeviceCenterFrequency, + 0, + m_settings.m_log2SoftDecim, + DeviceSampleSource::FC_POS_CENTER, + m_settings.m_rxDevSampleRate, + 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, + (m_settings.m_ncoEnableRx) ? m_settings.m_ncoFrequencyRx : 0, + nullptr) < 0) + { + qCritical("XTRXMIMO::applySettings: could not %s and set Rx NCO to %d Hz", + m_settings.m_ncoEnableRx ? "enable" : "disable", + m_settings.m_ncoFrequencyRx); + } + else + { + qDebug("XTRXMIMO::applySettings: %sd and set NCO Rx to %d Hz", + m_settings.m_ncoEnableRx ? "enable" : "disable", + m_settings.m_ncoFrequencyRx); + } + } + } + + // Post Tx + + if (doTxChangeSampleRate && !doRxChangeSampleRate && (m_settings.m_txDevSampleRate != 0)) + { + // if (m_sinkThread && m_sinkThread->isRunning()) + // { + // m_sinkThread->stopWork(); + // txThreadWasRunning = true; + // } + + bool success = m_deviceShared.m_dev->setSamplerate( + m_settings.m_txDevSampleRate, + m_settings.m_log2HardDecim, + m_settings.m_log2HardInterp, + true + ); + + doTxChangeFreq = true; + forceNCOFrequencyTx = true; + forwardChangeTxDSP = true; + m_settings.m_rxDevSampleRate = m_deviceShared.m_dev->getActualInputRate(); + m_settings.m_txDevSampleRate = m_deviceShared.m_dev->getActualOutputRate(); + m_settings.m_log2HardDecim = getLog2HardDecim(); + m_settings.m_log2HardInterp = getLog2HardInterp(); + + qDebug("XTRXMIMO::applySettings: sample rate set %s to Rx:%f Tx:%f with decimation of %d and interpolation of %d", + success ? "unchanged" : "changed", + m_settings.m_rxDevSampleRate, + m_settings.m_txDevSampleRate, + 1 << m_settings.m_log2HardDecim, + 1 << m_settings.m_log2HardInterp); + + // if (txThreadWasRunning) { + // m_sinkThread->startWork(); + // } + } + + if (doTxLPCalibration) + { + if (xtrx_tune_tx_bandwidth(m_deviceShared.m_dev->getDevice(), + XTRX_CH_A, + m_settings.m_lpfBWTx0, + 0) < 0) { + qCritical("XTRXMIMO::applySettings: could not set Tx0 LPF to %f Hz", m_settings.m_lpfBWTx0); + } else { + qDebug("XTRXMIMO::applySettings: Tx0 LPF set to %f Hz", m_settings.m_lpfBWTx0); + } + + if (xtrx_tune_tx_bandwidth(m_deviceShared.m_dev->getDevice(), + XTRX_CH_B, + m_settings.m_lpfBWTx1, + 0) < 0) { + qCritical("XTRXMIMO::applySettings: could not set Tx1 LPF to %f Hz", m_settings.m_lpfBWTx1); + } else { + qDebug("XTRXMIMO::applySettings: Tx1 LPF set to %f Hz", m_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, + m_settings.m_txDevSampleRate, + 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, + (m_settings.m_ncoEnableTx) ? m_settings.m_ncoFrequencyTx : 0, + nullptr) < 0) + { + qCritical("XTRXMIMO::applySettings: could not %s and set Tx NCO to %d Hz", + m_settings.m_ncoEnableTx ? "enable" : "disable", + m_settings.m_ncoFrequencyTx); + } + else + { + qDebug("XTRXMIMO::applySettings: %sd and set Tx NCO to %d Hz", + m_settings.m_ncoEnableTx ? "enable" : "disable", + m_settings.m_ncoFrequencyTx); + } + } + } + + unsigned int fifoRate = std::max( + (unsigned int) m_settings.m_txDevSampleRate / (1<push(report); + } + + int sampleRate = m_settings.m_rxDevSampleRate/(1<getDeviceEngineInputMessageQueue()->push(notifRx0); + DSPMIMOSignalNotification *notifRx1 = new DSPMIMOSignalNotification(sampleRate, m_settings.m_rxCenterFrequency + ncoShift, true, 1); + m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notifRx1); + + sampleRate = m_settings.m_txDevSampleRate/(1<getDeviceEngineInputMessageQueue()->push(notifTx0); + DSPMIMOSignalNotification *notifTx1 = new DSPMIMOSignalNotification(sampleRate, m_settings.m_txCenterFrequency + ncoShift, false, 1); + m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notifTx1); + } + + // if (forwardChangeRxDSP) + // { + // int sampleRate = m_settings.m_rxDevSampleRate/(1<getDeviceEngineInputMessageQueue()->push(notif0); + // DSPMIMOSignalNotification *notif1 = new DSPMIMOSignalNotification(sampleRate, m_settings.m_rxCenterFrequency + ncoShift, true, 1); + // m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif1); + // } + + // if (forwardChangeTxDSP) + // { + // int sampleRate = m_settings.m_txDevSampleRate/(1<getDeviceEngineInputMessageQueue()->push(notif0); + // DSPMIMOSignalNotification *notif1 = new DSPMIMOSignalNotification(sampleRate, m_settings.m_txCenterFrequency + ncoShift, false, 1); + // m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif1); + // } + + 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); + } + } +} + +int XTRXMIMO::webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setXtrxMimoSettings(new SWGSDRangel::SWGXtrxMIMOSettings()); + response.getXtrxMimoSettings()->init(); + webapiFormatDeviceSettings(response, m_settings); + return 200; +} + +int XTRXMIMO::webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage) +{ + (void) errorMessage; + XTRXMIMOSettings settings = m_settings; + webapiUpdateDeviceSettings(settings, deviceSettingsKeys, response); + + MsgConfigureXTRXMIMO *msg = MsgConfigureXTRXMIMO::create(settings, force); + m_inputMessageQueue.push(msg); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureXTRXMIMO *msgToGUI = MsgConfigureXTRXMIMO::create(settings, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatDeviceSettings(response, settings); + return 200; +} + +void XTRXMIMO::webapiUpdateDeviceSettings( + XTRXMIMOSettings& settings, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response) +{ + // common + if (deviceSettingsKeys.contains("extClock")) { + settings.m_extClock = response.getXtrxMimoSettings()->getExtClock() != 0; + } + if (deviceSettingsKeys.contains("extClockFreq")) { + settings.m_extClockFreq = response.getXtrxMimoSettings()->getExtClockFreq(); + } + if (deviceSettingsKeys.contains("gpioDir")) { + settings.m_gpioDir = response.getXtrxMimoSettings()->getGpioDir(); + } + if (deviceSettingsKeys.contains("gpioPins")) { + settings.m_gpioPins = response.getXtrxMimoSettings()->getGpioPins(); + } + if (deviceSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getXtrxInputSettings()->getUseReverseApi() != 0; + } + if (deviceSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getXtrxInputSettings()->getReverseApiAddress(); + } + if (deviceSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getXtrxInputSettings()->getReverseApiPort(); + } + if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getXtrxInputSettings()->getReverseApiDeviceIndex(); + } + // Rx + if (deviceSettingsKeys.contains("rxDevSampleRate")) { + settings.m_rxDevSampleRate = response.getXtrxMimoSettings()->getRxDevSampleRate(); + } + if (deviceSettingsKeys.contains("log2HardDecim")) { + settings.m_log2HardDecim = response.getXtrxMimoSettings()->getLog2HardDecim(); + } + if (deviceSettingsKeys.contains("log2SoftDecim")) { + settings.m_log2SoftDecim = response.getXtrxMimoSettings()->getLog2SoftDecim(); + } + if (deviceSettingsKeys.contains("iqOrder")) { + settings.m_iqOrder = response.getXtrxMimoSettings()->getIqOrder() != 0; + } + if (deviceSettingsKeys.contains("rxCenterFrequency")) { + settings.m_rxCenterFrequency = response.getXtrxMimoSettings()->getRxCenterFrequency(); + } + if (deviceSettingsKeys.contains("dcBlock")) { + settings.m_dcBlock = response.getXtrxMimoSettings()->getDcBlock() != 0; + } + if (deviceSettingsKeys.contains("iqCorrection")) { + settings.m_iqCorrection = response.getXtrxMimoSettings()->getIqCorrection() != 0; + } + if (deviceSettingsKeys.contains("ncoEnableRx")) { + settings.m_ncoEnableRx = response.getXtrxMimoSettings()->getNcoEnableRx() != 0; + } + if (deviceSettingsKeys.contains("ncoFrequencyRx")) { + settings.m_ncoFrequencyRx = response.getXtrxMimoSettings()->getNcoFrequencyRx(); + } + if (deviceSettingsKeys.contains("antennaPathRx")) { + settings.m_antennaPathRx = (XTRXMIMOSettings::RxAntenna) response.getXtrxMimoSettings()->getAntennaPathRx(); + } + // Rx0 + if (deviceSettingsKeys.contains("lpfBWRx0")) { + settings.m_lpfBWRx0 = response.getXtrxMimoSettings()->getLpfBwRx0(); + } + if (deviceSettingsKeys.contains("gainRx0")) { + settings.m_gainRx0 = response.getXtrxMimoSettings()->getGainRx0(); + } + if (deviceSettingsKeys.contains("gainModeRx0")) { + settings.m_gainModeRx0 = (XTRXMIMOSettings::GainMode) response.getXtrxMimoSettings()->getGainModeRx0(); + } + if (deviceSettingsKeys.contains("lnaGainRx0")) { + settings.m_lnaGainRx0 = response.getXtrxMimoSettings()->getLnaGainRx0(); + } + if (deviceSettingsKeys.contains("tiaGainRx0")) { + settings.m_tiaGainRx0 = response.getXtrxMimoSettings()->getTiaGainRx0(); + } + if (deviceSettingsKeys.contains("pgaGainRx0")) { + settings.m_pgaGainRx0 = response.getXtrxMimoSettings()->getPgaGainRx0(); + } + if (deviceSettingsKeys.contains("pwrmodeRx0")) { + settings.m_pwrmodeRx0 = response.getXtrxMimoSettings()->getPwrmodeRx0(); + } + // Rx1 + if (deviceSettingsKeys.contains("lpfBWRx1")) { + settings.m_lpfBWRx1 = response.getXtrxMimoSettings()->getLpfBwRx1(); + } + if (deviceSettingsKeys.contains("gainRx1")) { + settings.m_gainRx1 = response.getXtrxMimoSettings()->getGainRx1(); + } + if (deviceSettingsKeys.contains("gainModeRx1")) { + settings.m_gainModeRx1 = (XTRXMIMOSettings::GainMode) response.getXtrxMimoSettings()->getGainModeRx1(); + } + if (deviceSettingsKeys.contains("lnaGainRx1")) { + settings.m_lnaGainRx1 = response.getXtrxMimoSettings()->getLnaGainRx1(); + } + if (deviceSettingsKeys.contains("tiaGainRx1")) { + settings.m_tiaGainRx1 = response.getXtrxMimoSettings()->getTiaGainRx1(); + } + if (deviceSettingsKeys.contains("pgaGainRx1")) { + settings.m_pgaGainRx1 = response.getXtrxMimoSettings()->getPgaGainRx1(); + } + if (deviceSettingsKeys.contains("pwrmodeRx1")) { + settings.m_pwrmodeRx1 = response.getXtrxMimoSettings()->getPwrmodeRx1(); + } + // Tx + if (deviceSettingsKeys.contains("txDevSampleRate")) { + settings.m_txDevSampleRate = response.getXtrxMimoSettings()->getTxDevSampleRate(); + } + if (deviceSettingsKeys.contains("log2HardInterp")) { + settings.m_log2HardInterp = response.getXtrxMimoSettings()->getLog2HardInterp(); + } + if (deviceSettingsKeys.contains("log2SoftInterp")) { + settings.m_log2SoftInterp = response.getXtrxMimoSettings()->getLog2SoftInterp(); + } + if (deviceSettingsKeys.contains("txCenterFrequency")) { + settings.m_txCenterFrequency = response.getXtrxMimoSettings()->getTxCenterFrequency(); + } + if (deviceSettingsKeys.contains("ncoEnableTx")) { + settings.m_ncoEnableTx = response.getXtrxMimoSettings()->getNcoEnableTx() != 0; + } + if (deviceSettingsKeys.contains("ncoFrequencyTx")) { + settings.m_ncoFrequencyTx = response.getXtrxMimoSettings()->getNcoFrequencyTx(); + } + if (deviceSettingsKeys.contains("antennaPathTx")) { + settings.m_antennaPathTx = (XTRXMIMOSettings::TxAntenna) response.getXtrxMimoSettings()->getAntennaPathTx(); + } + // Tx0 + if (deviceSettingsKeys.contains("lpfBWTx0")) { + settings.m_lpfBWTx0 = response.getXtrxMimoSettings()->getLpfBwTx0(); + } + if (deviceSettingsKeys.contains("gainTx0")) { + settings.m_gainTx0 = response.getXtrxMimoSettings()->getGainTx0(); + } + if (deviceSettingsKeys.contains("pwrmodeTx0")) { + settings.m_pwrmodeRx0 = response.getXtrxMimoSettings()->getPwrmodeTx0(); + } + // Tx1 + if (deviceSettingsKeys.contains("lpfBWTx1")) { + settings.m_lpfBWTx1 = response.getXtrxMimoSettings()->getLpfBwTx1(); + } + if (deviceSettingsKeys.contains("gainTx1")) { + settings.m_gainTx1 = response.getXtrxMimoSettings()->getGainTx1(); + } + if (deviceSettingsKeys.contains("pwrmodeTx1")) { + settings.m_pwrmodeRx1 = response.getXtrxMimoSettings()->getPwrmodeTx1(); + } +} + +void XTRXMIMO::webapiFormatDeviceSettings( + SWGSDRangel::SWGDeviceSettings& response, + const XTRXMIMOSettings& settings) +{ + // common + response.getXtrxMimoSettings()->setExtClock(settings.m_extClock ? 1 : 0); + response.getXtrxMimoSettings()->setExtClockFreq(settings.m_extClockFreq); + + response.getXtrxMimoSettings()->setGpioDir(settings.m_gpioDir & 0xFF); + response.getXtrxMimoSettings()->setGpioPins(settings.m_gpioPins & 0xFF); + response.getXtrxMimoSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getXtrxMimoSettings()->getReverseApiAddress()) { + *response.getXtrxMimoSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getXtrxMimoSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getXtrxMimoSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getXtrxMimoSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + // Rx + response.getXtrxMimoSettings()->setRxDevSampleRate(settings.m_rxDevSampleRate); + response.getXtrxMimoSettings()->setLog2HardDecim(settings.m_log2HardDecim); + response.getXtrxMimoSettings()->setLog2SoftDecim(settings.m_log2SoftDecim); + response.getXtrxMimoSettings()->setIqOrder(settings.m_iqOrder ? 1 : 0); + response.getXtrxMimoSettings()->setRxCenterFrequency(settings.m_rxCenterFrequency); + response.getXtrxMimoSettings()->setDcBlock(settings.m_dcBlock ? 1 : 0); + response.getXtrxMimoSettings()->setIqCorrection(settings.m_iqCorrection ? 1 : 0); + response.getXtrxMimoSettings()->setNcoEnableRx(settings.m_ncoEnableRx ? 1 : 0); + response.getXtrxMimoSettings()->setNcoFrequencyRx(settings.m_ncoFrequencyRx); + response.getXtrxMimoSettings()->setAntennaPathRx((int) settings.m_antennaPathRx); + // Rx0 + response.getXtrxMimoSettings()->setLpfBwRx0(settings.m_lpfBWRx0); + response.getXtrxMimoSettings()->setGainRx0(settings.m_gainRx0); + response.getXtrxMimoSettings()->setGainModeRx0((int) settings.m_gainModeRx0); + response.getXtrxMimoSettings()->setLnaGainRx0(settings.m_lnaGainRx0); + response.getXtrxMimoSettings()->setTiaGainRx0(settings.m_tiaGainRx0); + response.getXtrxMimoSettings()->setPgaGainRx0(settings.m_pgaGainRx0); + response.getXtrxMimoSettings()->setPwrmodeRx0(settings.m_pwrmodeRx0); + // Rx1 + response.getXtrxMimoSettings()->setLpfBwRx1(settings.m_lpfBWRx1); + response.getXtrxMimoSettings()->setGainRx1(settings.m_gainRx1); + response.getXtrxMimoSettings()->setGainModeRx1((int) settings.m_gainModeRx1); + response.getXtrxMimoSettings()->setLnaGainRx1(settings.m_lnaGainRx1); + response.getXtrxMimoSettings()->setTiaGainRx1(settings.m_tiaGainRx1); + response.getXtrxMimoSettings()->setPgaGainRx1(settings.m_pgaGainRx1); + response.getXtrxMimoSettings()->setPwrmodeRx1(settings.m_pwrmodeRx1); + // Tx + response.getXtrxMimoSettings()->setTxDevSampleRate(settings.m_txDevSampleRate); + response.getXtrxMimoSettings()->setLog2HardInterp(settings.m_log2HardInterp); + response.getXtrxMimoSettings()->setLog2SoftInterp(settings.m_log2SoftInterp); + response.getXtrxMimoSettings()->setTxCenterFrequency(settings.m_txCenterFrequency); + response.getXtrxMimoSettings()->setNcoEnableTx(settings.m_ncoEnableTx ? 1 : 0); + response.getXtrxMimoSettings()->setNcoFrequencyTx(settings.m_ncoFrequencyTx); + response.getXtrxMimoSettings()->setAntennaPathTx((int) settings.m_antennaPathTx); + // Tx0 + response.getXtrxMimoSettings()->setLpfBwTx0(settings.m_lpfBWTx0); + response.getXtrxMimoSettings()->setGainTx0(settings.m_gainTx0); + response.getXtrxMimoSettings()->setPwrmodeTx0(settings.m_pwrmodeTx0); + // Tx1 + response.getXtrxMimoSettings()->setLpfBwTx1(settings.m_lpfBWTx1); + response.getXtrxMimoSettings()->setGainTx1(settings.m_gainTx1); + response.getXtrxMimoSettings()->setPwrmodeTx1(settings.m_pwrmodeTx1); +} + +int XTRXMIMO::webapiReportGet( + SWGSDRangel::SWGDeviceReport& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setXtrxInputReport(new SWGSDRangel::SWGXtrxInputReport()); + response.getXtrxInputReport()->init(); + webapiFormatDeviceReport(response); + return 200; +} + +int XTRXMIMO::webapiRunGet( + int subsystemIndex, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + if ((subsystemIndex == 0) || (subsystemIndex == 1)) + { + m_deviceAPI->getDeviceEngineStateStr(*response.getState(), subsystemIndex); + return 200; + } + else + { + errorMessage = QString("Subsystem invalid: must be 0 (Rx) or 1 (Tx)"); + return 404; + } + +} + +int XTRXMIMO::webapiRun( + bool run, + int subsystemIndex, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + if ((subsystemIndex == 0) || (subsystemIndex == 1)) + { + m_deviceAPI->getDeviceEngineStateStr(*response.getState(), subsystemIndex); + MsgStartStop *message = MsgStartStop::create(run, subsystemIndex == 0); + m_inputMessageQueue.push(message); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgStartStop *msgToGUI = MsgStartStop::create(run, subsystemIndex == 0); + m_guiMessageQueue->push(msgToGUI); + } + + return 200; + } + else + { + errorMessage = QString("Subsystem invalid: must be 0 (Rx) or 1 (Tx)"); + return 404; + } +} + +void XTRXMIMO::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response) +{ + bool success = false; + double temp = 0.0; + bool gpsStatus = false; + uint64_t fifolevelRx = 0; + uint64_t fifolevelTx = 0; + uint32_t fifosize = 1<<16; + + if (m_deviceShared.m_dev->getDevice()) + { + int ret = xtrx_val_get(m_deviceShared.m_dev->getDevice(), + XTRX_RX, XTRX_CH_AB, XTRX_PERF_LLFIFO, &fifolevelRx); + + success = (ret >= 0); + + ret = xtrx_val_get(m_deviceShared.m_dev->getDevice(), + XTRX_TX, XTRX_CH_AB, XTRX_PERF_LLFIFO, &fifolevelTx); + + success = success & (ret >= 0); + temp = m_deviceShared.get_board_temperature() / 256.0; + gpsStatus = m_deviceShared.get_gps_status(); + } + + response.getXtrxMimoReport()->setSuccess(success ? 1 : 0); + response.getXtrxMimoReport()->setFifoSize(fifosize); + response.getXtrxMimoReport()->setFifoFillRx(fifolevelRx); + response.getXtrxMimoReport()->setFifoFillTx(fifolevelTx); + response.getXtrxMimoReport()->setTemperature(temp); + response.getXtrxMimoReport()->setGpsLock(gpsStatus ? 1 : 0); +} + +void XTRXMIMO::webapiReverseSendSettings(QList& deviceSettingsKeys, const XTRXMIMOSettings& settings, bool force) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setDirection(2); // MIMO + swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex()); + swgDeviceSettings->setDeviceHwType(new QString("XTRX")); + swgDeviceSettings->setXtrxMimoSettings(new SWGSDRangel::SWGXtrxMIMOSettings()); + SWGSDRangel::SWGXtrxMIMOSettings *swgXTRXMIMOSettings = swgDeviceSettings->getXtrxMimoSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + // common + if (deviceSettingsKeys.contains("extClock") || force) { + swgXTRXMIMOSettings->setExtClock(settings.m_extClock ? 1 : 0); + } + if (deviceSettingsKeys.contains("extClock") || force) { + swgXTRXMIMOSettings->setExtClockFreq(settings.m_extClockFreq); + } + if (deviceSettingsKeys.contains("gpioDir") || force) { + swgXTRXMIMOSettings->setGpioDir(settings.m_gpioDir & 0xFF); + } + if (deviceSettingsKeys.contains("gpioPins") || force) { + swgXTRXMIMOSettings->setGpioPins(settings.m_gpioPins & 0xFF); + } + // Rx + if (deviceSettingsKeys.contains("rxDevSampleRate") || force) { + swgXTRXMIMOSettings->setRxDevSampleRate(settings.m_rxDevSampleRate); + } + if (deviceSettingsKeys.contains("log2HardDecim") || force) { + swgXTRXMIMOSettings->setLog2HardDecim(settings.m_log2HardDecim); + } + if (deviceSettingsKeys.contains("log2SoftDecim") || force) { + swgXTRXMIMOSettings->setLog2SoftDecim(settings.m_log2SoftDecim); + } + if (deviceSettingsKeys.contains("iqOrder") || force) { + swgXTRXMIMOSettings->setIqOrder(settings.m_iqOrder ? 1 : 0); + } + if (deviceSettingsKeys.contains("rxCenterFrequency") || force) { + swgXTRXMIMOSettings->setRxCenterFrequency(settings.m_rxCenterFrequency); + } + if (deviceSettingsKeys.contains("dcBlock") || force) { + swgXTRXMIMOSettings->setDcBlock(settings.m_dcBlock ? 1 : 0); + } + if (deviceSettingsKeys.contains("iqCorrection") || force) { + swgXTRXMIMOSettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0); + } + if (deviceSettingsKeys.contains("ncoEnableRx") || force) { + swgXTRXMIMOSettings->setNcoEnableRx(settings.m_ncoEnableRx ? 1 : 0); + } + if (deviceSettingsKeys.contains("ncoFrequencyRx") || force) { + swgXTRXMIMOSettings->setNcoFrequencyRx(settings.m_ncoFrequencyRx); + } + if (deviceSettingsKeys.contains("antennaPathRx") || force) { + swgXTRXMIMOSettings->setAntennaPathRx((int) settings.m_antennaPathRx); + } + // Rx0 + if (deviceSettingsKeys.contains("lpfBWRx0") || force) { + swgXTRXMIMOSettings->setLpfBwRx0(settings.m_lpfBWRx0); + } + if (deviceSettingsKeys.contains("gainRx0") || force) { + swgXTRXMIMOSettings->setGainRx0(settings.m_gainRx0); + } + if (deviceSettingsKeys.contains("gainModeRx0") || force) { + swgXTRXMIMOSettings->setGainModeRx0((int) settings.m_gainModeRx0); + } + if (deviceSettingsKeys.contains("lnaGainRx0") || force) { + swgXTRXMIMOSettings->setLnaGainRx0(settings.m_lnaGainRx0); + } + if (deviceSettingsKeys.contains("tiaGainRx0") || force) { + swgXTRXMIMOSettings->setTiaGainRx0(settings.m_tiaGainRx0); + } + if (deviceSettingsKeys.contains("pgaGainRx0") || force) { + swgXTRXMIMOSettings->setPgaGainRx0(settings.m_pgaGainRx0); + } + if (deviceSettingsKeys.contains("pwrmodeRx0") || force) { + swgXTRXMIMOSettings->setPwrmodeRx0(settings.m_pwrmodeRx0); + } + // Rx1 + if (deviceSettingsKeys.contains("lpfBWRx1") || force) { + swgXTRXMIMOSettings->setLpfBwRx1(settings.m_lpfBWRx1); + } + if (deviceSettingsKeys.contains("gainRx1") || force) { + swgXTRXMIMOSettings->setGainRx1(settings.m_gainRx1); + } + if (deviceSettingsKeys.contains("gainModeRx1") || force) { + swgXTRXMIMOSettings->setGainModeRx1((int) settings.m_gainModeRx1); + } + if (deviceSettingsKeys.contains("lnaGainRx1") || force) { + swgXTRXMIMOSettings->setLnaGainRx1(settings.m_lnaGainRx1); + } + if (deviceSettingsKeys.contains("tiaGainRx1") || force) { + swgXTRXMIMOSettings->setTiaGainRx1(settings.m_tiaGainRx1); + } + if (deviceSettingsKeys.contains("pgaGainRx1") || force) { + swgXTRXMIMOSettings->setPgaGainRx1(settings.m_pgaGainRx1); + } + if (deviceSettingsKeys.contains("pwrmodeRx1") || force) { + swgXTRXMIMOSettings->setPwrmodeRx1(settings.m_pwrmodeRx1); + } + // Tx + if (deviceSettingsKeys.contains("txDevSampleRate") || force) { + swgXTRXMIMOSettings->setTxDevSampleRate(settings.m_txDevSampleRate); + } + if (deviceSettingsKeys.contains("log2HardInterp") || force) { + swgXTRXMIMOSettings->setLog2HardInterp(settings.m_log2HardInterp); + } + if (deviceSettingsKeys.contains("log2SoftInterp") || force) { + swgXTRXMIMOSettings->setLog2SoftInterp(settings.m_log2SoftInterp); + } + if (deviceSettingsKeys.contains("txCenterFrequency") || force) { + swgXTRXMIMOSettings->setTxCenterFrequency(settings.m_txCenterFrequency); + } + if (deviceSettingsKeys.contains("ncoEnableTx") || force) { + swgXTRXMIMOSettings->setNcoEnableTx(settings.m_ncoEnableTx ? 1 : 0); + } + if (deviceSettingsKeys.contains("ncoFrequencyTx") || force) { + swgXTRXMIMOSettings->setNcoFrequencyTx(settings.m_ncoFrequencyTx); + } + if (deviceSettingsKeys.contains("antennaPathTx") || force) { + swgXTRXMIMOSettings->setAntennaPathTx((int) settings.m_antennaPathTx); + } + // Tx0 + if (deviceSettingsKeys.contains("lpfBWTx0") || force) { + swgXTRXMIMOSettings->setLpfBwTx0(settings.m_lpfBWTx0); + } + if (deviceSettingsKeys.contains("gainTx0") || force) { + swgXTRXMIMOSettings->setGainTx0(settings.m_gainTx0); + } + if (deviceSettingsKeys.contains("pwrmodeTx0") || force) { + swgXTRXMIMOSettings->setPwrmodeTx0(settings.m_pwrmodeTx0); + } + // Tx0 + if (deviceSettingsKeys.contains("lpfBWTx0") || force) { + swgXTRXMIMOSettings->setLpfBwTx0(settings.m_lpfBWTx0); + } + if (deviceSettingsKeys.contains("gainTx0") || force) { + swgXTRXMIMOSettings->setGainTx0(settings.m_gainTx0); + } + if (deviceSettingsKeys.contains("pwrmodeTx0") || force) { + swgXTRXMIMOSettings->setPwrmodeTx0(settings.m_pwrmodeTx0); + } + // Tx1 + if (deviceSettingsKeys.contains("lpfBWTx1") || force) { + swgXTRXMIMOSettings->setLpfBwTx1(settings.m_lpfBWTx1); + } + if (deviceSettingsKeys.contains("gainTx1") || force) { + swgXTRXMIMOSettings->setGainTx1(settings.m_gainTx1); + } + if (deviceSettingsKeys.contains("pwrmodeTx1") || force) { + swgXTRXMIMOSettings->setPwrmodeTx1(settings.m_pwrmodeTx1); + } + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer = new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + buffer->setParent(reply); + + delete swgDeviceSettings; +} + +void XTRXMIMO::webapiReverseSendStartStop(bool start) +{ + SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings(); + swgDeviceSettings->setDirection(2); // MIMO + swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex()); + swgDeviceSettings->setDeviceHwType(new QString("XTRX")); + + QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex); + m_networkRequest.setUrl(QUrl(deviceSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer = new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgDeviceSettings->asJson().toUtf8()); + buffer->seek(0); + QNetworkReply *reply; + + if (start) { + reply = m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer); + } else { + reply = m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer); + } + + buffer->setParent(reply); + delete swgDeviceSettings; +} + +void XTRXMIMO::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "XTRXMIMO::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("XTRXMIMO::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimo.h b/plugins/samplemimo/xtrxmimo/xtrxmimo.h new file mode 100644 index 000000000..eef166737 --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/xtrxmimo.h @@ -0,0 +1,301 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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) + { } + }; + + 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 getRxDevSampleRate() const; + uint32_t getTxDevSampleRate() const; + uint32_t getLog2HardDecim() const; + uint32_t getLog2HardInterp() const; + double getClockGen() const; + + virtual int webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage); + + virtual int webapiReportGet( + SWGSDRangel::SWGDeviceReport& response, + QString& errorMessage); + + + virtual int webapiRunGet( + int subsystemIndex, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + virtual int webapiRun( + bool run, + int subsystemIndex, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + static void webapiFormatDeviceSettings( + SWGSDRangel::SWGDeviceSettings& response, + const XTRXMIMOSettings& settings); + + static void webapiUpdateDeviceSettings( + XTRXMIMOSettings& settings, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response); + + void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response); + + 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..50f5ddf52 --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.cpp @@ -0,0 +1,1015 @@ +// 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) : + DeviceGUI(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 &))); + + sendSettings(); +} + +XTRXMIMOGUI::~XTRXMIMOGUI() +{ + delete ui; +} + +void XTRXMIMOGUI::destroy() +{ + delete this; +} + +void XTRXMIMOGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + 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_rxDevSampleRate = m_xtrxMIMO->getRxDevSampleRate(); + m_settings.m_txDevSampleRate = m_xtrxMIMO->getTxDevSampleRate(); + m_settings.m_log2HardDecim = m_xtrxMIMO->getLog2HardDecim(); + m_settings.m_log2HardInterp = m_xtrxMIMO->getLog2HardInterp(); + + 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(); + updateADCRate(); + + 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); + + 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(); + updateDACRate(); + + 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); + + 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)); + } + } + + setNCODisplay(); +} + +void XTRXMIMOGUI::displaySampleRate() +{ + float minF, maxF, stepF; + m_xtrxMIMO->getSRRange(minF, maxF, stepF); + uint32_t devSampleRate = m_rxElseTx ? m_settings.m_rxDevSampleRate : m_settings.m_txDevSampleRate; + uint32_t log2Soft = m_rxElseTx ? m_settings.m_log2SoftDecim : m_settings.m_log2SoftInterp; + + 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(devSampleRate); + ui->sampleRate->setToolTip("Device to host sample rate (S/s)"); + ui->deviceRateText->setToolTip("Baseband sample rate (S/s)"); + uint32_t basebandSampleRate = 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(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(devSampleRate / 1000.0f, 'g', 5))); + } + + ui->sampleRate->blockSignals(false); +} + +void XTRXMIMOGUI::setNCODisplay() +{ + ui->ncoFrequency->blockSignals(true); + + if (m_rxElseTx) + { + int ncoHalfRange = (m_settings.m_rxDevSampleRate * (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_txDevSampleRate * (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_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 > 6)) { + return; + } + + if (m_rxElseTx) { + m_settings.m_log2HardDecim = index; + } else { + m_settings.m_log2HardInterp = index; + } + + 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_rxDevSampleRate = ui->sampleRate->getValueNew(); + } else { + m_settings.m_rxDevSampleRate = ui->sampleRate->getValueNew() * (1 << m_settings.m_log2SoftDecim); + } + } + else + { + m_settings.m_log2SoftInterp = index; + + if (m_sampleRateMode) { + m_settings.m_txDevSampleRate = ui->sampleRate->getValueNew(); + } else { + m_settings.m_txDevSampleRate = 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_rxDevSampleRate = value; + } else { + m_settings.m_rxDevSampleRate = value * (1 << m_settings.m_log2SoftDecim); + } + } + else + { + if (m_sampleRateMode) { + m_settings.m_txDevSampleRate = value; + } else { + m_settings.m_txDevSampleRate = value * (1 << m_settings.m_log2SoftInterp); + } + } + + 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(); +} diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimogui.h b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h new file mode 100644 index 000000000..4699300fb --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.h @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "device/devicegui.h" + +#include "xtrxmimosettings.h" + +class DeviceUISet; +class XTRXMIMO; + +namespace Ui { + class XTRXMIMOGUI; +} + +class XTRXMIMOGUI : public DeviceGUI { + Q_OBJECT +public: + explicit XTRXMIMOGUI(DeviceUISet *deviceUISet, QWidget* parent = nullptr); + virtual ~XTRXMIMOGUI(); + virtual void destroy(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + +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(); + bool handleMessage(const Message& message); + +private slots: + void handleInputMessages(); + void updateHardware(); + void updateStatus(); + void on_streamSide_currentIndexChanged(int index); + void on_streamIndex_currentIndexChanged(int index); + void on_spectrumSide_currentIndexChanged(int index); + void on_spectrumIndex_currentIndexChanged(int index); + void on_startStopRx_toggled(bool checked); + void on_startStopTx_toggled(bool checked); + void on_centerFrequency_changed(quint64 value); + void on_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); +}; + +#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..c59b43367 --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/xtrxmimogui.ui @@ -0,0 +1,1331 @@ + + + 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 + + + + + + + + + + 54 + 0 + + + + ADC rate before hardware downsampling (k or MS/s) + + + 00000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + 54 + 0 + + + + Baseband I/Q sample rate kS/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 + + + + + + + + + + + + + 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 + + + + + 64 + + + + + + + + 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..1842463e1 --- /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.13.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 +DeviceGUI* XTRXMIMOPlugin::createSampleMIMOPluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet) +{ + (void) sourceId; + (void) widget; + (void) deviceUISet; + return nullptr; +} +#else +DeviceGUI* 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..18e939598 --- /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 DeviceGUI* createSampleMIMOPluginInstanceGUI( + const QString& sourceId, + QWidget **widget, + DeviceUISet *deviceUISet); + virtual DeviceSampleMIMO* createSampleMIMOPluginInstance(const QString& sourceId, DeviceAPI *deviceAPI); + virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const; + + static const QString m_hardwareID; + static const QString m_deviceTypeID; + +private: + static const PluginDescriptor m_pluginDescriptor; +}; + +#endif // _XTRXMIMO_XTRXMIMOPLUGIN_H diff --git a/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp new file mode 100644 index 000000000..fcde1096d --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.cpp @@ -0,0 +1,218 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_extClock = false; + m_extClockFreq = 0; // Auto + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + // Rx + m_rxDevSampleRate = 5e6; + 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; + m_iqOrder = true; + // 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_txDevSampleRate = 5e6; + 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.writeBool(2, m_extClock); + s.writeU32(3, m_extClockFreq); + 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); + s.writeDouble(27, m_rxDevSampleRate); + s.writeBool(28, m_iqOrder); + // 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); + s.writeDouble(75, m_txDevSampleRate); + // 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.readBool(2, &m_extClock, false); + d.readU32(3, &m_extClockFreq, 0); + 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(24, &m_ncoEnableRx, false); + d.readS32(25, &m_ncoFrequencyRx, 0); + d.readS32(26, &intval, 0); + m_antennaPathRx = (RxAntenna) intval; + d.readDouble(27, &m_rxDevSampleRate, 5e6); + d.readBool(28, &m_iqOrder, true); + // 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(70, &m_log2HardInterp, 2); + d.readU32(71, &m_log2SoftInterp, 0); + d.readS32(72, &intval, 0); + d.readBool(73, &m_ncoEnableTx, true); + d.readS32(74, &m_ncoFrequencyTx, 500000); + m_antennaPathTx = (TxAntenna) intval; + d.readDouble(75, &m_txDevSampleRate, 5e6); + // Tx0 + d.readFloat(80, &m_lpfBWTx0, 1.5e6); + d.readU32(81, &m_gainTx0, 20); + d.readU32(82, &m_pwrmodeTx0, 4); + // Tx1 + d.readFloat(90, &m_lpfBWTx1, 1.5e6); + d.readU32(91, &m_gainTx1, 20); + d.readU32(92, &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..8dc3a7a19 --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/xtrxmimosettings.h @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 + bool m_extClock; //!< True if external clock source + uint32_t m_extClockFreq; //!< Frequency (Hz) of external clock source + 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 + double m_rxDevSampleRate; + uint32_t m_log2HardDecim; + uint32_t m_log2SoftDecim; + uint64_t m_rxCenterFrequency; + 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; + bool m_iqOrder; + // 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 + double m_txDevSampleRate; + uint32_t m_log2HardInterp; + uint32_t m_log2SoftInterp; + uint64_t m_txCenterFrequency; + 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..2657f29c2 --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/xtrxmimowebapiadapter.cpp @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 "xtrxmimo.h" +#include "xtrxmimowebapiadapter.h" + +XTRXMIMOWebAPIAdapter::XTRXMIMOWebAPIAdapter() +{} + +XTRXMIMOWebAPIAdapter::~XTRXMIMOWebAPIAdapter() +{} + +int XTRXMIMOWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setXtrxMimoSettings(new SWGSDRangel::SWGXtrxMIMOSettings()); + response.getXtrxMimoSettings()->init(); + XTRXMIMO::webapiFormatDeviceSettings(response, m_settings); + return 200; +} + +int XTRXMIMOWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, // query + response + QString& errorMessage) +{ + (void) force; + (void) errorMessage; + XTRXMIMO::webapiUpdateDeviceSettings(m_settings, deviceSettingsKeys, response); + return 200; +} 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..d8b51bd34 --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/xtrxmithread.cpp @@ -0,0 +1,261 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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), + m_iqOrder(true) +{ + 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"); + } + + if (m_iqOrder) + { + lengths[0] = callbackSIIQ(0, (const qint16*) buffs[0], 2*nfo.out_samples); + lengths[1] = callbackSIIQ(1, (const qint16*) buffs[1], 2*nfo.out_samples); + } + else + { + lengths[0] = callbackSIQI(0, (const qint16*) buffs[0], 2*nfo.out_samples); + lengths[1] = callbackSIQI(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::callbackSIIQ(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_decimatorsIQ.decimate1(&it, buf, len); + } + else + { + switch (m_log2Decim) + { + case 1: + m_channels[channel].m_decimatorsIQ.decimate2_cen(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimatorsIQ.decimate4_cen(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimatorsIQ.decimate8_cen(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimatorsIQ.decimate16_cen(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimatorsIQ.decimate32_cen(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimatorsIQ.decimate64_cen(&it, buf, len); + break; + default: + break; + } + } + + return it - m_channels[channel].m_convertBuffer.begin(); +} + +int XTRXMIThread::callbackSIQI(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_decimatorsQI.decimate1(&it, buf, len); + } + else + { + switch (m_log2Decim) + { + case 1: + m_channels[channel].m_decimatorsQI.decimate2_cen(&it, buf, len); + break; + case 2: + m_channels[channel].m_decimatorsQI.decimate4_cen(&it, buf, len); + break; + case 3: + m_channels[channel].m_decimatorsQI.decimate8_cen(&it, buf, len); + break; + case 4: + m_channels[channel].m_decimatorsQI.decimate16_cen(&it, buf, len); + break; + case 5: + m_channels[channel].m_decimatorsQI.decimate32_cen(&it, buf, len); + break; + case 6: + m_channels[channel].m_decimatorsQI.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..eb35115d7 --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/xtrxmithread.h @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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; } + void setIQOrder(bool iqOrder) { m_iqOrder = iqOrder; } + +private: + struct Channel + { + SampleVector m_convertBuffer; + Decimators m_decimatorsIQ; + Decimators m_decimatorsQI; + }; + + 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; + bool m_iqOrder; + + void run(); + int callbackSIIQ(unsigned int channel, const qint16* buf, qint32 len); + int callbackSIQI(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..b466d6f42 --- /dev/null +++ b/plugins/samplemimo/xtrxmimo/xtrxmothread.cpp @@ -0,0 +1,239 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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"); + } + + qint16 buf0[2*DeviceXTRX::blockSize]; // I+Q = 2x16 bit samples + qint16 buf1[2*DeviceXTRX::blockSize]; + std::vector buffs(2); + master_ts ts = 4096*1024; + + buffs[0] = &buf0; + buffs[1] = &buf1; + + 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(buf0, buf1, 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 : buf1, + 2*nSamples); + } + else + { + switch (m_log2Interp) + { + case 1: + m_interpolators[channel].interpolate2_cen( + &begin, + channel == 0 ? buf0 : buf1, + 2*nSamples); + break; + case 2: + m_interpolators[channel].interpolate4_cen( + &begin, + channel == 0 ? buf0 : buf1, + 2*nSamples); + break; + case 3: + m_interpolators[channel].interpolate8_cen( + &begin, + channel == 0 ? buf0 : buf1, + 2*nSamples); + break; + case 4: + m_interpolators[channel].interpolate16_cen( + &begin, + channel == 0 ? buf0 : buf1, + 2*nSamples); + break; + case 5: + m_interpolators[channel].interpolate32_cen( + &begin, + channel == 0 ? buf0 : buf1, + 2*nSamples); + break; + case 6: + m_interpolators[channel].interpolate64_cen( + &begin, + channel == 0 ? buf0 : buf1, + 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