From 1acc00b7d90172d95635b81b0209763f7fe4c114 Mon Sep 17 00:00:00 2001 From: IonutMuthi Date: Wed, 20 Aug 2025 10:44:32 +0300 Subject: [PATCH 1/6] ad936x: refactoring to account for future expansions Signed-off-by: IonutMuthi --- .../plugins/ad936x/include/ad936x/ad936x.h | 10 +- .../ad936x/include/ad936x/ad936xhelper.h | 51 +++ packages/ad936x/plugins/ad936x/src/ad936x.cpp | 422 +++--------------- .../plugins/ad936x/src/ad936xhelper.cpp | 333 ++++++++++++++ 4 files changed, 459 insertions(+), 357 deletions(-) create mode 100644 packages/ad936x/plugins/ad936x/include/ad936x/ad936xhelper.h create mode 100644 packages/ad936x/plugins/ad936x/src/ad936xhelper.cpp diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/ad936x.h b/packages/ad936x/plugins/ad936x/include/ad936x/ad936x.h index ab0a874ee1..f4523c4b26 100644 --- a/packages/ad936x/plugins/ad936x/include/ad936x/ad936x.h +++ b/packages/ad936x/plugins/ad936x/include/ad936x/ad936x.h @@ -30,6 +30,7 @@ #include #include +#include namespace scopy { namespace ad936x { @@ -51,13 +52,10 @@ class SCOPY_AD936X_EXPORT AD936X : public QWidget QWidget *m_blockDiagramWidget; AnimatedRefreshBtn *m_refreshButton; - QWidget *generateGlobalSettingsWidget(QWidget *parent); + QWidget *generateRxChainWidget(iio_device *dev, QString title, QWidget *parent); + QWidget *generateTxChainWidget(iio_device *dev, QString title, QWidget *parent); - QWidget *generateRxChainWidget(QWidget *parent); - QWidget *generateRxWidget(iio_channel *chn, QString title, QWidget *parent); - - QWidget *generateTxChainWidget(QWidget *parent); - QWidget *generateTxWidget(iio_channel *chn, QString title, QWidget *parent); + AD936xHelper *m_helper; }; } // namespace ad936x } // namespace scopy diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/ad936xhelper.h b/packages/ad936x/plugins/ad936x/include/ad936x/ad936xhelper.h new file mode 100644 index 0000000000..369c49c51a --- /dev/null +++ b/packages/ad936x/plugins/ad936x/include/ad936x/ad936xhelper.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * 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, either 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef AD936X_HELPER_H +#define AD936X_HELPER_H + +#include "scopy-ad936x_export.h" +#include +#include + +namespace scopy { +namespace ad936x { + +class SCOPY_AD936X_EXPORT AD936xHelper : public QWidget +{ + Q_OBJECT +public: + AD936xHelper(QWidget *parent = nullptr); + + QWidget *generateGlobalSettingsWidget(iio_device *dev, QString title, QWidget *parent); + + QWidget *generateRxDeviceWidget(iio_device *dev, QString title, QWidget *parent); + QWidget *generateRxChannelWidget(iio_channel *chn, QString title, QWidget *parent); + + QWidget *generateTxDeviceWidget(iio_device *dev, QString title, QWidget *parent); + QWidget *generateTxChannelWidget(iio_channel *chn, QString title, QWidget *parent); + +Q_SIGNALS: + void readRequested(); +}; +} // namespace ad936x +} // namespace scopy +#endif // AD936X_HELPER_H diff --git a/packages/ad936x/plugins/ad936x/src/ad936x.cpp b/packages/ad936x/plugins/ad936x/src/ad936x.cpp index e1fcf5db00..6fcec1d1cf 100644 --- a/packages/ad936x/plugins/ad936x/src/ad936x.cpp +++ b/packages/ad936x/plugins/ad936x/src/ad936x.cpp @@ -21,9 +21,6 @@ #include "ad936x.h" -#include "fastlockprofileswidget.h" -#include "firfilterqwidget.h" - #include #include #include @@ -100,14 +97,32 @@ AD936X::AD936X(iio_context *ctx, QWidget *parent) controlsLayout->addWidget(scrollArea); if(m_ctx != nullptr) { + + iio_device *plutoDevice = nullptr; + int device_count = iio_context_get_devices_count(m_ctx); + for(int i = 0; i < device_count; ++i) { + iio_device *dev = iio_context_get_device(m_ctx, i); + const char *dev_name = iio_device_get_name(dev); + if(dev_name && QString(dev_name).contains("ad936", Qt::CaseInsensitive)) { + plutoDevice = dev; + break; + } + } + + m_helper = new AD936xHelper(); + connect(this, &AD936X::readRequested, m_helper, &AD936xHelper::readRequested); + /// first widget the global settings can be created with iiowigets only - controlWidgetLayout->addWidget(generateGlobalSettingsWidget(controlsWidget)); + controlWidgetLayout->addWidget(m_helper->generateGlobalSettingsWidget( + plutoDevice, "AD9361 / AD9364 Global Settings", controlsWidget)); /// second is Rx ( receive chain) - controlWidgetLayout->addWidget(generateRxChainWidget(controlsWidget)); + controlWidgetLayout->addWidget( + generateRxChainWidget(plutoDevice, "AD9361 / AD9364 Receive Chain", controlsWidget)); /// third is Tx (transimt chain) - controlWidgetLayout->addWidget(generateTxChainWidget(controlsWidget)); + controlWidgetLayout->addWidget( + generateTxChainWidget(plutoDevice, "AD9361 / AD9364 Transmit Chain", controlsWidget)); controlWidgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); } @@ -163,157 +178,31 @@ AD936X::AD936X(iio_context *ctx, QWidget *parent) AD936X::~AD936X() {} -QWidget *AD936X::generateGlobalSettingsWidget(QWidget *parent) +QWidget *AD936X::generateRxChainWidget(iio_device *dev, QString title, QWidget *parent) { - QWidget *globalSettingsWidget = new QWidget(parent); - Style::setBackgroundColor(globalSettingsWidget, json::theme::background_primary); - Style::setStyle(globalSettingsWidget, style::properties::widget::border_interactive); - - QVBoxLayout *layout = new QVBoxLayout(globalSettingsWidget); - globalSettingsWidget->setLayout(layout); - - QLabel *title = new QLabel("AD9361 / AD9364 Global Settings", globalSettingsWidget); - Style::setStyle(title, style::properties::label::menuBig); - layout->addWidget(title); - - iio_device *plutoDevice = nullptr; - int device_count = iio_context_get_devices_count(m_ctx); - for(int i = 0; i < device_count; ++i) { - iio_device *dev = iio_context_get_device(m_ctx, i); - const char *dev_name = iio_device_get_name(dev); - if(dev_name && QString(dev_name).contains("ad936", Qt::CaseInsensitive)) { - plutoDevice = dev; - break; - } - } - - if(plutoDevice == nullptr) { - qWarning(CAT_AD936X) << "No AD936X device found"; - return globalSettingsWidget; - } - - QHBoxLayout *hlayout = new QHBoxLayout(); - - //// ensm_mode - IIOWidget *ensmMode = IIOWidgetBuilder(globalSettingsWidget) - .device(plutoDevice) - .attribute("ensm_mode") - .optionsAttribute("ensm_mode_available") - .title("ENSM Mode") - .uiStrategy(IIOWidgetBuilder::ComboUi) - .buildSingle(); - - hlayout->addWidget(ensmMode); - - connect(this, &AD936X::readRequested, ensmMode, &IIOWidget::readAsync); - - ////calib_mode - IIOWidget *calibMode = IIOWidgetBuilder(globalSettingsWidget) - .device(plutoDevice) - .attribute("calib_mode") - .optionsAttribute("calib_mode_available") - .title("Calibration Mode") - .uiStrategy(IIOWidgetBuilder::ComboUi) - .buildSingle(); - hlayout->addWidget(calibMode); - connect(this, &AD936X::readRequested, calibMode, &IIOWidget::readAsync); - - Style::setStyle(calibMode, style::properties::widget::basicBackground, true, true); - - // trx_rate_governor - IIOWidget *trxRateGovernor = IIOWidgetBuilder(globalSettingsWidget) - .device(plutoDevice) - .attribute("trx_rate_governor") - .optionsAttribute("trx_rate_governor_available") - .title("TRX Rate Governor") - .uiStrategy(IIOWidgetBuilder::ComboUi) - .buildSingle(); - hlayout->addWidget(trxRateGovernor); - connect(this, &AD936X::readRequested, trxRateGovernor, &IIOWidget::readAsync); - - FirFilterQWidget *firFilter = new FirFilterQWidget(plutoDevice, nullptr, globalSettingsWidget); - hlayout->addWidget(firFilter); - - layout->addLayout(hlayout); - - // rx_path_rates - IIOWidget *rxPathRates = IIOWidgetBuilder(globalSettingsWidget) - .device(plutoDevice) - .attribute("rx_path_rates") - .title("RX Path Rates") - .buildSingle(); - layout->addWidget(rxPathRates); - rxPathRates->setEnabled(false); - connect(this, &AD936X::readRequested, rxPathRates, &IIOWidget::readAsync); - - // tx_path_rates - IIOWidget *txPathRates = IIOWidgetBuilder(globalSettingsWidget) - .device(plutoDevice) - .attribute("tx_path_rates") - .title("Tx Path Rates") - .buildSingle(); - layout->addWidget(txPathRates); - txPathRates->setEnabled(false); - connect(this, &AD936X::readRequested, txPathRates, &IIOWidget::readAsync); - - connect(firFilter, &FirFilterQWidget::filterChanged, this, [=, this]() { - rxPathRates->read(); - txPathRates->read(); - }); + QWidget *widget = new QWidget(parent); + Style::setBackgroundColor(widget, json::theme::background_primary); + Style::setStyle(widget, style::properties::widget::border_interactive); - // xo_correction - IIOWidget *xoCorrection = IIOWidgetBuilder(globalSettingsWidget) - .device(plutoDevice) - .attribute("xo_correction") - .optionsAttribute("xo_correction_available") - .title("XO Correction") - .uiStrategy(IIOWidgetBuilder::RangeUi) - .buildSingle(); - layout->addWidget(xoCorrection); - connect(this, &AD936X::readRequested, xoCorrection, &IIOWidget::readAsync); + QVBoxLayout *mainLayout = new QVBoxLayout(widget); + widget->setLayout(mainLayout); - layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); - - return globalSettingsWidget; -} - -QWidget *AD936X::generateRxChainWidget(QWidget *parent) -{ - QWidget *rxChainWidget = new QWidget(parent); - Style::setBackgroundColor(rxChainWidget, json::theme::background_primary); - Style::setStyle(rxChainWidget, style::properties::widget::border_interactive); - - QVBoxLayout *mainLayout = new QVBoxLayout(rxChainWidget); - rxChainWidget->setLayout(mainLayout); - - QLabel *title = new QLabel("AD9361 / AD9364 Receive Chain", rxChainWidget); - Style::setStyle(title, style::properties::label::menuBig); - mainLayout->addWidget(title); - - iio_device *plutoDevice = nullptr; - int device_count = iio_context_get_devices_count(m_ctx); - for(int i = 0; i < device_count; ++i) { - iio_device *dev = iio_context_get_device(m_ctx, i); - const char *dev_name = iio_device_get_name(dev); - if(dev_name && QString(dev_name).contains("ad936", Qt::CaseInsensitive)) { - plutoDevice = dev; - break; - } - } + QLabel *titleLabel = new QLabel(title, widget); + Style::setStyle(titleLabel, style::properties::label::menuBig); + mainLayout->addWidget(titleLabel); - if(plutoDevice == nullptr) { + if(dev == nullptr) { qWarning(CAT_AD936X) << "No AD936X device found"; - return rxChainWidget; } QGridLayout *layout = new QGridLayout(); bool isOutput = false; - iio_channel *voltage0 = iio_device_find_channel(plutoDevice, "voltage0", isOutput); + iio_channel *voltage0 = iio_device_find_channel(dev, "voltage0", isOutput); // voltage0: rf_bandwidth - IIOWidget *rfBandwidth = IIOWidgetBuilder(rxChainWidget) + IIOWidget *rfBandwidth = IIOWidgetBuilder(widget) .channel(voltage0) .attribute("rf_bandwidth") .optionsAttribute("rf_bandwidth_available") @@ -324,7 +213,7 @@ QWidget *AD936X::generateRxChainWidget(QWidget *parent) connect(this, &AD936X::readRequested, rfBandwidth, &IIOWidget::readAsync); // voltage0: sampling_frequency - IIOWidget *samplingFrequency = IIOWidgetBuilder(rxChainWidget) + IIOWidget *samplingFrequency = IIOWidgetBuilder(widget) .channel(voltage0) .attribute("sampling_frequency") .optionsAttribute("sampling_frequency_available") @@ -335,7 +224,7 @@ QWidget *AD936X::generateRxChainWidget(QWidget *parent) connect(this, &AD936X::readRequested, samplingFrequency, &IIOWidget::readAsync); // voltage 0 : rf_port_select - IIOWidget *rfPortSelect = IIOWidgetBuilder(rxChainWidget) + IIOWidget *rfPortSelect = IIOWidgetBuilder(widget) .channel(voltage0) .attribute("rf_port_select") .optionsAttribute("rf_port_select_available") @@ -345,38 +234,8 @@ QWidget *AD936X::generateRxChainWidget(QWidget *parent) layout->addWidget(rfPortSelect, 0, 2, 2, 1); connect(this, &AD936X::readRequested, rfPortSelect, &IIOWidget::readAsync); - // because this channel is marked as output by libiio we need to mark altvoltage0 as output - iio_channel *altVoltage0 = iio_device_find_channel(plutoDevice, "altvoltage0", true); - - // altvoltage0: RX_LO // frequency - IIOWidget *altVoltage0Frequency = IIOWidgetBuilder(rxChainWidget) - .channel(altVoltage0) - .attribute("frequency") - .optionsAttribute("frequency_available") - .title("RX LO Frequency(MHz)") - .uiStrategy(IIOWidgetBuilder::RangeUi) - .buildSingle(); - connect(this, &AD936X::readRequested, altVoltage0Frequency, &IIOWidget::readAsync); - - MenuOnOffSwitch *useExternalRxLo = new MenuOnOffSwitch("External Rx LO", rxChainWidget, false); - useExternalRxLo->onOffswitch()->setChecked(true); - - layout->addWidget(altVoltage0Frequency, 0, 3, 2, 1); - layout->addWidget(useExternalRxLo, 2, 3, 1, 1); - - // fastlock profile - FastlockProfilesWidget *fastlockProfile = new FastlockProfilesWidget(altVoltage0, rxChainWidget); - - connect(useExternalRxLo->onOffswitch(), &QAbstractButton::toggled, this, - [=, this](bool toggled) { fastlockProfile->setEnabled(toggled); }); - - layout->addWidget(fastlockProfile, 0, 4, 3, 1); - - connect(fastlockProfile, &FastlockProfilesWidget::recallCalled, this, - [=, this] { altVoltage0Frequency->read(); }); - // quadrature_tracking_en - IIOWidget *quadratureTrackingEn = IIOWidgetBuilder(rxChainWidget) + IIOWidget *quadratureTrackingEn = IIOWidgetBuilder(this) .channel(voltage0) .attribute("quadrature_tracking_en") .uiStrategy(IIOWidgetBuilder::CheckBoxUi) @@ -387,7 +246,7 @@ QWidget *AD936X::generateRxChainWidget(QWidget *parent) connect(this, &AD936X::readRequested, quadratureTrackingEn, &IIOWidget::readAsync); // rf_dc_offset_tracking_en - IIOWidget *rcDcOffsetTrackingEn = IIOWidgetBuilder(rxChainWidget) + IIOWidget *rcDcOffsetTrackingEn = IIOWidgetBuilder(widget) .channel(voltage0) .attribute("rf_dc_offset_tracking_en") .uiStrategy(IIOWidgetBuilder::CheckBoxUi) @@ -398,7 +257,7 @@ QWidget *AD936X::generateRxChainWidget(QWidget *parent) connect(this, &AD936X::readRequested, rcDcOffsetTrackingEn, &IIOWidget::readAsync); // bb_dc_offset_tracking_en - IIOWidget *bbDcOffsetTrackingEn = IIOWidgetBuilder(rxChainWidget) + IIOWidget *bbDcOffsetTrackingEn = IIOWidgetBuilder(widget) .channel(voltage0) .attribute("bb_dc_offset_tracking_en") .title("BB DC") @@ -410,125 +269,49 @@ QWidget *AD936X::generateRxChainWidget(QWidget *parent) mainLayout->addLayout(layout); - QHBoxLayout *rxWidgetsLayout = new QHBoxLayout(); - rxWidgetsLayout->addWidget(generateRxWidget(voltage0, "RX1", rxChainWidget)); + QHBoxLayout *rxDeviceLayout = new QHBoxLayout(); + rxDeviceLayout->setMargin(0); + rxDeviceLayout->setSpacing(10); + + QWidget *rxDeviceWidget = m_helper->generateRxDeviceWidget(dev, "ad9361-phy", widget); - iio_channel *voltage1 = iio_device_find_channel(plutoDevice, "voltage1", isOutput); + rxDeviceLayout->addWidget(rxDeviceWidget); + rxDeviceWidget->layout()->addWidget(m_helper->generateRxChannelWidget(voltage0, "RX 1", rxDeviceWidget)); + iio_channel *voltage1 = iio_device_find_channel(dev, "voltage1", isOutput); if(voltage1 && iio_channel_find_attr(voltage1, "hardwaregain")) { - rxWidgetsLayout->addWidget(generateRxWidget(voltage1, "RX2", rxChainWidget)); + rxDeviceWidget->layout()->addWidget( + m_helper->generateRxChannelWidget(voltage1, "RX 2", rxDeviceWidget)); } - rxWidgetsLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Preferred)); - mainLayout->addLayout(rxWidgetsLayout); + rxDeviceLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); + + mainLayout->addLayout(rxDeviceLayout); mainLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); - return rxChainWidget; + return widget; } -QWidget *AD936X::generateRxWidget(iio_channel *chn, QString title, QWidget *parent) +QWidget *AD936X::generateTxChainWidget(iio_device *dev, QString title, QWidget *parent) { - QWidget *rxWidget = new QWidget(parent); - Style::setStyle(rxWidget, style::properties::widget::border_interactive); + QWidget *widget = new QWidget(parent); + Style::setBackgroundColor(widget, json::theme::background_primary); + Style::setStyle(widget, style::properties::widget::border_interactive); - QVBoxLayout *layout = new QVBoxLayout(rxWidget); - rxWidget->setLayout(layout); + QVBoxLayout *layout = new QVBoxLayout(widget); + widget->setLayout(layout); - QLabel *titleLabel = new QLabel(title, rxWidget); + QLabel *titleLabel = new QLabel(title, widget); Style::setStyle(titleLabel, style::properties::label::menuBig); layout->addWidget(titleLabel); - // voltage0: hardwaregain - IIOWidget *hardwaregain = IIOWidgetBuilder(rxWidget) - .channel(chn) - .attribute("hardwaregain") - .uiStrategy(IIOWidgetBuilder::RangeUi) - .optionsAttribute("hardwaregain_available") - .title("Hardware Gain(dB)") - .buildSingle(); - layout->addWidget(hardwaregain); - connect(this, &AD936X::readRequested, hardwaregain, &IIOWidget::readAsync); - - hardwaregain->setDataToUIConversion([this](QString data) { - // data has dB as string in the value - auto result = data.split(" "); - return result.first(); - }); - - hardwaregain->lastReturnCode(); - - // voltage: rssi - IIOWidget *rssi = IIOWidgetBuilder(rxWidget).channel(chn).attribute("rssi").title("RSSI(dB)").buildSingle(); - layout->addWidget(rssi); - rssi->setEnabled(false); - - QTimer timer; - - QObject::connect(&timer, &QTimer::timeout, [&]() { rssi->readAsync(); }); - - timer.start(1000); - - // voltage: gain_control_mode - IIOWidget *gainControlMode = IIOWidgetBuilder(rxWidget) - .channel(chn) - .attribute("gain_control_mode") - .uiStrategy(IIOWidgetBuilder::ComboUi) - .optionsAttribute("gain_control_mode_available") - .title("Gain Control Mode") - .buildSingle(); - layout->addWidget(gainControlMode); - connect(this, &AD936X::readRequested, gainControlMode, &IIOWidget::readAsync); - - connect(dynamic_cast(gainControlMode->getUiStrategy()), &ComboAttrUi::displayedNewData, this, - [this, hardwaregain, rssi](QString data, QString optionalData) { - if(data == "manual") { - hardwaregain->setEnabled(true); - } else { - hardwaregain->setEnabled(false); - } - hardwaregain->readAsync(); - rssi->readAsync(); - }); - - return rxWidget; -} - -QWidget *AD936X::generateTxChainWidget(QWidget *parent) -{ - QWidget *txChainWidget = new QWidget(parent); - Style::setBackgroundColor(txChainWidget, json::theme::background_primary); - Style::setStyle(txChainWidget, style::properties::widget::border_interactive); - - QVBoxLayout *layout = new QVBoxLayout(txChainWidget); - txChainWidget->setLayout(layout); - - QLabel *title = new QLabel("AD9361 / AD9364 Transmit Chain", txChainWidget); - Style::setStyle(title, style::properties::label::menuBig); - layout->addWidget(title); - - iio_device *plutoDevice = nullptr; - int device_count = iio_context_get_devices_count(m_ctx); - for(int i = 0; i < device_count; ++i) { - iio_device *dev = iio_context_get_device(m_ctx, i); - const char *dev_name = iio_device_get_name(dev); - if(dev_name && QString(dev_name).contains("ad936", Qt::CaseInsensitive)) { - plutoDevice = dev; - break; - } - } - - if(plutoDevice == nullptr) { - qWarning(CAT_AD936X) << "No AD936X device found"; - return txChainWidget; - } - QGridLayout *lay = new QGridLayout(); bool isOutput = true; - iio_channel *voltage0 = iio_device_find_channel(plutoDevice, "voltage0", isOutput); + iio_channel *voltage0 = iio_device_find_channel(dev, "voltage0", isOutput); // voltage0: rf_bandwidth - IIOWidget *rfBandwidth = IIOWidgetBuilder(txChainWidget) + IIOWidget *rfBandwidth = IIOWidgetBuilder(widget) .channel(voltage0) .attribute("rf_bandwidth") .optionsAttribute("rf_bandwidth_available") @@ -539,7 +322,7 @@ QWidget *AD936X::generateTxChainWidget(QWidget *parent) connect(this, &AD936X::readRequested, rfBandwidth, &IIOWidget::readAsync); // voltage0: sampling_frequency - IIOWidget *samplingFrequency = IIOWidgetBuilder(txChainWidget) + IIOWidget *samplingFrequency = IIOWidgetBuilder(widget) .channel(voltage0) .attribute("sampling_frequency") .optionsAttribute("sampling_frequency_available") @@ -550,7 +333,7 @@ QWidget *AD936X::generateTxChainWidget(QWidget *parent) connect(this, &AD936X::readRequested, samplingFrequency, &IIOWidget::readAsync); // voltage0: rf_port_select - IIOWidget *rfPortSelect = IIOWidgetBuilder(txChainWidget) + IIOWidget *rfPortSelect = IIOWidgetBuilder(widget) .channel(voltage0) .attribute("rf_port_select") .optionsAttribute("rf_port_select_available") @@ -560,87 +343,24 @@ QWidget *AD936X::generateTxChainWidget(QWidget *parent) lay->addWidget(rfPortSelect, 0, 2, 2, 1); connect(this, &AD936X::readRequested, rfPortSelect, &IIOWidget::readAsync); - iio_channel *altVoltage1 = iio_device_find_channel(plutoDevice, "altvoltage1", isOutput); - - // altvoltage1: TX_LO // frequency - IIOWidget *altVoltage1Frequency = IIOWidgetBuilder(txChainWidget) - .channel(altVoltage1) - .attribute("frequency") - .optionsAttribute("frequency_available") - .uiStrategy(IIOWidgetBuilder::RangeUi) - .title("TX LO Frequency(MHz)") - .buildSingle(); - connect(this, &AD936X::readRequested, altVoltage1Frequency, &IIOWidget::readAsync); - - MenuOnOffSwitch *useExternalTxLo = new MenuOnOffSwitch("External Tx LO", txChainWidget, false); - useExternalTxLo->onOffswitch()->setChecked(true); - - lay->addWidget(altVoltage1Frequency, 0, 3, 2, 1); - lay->addWidget(useExternalTxLo, 2, 3, 1, 1); - - // fastlock profile - FastlockProfilesWidget *fastlockProfile = new FastlockProfilesWidget(altVoltage1, txChainWidget); - - connect(useExternalTxLo->onOffswitch(), &QAbstractButton::toggled, this, - [=, this](bool toggled) { fastlockProfile->setEnabled(toggled); }); - - lay->addWidget(fastlockProfile, 0, 4, 3, 1); - - connect(fastlockProfile, &FastlockProfilesWidget::recallCalled, this, - [=, this] { altVoltage1Frequency->read(); }); - layout->addLayout(lay); QHBoxLayout *txWidgetsLayout = new QHBoxLayout(); - txWidgetsLayout->addWidget(generateTxWidget(voltage0, "TX 1", txChainWidget)); + QWidget *txDeviceWidget = m_helper->generateTxDeviceWidget(dev, "ad9361-phy", widget); - iio_channel *voltage1 = iio_device_find_channel(plutoDevice, "voltage1", isOutput); + txWidgetsLayout->addWidget(txDeviceWidget); + txDeviceWidget->layout()->addWidget(m_helper->generateTxChannelWidget(voltage0, "TX 1", txDeviceWidget)); + iio_channel *voltage1 = iio_device_find_channel(dev, "voltage1", isOutput); if(voltage1 && iio_channel_find_attr(voltage1, "hardwaregain")) { - txWidgetsLayout->addWidget(generateTxWidget(voltage1, "TX 2", txChainWidget)); + txDeviceWidget->layout()->addWidget( + m_helper->generateTxChannelWidget(voltage1, "TX 2", txDeviceWidget)); } txWidgetsLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Preferred)); - layout->addLayout(txWidgetsLayout); + layout->addWidget(txDeviceWidget); layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); - return txChainWidget; -} - -QWidget *AD936X::generateTxWidget(iio_channel *chn, QString title, QWidget *parent) -{ - QWidget *txWidget = new QWidget(parent); - Style::setStyle(txWidget, style::properties::widget::border_interactive); - - QVBoxLayout *layout = new QVBoxLayout(txWidget); - txWidget->setLayout(layout); - - QLabel *titleLabel = new QLabel(title, txWidget); - Style::setStyle(titleLabel, style::properties::label::menuBig); - layout->addWidget(titleLabel); - - // adi,tx-attenuation-mdB - IIOWidget *txAttenuation = IIOWidgetBuilder(txWidget) - .channel(chn) - .attribute("hardwaregain") - .uiStrategy(IIOWidgetBuilder::RangeUi) - .optionsAttribute("hardwaregain_available") - .title("Attenuation(dB)") - .buildSingle(); - layout->addWidget(txAttenuation); - connect(this, &AD936X::readRequested, txAttenuation, &IIOWidget::readAsync); - - txAttenuation->setDataToUIConversion([this](QString data) { - // data has dB as string in the value - auto result = data.split(" "); - return result.first(); - }); - - IIOWidget *rssi = IIOWidgetBuilder(txWidget).channel(chn).attribute("rssi").title("RSSI(dB)").buildSingle(); - layout->addWidget(rssi); - rssi->setEnabled(false); - connect(this, &AD936X::readRequested, rssi, &IIOWidget::readAsync); - - return txWidget; + return widget; } diff --git a/packages/ad936x/plugins/ad936x/src/ad936xhelper.cpp b/packages/ad936x/plugins/ad936x/src/ad936xhelper.cpp new file mode 100644 index 0000000000..2c83fc07e8 --- /dev/null +++ b/packages/ad936x/plugins/ad936x/src/ad936xhelper.cpp @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * 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, either 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "ad936xhelper.h" + +#include "fastlockprofileswidget.h" +#include "firfilterqwidget.h" + +#include +#include + +#include + +Q_LOGGING_CATEGORY(CAT_AD936X_HELPER, "AD936X_HELPER"); + +using namespace scopy; +using namespace ad936x; + +AD936xHelper::AD936xHelper(QWidget *parent) + : QWidget(parent) +{} + +QWidget *AD936xHelper::generateGlobalSettingsWidget(iio_device *dev, QString title, QWidget *parent) +{ + QWidget *globalSettingsWidget = new QWidget(parent); + Style::setBackgroundColor(globalSettingsWidget, json::theme::background_primary); + Style::setStyle(globalSettingsWidget, style::properties::widget::border_interactive); + + QVBoxLayout *layout = new QVBoxLayout(globalSettingsWidget); + globalSettingsWidget->setLayout(layout); + + QLabel *titleLabel = new QLabel(title, globalSettingsWidget); + Style::setStyle(titleLabel, style::properties::label::menuBig); + layout->addWidget(titleLabel); + + QHBoxLayout *hlayout = new QHBoxLayout(); + + //// ensm_mode + IIOWidget *ensmMode = IIOWidgetBuilder(globalSettingsWidget) + .device(dev) + .attribute("ensm_mode") + .optionsAttribute("ensm_mode_available") + .title("ENSM Mode") + .uiStrategy(IIOWidgetBuilder::ComboUi) + .buildSingle(); + + hlayout->addWidget(ensmMode); + + connect(this, &AD936xHelper::readRequested, ensmMode, &IIOWidget::readAsync); + + ////calib_mode + IIOWidget *calibMode = IIOWidgetBuilder(globalSettingsWidget) + .device(dev) + .attribute("calib_mode") + .optionsAttribute("calib_mode_available") + .title("Calibration Mode") + .uiStrategy(IIOWidgetBuilder::ComboUi) + .buildSingle(); + hlayout->addWidget(calibMode); + connect(this, &AD936xHelper::readRequested, calibMode, &IIOWidget::readAsync); + + Style::setStyle(calibMode, style::properties::widget::basicBackground, true, true); + + // trx_rate_governor + IIOWidget *trxRateGovernor = IIOWidgetBuilder(globalSettingsWidget) + .device(dev) + .attribute("trx_rate_governor") + .optionsAttribute("trx_rate_governor_available") + .title("TRX Rate Governor") + .uiStrategy(IIOWidgetBuilder::ComboUi) + .buildSingle(); + hlayout->addWidget(trxRateGovernor); + connect(this, &AD936xHelper::readRequested, trxRateGovernor, &IIOWidget::readAsync); + + FirFilterQWidget *firFilter = new FirFilterQWidget(dev, nullptr, globalSettingsWidget); + hlayout->addWidget(firFilter); + + layout->addLayout(hlayout); + + // rx_path_rates + IIOWidget *rxPathRates = IIOWidgetBuilder(globalSettingsWidget) + .device(dev) + .attribute("rx_path_rates") + .title("RX Path Rates") + .buildSingle(); + layout->addWidget(rxPathRates); + rxPathRates->setEnabled(false); + connect(this, &AD936xHelper::readRequested, rxPathRates, &IIOWidget::readAsync); + + // tx_path_rates + IIOWidget *txPathRates = IIOWidgetBuilder(globalSettingsWidget) + .device(dev) + .attribute("tx_path_rates") + .title("Tx Path Rates") + .buildSingle(); + layout->addWidget(txPathRates); + txPathRates->setEnabled(false); + connect(this, &AD936xHelper::readRequested, txPathRates, &IIOWidget::readAsync); + + connect(firFilter, &FirFilterQWidget::filterChanged, this, [=, this]() { + rxPathRates->read(); + txPathRates->read(); + }); + + // xo_correction + IIOWidget *xoCorrection = IIOWidgetBuilder(globalSettingsWidget) + .device(dev) + .attribute("xo_correction") + .optionsAttribute("xo_correction_available") + .title("XO Correction") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .buildSingle(); + layout->addWidget(xoCorrection); + connect(this, &AD936xHelper::readRequested, xoCorrection, &IIOWidget::readAsync); + + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); + + return globalSettingsWidget; +} + +QWidget *AD936xHelper::generateRxDeviceWidget(iio_device *dev, QString title, QWidget *parent) +{ + QWidget *widget = new QWidget(parent); + Style::setStyle(widget, style::properties::widget::border_interactive); + + QGridLayout *layout = new QGridLayout(widget); + layout->setSpacing(10); + widget->setLayout(layout); + + QLabel *titleLabel = new QLabel(title, widget); + Style::setStyle(titleLabel, style::properties::label::menuBig); + layout->addWidget(titleLabel, 0, 0); + + // because this channel is marked as output by libiio we need to mark altvoltage0 as output + iio_channel *altVoltage0 = iio_device_find_channel(dev, "altvoltage0", true); + + // altvoltage0: RX_LO // frequency + IIOWidget *altVoltage0Frequency = IIOWidgetBuilder(widget) + .channel(altVoltage0) + .attribute("frequency") + .optionsAttribute("frequency_available") + .title("RX LO Frequency(MHz)") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .buildSingle(); + connect(this, &AD936xHelper::readRequested, altVoltage0Frequency, &IIOWidget::readAsync); + + MenuOnOffSwitch *useExternalRxLo = new MenuOnOffSwitch("External Rx LO", widget, false); + useExternalRxLo->onOffswitch()->setChecked(true); + + layout->addWidget(altVoltage0Frequency, 1, 0, 2, 1); + layout->addWidget(useExternalRxLo, 2, 0, 1, 1); + + // fastlock profile + FastlockProfilesWidget *fastlockProfile = new FastlockProfilesWidget(altVoltage0, this); + + connect(useExternalRxLo->onOffswitch(), &QAbstractButton::toggled, this, + [=, this](bool toggled) { fastlockProfile->setEnabled(toggled); }); + + layout->addWidget(fastlockProfile, 1, 1, 2, 1); + + connect(fastlockProfile, &FastlockProfilesWidget::recallCalled, this, + [=, this] { altVoltage0Frequency->read(); }); + + return widget; +} + +QWidget *AD936xHelper::generateRxChannelWidget(iio_channel *chn, QString title, QWidget *parent) +{ + QWidget *rxWidget = new QWidget(parent); + Style::setStyle(rxWidget, style::properties::widget::border_interactive); + + QVBoxLayout *layout = new QVBoxLayout(rxWidget); + rxWidget->setLayout(layout); + + QLabel *titleLabel = new QLabel(title, rxWidget); + Style::setStyle(titleLabel, style::properties::label::menuBig); + layout->addWidget(titleLabel); + + // voltage0: hardwaregain + IIOWidget *hardwaregain = IIOWidgetBuilder(rxWidget) + .channel(chn) + .attribute("hardwaregain") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .optionsAttribute("hardwaregain_available") + .title("Hardware Gain(dB)") + .buildSingle(); + layout->addWidget(hardwaregain); + connect(this, &AD936xHelper::readRequested, hardwaregain, &IIOWidget::readAsync); + + hardwaregain->setDataToUIConversion([this](QString data) { + // data has dB as string in the value + auto result = data.split(" "); + return result.first(); + }); + + hardwaregain->lastReturnCode(); + + // voltage: rssi + IIOWidget *rssi = IIOWidgetBuilder(rxWidget).channel(chn).attribute("rssi").title("RSSI(dB)").buildSingle(); + layout->addWidget(rssi); + rssi->setEnabled(false); + + QTimer timer; + + QObject::connect(&timer, &QTimer::timeout, [&]() { rssi->readAsync(); }); + + timer.start(1000); + + // voltage: gain_control_mode + IIOWidget *gainControlMode = IIOWidgetBuilder(rxWidget) + .channel(chn) + .attribute("gain_control_mode") + .uiStrategy(IIOWidgetBuilder::ComboUi) + .optionsAttribute("gain_control_mode_available") + .title("Gain Control Mode") + .buildSingle(); + layout->addWidget(gainControlMode); + connect(this, &AD936xHelper::readRequested, gainControlMode, &IIOWidget::readAsync); + + connect(dynamic_cast(gainControlMode->getUiStrategy()), &ComboAttrUi::displayedNewData, this, + [this, hardwaregain, rssi](QString data, QString optionalData) { + if(data == "manual") { + hardwaregain->setEnabled(true); + } else { + hardwaregain->setEnabled(false); + } + hardwaregain->readAsync(); + rssi->readAsync(); + }); + + return rxWidget; +} + +QWidget *AD936xHelper::generateTxDeviceWidget(iio_device *dev, QString title, QWidget *parent) +{ + QWidget *widget = new QWidget(parent); + Style::setStyle(widget, style::properties::widget::border_interactive); + + QGridLayout *layout = new QGridLayout(widget); + layout->setSpacing(10); + widget->setLayout(layout); + + QLabel *titleLabel = new QLabel(title, widget); + Style::setStyle(titleLabel, style::properties::label::menuBig); + layout->addWidget(titleLabel, 0, 0); + + bool isOutput = true; + iio_channel *altVoltage1 = iio_device_find_channel(dev, "altvoltage1", isOutput); + + // altvoltage1: TX_LO // frequency + IIOWidget *altVoltage1Frequency = IIOWidgetBuilder(widget) + .channel(altVoltage1) + .attribute("frequency") + .optionsAttribute("frequency_available") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .title("TX LO Frequency(MHz)") + .buildSingle(); + connect(this, &AD936xHelper::readRequested, altVoltage1Frequency, &IIOWidget::readAsync); + + MenuOnOffSwitch *useExternalTxLo = new MenuOnOffSwitch("External Tx LO", widget, false); + useExternalTxLo->onOffswitch()->setChecked(true); + + layout->addWidget(altVoltage1Frequency, 1, 0, 2, 1); + layout->addWidget(useExternalTxLo, 2, 0, 1, 1); + + // fastlock profile + FastlockProfilesWidget *fastlockProfile = new FastlockProfilesWidget(altVoltage1, widget); + + connect(useExternalTxLo->onOffswitch(), &QAbstractButton::toggled, this, + [=, this](bool toggled) { fastlockProfile->setEnabled(toggled); }); + + connect(fastlockProfile, &FastlockProfilesWidget::recallCalled, this, + [=, this] { altVoltage1Frequency->read(); }); + + layout->addWidget(fastlockProfile, 1, 1, 2, 1); + + return widget; +} + +QWidget *AD936xHelper::generateTxChannelWidget(iio_channel *chn, QString title, QWidget *parent) +{ + QWidget *txWidget = new QWidget(parent); + Style::setStyle(txWidget, style::properties::widget::border_interactive); + + QVBoxLayout *layout = new QVBoxLayout(txWidget); + txWidget->setLayout(layout); + + QLabel *titleLabel = new QLabel(title, txWidget); + Style::setStyle(titleLabel, style::properties::label::menuBig); + layout->addWidget(titleLabel); + + // adi,tx-attenuation-mdB + IIOWidget *txAttenuation = IIOWidgetBuilder(txWidget) + .channel(chn) + .attribute("hardwaregain") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .optionsAttribute("hardwaregain_available") + .title("Attenuation(dB)") + .buildSingle(); + layout->addWidget(txAttenuation); + connect(this, &AD936xHelper::readRequested, txAttenuation, &IIOWidget::readAsync); + + txAttenuation->setDataToUIConversion([this](QString data) { + // data has dB as string in the value + auto result = data.split(" "); + return result.first(); + }); + + IIOWidget *rssi = IIOWidgetBuilder(txWidget).channel(chn).attribute("rssi").title("RSSI(dB)").buildSingle(); + layout->addWidget(rssi); + rssi->setEnabled(false); + connect(this, &AD936xHelper::readRequested, rssi, &IIOWidget::readAsync); + + return txWidget; +} From c66fdf665b5e2783fa4538db3cb3b8b3cc242c24 Mon Sep 17 00:00:00 2001 From: IonutMuthi Date: Fri, 22 Aug 2025 10:13:34 +0300 Subject: [PATCH 2/6] refactoring: move ad936x to a folder Signed-off-by: IonutMuthi --- packages/ad936x/plugins/ad936x/CMakeLists.txt | 8 ++++++-- .../plugins/ad936x/include/ad936x/{ => ad936x}/ad936x.h | 1 + .../ad936x/include/ad936x/{ => ad936x}/ad963xadvanced.h | 0 .../ad936x/plugins/ad936x/src/{ => ad936x}/ad936x.cpp | 2 +- .../plugins/ad936x/src/{ => ad936x}/ad936xadvanced.cpp | 2 +- packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp | 5 +++-- 6 files changed, 12 insertions(+), 6 deletions(-) rename packages/ad936x/plugins/ad936x/include/ad936x/{ => ad936x}/ad936x.h (99%) mode change 100644 => 100755 rename packages/ad936x/plugins/ad936x/include/ad936x/{ => ad936x}/ad963xadvanced.h (100%) mode change 100644 => 100755 rename packages/ad936x/plugins/ad936x/src/{ => ad936x}/ad936x.cpp (99%) mode change 100644 => 100755 rename packages/ad936x/plugins/ad936x/src/{ => ad936x}/ad936xadvanced.cpp (99%) mode change 100644 => 100755 diff --git a/packages/ad936x/plugins/ad936x/CMakeLists.txt b/packages/ad936x/plugins/ad936x/CMakeLists.txt index de0df60bd5..4374c50031 100644 --- a/packages/ad936x/plugins/ad936x/CMakeLists.txt +++ b/packages/ad936x/plugins/ad936x/CMakeLists.txt @@ -47,8 +47,12 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE) -file(GLOB SRC_LIST src/*.cpp src/*.cc) -file(GLOB HEADER_LIST include/${SCOPY_MODULE}/*.h include/${SCOPY_MODULE}/*.hpp) +file(GLOB SRC_LIST src/*.cpp src/*.cc + src/ad936x/*.cpp src/ad936x/*.cc + src/fmcomms5/*.cpp src/ad936x/*.cc) +file(GLOB HEADER_LIST include/${SCOPY_MODULE}/*.h include/${SCOPY_MODULE}/*.hpp +include/${SCOPY_MODULE}/ad936x/*.h include/${SCOPY_MODULE}/ad936x/*.hpp +include/${SCOPY_MODULE}/fmcomms5/*.h include/${SCOPY_MODULE}/fmcomms5/*.hpp) file(GLOB UI_LIST ui/*.ui) set(ENABLE_TESTING ON) diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/ad936x.h b/packages/ad936x/plugins/ad936x/include/ad936x/ad936x/ad936x.h old mode 100644 new mode 100755 similarity index 99% rename from packages/ad936x/plugins/ad936x/include/ad936x/ad936x.h rename to packages/ad936x/plugins/ad936x/include/ad936x/ad936x/ad936x.h index f4523c4b26..68dc29de9c --- a/packages/ad936x/plugins/ad936x/include/ad936x/ad936x.h +++ b/packages/ad936x/plugins/ad936x/include/ad936x/ad936x/ad936x.h @@ -34,6 +34,7 @@ namespace scopy { namespace ad936x { + class SCOPY_AD936X_EXPORT AD936X : public QWidget { Q_OBJECT diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/ad963xadvanced.h b/packages/ad936x/plugins/ad936x/include/ad936x/ad936x/ad963xadvanced.h old mode 100644 new mode 100755 similarity index 100% rename from packages/ad936x/plugins/ad936x/include/ad936x/ad963xadvanced.h rename to packages/ad936x/plugins/ad936x/include/ad936x/ad936x/ad963xadvanced.h diff --git a/packages/ad936x/plugins/ad936x/src/ad936x.cpp b/packages/ad936x/plugins/ad936x/src/ad936x/ad936x.cpp old mode 100644 new mode 100755 similarity index 99% rename from packages/ad936x/plugins/ad936x/src/ad936x.cpp rename to packages/ad936x/plugins/ad936x/src/ad936x/ad936x.cpp index 6fcec1d1cf..a8b7e6f376 --- a/packages/ad936x/plugins/ad936x/src/ad936x.cpp +++ b/packages/ad936x/plugins/ad936x/src/ad936x/ad936x.cpp @@ -19,7 +19,7 @@ * */ -#include "ad936x.h" +#include "ad936x/ad936x.h" #include #include diff --git a/packages/ad936x/plugins/ad936x/src/ad936xadvanced.cpp b/packages/ad936x/plugins/ad936x/src/ad936x/ad936xadvanced.cpp old mode 100644 new mode 100755 similarity index 99% rename from packages/ad936x/plugins/ad936x/src/ad936xadvanced.cpp rename to packages/ad936x/plugins/ad936x/src/ad936x/ad936xadvanced.cpp index 7431fedcc8..c3ff108b88 --- a/packages/ad936x/plugins/ad936x/src/ad936xadvanced.cpp +++ b/packages/ad936x/plugins/ad936x/src/ad936x/ad936xadvanced.cpp @@ -19,7 +19,7 @@ * */ -#include "ad963xadvanced.h" +#include "ad936x/ad963xadvanced.h" #include #include diff --git a/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp b/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp index 196cff1c3b..8c6b1f822d 100644 --- a/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp +++ b/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp @@ -26,9 +26,10 @@ #include #include #include "scopy-ad936x_config.h" +#include -#include "ad936x.h" -#include "ad963xadvanced.h" +#include "ad936x/ad936x.h" +#include "ad936x/ad963xadvanced.h" #include From c438fba5796cdefe6f709aec121fd445b9567a75 Mon Sep 17 00:00:00 2001 From: IonutMuthi Date: Fri, 22 Aug 2025 10:14:52 +0300 Subject: [PATCH 3/6] fmcomms5: base functionality Signed-off-by: IonutMuthi --- .../ad936x/include/ad936x/fmcomms5/fmcomms5.h | 43 ++ .../ad936x/fmcomms5/fmcomms5advanced.h | 71 ++++ .../plugins/ad936x/src/ad936xplugin.cpp | 51 ++- .../plugins/ad936x/src/fmcomms5/fmcomms5.cpp | 397 ++++++++++++++++++ .../ad936x/src/fmcomms5/fmcomms5advanced.cpp | 217 ++++++++++ 5 files changed, 768 insertions(+), 11 deletions(-) create mode 100755 packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5.h create mode 100644 packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h create mode 100755 packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp create mode 100644 packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5.h b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5.h new file mode 100755 index 0000000000..1705926d17 --- /dev/null +++ b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5.h @@ -0,0 +1,43 @@ +#ifndef FMCOMMS5_H +#define FMCOMMS5_H + +#include "scopy-ad936x_export.h" +#include +#include +#include + +#include + +#include +#include + +namespace scopy { +namespace ad936x { +class SCOPY_AD936X_EXPORT FMCOMMS5 : public QWidget +{ + Q_OBJECT +public: + explicit FMCOMMS5(iio_context *ctx, QWidget *parent = nullptr); + ~FMCOMMS5(); + + +Q_SIGNALS: + void readRequested(); + + +private: + iio_context *m_ctx = nullptr; + ToolTemplate *m_tool; + QVBoxLayout *m_mainLayout; + QWidget *m_controlsWidget; + QWidget *m_blockDiagramWidget; + AnimatedRefreshBtn *m_refreshButton; + + QWidget *generateRxChainWidget(iio_device *dev, QString title, QWidget *parent); + QWidget *generateTxChainWidget(iio_device *dev, QString title, QWidget *parent); + + AD936xHelper *m_helper; +}; +} // namespace ad936x +} // namespace scopy +#endif // FMCOMMS5_H diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h new file mode 100644 index 0000000000..0b7a6e14ff --- /dev/null +++ b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h @@ -0,0 +1,71 @@ +#ifndef FMCOMMS5ADVANCED_H +#define FMCOMMS5ADVANCED_H + +#include "scopy-ad936x_export.h" +#include +#include +#include +#include + +#include "auxadcdaciowidget.h" +#include "elnawidget.h" +#include "ensmmodeclockswidget.h" +#include "gainwidget.h" +#include "rssiwidget.h" +#include "txmonitorwidget.h" +#include "miscwidget.h" +#include "bistwidget.h" + +namespace scopy { +namespace ad936x { +class SCOPY_AD936X_EXPORT Fmcomms5Advanced : public QWidget +{ + Q_OBJECT +public: + explicit Fmcomms5Advanced(iio_context *ctx, QWidget *parent = nullptr); + ~Fmcomms5Advanced(); + + +Q_SIGNALS: + void readRequested(); + +private: + void init(); + + QPushButton *m_ensmModeClocksBtn = nullptr; + QPushButton *m_eLnaBtn = nullptr; + QPushButton *m_rssiBtn = nullptr; + QPushButton *m_gainBtn = nullptr; + QPushButton *m_txMonitorBtn = nullptr; + QPushButton *m_auxAdcDacIioBtn = nullptr; + QPushButton *m_miscBtn = nullptr; + QPushButton *m_bistBtn = nullptr; + QPushButton *m_fmcomms5Btn = nullptr; + + + QPushButton *m_syncBtn = nullptr; + QPushButton *m_saveSettingsBtn = nullptr; + + iio_context *m_ctx = nullptr; + ToolTemplate *m_tool; + QVBoxLayout *m_mainLayout; + AnimatedRefreshBtn *m_refreshButton; + EnsmModeClocksWidget *m_ensmModeClocks; + ElnaWidget *m_elna; + RssiWidget *m_rssi; + GainWidget *m_gainWidget; + TxMonitorWidget *m_txMonitor; + AuxAdcDacIoWidget *m_auxAdcDacIo; + MiscWidget *m_misc; + BistWidget *m_bist; + + iio_device *m_plutoDevice = nullptr; + QStackedWidget *m_centralWidget = nullptr; + + bool m_isToolInitialized; + void showEvent(QShowEvent *event) override; +}; + +} // namespace ad936x +} // namespace scopy +#endif // FMCOMMS5ADVANCED_H diff --git a/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp b/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp index 8c6b1f822d..a9f44757f1 100644 --- a/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp +++ b/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp @@ -31,7 +31,8 @@ #include "ad936x/ad936x.h" #include "ad936x/ad963xadvanced.h" -#include +#include +#include Q_LOGGING_CATEGORY(CAT_AD936XPLUGIN, "Ad936xPlugin") using namespace scopy::ad936x; @@ -85,8 +86,8 @@ bool Ad936xPlugin::loadIcon() void Ad936xPlugin::loadToolList() { m_toolList.append(SCOPY_NEW_TOOLMENUENTRY("ad963xTool", "AD936X", - ":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) + - "/icons/gear_wheel.svg")); + ":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) + + "/icons/gear_wheel.svg")); m_toolList.append(SCOPY_NEW_TOOLMENUENTRY("ad963xAdvancedTool", "AD936X Advanced", ":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) + "/icons/gear_wheel.svg")); @@ -107,15 +108,43 @@ bool Ad936xPlugin::onConnect() return false; } - AD936X *ad936X = new AD936X(conn->context()); - m_toolList[0]->setTool(ad936X); - m_toolList[0]->setEnabled(true); - m_toolList[0]->setRunBtnVisible(true); + bool isFmcomms5 = false; + int device_count = iio_context_get_devices_count(conn->context()); + for(int i = 0; i < device_count; ++i) { + iio_device *dev = iio_context_get_device(conn->context(), i); + const char *dev_name = iio_device_get_name(dev); + if(dev_name && QString(dev_name).contains("ad9361-phy-B", Qt::CaseInsensitive)) { + isFmcomms5 = true; + break; + } + } + + if (isFmcomms5) { + FMCOMMS5 *fmcomms5 = new FMCOMMS5(conn->context()); + m_toolList[0]->setTool(fmcomms5); + m_toolList[0]->setName("FMCOMMS5"); + m_toolList[0]->setEnabled(true); + m_toolList[0]->setRunBtnVisible(true); + + Fmcomms5Advanced *fmcomms5Advanced = new Fmcomms5Advanced(conn->context()); + m_toolList[1]->setTool(fmcomms5Advanced); + m_toolList[1]->setName("FMCOMMS5 Advanced"); + m_toolList[1]->setEnabled(true); + m_toolList[1]->setRunBtnVisible(true); + + } else { + AD936X *ad936X = new AD936X(conn->context()); + m_toolList[0]->setTool(ad936X); + m_toolList[0]->setEnabled(true); + m_toolList[0]->setRunBtnVisible(true); + + AD936XAdvanced *ad936XAdvanced = new AD936XAdvanced(conn->context()); + m_toolList[1]->setTool(ad936XAdvanced); + m_toolList[1]->setEnabled(true); + m_toolList[1]->setRunBtnVisible(true); + } + - AD936XAdvanced *ad936XAdvanced = new AD936XAdvanced(conn->context()); - m_toolList[1]->setTool(ad936XAdvanced); - m_toolList[1]->setEnabled(true); - m_toolList[1]->setRunBtnVisible(true); return true; } diff --git a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp new file mode 100755 index 0000000000..42f052c346 --- /dev/null +++ b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp @@ -0,0 +1,397 @@ +#include "fmcomms5/fmcomms5.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_FMCOMMS5, "FMCOMMS5"); + +using namespace scopy; +using namespace ad936x; + +FMCOMMS5::FMCOMMS5(iio_context *ctx, QWidget *parent) + : QWidget(parent) + , m_ctx(ctx) +{ + m_mainLayout = new QVBoxLayout(this); + m_mainLayout->setMargin(0); + m_mainLayout->setContentsMargins(0, 0, 0, 0); + + m_tool = new ToolTemplate(this); + m_tool->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_tool->topContainer()->setVisible(true); + m_tool->topContainerMenuControl()->setVisible(false); + + m_mainLayout->addWidget(m_tool); + + m_refreshButton = new AnimatedRefreshBtn(false, this); + m_tool->addWidgetToTopContainerHelper(m_refreshButton, TTA_RIGHT); + + connect(m_refreshButton, &QPushButton::clicked, this, [this]() { + m_refreshButton->startAnimation(); + + QFutureWatcher *watcher = new QFutureWatcher(this); + connect( + watcher, &QFutureWatcher::finished, this, + [this, watcher]() { + m_refreshButton->stopAnimation(); + watcher->deleteLater(); + }, + Qt::QueuedConnection); + + QFuture future = QtConcurrent::run([this]() { Q_EMIT readRequested(); }); + + watcher->setFuture(future); + }); + + QStackedWidget *centralWidget = new QStackedWidget(this); + + m_controlsWidget = new QWidget(this); + QVBoxLayout *controlsLayout = new QVBoxLayout(m_controlsWidget); + controlsLayout->setMargin(0); + controlsLayout->setContentsMargins(0, 0, 0, 0); + m_controlsWidget->setLayout(controlsLayout); + + QWidget *controlsWidget = new QWidget(this); + QVBoxLayout *controlWidgetLayout = new QVBoxLayout(controlsWidget); + controlWidgetLayout->setMargin(0); + controlWidgetLayout->setContentsMargins(0, 0, 0, 0); + controlsWidget->setLayout(controlWidgetLayout); + + QScrollArea *scrollArea = new QScrollArea(this); + scrollArea->setWidgetResizable(true); + scrollArea->setWidget(controlsWidget); + + controlsLayout->addWidget(scrollArea); + + if(m_ctx != nullptr) { + + iio_device *plutoDevice = nullptr; + int device_count = iio_context_get_devices_count(m_ctx); + for(int i = 0; i < device_count; ++i) { + iio_device *dev = iio_context_get_device(m_ctx, i); + const char *dev_name = iio_device_get_name(dev); + if(dev_name && QString(dev_name).contains("ad936", Qt::CaseInsensitive)) { + plutoDevice = dev; + break; + } + } + + m_helper = new AD936xHelper(); + connect(this, &FMCOMMS5::readRequested, m_helper, &AD936xHelper::readRequested); + + /// first widget the global settings can be created with iiowigets only + controlWidgetLayout->addWidget(m_helper->generateGlobalSettingsWidget( + plutoDevice, "FMCOMMS5 Global Settings", controlsWidget)); + + /// second is Rx ( receive chain) + controlWidgetLayout->addWidget( + generateRxChainWidget(plutoDevice, "FMCOMMS5 Receive Chain", controlsWidget)); + + /// third is Tx (transimt chain) + controlWidgetLayout->addWidget( + generateTxChainWidget(plutoDevice, "FMCOMMS5 Transmit Chain", controlsWidget)); + + controlWidgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); + } + + m_blockDiagramWidget = new QWidget(this); + Style::setBackgroundColor(m_blockDiagramWidget, json::theme::background_primary); + QVBoxLayout *blockDiagramLayout = new QVBoxLayout(m_blockDiagramWidget); + m_blockDiagramWidget->setLayout(blockDiagramLayout); + + QWidget *blockDiagramWidget = new QWidget(this); + QVBoxLayout *blockDiagramWidgetLayout = new QVBoxLayout(blockDiagramWidget); + blockDiagramWidget->setLayout(blockDiagramWidgetLayout); + + QScrollArea *blockDiagramWidgetScrollArea = new QScrollArea(this); + blockDiagramWidgetScrollArea->setWidgetResizable(true); + blockDiagramWidgetScrollArea->setWidget(blockDiagramWidget); + + blockDiagramLayout->addWidget(blockDiagramWidgetScrollArea); + + QLabel *blockDiagram = new QLabel(m_blockDiagramWidget); + blockDiagramWidgetLayout->addWidget(blockDiagram); + blockDiagram->setAlignment(Qt::AlignCenter); + // todo replace + QPixmap pixmap(":/pluto/ad936x.svg"); + blockDiagram->setPixmap(pixmap); + + centralWidget->addWidget(m_controlsWidget); + centralWidget->addWidget(m_blockDiagramWidget); + + m_tool->addWidgetToCentralContainerHelper(centralWidget); + + QButtonGroup *centralWidgetButtons = new QButtonGroup(this); + centralWidgetButtons->setExclusive(true); + + QPushButton *ad963xBtn = new QPushButton("Controls", this); + ad963xBtn->setCheckable(true); + ad963xBtn->setChecked(true); + Style::setStyle(ad963xBtn, style::properties::button::blueGrayButton); + connect(ad963xBtn, &QPushButton::clicked, this, + [=, this]() { centralWidget->setCurrentWidget(m_controlsWidget); }); + + QPushButton *blockDiagramBtn = new QPushButton("Block Diagram", this); + blockDiagramBtn->setCheckable(true); + Style::setStyle(blockDiagramBtn, style::properties::button::blueGrayButton); + connect(blockDiagramBtn, &QPushButton::clicked, this, + [=, this]() { centralWidget->setCurrentWidget(m_blockDiagramWidget); }); + + centralWidgetButtons->addButton(ad963xBtn); + centralWidgetButtons->addButton(blockDiagramBtn); + + m_tool->addWidgetToTopContainerHelper(ad963xBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(blockDiagramBtn, TTA_LEFT); +} + +FMCOMMS5::~FMCOMMS5() {} + + +QWidget *FMCOMMS5::generateRxChainWidget(iio_device *dev, QString title, QWidget *parent) +{ + QWidget *widget = new QWidget(parent); + Style::setBackgroundColor(widget, json::theme::background_primary); + Style::setStyle(widget, style::properties::widget::border_interactive); + + QVBoxLayout *mainLayout = new QVBoxLayout(widget); + widget->setLayout(mainLayout); + + QLabel *titleLabel = new QLabel(title, widget); + Style::setStyle(titleLabel, style::properties::label::menuBig); + mainLayout->addWidget(titleLabel); + + if(dev == nullptr) { + qWarning(CAT_FMCOMMS5) << "No FMCOMMS5 device found"; + } + + QGridLayout *layout = new QGridLayout(); + + bool isOutput = false; + + iio_channel *voltage0 = iio_device_find_channel(dev, "voltage0", isOutput); + + // voltage0: rf_bandwidth + IIOWidget *rfBandwidth = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("rf_bandwidth") + .optionsAttribute("rf_bandwidth_available") + .title("RF Bandwidth(MHz)") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .buildSingle(); + layout->addWidget(rfBandwidth, 0, 0, 2, 1); + connect(this, &FMCOMMS5::readRequested, rfBandwidth, &IIOWidget::readAsync); + + // voltage0: sampling_frequency + IIOWidget *samplingFrequency = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("sampling_frequency") + .optionsAttribute("sampling_frequency_available") + .title("Sampling Rate(MSPS)") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .buildSingle(); + layout->addWidget(samplingFrequency, 0, 1, 2, 1); + connect(this, &FMCOMMS5::readRequested, samplingFrequency, &IIOWidget::readAsync); + + // voltage 0 : rf_port_select + IIOWidget *rfPortSelect = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("rf_port_select") + .optionsAttribute("rf_port_select_available") + .title("RF Port Select") + .uiStrategy(IIOWidgetBuilder::ComboUi) + .buildSingle(); + layout->addWidget(rfPortSelect, 0, 2, 2, 1); + connect(this, &FMCOMMS5::readRequested, rfPortSelect, &IIOWidget::readAsync); + + // quadrature_tracking_en + IIOWidget *quadratureTrackingEn = IIOWidgetBuilder(this) + .channel(voltage0) + .attribute("quadrature_tracking_en") + .uiStrategy(IIOWidgetBuilder::CheckBoxUi) + .title("Quadrature") + .buildSingle(); + layout->addWidget(quadratureTrackingEn, 0, 5); + quadratureTrackingEn->showProgressBar(false); + connect(this, &FMCOMMS5::readRequested, quadratureTrackingEn, &IIOWidget::readAsync); + + // rf_dc_offset_tracking_en + IIOWidget *rcDcOffsetTrackingEn = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("rf_dc_offset_tracking_en") + .uiStrategy(IIOWidgetBuilder::CheckBoxUi) + .title("RF DC") + .buildSingle(); + layout->addWidget(rcDcOffsetTrackingEn, 1, 5); + rcDcOffsetTrackingEn->showProgressBar(false); + connect(this, &FMCOMMS5::readRequested, rcDcOffsetTrackingEn, &IIOWidget::readAsync); + + // bb_dc_offset_tracking_en + IIOWidget *bbDcOffsetTrackingEn = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("bb_dc_offset_tracking_en") + .title("BB DC") + .uiStrategy(IIOWidgetBuilder::CheckBoxUi) + .buildSingle(); + layout->addWidget(bbDcOffsetTrackingEn, 2, 5); + bbDcOffsetTrackingEn->showProgressBar(false); + connect(this, &FMCOMMS5::readRequested, bbDcOffsetTrackingEn, &IIOWidget::readAsync); + + mainLayout->addLayout(layout); + + QHBoxLayout *rxDeviceLayout = new QHBoxLayout(); + rxDeviceLayout->setMargin(0); + rxDeviceLayout->setSpacing(10); + + QWidget *rxDeviceWidget = m_helper->generateRxDeviceWidget(dev, "ad9361-phy", widget); + + rxDeviceLayout->addWidget(rxDeviceWidget); + rxDeviceWidget->layout()->addWidget(m_helper->generateRxChannelWidget(voltage0, "RX 1", rxDeviceWidget)); + iio_channel *voltage1 = iio_device_find_channel(dev, "voltage1", isOutput); + if(voltage1 && iio_channel_find_attr(voltage1, "hardwaregain")) { + rxDeviceWidget->layout()->addWidget( + m_helper->generateRxChannelWidget(voltage1, "RX 2", rxDeviceWidget)); + } + + //////// second device for fmcomms5 RX + + iio_device *dev2 = nullptr; + int device_count = iio_context_get_devices_count(m_ctx); + for(int i = 0; i < device_count; ++i) { + iio_device *aux = iio_context_get_device(m_ctx, i); + const char *dev_name = iio_device_get_name(aux); + if(dev_name && QString(dev_name).contains("ad9361-phy-b", Qt::CaseInsensitive)) { + dev2 = aux; + break; + } + } + + QWidget *rxDevice2Widget = m_helper->generateRxDeviceWidget(dev2, "ad9361-phy-B", widget); + + rxDeviceLayout->addWidget(rxDevice2Widget); + + iio_channel *voltage0B = iio_device_find_channel(dev2, "voltage0", isOutput); + + rxDevice2Widget->layout()->addWidget(m_helper->generateRxChannelWidget(voltage0B, "RX 3", rxDevice2Widget)); + iio_channel *voltage1B = iio_device_find_channel(dev2, "voltage1", isOutput); + if(voltage1 && iio_channel_find_attr(voltage1B, "hardwaregain")) { + rxDevice2Widget->layout()->addWidget(m_helper->generateRxChannelWidget(voltage1B, "RX 4", rxDevice2Widget)); + } + + ///////////////////////// + + mainLayout->addLayout(rxDeviceLayout); + mainLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); + + return widget; +} + +QWidget *FMCOMMS5::generateTxChainWidget(iio_device *dev, QString title, QWidget *parent) +{ + QWidget *widget = new QWidget(parent); + Style::setBackgroundColor(widget, json::theme::background_primary); + Style::setStyle(widget, style::properties::widget::border_interactive); + + QVBoxLayout *layout = new QVBoxLayout(widget); + widget->setLayout(layout); + + QLabel *titleLabel = new QLabel(title, widget); + Style::setStyle(titleLabel, style::properties::label::menuBig); + layout->addWidget(titleLabel); + + QGridLayout *lay = new QGridLayout(); + + bool isOutput = true; + iio_channel *voltage0 = iio_device_find_channel(dev, "voltage0", isOutput); + + // voltage0: rf_bandwidth + IIOWidget *rfBandwidth = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("rf_bandwidth") + .optionsAttribute("rf_bandwidth_available") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .title("RF Bandwidth(MHz)") + .buildSingle(); + lay->addWidget(rfBandwidth, 0, 0, 2, 1); + connect(this, &FMCOMMS5::readRequested, rfBandwidth, &IIOWidget::readAsync); + + // voltage0: sampling_frequency + IIOWidget *samplingFrequency = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("sampling_frequency") + .optionsAttribute("sampling_frequency_available") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .title("Sampling Rate(MSPS)") + .buildSingle(); + lay->addWidget(samplingFrequency, 0, 1, 2, 1); + connect(this, &FMCOMMS5::readRequested, samplingFrequency, &IIOWidget::readAsync); + + // voltage0: rf_port_select + IIOWidget *rfPortSelect = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("rf_port_select") + .optionsAttribute("rf_port_select_available") + .uiStrategy(IIOWidgetBuilder::ComboUi) + .title("RF Port Select") + .buildSingle(); + lay->addWidget(rfPortSelect, 0, 2, 2, 1); + connect(this, &FMCOMMS5::readRequested, rfPortSelect, &IIOWidget::readAsync); + + layout->addLayout(lay); + + QHBoxLayout *txWidgetsLayout = new QHBoxLayout(); + txWidgetsLayout->setMargin(0); + txWidgetsLayout->setSpacing(10); + + QWidget *txDeviceWidget = m_helper->generateTxDeviceWidget(dev, "ad9361-phy", widget); + + txWidgetsLayout->addWidget(txDeviceWidget); + + txDeviceWidget->layout()->addWidget(m_helper->generateTxChannelWidget(voltage0, "TX 1", txDeviceWidget)); + iio_channel *voltage1 = iio_device_find_channel(dev, "voltage1", isOutput); + if(voltage1 && iio_channel_find_attr(voltage1, "hardwaregain")) { + txDeviceWidget->layout()->addWidget( + m_helper->generateTxChannelWidget(voltage1, "TX 2", txDeviceWidget)); + } + + //////// second device for fmcomms5 TX + + iio_device *dev2 = nullptr; + int device_count = iio_context_get_devices_count(m_ctx); + for(int i = 0; i < device_count; ++i) { + iio_device *aux = iio_context_get_device(m_ctx, i); + const char *dev_name = iio_device_get_name(aux); + if(dev_name && QString(dev_name).contains("ad9361-phy-b", Qt::CaseInsensitive)) { + dev2 = aux; + break; + } + } + + QWidget *txDevice2Widget = m_helper->generateTxDeviceWidget(dev2, "ad9361-phy-B", widget); + + txWidgetsLayout->addWidget(txDevice2Widget); + + iio_channel *voltage0B = iio_device_find_channel(dev2, "voltage0", isOutput); + + txDevice2Widget->layout()->addWidget(m_helper->generateTxChannelWidget(voltage0B, "TX 3", txDevice2Widget)); + iio_channel *voltage1B = iio_device_find_channel(dev2, "voltage1", isOutput); + if(voltage1 && iio_channel_find_attr(voltage1B, "hardwaregain")) { + txDevice2Widget->layout()->addWidget(m_helper->generateTxChannelWidget(voltage1B, "TX 4", txDevice2Widget)); + } + + ///////////////////////// + + layout->addLayout(txWidgetsLayout); + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); + + return widget; +} diff --git a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp new file mode 100644 index 0000000000..79b7bd2bca --- /dev/null +++ b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp @@ -0,0 +1,217 @@ +#include "fmcomms5/fmcomms5advanced.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_FMCOMMS5_ADVANCED, "FMCOMMS5_ADVANCED") + +using namespace scopy; +using namespace ad936x; + +Fmcomms5Advanced::Fmcomms5Advanced(iio_context *ctx,QWidget *parent) + : m_ctx(ctx) + , QWidget{parent} +{ + m_mainLayout = new QVBoxLayout(this); + m_mainLayout->setMargin(0); + m_mainLayout->setContentsMargins(0, 0, 0, 0); + + m_tool = new ToolTemplate(this); + m_tool->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_tool->topContainer()->setVisible(true); + m_tool->bottomContainer()->setVisible(true); + m_tool->topContainerMenuControl()->setVisible(false); + + m_mainLayout->addWidget(m_tool); + + m_refreshButton = new AnimatedRefreshBtn(false, this); + m_tool->addWidgetToTopContainerHelper(m_refreshButton, TTA_RIGHT); + + connect(m_refreshButton, &QPushButton::clicked, this, [this]() { + m_refreshButton->startAnimation(); + + QFutureWatcher *watcher = new QFutureWatcher(this); + connect( + watcher, &QFutureWatcher::finished, this, + [this, watcher]() { + m_refreshButton->stopAnimation(); + watcher->deleteLater(); + }, + Qt::QueuedConnection); + + QFuture future = QtConcurrent::run([this]() { Q_EMIT readRequested(); }); + + watcher->setFuture(future); + }); + + // main widget body + + m_isToolInitialized = false; + + QStackedWidget *centralWidget = new QStackedWidget(this); + m_tool->addWidgetToCentralContainerHelper(centralWidget); + + QButtonGroup *navigationButtons = new QButtonGroup(this); + navigationButtons->setExclusive(true); + + if(m_ctx != nullptr) { + iio_device *plutoDevice = nullptr; + int device_count = iio_context_get_devices_count(ctx); + for(int i = 0; i < device_count; ++i) { + iio_device *dev = iio_context_get_device(ctx, i); + const char *dev_name = iio_device_get_name(dev); + if(dev_name && QString(dev_name).contains("ad936", Qt::CaseInsensitive)) { + plutoDevice = dev; + break; + } + } + if(plutoDevice == nullptr) { + qWarning(CAT_FMCOMMS5_ADVANCED) << "No AD936x device found in context!"; + return; + } + + m_plutoDevice = plutoDevice; + m_centralWidget = centralWidget; + + // Create buttons + m_ensmModeClocksBtn = new QPushButton("ENSM/Mode/Clocks", this); + Style::setStyle(m_ensmModeClocksBtn, style::properties::button::blueGrayButton); + m_ensmModeClocksBtn->setCheckable(true); + m_ensmModeClocksBtn->setChecked(true); + m_eLnaBtn = new QPushButton("eLNA", this); + Style::setStyle(m_eLnaBtn, style::properties::button::blueGrayButton); + m_eLnaBtn->setCheckable(true); + m_rssiBtn = new QPushButton("RSSI", this); + Style::setStyle(m_rssiBtn, style::properties::button::blueGrayButton); + m_rssiBtn->setCheckable(true); + m_gainBtn = new QPushButton("GAIN", this); + Style::setStyle(m_gainBtn, style::properties::button::blueGrayButton); + m_gainBtn->setCheckable(true); + m_txMonitorBtn = new QPushButton("TX MONITOR", this); + Style::setStyle(m_txMonitorBtn, style::properties::button::blueGrayButton); + m_txMonitorBtn->setCheckable(true); + m_auxAdcDacIioBtn = new QPushButton("Aux ADC/DAC/IIO", this); + Style::setStyle(m_auxAdcDacIioBtn, style::properties::button::blueGrayButton); + m_auxAdcDacIioBtn->setCheckable(true); + m_miscBtn = new QPushButton("MISC", this); + Style::setStyle(m_miscBtn, style::properties::button::blueGrayButton); + m_miscBtn->setCheckable(true); + m_bistBtn = new QPushButton("BIST", this); + Style::setStyle(m_bistBtn, style::properties::button::blueGrayButton); + m_bistBtn->setCheckable(true); + + m_fmcomms5Btn = new QPushButton("FMCOMMS5", this); + Style::setStyle(m_fmcomms5Btn, style::properties::button::blueGrayButton); + m_fmcomms5Btn->setCheckable(true); + + navigationButtons->addButton(m_ensmModeClocksBtn); + navigationButtons->addButton(m_eLnaBtn); + navigationButtons->addButton(m_rssiBtn); + navigationButtons->addButton(m_gainBtn); + navigationButtons->addButton(m_txMonitorBtn); + navigationButtons->addButton(m_auxAdcDacIioBtn); + navigationButtons->addButton(m_miscBtn); + navigationButtons->addButton(m_bistBtn); + navigationButtons->addButton(m_fmcomms5Btn); + + m_tool->addWidgetToTopContainerHelper(m_ensmModeClocksBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_eLnaBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_rssiBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_gainBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_txMonitorBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_auxAdcDacIioBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_miscBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_bistBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_fmcomms5Btn, TTA_LEFT); + + + m_syncBtn = new QPushButton("MSC Sync", this); + Style::setStyle(m_syncBtn, style::properties::button::blueGrayButton); + m_syncBtn->setCheckable(true); + + m_saveSettingsBtn = new QPushButton("Save Settings", this); + Style::setStyle(m_saveSettingsBtn, style::properties::button::blueGrayButton); + m_saveSettingsBtn->setCheckable(true); + + + m_tool->addWidgetToBottomContainerHelper(m_syncBtn, TTA_LEFT); + m_tool->addWidgetToBottomContainerHelper(m_saveSettingsBtn, TTA_LEFT); + + bool useLazyLoading = scopy::Preferences::get("iiowidgets_use_lazy_loading").toBool(); + if(!useLazyLoading) { + init(); + } + } +} + +Fmcomms5Advanced::~Fmcomms5Advanced() {} + +void Fmcomms5Advanced::showEvent(QShowEvent *event) +{ + + if(!m_isToolInitialized) { + bool useLazyLoading = scopy::Preferences::get("iiowidgets_use_lazy_loading").toBool(); + if(useLazyLoading) { + init(); + } + } + QWidget::showEvent(event); +} + + +void Fmcomms5Advanced::init() +{ + + // ENSM Mode Clocks + m_ensmModeClocks = new EnsmModeClocksWidget(m_plutoDevice, m_centralWidget); + m_centralWidget->addWidget(m_ensmModeClocks); + connect(this, &Fmcomms5Advanced::readRequested, m_ensmModeClocks, &EnsmModeClocksWidget::readRequested); + connect(m_ensmModeClocksBtn, &QPushButton::clicked, this, + [=, this]() { m_centralWidget->setCurrentWidget(m_ensmModeClocks); }); + // eLNA + m_elna = new ElnaWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_elna, &ElnaWidget::readRequested); + m_centralWidget->addWidget(m_elna); + connect(m_eLnaBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_elna); }); + // RSSI + m_rssi = new RssiWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_rssi, &RssiWidget::readRequested); + m_centralWidget->addWidget(m_rssi); + connect(m_rssiBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_rssi); }); + // GAIN + m_gainWidget = new GainWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_gainWidget, &GainWidget::readRequested); + m_centralWidget->addWidget(m_gainWidget); + connect(m_gainBtn, &QPushButton::clicked, this, + [=, this]() { m_centralWidget->setCurrentWidget(m_gainWidget); }); + // TX MONITOR + m_txMonitor = new TxMonitorWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_txMonitor, &TxMonitorWidget::readRequested); + m_centralWidget->addWidget(m_txMonitor); + connect(m_txMonitorBtn, &QPushButton::clicked, this, + [=, this]() { m_centralWidget->setCurrentWidget(m_txMonitor); }); + // AUX ADC/DAC/IIO + m_auxAdcDacIo = new AuxAdcDacIoWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_auxAdcDacIo, &AuxAdcDacIoWidget::readRequested); + m_centralWidget->addWidget(m_auxAdcDacIo); + connect(m_auxAdcDacIioBtn, &QPushButton::clicked, this, + [=, this]() { m_centralWidget->setCurrentWidget(m_auxAdcDacIo); }); + // MISC + m_misc = new MiscWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_misc, &MiscWidget::readRequested); + m_centralWidget->addWidget(m_misc); + connect(m_miscBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_misc); }); + // BIST + m_bist = new BistWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_bist, &BistWidget::readRequested); + m_centralWidget->addWidget(m_bist); + connect(m_bistBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_bist); }); + + m_isToolInitialized = true; +} From 092c18cec7ad2a13ee796ce33aee7ca7879eb592 Mon Sep 17 00:00:00 2001 From: IonutMuthi Date: Tue, 26 Aug 2025 14:46:36 +0300 Subject: [PATCH 4/6] fmcomms5: added advanced fmcomms5 tab wip Signed-off-by: IonutMuthi --- packages/ad936x/plugins/ad936x/CMakeLists.txt | 30 +- .../ad936x/include/ad936x/fmcomms5/fmcomms5.h | 49 +- .../ad936x/fmcomms5/fmcomms5advanced.h | 90 ++- .../include/ad936x/fmcomms5/fmcomms5tab.h | 71 ++ .../plugins/ad936x/src/ad936xplugin.cpp | 7 +- .../plugins/ad936x/src/fmcomms5/fmcomms5.cpp | 736 +++++++++--------- .../ad936x/src/fmcomms5/fmcomms5advanced.cpp | 419 +++++----- .../ad936x/src/fmcomms5/fmcomms5tab.cpp | 534 +++++++++++++ 8 files changed, 1333 insertions(+), 603 deletions(-) mode change 100755 => 100644 packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5.h create mode 100644 packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5tab.h mode change 100755 => 100644 packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp create mode 100644 packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5tab.cpp diff --git a/packages/ad936x/plugins/ad936x/CMakeLists.txt b/packages/ad936x/plugins/ad936x/CMakeLists.txt index 4374c50031..81b20aff7f 100644 --- a/packages/ad936x/plugins/ad936x/CMakeLists.txt +++ b/packages/ad936x/plugins/ad936x/CMakeLists.txt @@ -47,12 +47,26 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE) -file(GLOB SRC_LIST src/*.cpp src/*.cc - src/ad936x/*.cpp src/ad936x/*.cc - src/fmcomms5/*.cpp src/ad936x/*.cc) -file(GLOB HEADER_LIST include/${SCOPY_MODULE}/*.h include/${SCOPY_MODULE}/*.hpp -include/${SCOPY_MODULE}/ad936x/*.h include/${SCOPY_MODULE}/ad936x/*.hpp -include/${SCOPY_MODULE}/fmcomms5/*.h include/${SCOPY_MODULE}/fmcomms5/*.hpp) +file( + GLOB + SRC_LIST + src/*.cpp + src/*.cc + src/ad936x/*.cpp + src/ad936x/*.cc + src/fmcomms5/*.cpp + src/ad936x/*.cc +) +file( + GLOB + HEADER_LIST + include/${SCOPY_MODULE}/*.h + include/${SCOPY_MODULE}/*.hpp + include/${SCOPY_MODULE}/ad936x/*.h + include/${SCOPY_MODULE}/ad936x/*.hpp + include/${SCOPY_MODULE}/fmcomms5/*.h + include/${SCOPY_MODULE}/fmcomms5/*.hpp +) file(GLOB UI_LIST ui/*.ui) set(ENABLE_TESTING ON) @@ -63,6 +77,9 @@ endif() set(PROJECT_SOURCES ${SRC_LIST} ${HEADER_LIST} ${UI_LIST}) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS REQUIRED Widgets Core) +find_library(AD9361_LIBRARIES NAMES ad9361 libad9361 REQUIRED) +find_path(AD9361_INCLUDE_DIRS ad9361.h REQUIRED) + set(AD936X_FILTERS_BUILD_PATH ${CMAKE_CURRENT_BINARY_DIR}/resources/ad936x-filters) file(GLOB FIR_FILTERS ${CMAKE_CURRENT_SOURCE_DIR}/resources/ad936x-filters/*) @@ -96,6 +113,7 @@ target_link_libraries( scopy-iioutil scopy-iio-widgets scopy-pkg-manager + ${AD9361_LIBRARIES} ) set(PLUTO_TARGET_NAME ${PROJECT_NAME} PARENT_SCOPE) diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5.h b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5.h old mode 100755 new mode 100644 index 1705926d17..520268502f --- a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5.h +++ b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5.h @@ -1,3 +1,24 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * 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, either 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + #ifndef FMCOMMS5_H #define FMCOMMS5_H @@ -15,28 +36,26 @@ namespace scopy { namespace ad936x { class SCOPY_AD936X_EXPORT FMCOMMS5 : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit FMCOMMS5(iio_context *ctx, QWidget *parent = nullptr); - ~FMCOMMS5(); - + explicit FMCOMMS5(iio_context *ctx, QWidget *parent = nullptr); + ~FMCOMMS5(); Q_SIGNALS: - void readRequested(); - + void readRequested(); private: - iio_context *m_ctx = nullptr; - ToolTemplate *m_tool; - QVBoxLayout *m_mainLayout; - QWidget *m_controlsWidget; - QWidget *m_blockDiagramWidget; - AnimatedRefreshBtn *m_refreshButton; + iio_context *m_ctx = nullptr; + ToolTemplate *m_tool; + QVBoxLayout *m_mainLayout; + QWidget *m_controlsWidget; + QWidget *m_blockDiagramWidget; + AnimatedRefreshBtn *m_refreshButton; - QWidget *generateRxChainWidget(iio_device *dev, QString title, QWidget *parent); - QWidget *generateTxChainWidget(iio_device *dev, QString title, QWidget *parent); + QWidget *generateRxChainWidget(iio_device *dev, QString title, QWidget *parent); + QWidget *generateTxChainWidget(iio_device *dev, QString title, QWidget *parent); - AD936xHelper *m_helper; + AD936xHelper *m_helper; }; } // namespace ad936x } // namespace scopy diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h index 0b7a6e14ff..0f41d7ddba 100644 --- a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h +++ b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h @@ -1,3 +1,24 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * 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, either 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + #ifndef FMCOMMS5ADVANCED_H #define FMCOMMS5ADVANCED_H @@ -15,55 +36,56 @@ #include "txmonitorwidget.h" #include "miscwidget.h" #include "bistwidget.h" +#include "fmcomms5/fmcomms5tab.h" namespace scopy { namespace ad936x { class SCOPY_AD936X_EXPORT Fmcomms5Advanced : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit Fmcomms5Advanced(iio_context *ctx, QWidget *parent = nullptr); - ~Fmcomms5Advanced(); - + explicit Fmcomms5Advanced(iio_context *ctx, QWidget *parent = nullptr); + ~Fmcomms5Advanced(); Q_SIGNALS: - void readRequested(); + void readRequested(); private: - void init(); - - QPushButton *m_ensmModeClocksBtn = nullptr; - QPushButton *m_eLnaBtn = nullptr; - QPushButton *m_rssiBtn = nullptr; - QPushButton *m_gainBtn = nullptr; - QPushButton *m_txMonitorBtn = nullptr; - QPushButton *m_auxAdcDacIioBtn = nullptr; - QPushButton *m_miscBtn = nullptr; - QPushButton *m_bistBtn = nullptr; - QPushButton *m_fmcomms5Btn = nullptr; + void init(); + QPushButton *m_ensmModeClocksBtn = nullptr; + QPushButton *m_eLnaBtn = nullptr; + QPushButton *m_rssiBtn = nullptr; + QPushButton *m_gainBtn = nullptr; + QPushButton *m_txMonitorBtn = nullptr; + QPushButton *m_auxAdcDacIioBtn = nullptr; + QPushButton *m_miscBtn = nullptr; + QPushButton *m_bistBtn = nullptr; + QPushButton *m_fmcomms5Btn = nullptr; - QPushButton *m_syncBtn = nullptr; - QPushButton *m_saveSettingsBtn = nullptr; + QPushButton *m_syncBtn = nullptr; + void ad9361MultichipSync(); - iio_context *m_ctx = nullptr; - ToolTemplate *m_tool; - QVBoxLayout *m_mainLayout; - AnimatedRefreshBtn *m_refreshButton; - EnsmModeClocksWidget *m_ensmModeClocks; - ElnaWidget *m_elna; - RssiWidget *m_rssi; - GainWidget *m_gainWidget; - TxMonitorWidget *m_txMonitor; - AuxAdcDacIoWidget *m_auxAdcDacIo; - MiscWidget *m_misc; - BistWidget *m_bist; + iio_context *m_ctx = nullptr; + ToolTemplate *m_tool; + QVBoxLayout *m_mainLayout; + AnimatedRefreshBtn *m_refreshButton; + EnsmModeClocksWidget *m_ensmModeClocks; + ElnaWidget *m_elna; + RssiWidget *m_rssi; + GainWidget *m_gainWidget; + TxMonitorWidget *m_txMonitor; + AuxAdcDacIoWidget *m_auxAdcDacIo; + MiscWidget *m_misc; + BistWidget *m_bist; + Fmcomms5Tab *m_fmcomms5; - iio_device *m_plutoDevice = nullptr; - QStackedWidget *m_centralWidget = nullptr; + iio_device *m_plutoDevice = nullptr; + iio_device *m_plutoDeviceB = nullptr; + QStackedWidget *m_centralWidget = nullptr; - bool m_isToolInitialized; - void showEvent(QShowEvent *event) override; + bool m_isToolInitialized; + void showEvent(QShowEvent *event) override; }; } // namespace ad936x diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5tab.h b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5tab.h new file mode 100644 index 0000000000..bfaf795618 --- /dev/null +++ b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5tab.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * 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, either 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef FMCOMMS5TAB_H +#define FMCOMMS5TAB_H + +#include +#include +#include +#include +#include +#include + +namespace scopy { +namespace ad936x { +class Fmcomms5Tab : public QWidget +{ + Q_OBJECT +public: + explicit Fmcomms5Tab(iio_context *ctx, QWidget *parent = nullptr); + ~Fmcomms5Tab(); + +Q_SIGNALS: + void readRequested(); + +private: + iio_context *m_ctx; + QVBoxLayout *m_layout; + iio_device *m_device = nullptr; + iio_device *m_deviceB = nullptr; + QCheckBox *m_silentCalibration; + QProgressBar *m_calibProgressBar; + QPushButton *m_calibrateBtn; + + void initDevices(); + + void calibrate(); + void trxPhaseRottation(iio_device *dev, double val); + + void resetCalibration(); + void callSwitchPortsEnableCb(int val); + double tuneTrxPhaseOffset(iio_device *ldev, int *ret, long long cal_freq, long long cal_tone, double sign, + double abort, std::function tune); + + // helper functions TODO MOVE TO CLASS + double scalePhase0360(double val); + double calcPhaseOffset(double fsample, double dds_freq, double offset, double mag); + void getMarkers(double *offset, double *mag); + int getCalSamples(long long calTone, long long calFreq); +}; +} // namespace ad936x +} // namespace scopy +#endif // FMCOMMS5TAB_H diff --git a/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp b/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp index a9f44757f1..ee90e2b84b 100644 --- a/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp +++ b/packages/ad936x/plugins/ad936x/src/ad936xplugin.cpp @@ -86,8 +86,8 @@ bool Ad936xPlugin::loadIcon() void Ad936xPlugin::loadToolList() { m_toolList.append(SCOPY_NEW_TOOLMENUENTRY("ad963xTool", "AD936X", - ":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) + - "/icons/gear_wheel.svg")); + ":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) + + "/icons/gear_wheel.svg")); m_toolList.append(SCOPY_NEW_TOOLMENUENTRY("ad963xAdvancedTool", "AD936X Advanced", ":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) + "/icons/gear_wheel.svg")); @@ -119,7 +119,7 @@ bool Ad936xPlugin::onConnect() } } - if (isFmcomms5) { + if(isFmcomms5) { FMCOMMS5 *fmcomms5 = new FMCOMMS5(conn->context()); m_toolList[0]->setTool(fmcomms5); m_toolList[0]->setName("FMCOMMS5"); @@ -144,7 +144,6 @@ bool Ad936xPlugin::onConnect() m_toolList[1]->setRunBtnVisible(true); } - return true; } diff --git a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp old mode 100755 new mode 100644 index 42f052c346..126a03e879 --- a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp +++ b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp @@ -1,3 +1,24 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * 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, either 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + #include "fmcomms5/fmcomms5.h" #include @@ -19,379 +40,380 @@ using namespace scopy; using namespace ad936x; FMCOMMS5::FMCOMMS5(iio_context *ctx, QWidget *parent) - : QWidget(parent) - , m_ctx(ctx) + : QWidget(parent) + , m_ctx(ctx) { - m_mainLayout = new QVBoxLayout(this); - m_mainLayout->setMargin(0); - m_mainLayout->setContentsMargins(0, 0, 0, 0); - - m_tool = new ToolTemplate(this); - m_tool->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - m_tool->topContainer()->setVisible(true); - m_tool->topContainerMenuControl()->setVisible(false); + m_mainLayout = new QVBoxLayout(this); + m_mainLayout->setMargin(0); + m_mainLayout->setContentsMargins(0, 0, 0, 0); + + m_tool = new ToolTemplate(this); + m_tool->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_tool->topContainer()->setVisible(true); + m_tool->topContainerMenuControl()->setVisible(false); - m_mainLayout->addWidget(m_tool); + m_mainLayout->addWidget(m_tool); - m_refreshButton = new AnimatedRefreshBtn(false, this); - m_tool->addWidgetToTopContainerHelper(m_refreshButton, TTA_RIGHT); + m_refreshButton = new AnimatedRefreshBtn(false, this); + m_tool->addWidgetToTopContainerHelper(m_refreshButton, TTA_RIGHT); - connect(m_refreshButton, &QPushButton::clicked, this, [this]() { - m_refreshButton->startAnimation(); - - QFutureWatcher *watcher = new QFutureWatcher(this); - connect( - watcher, &QFutureWatcher::finished, this, - [this, watcher]() { - m_refreshButton->stopAnimation(); - watcher->deleteLater(); - }, - Qt::QueuedConnection); - - QFuture future = QtConcurrent::run([this]() { Q_EMIT readRequested(); }); - - watcher->setFuture(future); - }); - - QStackedWidget *centralWidget = new QStackedWidget(this); - - m_controlsWidget = new QWidget(this); - QVBoxLayout *controlsLayout = new QVBoxLayout(m_controlsWidget); - controlsLayout->setMargin(0); - controlsLayout->setContentsMargins(0, 0, 0, 0); - m_controlsWidget->setLayout(controlsLayout); - - QWidget *controlsWidget = new QWidget(this); - QVBoxLayout *controlWidgetLayout = new QVBoxLayout(controlsWidget); - controlWidgetLayout->setMargin(0); - controlWidgetLayout->setContentsMargins(0, 0, 0, 0); - controlsWidget->setLayout(controlWidgetLayout); - - QScrollArea *scrollArea = new QScrollArea(this); - scrollArea->setWidgetResizable(true); - scrollArea->setWidget(controlsWidget); - - controlsLayout->addWidget(scrollArea); - - if(m_ctx != nullptr) { - - iio_device *plutoDevice = nullptr; - int device_count = iio_context_get_devices_count(m_ctx); - for(int i = 0; i < device_count; ++i) { - iio_device *dev = iio_context_get_device(m_ctx, i); - const char *dev_name = iio_device_get_name(dev); - if(dev_name && QString(dev_name).contains("ad936", Qt::CaseInsensitive)) { - plutoDevice = dev; - break; - } - } - - m_helper = new AD936xHelper(); - connect(this, &FMCOMMS5::readRequested, m_helper, &AD936xHelper::readRequested); - - /// first widget the global settings can be created with iiowigets only - controlWidgetLayout->addWidget(m_helper->generateGlobalSettingsWidget( - plutoDevice, "FMCOMMS5 Global Settings", controlsWidget)); - - /// second is Rx ( receive chain) - controlWidgetLayout->addWidget( - generateRxChainWidget(plutoDevice, "FMCOMMS5 Receive Chain", controlsWidget)); - - /// third is Tx (transimt chain) - controlWidgetLayout->addWidget( - generateTxChainWidget(plutoDevice, "FMCOMMS5 Transmit Chain", controlsWidget)); - - controlWidgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); - } - - m_blockDiagramWidget = new QWidget(this); - Style::setBackgroundColor(m_blockDiagramWidget, json::theme::background_primary); - QVBoxLayout *blockDiagramLayout = new QVBoxLayout(m_blockDiagramWidget); - m_blockDiagramWidget->setLayout(blockDiagramLayout); - - QWidget *blockDiagramWidget = new QWidget(this); - QVBoxLayout *blockDiagramWidgetLayout = new QVBoxLayout(blockDiagramWidget); - blockDiagramWidget->setLayout(blockDiagramWidgetLayout); - - QScrollArea *blockDiagramWidgetScrollArea = new QScrollArea(this); - blockDiagramWidgetScrollArea->setWidgetResizable(true); - blockDiagramWidgetScrollArea->setWidget(blockDiagramWidget); - - blockDiagramLayout->addWidget(blockDiagramWidgetScrollArea); - - QLabel *blockDiagram = new QLabel(m_blockDiagramWidget); - blockDiagramWidgetLayout->addWidget(blockDiagram); - blockDiagram->setAlignment(Qt::AlignCenter); - // todo replace - QPixmap pixmap(":/pluto/ad936x.svg"); - blockDiagram->setPixmap(pixmap); + connect(m_refreshButton, &QPushButton::clicked, this, [this]() { + m_refreshButton->startAnimation(); + + QFutureWatcher *watcher = new QFutureWatcher(this); + connect( + watcher, &QFutureWatcher::finished, this, + [this, watcher]() { + m_refreshButton->stopAnimation(); + watcher->deleteLater(); + }, + Qt::QueuedConnection); + + QFuture future = QtConcurrent::run([this]() { Q_EMIT readRequested(); }); + + watcher->setFuture(future); + }); + + QStackedWidget *centralWidget = new QStackedWidget(this); + + m_controlsWidget = new QWidget(this); + QVBoxLayout *controlsLayout = new QVBoxLayout(m_controlsWidget); + controlsLayout->setMargin(0); + controlsLayout->setContentsMargins(0, 0, 0, 0); + m_controlsWidget->setLayout(controlsLayout); + + QWidget *controlsWidget = new QWidget(this); + QVBoxLayout *controlWidgetLayout = new QVBoxLayout(controlsWidget); + controlWidgetLayout->setMargin(0); + controlWidgetLayout->setContentsMargins(0, 0, 0, 0); + controlsWidget->setLayout(controlWidgetLayout); + + QScrollArea *scrollArea = new QScrollArea(this); + scrollArea->setWidgetResizable(true); + scrollArea->setWidget(controlsWidget); + + controlsLayout->addWidget(scrollArea); + + if(m_ctx != nullptr) { + + iio_device *plutoDevice = nullptr; + int device_count = iio_context_get_devices_count(m_ctx); + for(int i = 0; i < device_count; ++i) { + iio_device *dev = iio_context_get_device(m_ctx, i); + const char *dev_name = iio_device_get_name(dev); + if(dev_name && QString(dev_name).contains("ad936", Qt::CaseInsensitive)) { + plutoDevice = dev; + break; + } + } + + m_helper = new AD936xHelper(); + connect(this, &FMCOMMS5::readRequested, m_helper, &AD936xHelper::readRequested); + + /// first widget the global settings can be created with iiowigets only + controlWidgetLayout->addWidget(m_helper->generateGlobalSettingsWidget( + plutoDevice, "FMCOMMS5 Global Settings", controlsWidget)); + + /// second is Rx ( receive chain) + controlWidgetLayout->addWidget( + generateRxChainWidget(plutoDevice, "FMCOMMS5 Receive Chain", controlsWidget)); + + /// third is Tx (transimt chain) + controlWidgetLayout->addWidget( + generateTxChainWidget(plutoDevice, "FMCOMMS5 Transmit Chain", controlsWidget)); + + controlWidgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); + } + + m_blockDiagramWidget = new QWidget(this); + Style::setBackgroundColor(m_blockDiagramWidget, json::theme::background_primary); + QVBoxLayout *blockDiagramLayout = new QVBoxLayout(m_blockDiagramWidget); + m_blockDiagramWidget->setLayout(blockDiagramLayout); + + QWidget *blockDiagramWidget = new QWidget(this); + QVBoxLayout *blockDiagramWidgetLayout = new QVBoxLayout(blockDiagramWidget); + blockDiagramWidget->setLayout(blockDiagramWidgetLayout); + + QScrollArea *blockDiagramWidgetScrollArea = new QScrollArea(this); + blockDiagramWidgetScrollArea->setWidgetResizable(true); + blockDiagramWidgetScrollArea->setWidget(blockDiagramWidget); + + blockDiagramLayout->addWidget(blockDiagramWidgetScrollArea); + + QLabel *blockDiagram = new QLabel(m_blockDiagramWidget); + blockDiagramWidgetLayout->addWidget(blockDiagram); + blockDiagram->setAlignment(Qt::AlignCenter); + // todo replace + QPixmap pixmap(":/pluto/ad936x.svg"); + blockDiagram->setPixmap(pixmap); - centralWidget->addWidget(m_controlsWidget); - centralWidget->addWidget(m_blockDiagramWidget); - - m_tool->addWidgetToCentralContainerHelper(centralWidget); + centralWidget->addWidget(m_controlsWidget); + centralWidget->addWidget(m_blockDiagramWidget); + + m_tool->addWidgetToCentralContainerHelper(centralWidget); - QButtonGroup *centralWidgetButtons = new QButtonGroup(this); - centralWidgetButtons->setExclusive(true); - - QPushButton *ad963xBtn = new QPushButton("Controls", this); - ad963xBtn->setCheckable(true); - ad963xBtn->setChecked(true); - Style::setStyle(ad963xBtn, style::properties::button::blueGrayButton); - connect(ad963xBtn, &QPushButton::clicked, this, - [=, this]() { centralWidget->setCurrentWidget(m_controlsWidget); }); - - QPushButton *blockDiagramBtn = new QPushButton("Block Diagram", this); - blockDiagramBtn->setCheckable(true); - Style::setStyle(blockDiagramBtn, style::properties::button::blueGrayButton); - connect(blockDiagramBtn, &QPushButton::clicked, this, - [=, this]() { centralWidget->setCurrentWidget(m_blockDiagramWidget); }); + QButtonGroup *centralWidgetButtons = new QButtonGroup(this); + centralWidgetButtons->setExclusive(true); + + QPushButton *ad963xBtn = new QPushButton("Controls", this); + ad963xBtn->setCheckable(true); + ad963xBtn->setChecked(true); + Style::setStyle(ad963xBtn, style::properties::button::blueGrayButton); + connect(ad963xBtn, &QPushButton::clicked, this, + [=, this]() { centralWidget->setCurrentWidget(m_controlsWidget); }); + + QPushButton *blockDiagramBtn = new QPushButton("Block Diagram", this); + blockDiagramBtn->setCheckable(true); + Style::setStyle(blockDiagramBtn, style::properties::button::blueGrayButton); + connect(blockDiagramBtn, &QPushButton::clicked, this, + [=, this]() { centralWidget->setCurrentWidget(m_blockDiagramWidget); }); - centralWidgetButtons->addButton(ad963xBtn); - centralWidgetButtons->addButton(blockDiagramBtn); + centralWidgetButtons->addButton(ad963xBtn); + centralWidgetButtons->addButton(blockDiagramBtn); - m_tool->addWidgetToTopContainerHelper(ad963xBtn, TTA_LEFT); - m_tool->addWidgetToTopContainerHelper(blockDiagramBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(ad963xBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(blockDiagramBtn, TTA_LEFT); } FMCOMMS5::~FMCOMMS5() {} - QWidget *FMCOMMS5::generateRxChainWidget(iio_device *dev, QString title, QWidget *parent) { - QWidget *widget = new QWidget(parent); - Style::setBackgroundColor(widget, json::theme::background_primary); - Style::setStyle(widget, style::properties::widget::border_interactive); - - QVBoxLayout *mainLayout = new QVBoxLayout(widget); - widget->setLayout(mainLayout); - - QLabel *titleLabel = new QLabel(title, widget); - Style::setStyle(titleLabel, style::properties::label::menuBig); - mainLayout->addWidget(titleLabel); - - if(dev == nullptr) { - qWarning(CAT_FMCOMMS5) << "No FMCOMMS5 device found"; - } - - QGridLayout *layout = new QGridLayout(); - - bool isOutput = false; - - iio_channel *voltage0 = iio_device_find_channel(dev, "voltage0", isOutput); - - // voltage0: rf_bandwidth - IIOWidget *rfBandwidth = IIOWidgetBuilder(widget) - .channel(voltage0) - .attribute("rf_bandwidth") - .optionsAttribute("rf_bandwidth_available") - .title("RF Bandwidth(MHz)") - .uiStrategy(IIOWidgetBuilder::RangeUi) - .buildSingle(); - layout->addWidget(rfBandwidth, 0, 0, 2, 1); - connect(this, &FMCOMMS5::readRequested, rfBandwidth, &IIOWidget::readAsync); - - // voltage0: sampling_frequency - IIOWidget *samplingFrequency = IIOWidgetBuilder(widget) - .channel(voltage0) - .attribute("sampling_frequency") - .optionsAttribute("sampling_frequency_available") - .title("Sampling Rate(MSPS)") - .uiStrategy(IIOWidgetBuilder::RangeUi) - .buildSingle(); - layout->addWidget(samplingFrequency, 0, 1, 2, 1); - connect(this, &FMCOMMS5::readRequested, samplingFrequency, &IIOWidget::readAsync); - - // voltage 0 : rf_port_select - IIOWidget *rfPortSelect = IIOWidgetBuilder(widget) - .channel(voltage0) - .attribute("rf_port_select") - .optionsAttribute("rf_port_select_available") - .title("RF Port Select") - .uiStrategy(IIOWidgetBuilder::ComboUi) - .buildSingle(); - layout->addWidget(rfPortSelect, 0, 2, 2, 1); - connect(this, &FMCOMMS5::readRequested, rfPortSelect, &IIOWidget::readAsync); - - // quadrature_tracking_en - IIOWidget *quadratureTrackingEn = IIOWidgetBuilder(this) - .channel(voltage0) - .attribute("quadrature_tracking_en") - .uiStrategy(IIOWidgetBuilder::CheckBoxUi) - .title("Quadrature") - .buildSingle(); - layout->addWidget(quadratureTrackingEn, 0, 5); - quadratureTrackingEn->showProgressBar(false); - connect(this, &FMCOMMS5::readRequested, quadratureTrackingEn, &IIOWidget::readAsync); - - // rf_dc_offset_tracking_en - IIOWidget *rcDcOffsetTrackingEn = IIOWidgetBuilder(widget) - .channel(voltage0) - .attribute("rf_dc_offset_tracking_en") - .uiStrategy(IIOWidgetBuilder::CheckBoxUi) - .title("RF DC") - .buildSingle(); - layout->addWidget(rcDcOffsetTrackingEn, 1, 5); - rcDcOffsetTrackingEn->showProgressBar(false); - connect(this, &FMCOMMS5::readRequested, rcDcOffsetTrackingEn, &IIOWidget::readAsync); - - // bb_dc_offset_tracking_en - IIOWidget *bbDcOffsetTrackingEn = IIOWidgetBuilder(widget) - .channel(voltage0) - .attribute("bb_dc_offset_tracking_en") - .title("BB DC") - .uiStrategy(IIOWidgetBuilder::CheckBoxUi) - .buildSingle(); - layout->addWidget(bbDcOffsetTrackingEn, 2, 5); - bbDcOffsetTrackingEn->showProgressBar(false); - connect(this, &FMCOMMS5::readRequested, bbDcOffsetTrackingEn, &IIOWidget::readAsync); - - mainLayout->addLayout(layout); - - QHBoxLayout *rxDeviceLayout = new QHBoxLayout(); - rxDeviceLayout->setMargin(0); - rxDeviceLayout->setSpacing(10); - - QWidget *rxDeviceWidget = m_helper->generateRxDeviceWidget(dev, "ad9361-phy", widget); - - rxDeviceLayout->addWidget(rxDeviceWidget); - rxDeviceWidget->layout()->addWidget(m_helper->generateRxChannelWidget(voltage0, "RX 1", rxDeviceWidget)); - iio_channel *voltage1 = iio_device_find_channel(dev, "voltage1", isOutput); - if(voltage1 && iio_channel_find_attr(voltage1, "hardwaregain")) { - rxDeviceWidget->layout()->addWidget( - m_helper->generateRxChannelWidget(voltage1, "RX 2", rxDeviceWidget)); - } - - //////// second device for fmcomms5 RX - - iio_device *dev2 = nullptr; - int device_count = iio_context_get_devices_count(m_ctx); - for(int i = 0; i < device_count; ++i) { - iio_device *aux = iio_context_get_device(m_ctx, i); - const char *dev_name = iio_device_get_name(aux); - if(dev_name && QString(dev_name).contains("ad9361-phy-b", Qt::CaseInsensitive)) { - dev2 = aux; - break; - } - } - - QWidget *rxDevice2Widget = m_helper->generateRxDeviceWidget(dev2, "ad9361-phy-B", widget); - - rxDeviceLayout->addWidget(rxDevice2Widget); - - iio_channel *voltage0B = iio_device_find_channel(dev2, "voltage0", isOutput); - - rxDevice2Widget->layout()->addWidget(m_helper->generateRxChannelWidget(voltage0B, "RX 3", rxDevice2Widget)); - iio_channel *voltage1B = iio_device_find_channel(dev2, "voltage1", isOutput); - if(voltage1 && iio_channel_find_attr(voltage1B, "hardwaregain")) { - rxDevice2Widget->layout()->addWidget(m_helper->generateRxChannelWidget(voltage1B, "RX 4", rxDevice2Widget)); - } - - ///////////////////////// - - mainLayout->addLayout(rxDeviceLayout); - mainLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); - - return widget; + QWidget *widget = new QWidget(parent); + Style::setBackgroundColor(widget, json::theme::background_primary); + Style::setStyle(widget, style::properties::widget::border_interactive); + + QVBoxLayout *mainLayout = new QVBoxLayout(widget); + widget->setLayout(mainLayout); + + QLabel *titleLabel = new QLabel(title, widget); + Style::setStyle(titleLabel, style::properties::label::menuBig); + mainLayout->addWidget(titleLabel); + + if(dev == nullptr) { + qWarning(CAT_FMCOMMS5) << "No FMCOMMS5 device found"; + } + + QGridLayout *layout = new QGridLayout(); + + bool isOutput = false; + + iio_channel *voltage0 = iio_device_find_channel(dev, "voltage0", isOutput); + + // voltage0: rf_bandwidth + IIOWidget *rfBandwidth = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("rf_bandwidth") + .optionsAttribute("rf_bandwidth_available") + .title("RF Bandwidth(MHz)") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .buildSingle(); + layout->addWidget(rfBandwidth, 0, 0, 2, 1); + connect(this, &FMCOMMS5::readRequested, rfBandwidth, &IIOWidget::readAsync); + + // voltage0: sampling_frequency + IIOWidget *samplingFrequency = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("sampling_frequency") + .optionsAttribute("sampling_frequency_available") + .title("Sampling Rate(MSPS)") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .buildSingle(); + layout->addWidget(samplingFrequency, 0, 1, 2, 1); + connect(this, &FMCOMMS5::readRequested, samplingFrequency, &IIOWidget::readAsync); + + // voltage 0 : rf_port_select + IIOWidget *rfPortSelect = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("rf_port_select") + .optionsAttribute("rf_port_select_available") + .title("RF Port Select") + .uiStrategy(IIOWidgetBuilder::ComboUi) + .buildSingle(); + layout->addWidget(rfPortSelect, 0, 2, 2, 1); + connect(this, &FMCOMMS5::readRequested, rfPortSelect, &IIOWidget::readAsync); + + // quadrature_tracking_en + IIOWidget *quadratureTrackingEn = IIOWidgetBuilder(this) + .channel(voltage0) + .attribute("quadrature_tracking_en") + .uiStrategy(IIOWidgetBuilder::CheckBoxUi) + .title("Quadrature") + .buildSingle(); + layout->addWidget(quadratureTrackingEn, 0, 5); + quadratureTrackingEn->showProgressBar(false); + connect(this, &FMCOMMS5::readRequested, quadratureTrackingEn, &IIOWidget::readAsync); + + // rf_dc_offset_tracking_en + IIOWidget *rcDcOffsetTrackingEn = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("rf_dc_offset_tracking_en") + .uiStrategy(IIOWidgetBuilder::CheckBoxUi) + .title("RF DC") + .buildSingle(); + layout->addWidget(rcDcOffsetTrackingEn, 1, 5); + rcDcOffsetTrackingEn->showProgressBar(false); + connect(this, &FMCOMMS5::readRequested, rcDcOffsetTrackingEn, &IIOWidget::readAsync); + + // bb_dc_offset_tracking_en + IIOWidget *bbDcOffsetTrackingEn = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("bb_dc_offset_tracking_en") + .title("BB DC") + .uiStrategy(IIOWidgetBuilder::CheckBoxUi) + .buildSingle(); + layout->addWidget(bbDcOffsetTrackingEn, 2, 5); + bbDcOffsetTrackingEn->showProgressBar(false); + connect(this, &FMCOMMS5::readRequested, bbDcOffsetTrackingEn, &IIOWidget::readAsync); + + mainLayout->addLayout(layout); + + QHBoxLayout *rxDeviceLayout = new QHBoxLayout(); + rxDeviceLayout->setMargin(0); + rxDeviceLayout->setSpacing(10); + + QWidget *rxDeviceWidget = m_helper->generateRxDeviceWidget(dev, "ad9361-phy", widget); + + rxDeviceLayout->addWidget(rxDeviceWidget); + rxDeviceWidget->layout()->addWidget(m_helper->generateRxChannelWidget(voltage0, "RX 1", rxDeviceWidget)); + iio_channel *voltage1 = iio_device_find_channel(dev, "voltage1", isOutput); + if(voltage1 && iio_channel_find_attr(voltage1, "hardwaregain")) { + rxDeviceWidget->layout()->addWidget( + m_helper->generateRxChannelWidget(voltage1, "RX 2", rxDeviceWidget)); + } + + //////// second device for fmcomms5 RX + + iio_device *dev2 = nullptr; + int device_count = iio_context_get_devices_count(m_ctx); + for(int i = 0; i < device_count; ++i) { + iio_device *aux = iio_context_get_device(m_ctx, i); + const char *dev_name = iio_device_get_name(aux); + if(dev_name && QString(dev_name).contains("ad9361-phy-b", Qt::CaseInsensitive)) { + dev2 = aux; + break; + } + } + + QWidget *rxDevice2Widget = m_helper->generateRxDeviceWidget(dev2, "ad9361-phy-B", widget); + + rxDeviceLayout->addWidget(rxDevice2Widget); + + iio_channel *voltage0B = iio_device_find_channel(dev2, "voltage0", isOutput); + + rxDevice2Widget->layout()->addWidget(m_helper->generateRxChannelWidget(voltage0B, "RX 3", rxDevice2Widget)); + iio_channel *voltage1B = iio_device_find_channel(dev2, "voltage1", isOutput); + if(voltage1 && iio_channel_find_attr(voltage1B, "hardwaregain")) { + rxDevice2Widget->layout()->addWidget( + m_helper->generateRxChannelWidget(voltage1B, "RX 4", rxDevice2Widget)); + } + + ///////////////////////// + + mainLayout->addLayout(rxDeviceLayout); + mainLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); + + return widget; } QWidget *FMCOMMS5::generateTxChainWidget(iio_device *dev, QString title, QWidget *parent) { - QWidget *widget = new QWidget(parent); - Style::setBackgroundColor(widget, json::theme::background_primary); - Style::setStyle(widget, style::properties::widget::border_interactive); - - QVBoxLayout *layout = new QVBoxLayout(widget); - widget->setLayout(layout); - - QLabel *titleLabel = new QLabel(title, widget); - Style::setStyle(titleLabel, style::properties::label::menuBig); - layout->addWidget(titleLabel); - - QGridLayout *lay = new QGridLayout(); - - bool isOutput = true; - iio_channel *voltage0 = iio_device_find_channel(dev, "voltage0", isOutput); - - // voltage0: rf_bandwidth - IIOWidget *rfBandwidth = IIOWidgetBuilder(widget) - .channel(voltage0) - .attribute("rf_bandwidth") - .optionsAttribute("rf_bandwidth_available") - .uiStrategy(IIOWidgetBuilder::RangeUi) - .title("RF Bandwidth(MHz)") - .buildSingle(); - lay->addWidget(rfBandwidth, 0, 0, 2, 1); - connect(this, &FMCOMMS5::readRequested, rfBandwidth, &IIOWidget::readAsync); - - // voltage0: sampling_frequency - IIOWidget *samplingFrequency = IIOWidgetBuilder(widget) - .channel(voltage0) - .attribute("sampling_frequency") - .optionsAttribute("sampling_frequency_available") - .uiStrategy(IIOWidgetBuilder::RangeUi) - .title("Sampling Rate(MSPS)") - .buildSingle(); - lay->addWidget(samplingFrequency, 0, 1, 2, 1); - connect(this, &FMCOMMS5::readRequested, samplingFrequency, &IIOWidget::readAsync); - - // voltage0: rf_port_select - IIOWidget *rfPortSelect = IIOWidgetBuilder(widget) - .channel(voltage0) - .attribute("rf_port_select") - .optionsAttribute("rf_port_select_available") - .uiStrategy(IIOWidgetBuilder::ComboUi) - .title("RF Port Select") - .buildSingle(); - lay->addWidget(rfPortSelect, 0, 2, 2, 1); - connect(this, &FMCOMMS5::readRequested, rfPortSelect, &IIOWidget::readAsync); - - layout->addLayout(lay); - - QHBoxLayout *txWidgetsLayout = new QHBoxLayout(); - txWidgetsLayout->setMargin(0); - txWidgetsLayout->setSpacing(10); - - QWidget *txDeviceWidget = m_helper->generateTxDeviceWidget(dev, "ad9361-phy", widget); - - txWidgetsLayout->addWidget(txDeviceWidget); - - txDeviceWidget->layout()->addWidget(m_helper->generateTxChannelWidget(voltage0, "TX 1", txDeviceWidget)); - iio_channel *voltage1 = iio_device_find_channel(dev, "voltage1", isOutput); - if(voltage1 && iio_channel_find_attr(voltage1, "hardwaregain")) { - txDeviceWidget->layout()->addWidget( - m_helper->generateTxChannelWidget(voltage1, "TX 2", txDeviceWidget)); - } - - //////// second device for fmcomms5 TX - - iio_device *dev2 = nullptr; - int device_count = iio_context_get_devices_count(m_ctx); - for(int i = 0; i < device_count; ++i) { - iio_device *aux = iio_context_get_device(m_ctx, i); - const char *dev_name = iio_device_get_name(aux); - if(dev_name && QString(dev_name).contains("ad9361-phy-b", Qt::CaseInsensitive)) { - dev2 = aux; - break; - } - } - - QWidget *txDevice2Widget = m_helper->generateTxDeviceWidget(dev2, "ad9361-phy-B", widget); - - txWidgetsLayout->addWidget(txDevice2Widget); - - iio_channel *voltage0B = iio_device_find_channel(dev2, "voltage0", isOutput); - - txDevice2Widget->layout()->addWidget(m_helper->generateTxChannelWidget(voltage0B, "TX 3", txDevice2Widget)); - iio_channel *voltage1B = iio_device_find_channel(dev2, "voltage1", isOutput); - if(voltage1 && iio_channel_find_attr(voltage1B, "hardwaregain")) { - txDevice2Widget->layout()->addWidget(m_helper->generateTxChannelWidget(voltage1B, "TX 4", txDevice2Widget)); - } - - ///////////////////////// - - layout->addLayout(txWidgetsLayout); - layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); - - return widget; + QWidget *widget = new QWidget(parent); + Style::setBackgroundColor(widget, json::theme::background_primary); + Style::setStyle(widget, style::properties::widget::border_interactive); + + QVBoxLayout *layout = new QVBoxLayout(widget); + widget->setLayout(layout); + + QLabel *titleLabel = new QLabel(title, widget); + Style::setStyle(titleLabel, style::properties::label::menuBig); + layout->addWidget(titleLabel); + + QGridLayout *lay = new QGridLayout(); + + bool isOutput = true; + iio_channel *voltage0 = iio_device_find_channel(dev, "voltage0", isOutput); + + // voltage0: rf_bandwidth + IIOWidget *rfBandwidth = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("rf_bandwidth") + .optionsAttribute("rf_bandwidth_available") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .title("RF Bandwidth(MHz)") + .buildSingle(); + lay->addWidget(rfBandwidth, 0, 0, 2, 1); + connect(this, &FMCOMMS5::readRequested, rfBandwidth, &IIOWidget::readAsync); + + // voltage0: sampling_frequency + IIOWidget *samplingFrequency = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("sampling_frequency") + .optionsAttribute("sampling_frequency_available") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .title("Sampling Rate(MSPS)") + .buildSingle(); + lay->addWidget(samplingFrequency, 0, 1, 2, 1); + connect(this, &FMCOMMS5::readRequested, samplingFrequency, &IIOWidget::readAsync); + + // voltage0: rf_port_select + IIOWidget *rfPortSelect = IIOWidgetBuilder(widget) + .channel(voltage0) + .attribute("rf_port_select") + .optionsAttribute("rf_port_select_available") + .uiStrategy(IIOWidgetBuilder::ComboUi) + .title("RF Port Select") + .buildSingle(); + lay->addWidget(rfPortSelect, 0, 2, 2, 1); + connect(this, &FMCOMMS5::readRequested, rfPortSelect, &IIOWidget::readAsync); + + layout->addLayout(lay); + + QHBoxLayout *txWidgetsLayout = new QHBoxLayout(); + txWidgetsLayout->setMargin(0); + txWidgetsLayout->setSpacing(10); + + QWidget *txDeviceWidget = m_helper->generateTxDeviceWidget(dev, "ad9361-phy", widget); + + txWidgetsLayout->addWidget(txDeviceWidget); + + txDeviceWidget->layout()->addWidget(m_helper->generateTxChannelWidget(voltage0, "TX 1", txDeviceWidget)); + iio_channel *voltage1 = iio_device_find_channel(dev, "voltage1", isOutput); + if(voltage1 && iio_channel_find_attr(voltage1, "hardwaregain")) { + txDeviceWidget->layout()->addWidget( + m_helper->generateTxChannelWidget(voltage1, "TX 2", txDeviceWidget)); + } + + //////// second device for fmcomms5 TX + + iio_device *dev2 = nullptr; + int device_count = iio_context_get_devices_count(m_ctx); + for(int i = 0; i < device_count; ++i) { + iio_device *aux = iio_context_get_device(m_ctx, i); + const char *dev_name = iio_device_get_name(aux); + if(dev_name && QString(dev_name).contains("ad9361-phy-b", Qt::CaseInsensitive)) { + dev2 = aux; + break; + } + } + + QWidget *txDevice2Widget = m_helper->generateTxDeviceWidget(dev2, "ad9361-phy-B", widget); + + txWidgetsLayout->addWidget(txDevice2Widget); + + iio_channel *voltage0B = iio_device_find_channel(dev2, "voltage0", isOutput); + + txDevice2Widget->layout()->addWidget(m_helper->generateTxChannelWidget(voltage0B, "TX 3", txDevice2Widget)); + iio_channel *voltage1B = iio_device_find_channel(dev2, "voltage1", isOutput); + if(voltage1 && iio_channel_find_attr(voltage1B, "hardwaregain")) { + txDevice2Widget->layout()->addWidget( + m_helper->generateTxChannelWidget(voltage1B, "TX 4", txDevice2Widget)); + } + + ///////////////////////// + + layout->addLayout(txWidgetsLayout); + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); + + return widget; } diff --git a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp index 79b7bd2bca..65c7a949d6 100644 --- a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp +++ b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp @@ -1,3 +1,24 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * 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, either 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + #include "fmcomms5/fmcomms5advanced.h" #include @@ -9,145 +30,161 @@ #include #include +#include + Q_LOGGING_CATEGORY(CAT_FMCOMMS5_ADVANCED, "FMCOMMS5_ADVANCED") using namespace scopy; using namespace ad936x; -Fmcomms5Advanced::Fmcomms5Advanced(iio_context *ctx,QWidget *parent) - : m_ctx(ctx) - , QWidget{parent} +Fmcomms5Advanced::Fmcomms5Advanced(iio_context *ctx, QWidget *parent) + : m_ctx(ctx) + , QWidget{parent} { - m_mainLayout = new QVBoxLayout(this); - m_mainLayout->setMargin(0); - m_mainLayout->setContentsMargins(0, 0, 0, 0); - - m_tool = new ToolTemplate(this); - m_tool->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - m_tool->topContainer()->setVisible(true); - m_tool->bottomContainer()->setVisible(true); - m_tool->topContainerMenuControl()->setVisible(false); - - m_mainLayout->addWidget(m_tool); - - m_refreshButton = new AnimatedRefreshBtn(false, this); - m_tool->addWidgetToTopContainerHelper(m_refreshButton, TTA_RIGHT); - - connect(m_refreshButton, &QPushButton::clicked, this, [this]() { - m_refreshButton->startAnimation(); - - QFutureWatcher *watcher = new QFutureWatcher(this); - connect( - watcher, &QFutureWatcher::finished, this, - [this, watcher]() { - m_refreshButton->stopAnimation(); - watcher->deleteLater(); - }, - Qt::QueuedConnection); - - QFuture future = QtConcurrent::run([this]() { Q_EMIT readRequested(); }); - - watcher->setFuture(future); - }); - - // main widget body - - m_isToolInitialized = false; - - QStackedWidget *centralWidget = new QStackedWidget(this); - m_tool->addWidgetToCentralContainerHelper(centralWidget); - - QButtonGroup *navigationButtons = new QButtonGroup(this); - navigationButtons->setExclusive(true); - - if(m_ctx != nullptr) { - iio_device *plutoDevice = nullptr; - int device_count = iio_context_get_devices_count(ctx); - for(int i = 0; i < device_count; ++i) { - iio_device *dev = iio_context_get_device(ctx, i); - const char *dev_name = iio_device_get_name(dev); - if(dev_name && QString(dev_name).contains("ad936", Qt::CaseInsensitive)) { - plutoDevice = dev; - break; - } - } - if(plutoDevice == nullptr) { - qWarning(CAT_FMCOMMS5_ADVANCED) << "No AD936x device found in context!"; - return; - } - - m_plutoDevice = plutoDevice; - m_centralWidget = centralWidget; - - // Create buttons - m_ensmModeClocksBtn = new QPushButton("ENSM/Mode/Clocks", this); - Style::setStyle(m_ensmModeClocksBtn, style::properties::button::blueGrayButton); - m_ensmModeClocksBtn->setCheckable(true); - m_ensmModeClocksBtn->setChecked(true); - m_eLnaBtn = new QPushButton("eLNA", this); - Style::setStyle(m_eLnaBtn, style::properties::button::blueGrayButton); - m_eLnaBtn->setCheckable(true); - m_rssiBtn = new QPushButton("RSSI", this); - Style::setStyle(m_rssiBtn, style::properties::button::blueGrayButton); - m_rssiBtn->setCheckable(true); - m_gainBtn = new QPushButton("GAIN", this); - Style::setStyle(m_gainBtn, style::properties::button::blueGrayButton); - m_gainBtn->setCheckable(true); - m_txMonitorBtn = new QPushButton("TX MONITOR", this); - Style::setStyle(m_txMonitorBtn, style::properties::button::blueGrayButton); - m_txMonitorBtn->setCheckable(true); - m_auxAdcDacIioBtn = new QPushButton("Aux ADC/DAC/IIO", this); - Style::setStyle(m_auxAdcDacIioBtn, style::properties::button::blueGrayButton); - m_auxAdcDacIioBtn->setCheckable(true); - m_miscBtn = new QPushButton("MISC", this); - Style::setStyle(m_miscBtn, style::properties::button::blueGrayButton); - m_miscBtn->setCheckable(true); - m_bistBtn = new QPushButton("BIST", this); - Style::setStyle(m_bistBtn, style::properties::button::blueGrayButton); - m_bistBtn->setCheckable(true); - - m_fmcomms5Btn = new QPushButton("FMCOMMS5", this); - Style::setStyle(m_fmcomms5Btn, style::properties::button::blueGrayButton); - m_fmcomms5Btn->setCheckable(true); - - navigationButtons->addButton(m_ensmModeClocksBtn); - navigationButtons->addButton(m_eLnaBtn); - navigationButtons->addButton(m_rssiBtn); - navigationButtons->addButton(m_gainBtn); - navigationButtons->addButton(m_txMonitorBtn); - navigationButtons->addButton(m_auxAdcDacIioBtn); - navigationButtons->addButton(m_miscBtn); - navigationButtons->addButton(m_bistBtn); - navigationButtons->addButton(m_fmcomms5Btn); - - m_tool->addWidgetToTopContainerHelper(m_ensmModeClocksBtn, TTA_LEFT); - m_tool->addWidgetToTopContainerHelper(m_eLnaBtn, TTA_LEFT); - m_tool->addWidgetToTopContainerHelper(m_rssiBtn, TTA_LEFT); - m_tool->addWidgetToTopContainerHelper(m_gainBtn, TTA_LEFT); - m_tool->addWidgetToTopContainerHelper(m_txMonitorBtn, TTA_LEFT); - m_tool->addWidgetToTopContainerHelper(m_auxAdcDacIioBtn, TTA_LEFT); - m_tool->addWidgetToTopContainerHelper(m_miscBtn, TTA_LEFT); - m_tool->addWidgetToTopContainerHelper(m_bistBtn, TTA_LEFT); - m_tool->addWidgetToTopContainerHelper(m_fmcomms5Btn, TTA_LEFT); - - - m_syncBtn = new QPushButton("MSC Sync", this); - Style::setStyle(m_syncBtn, style::properties::button::blueGrayButton); - m_syncBtn->setCheckable(true); - - m_saveSettingsBtn = new QPushButton("Save Settings", this); - Style::setStyle(m_saveSettingsBtn, style::properties::button::blueGrayButton); - m_saveSettingsBtn->setCheckable(true); - - - m_tool->addWidgetToBottomContainerHelper(m_syncBtn, TTA_LEFT); - m_tool->addWidgetToBottomContainerHelper(m_saveSettingsBtn, TTA_LEFT); - - bool useLazyLoading = scopy::Preferences::get("iiowidgets_use_lazy_loading").toBool(); - if(!useLazyLoading) { - init(); - } - } + m_mainLayout = new QVBoxLayout(this); + m_mainLayout->setMargin(0); + m_mainLayout->setContentsMargins(0, 0, 0, 0); + + m_tool = new ToolTemplate(this); + m_tool->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_tool->topContainer()->setVisible(true); + m_tool->bottomContainer()->setVisible(true); + m_tool->topContainerMenuControl()->setVisible(false); + + m_mainLayout->addWidget(m_tool); + + m_refreshButton = new AnimatedRefreshBtn(false, this); + m_tool->addWidgetToTopContainerHelper(m_refreshButton, TTA_RIGHT); + + connect(m_refreshButton, &QPushButton::clicked, this, [this]() { + m_refreshButton->startAnimation(); + + QFutureWatcher *watcher = new QFutureWatcher(this); + connect( + watcher, &QFutureWatcher::finished, this, + [this, watcher]() { + m_refreshButton->stopAnimation(); + watcher->deleteLater(); + }, + Qt::QueuedConnection); + + QFuture future = QtConcurrent::run([this]() { Q_EMIT readRequested(); }); + + watcher->setFuture(future); + }); + + // main widget body + + m_isToolInitialized = false; + + QStackedWidget *centralWidget = new QStackedWidget(this); + m_tool->addWidgetToCentralContainerHelper(centralWidget); + + QButtonGroup *navigationButtons = new QButtonGroup(this); + navigationButtons->setExclusive(true); + + if(m_ctx != nullptr) { + iio_device *plutoDevice = nullptr; + iio_device *plutoDeviceB = nullptr; + int foundDevices = 0; + int device_count = iio_context_get_devices_count(m_ctx); + for(int i = 0; i < device_count; ++i) { + iio_device *dev = iio_context_get_device(m_ctx, i); + const char *dev_name = iio_device_get_name(dev); + if(dev_name && QString(dev_name).compare("ad9361-phy", Qt::CaseInsensitive) == 0) { + plutoDevice = dev; + foundDevices++; + } + + if(dev_name && QString(dev_name).compare("ad9361-phy-B", Qt::CaseInsensitive) == 0) { + plutoDeviceB = dev; + foundDevices++; + } + + if(foundDevices == 2) { + break; + } + } + if(plutoDevice == nullptr) { + qWarning(CAT_FMCOMMS5_ADVANCED) << "No ad9361-phy device found in context!"; + return; + } + + if(plutoDeviceB == nullptr) { + qWarning(CAT_FMCOMMS5_ADVANCED) << "No ad9361-phy-B device found in context!"; + return; + } + + m_plutoDevice = plutoDevice; + m_plutoDeviceB = plutoDeviceB; + m_centralWidget = centralWidget; + + // Create buttons + m_ensmModeClocksBtn = new QPushButton("ENSM/Mode/Clocks", this); + Style::setStyle(m_ensmModeClocksBtn, style::properties::button::blueGrayButton); + m_ensmModeClocksBtn->setCheckable(true); + m_ensmModeClocksBtn->setChecked(true); + m_eLnaBtn = new QPushButton("eLNA", this); + Style::setStyle(m_eLnaBtn, style::properties::button::blueGrayButton); + m_eLnaBtn->setCheckable(true); + m_rssiBtn = new QPushButton("RSSI", this); + Style::setStyle(m_rssiBtn, style::properties::button::blueGrayButton); + m_rssiBtn->setCheckable(true); + m_gainBtn = new QPushButton("GAIN", this); + Style::setStyle(m_gainBtn, style::properties::button::blueGrayButton); + m_gainBtn->setCheckable(true); + m_txMonitorBtn = new QPushButton("TX MONITOR", this); + Style::setStyle(m_txMonitorBtn, style::properties::button::blueGrayButton); + m_txMonitorBtn->setCheckable(true); + m_auxAdcDacIioBtn = new QPushButton("Aux ADC/DAC/IIO", this); + Style::setStyle(m_auxAdcDacIioBtn, style::properties::button::blueGrayButton); + m_auxAdcDacIioBtn->setCheckable(true); + m_miscBtn = new QPushButton("MISC", this); + Style::setStyle(m_miscBtn, style::properties::button::blueGrayButton); + m_miscBtn->setCheckable(true); + m_bistBtn = new QPushButton("BIST", this); + Style::setStyle(m_bistBtn, style::properties::button::blueGrayButton); + m_bistBtn->setCheckable(true); + + m_fmcomms5Btn = new QPushButton("FMCOMMS5", this); + Style::setStyle(m_fmcomms5Btn, style::properties::button::blueGrayButton); + m_fmcomms5Btn->setCheckable(true); + + navigationButtons->addButton(m_ensmModeClocksBtn); + navigationButtons->addButton(m_eLnaBtn); + navigationButtons->addButton(m_rssiBtn); + navigationButtons->addButton(m_gainBtn); + navigationButtons->addButton(m_txMonitorBtn); + navigationButtons->addButton(m_auxAdcDacIioBtn); + navigationButtons->addButton(m_miscBtn); + navigationButtons->addButton(m_bistBtn); + navigationButtons->addButton(m_fmcomms5Btn); + + m_tool->addWidgetToTopContainerHelper(m_ensmModeClocksBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_eLnaBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_rssiBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_gainBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_txMonitorBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_auxAdcDacIioBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_miscBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_bistBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(m_fmcomms5Btn, TTA_LEFT); + + m_syncBtn = new QPushButton("MSC Sync", this); + Style::setStyle(m_syncBtn, style::properties::button::basicButton); + m_syncBtn->setCheckable(true); + connect(m_syncBtn, &QPushButton::clicked, this, [=]() { + ad9361_multichip_sync(m_plutoDevice, &m_plutoDeviceB, 1, + FIXUP_INTERFACE_TIMING | CHECK_SAMPLE_RATES); + }); + + m_tool->addWidgetToBottomContainerHelper(m_syncBtn, TTA_LEFT); + + bool useLazyLoading = scopy::Preferences::get("iiowidgets_use_lazy_loading").toBool(); + if(!useLazyLoading) { + init(); + } + } } Fmcomms5Advanced::~Fmcomms5Advanced() {} @@ -155,63 +192,71 @@ Fmcomms5Advanced::~Fmcomms5Advanced() {} void Fmcomms5Advanced::showEvent(QShowEvent *event) { - if(!m_isToolInitialized) { - bool useLazyLoading = scopy::Preferences::get("iiowidgets_use_lazy_loading").toBool(); - if(useLazyLoading) { - init(); - } - } - QWidget::showEvent(event); + if(!m_isToolInitialized) { + bool useLazyLoading = scopy::Preferences::get("iiowidgets_use_lazy_loading").toBool(); + if(useLazyLoading) { + init(); + } + } + QWidget::showEvent(event); } - void Fmcomms5Advanced::init() { - // ENSM Mode Clocks - m_ensmModeClocks = new EnsmModeClocksWidget(m_plutoDevice, m_centralWidget); - m_centralWidget->addWidget(m_ensmModeClocks); - connect(this, &Fmcomms5Advanced::readRequested, m_ensmModeClocks, &EnsmModeClocksWidget::readRequested); - connect(m_ensmModeClocksBtn, &QPushButton::clicked, this, - [=, this]() { m_centralWidget->setCurrentWidget(m_ensmModeClocks); }); - // eLNA - m_elna = new ElnaWidget(m_plutoDevice, m_centralWidget); - connect(this, &Fmcomms5Advanced::readRequested, m_elna, &ElnaWidget::readRequested); - m_centralWidget->addWidget(m_elna); - connect(m_eLnaBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_elna); }); - // RSSI - m_rssi = new RssiWidget(m_plutoDevice, m_centralWidget); - connect(this, &Fmcomms5Advanced::readRequested, m_rssi, &RssiWidget::readRequested); - m_centralWidget->addWidget(m_rssi); - connect(m_rssiBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_rssi); }); - // GAIN - m_gainWidget = new GainWidget(m_plutoDevice, m_centralWidget); - connect(this, &Fmcomms5Advanced::readRequested, m_gainWidget, &GainWidget::readRequested); - m_centralWidget->addWidget(m_gainWidget); - connect(m_gainBtn, &QPushButton::clicked, this, - [=, this]() { m_centralWidget->setCurrentWidget(m_gainWidget); }); - // TX MONITOR - m_txMonitor = new TxMonitorWidget(m_plutoDevice, m_centralWidget); - connect(this, &Fmcomms5Advanced::readRequested, m_txMonitor, &TxMonitorWidget::readRequested); - m_centralWidget->addWidget(m_txMonitor); - connect(m_txMonitorBtn, &QPushButton::clicked, this, - [=, this]() { m_centralWidget->setCurrentWidget(m_txMonitor); }); - // AUX ADC/DAC/IIO - m_auxAdcDacIo = new AuxAdcDacIoWidget(m_plutoDevice, m_centralWidget); - connect(this, &Fmcomms5Advanced::readRequested, m_auxAdcDacIo, &AuxAdcDacIoWidget::readRequested); - m_centralWidget->addWidget(m_auxAdcDacIo); - connect(m_auxAdcDacIioBtn, &QPushButton::clicked, this, - [=, this]() { m_centralWidget->setCurrentWidget(m_auxAdcDacIo); }); - // MISC - m_misc = new MiscWidget(m_plutoDevice, m_centralWidget); - connect(this, &Fmcomms5Advanced::readRequested, m_misc, &MiscWidget::readRequested); - m_centralWidget->addWidget(m_misc); - connect(m_miscBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_misc); }); - // BIST - m_bist = new BistWidget(m_plutoDevice, m_centralWidget); - connect(this, &Fmcomms5Advanced::readRequested, m_bist, &BistWidget::readRequested); - m_centralWidget->addWidget(m_bist); - connect(m_bistBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_bist); }); - - m_isToolInitialized = true; + // ENSM Mode Clocks + m_ensmModeClocks = new EnsmModeClocksWidget(m_plutoDevice, m_centralWidget); + m_centralWidget->addWidget(m_ensmModeClocks); + connect(this, &Fmcomms5Advanced::readRequested, m_ensmModeClocks, &EnsmModeClocksWidget::readRequested); + connect(m_ensmModeClocksBtn, &QPushButton::clicked, this, + [=, this]() { m_centralWidget->setCurrentWidget(m_ensmModeClocks); }); + // eLNA + m_elna = new ElnaWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_elna, &ElnaWidget::readRequested); + m_centralWidget->addWidget(m_elna); + connect(m_eLnaBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_elna); }); + // RSSI + m_rssi = new RssiWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_rssi, &RssiWidget::readRequested); + m_centralWidget->addWidget(m_rssi); + connect(m_rssiBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_rssi); }); + // GAIN + m_gainWidget = new GainWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_gainWidget, &GainWidget::readRequested); + m_centralWidget->addWidget(m_gainWidget); + connect(m_gainBtn, &QPushButton::clicked, this, + [=, this]() { m_centralWidget->setCurrentWidget(m_gainWidget); }); + // TX MONITOR + m_txMonitor = new TxMonitorWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_txMonitor, &TxMonitorWidget::readRequested); + m_centralWidget->addWidget(m_txMonitor); + connect(m_txMonitorBtn, &QPushButton::clicked, this, + [=, this]() { m_centralWidget->setCurrentWidget(m_txMonitor); }); + // AUX ADC/DAC/IIO + m_auxAdcDacIo = new AuxAdcDacIoWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_auxAdcDacIo, &AuxAdcDacIoWidget::readRequested); + m_centralWidget->addWidget(m_auxAdcDacIo); + connect(m_auxAdcDacIioBtn, &QPushButton::clicked, this, + [=, this]() { m_centralWidget->setCurrentWidget(m_auxAdcDacIo); }); + // MISC + m_misc = new MiscWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_misc, &MiscWidget::readRequested); + m_centralWidget->addWidget(m_misc); + connect(m_miscBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_misc); }); + // BIST + m_bist = new BistWidget(m_plutoDevice, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_bist, &BistWidget::readRequested); + m_centralWidget->addWidget(m_bist); + connect(m_bistBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_bist); }); + + // FMCOMMS5 + m_fmcomms5 = new Fmcomms5Tab(m_ctx, m_centralWidget); + connect(this, &Fmcomms5Advanced::readRequested, m_fmcomms5, &Fmcomms5Tab::readRequested); + m_centralWidget->addWidget(m_fmcomms5); + connect(m_fmcomms5Btn, &QPushButton::clicked, this, + [=, this]() { m_centralWidget->setCurrentWidget(m_fmcomms5); }); + + m_isToolInitialized = true; } + +void Fmcomms5Advanced::ad9361MultichipSync() {} diff --git a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5tab.cpp b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5tab.cpp new file mode 100644 index 0000000000..3ab9cf9c21 --- /dev/null +++ b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5tab.cpp @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * 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, either 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "fmcomms5/fmcomms5tab.h" + +#include +#include +#include +#include + +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define CAP_DEVICE "cf-ad9361-lpc" +#define CAP_DEVICE_ALT "cf-ad9361-A" +#define CAP_SLAVE_DEVICE "cf-ad9361-B" +#define DDS_DEVICE "cf-ad9361-dds-core-lpc" +#define DDS_SLAVE_DEVICE "cf-ad9361-dds-core-B" + +Q_LOGGING_CATEGORY(CAT_FMCOMMS5_TAB, "FMCOMMS5_TAB") + +using namespace scopy; +using namespace ad936x; + +Fmcomms5Tab::Fmcomms5Tab(iio_context *ctx, QWidget *parent) + : m_ctx(ctx) + , QWidget{parent} +{ + Style::setBackgroundColor(this, json::theme::background_primary); + + m_layout = new QVBoxLayout(this); + m_layout->setMargin(0); + m_layout->setContentsMargins(0, 0, 0, 0); + setLayout(m_layout); + + QWidget *widget = new QWidget(this); + QVBoxLayout *layout = new QVBoxLayout(widget); + widget->setLayout(layout); + layout->setContentsMargins(5, 5, 5, 5); + layout->setSpacing(10); + + m_layout->addWidget(widget); + + Style::setStyle(widget, style::properties::widget::border_interactive); + + QLabel *title = new QLabel("CAL Switch Control", widget); + Style::setStyle(title, style::properties::label::menuBig); + layout->addWidget(title); + + initDevices(); + + QMap *calSwitchControlOptions = new QMap(); + calSwitchControlOptions->insert("0", "DISABLE"); + calSwitchControlOptions->insert("1", "TX1B_B->RX1C_B"); + calSwitchControlOptions->insert("2", "TX1B_A->RX1C_B"); + calSwitchControlOptions->insert("3", "TX1B_B->RX1C_A"); + calSwitchControlOptions->insert("4", "TX1B_A->RX1C_A"); + + auto values = calSwitchControlOptions->values(); + QString optionasData = ""; + for(int i = 0; i < values.size(); i++) { + optionasData += " " + values.at(i); + } + + IIOWidget *calSwitchControl = IIOWidgetBuilder(widget) + .device(m_device) + .attribute("calibration_switch_control") + .uiStrategy(IIOWidgetBuilder::ComboUi) + .optionsValues(optionasData) + .title("") + .buildSingle(); + layout->addWidget(calSwitchControl); + + calSwitchControl->setUItoDataConversion([this, calSwitchControlOptions](QString data) { + return IIOWidgetUtils::comboUiToDataConversionFunction(data, calSwitchControlOptions); + }); + calSwitchControl->setDataToUIConversion([this, calSwitchControlOptions](QString data) { + return IIOWidgetUtils::comboDataToUiConversionFunction(data, calSwitchControlOptions); + }); + + m_calibrateBtn = new QPushButton("Calibrate", this); + Style::setStyle(m_calibrateBtn, style::properties::button::basicButton); + connect(m_calibrateBtn, &QPushButton::clicked, this, &Fmcomms5Tab::calibrate); + + QPushButton *resetCalibrationBtn = new QPushButton("Reset Calibration", this); + Style::setStyle(resetCalibrationBtn, style::properties::button::basicButton); + connect(resetCalibrationBtn, &QPushButton::clicked, this, &Fmcomms5Tab::resetCalibration); + + QHBoxLayout *calibBtnLayout = new QHBoxLayout(); + calibBtnLayout->setSpacing(10); + + calibBtnLayout->addWidget(m_calibrateBtn); + calibBtnLayout->addWidget(resetCalibrationBtn); + layout->addLayout(calibBtnLayout); + + // ??? seems to be related to how calibration affects ui + // in iio-osc calls osc_plot_set_visible(plot_xcorr_4ch, false); + m_silentCalibration = new QCheckBox("Silent Calibration", this); + layout->addWidget(m_silentCalibration); + + m_calibProgressBar = new QProgressBar(this); + m_calibProgressBar->setRange(0, 100); + m_calibProgressBar->setValue(0); + m_calibProgressBar->setVisible(false); + + connect(m_silentCalibration, &QCheckBox::toggled, m_calibProgressBar, &QProgressBar::setVisible); + + layout->addWidget(m_calibProgressBar); + + // TODO REMOVE + m_silentCalibration->setChecked(true); + + // TX Phase + IIOWidget *txPhase = IIOWidgetBuilder(widget) + .device(m_device) + .attribute("calibration_switch_control") + .uiStrategy(IIOWidgetBuilder::RangeUi) + .optionsValues("[0 0.1 360]") + .title("TX Phase") + .buildSingle(); + layout->addWidget(txPhase); + + m_layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); +} + +Fmcomms5Tab::~Fmcomms5Tab() {} + +void Fmcomms5Tab::initDevices() +{ + iio_device *plutoDevice = nullptr; + iio_device *plutoDeviceB = nullptr; + int foundDevices = 0; + int device_count = iio_context_get_devices_count(m_ctx); + for(int i = 0; i < device_count; ++i) { + iio_device *dev = iio_context_get_device(m_ctx, i); + const char *dev_name = iio_device_get_name(dev); + if(dev_name && QString(dev_name).compare("ad9361-phy", Qt::CaseInsensitive) == 0) { + plutoDevice = dev; + foundDevices++; + } + + if(dev_name && QString(dev_name).compare("ad9361-phy-B", Qt::CaseInsensitive) == 0) { + plutoDeviceB = dev; + foundDevices++; + } + + if(foundDevices == 2) { + break; + } + } + if(plutoDevice == nullptr) { + qWarning(CAT_FMCOMMS5_TAB) << "No ad9361-phy device found in context!"; + return; + } + + if(plutoDeviceB == nullptr) { + qWarning(CAT_FMCOMMS5_TAB) << "No ad9361-phy-B device found in context!"; + return; + } + + m_device = plutoDevice; + m_deviceB = plutoDeviceB; +} + +void Fmcomms5Tab::calibrate() +{ + // https://github.com/analogdevicesinc/iio-oscilloscope/blob/7a672e3e3e86aeb4fea2e594acff844010afe6fa/plugins/fmcomms2_adv.c#L897C13-L897C23 + + m_calibrateBtn->setEnabled(false); + int ret = 0; + double rx_phase_lpc = 0, rx_phase_hpc = 0, tx_phase_hpc = 0; + long long cal_tone = 0, cal_freq = 0; + int samples = 0; + + iio_channel *in0 = iio_device_find_channel(m_device, "voltage0", false); + iio_channel *in0B = iio_device_find_channel(m_deviceB, "voltage0", false); + + if(!in0 || !in0B) { + qWarning(CAT_FMCOMMS5_TAB) << "Could not find channels"; + return; + } + + // find caputre core + iio_device *cf_ad9361_lpc = iio_context_find_device(m_ctx, CAP_DEVICE_ALT); + iio_device *cf_ad9361_hpc = iio_context_find_device(m_ctx, CAP_SLAVE_DEVICE); + + if(!cf_ad9361_lpc || !cf_ad9361_hpc) { + qWarning(CAT_FMCOMMS5_TAB) << "Could not find capture cores"; + return; + } + + // Find DDS master/slave devices + iio_device *dev_dds_master = iio_context_find_device(m_ctx, DDS_DEVICE); + iio_device *dev_dds_B = iio_context_find_device(m_ctx, DDS_SLAVE_DEVICE); + + if(!dev_dds_master || !dev_dds_B) { + qWarning(CAT_FMCOMMS5_TAB) << "Could not find dds cores"; + return; + } + + m_calibProgressBar->setValue(0); + + // Read calibration tone and frequency + iio_channel *dds_ch = iio_device_find_channel(dev_dds_master, "altvoltage0", true); + if(!dds_ch) { + if(!m_silentCalibration->isChecked()) + qWarning(CAT_FMCOMMS5_TAB) << "Could not find DDS channel"; + return; + } + iio_channel_attr_read_longlong(dds_ch, "frequency", &cal_tone); + iio_channel_attr_read_longlong(dds_ch, "sampling_frequency", &cal_freq); + + samples = getCalSamples(cal_tone, cal_freq); // Implement this helper + + // Turn off quadrature tracking + iio_channel_attr_write(in0, "quadrature_tracking_en", "0"); + iio_channel_attr_write(in0B, "quadrature_tracking_en", "0"); + + // Reset any Tx rotation to zero + trxPhaseRottation(cf_ad9361_lpc, 0.0); + trxPhaseRottation(cf_ad9361_hpc, 0.0); + + m_calibProgressBar->setValue(16); + + // Calibration sequence + // 1. Calibrate RX: TX1B_B (HPC) -> RX1C_B (HPC) + callSwitchPortsEnableCb(1); + rx_phase_hpc = + tuneTrxPhaseOffset(cf_ad9361_hpc, // cf_ad9361_hpc + &ret, // Error/status pointer + cal_freq, // Calibration frequency + cal_tone, // Calibration tone + 1.0, // Sign + 0.01, // Abort threshold + [this](iio_device *dev, double phase) { this->trxPhaseRottation(dev, phase); }); + + if(ret < 0) { + qWarning(CAT_FMCOMMS5_TAB) << "Failed to tune phase"; + return; + } + + m_calibProgressBar->setValue(40); + + // 2. Calibrate RX: TX1B_B (HPC) -> RX1C_A (LPC) + callSwitchPortsEnableCb(3); + trxPhaseRottation(m_device, 0.0); + rx_phase_lpc = + tuneTrxPhaseOffset(cf_ad9361_lpc, // cf_ad9361_lpc + &ret, // Error/status pointer + cal_freq, // Calibration frequency + cal_tone, // Calibration tone + 1.0, // Sign + 0.01, // Abort threshold + [this](iio_device *dev, double phase) { this->trxPhaseRottation(dev, phase); }); + + if(ret < 0) { + qWarning(CAT_FMCOMMS5_TAB) << "Failed to tune phase"; + return; + } + + m_calibProgressBar->setValue(64); + + // 3. Calibrate TX: TX1B_A (LPC) -> RX1C_A (LPC) + callSwitchPortsEnableCb(4); + trxPhaseRottation(m_device, 0.0); + tx_phase_hpc = + tuneTrxPhaseOffset(dev_dds_B, // dev_dds_B + &ret, // Error/status pointer + cal_freq, // Calibration frequency + cal_tone, // Calibration tone + -1.0, // Sign + 0.001, // Abort threshold + [this](iio_device *dev, double phase) { this->trxPhaseRottation(dev, phase); }); + + if(ret < 0) { + qWarning(CAT_FMCOMMS5_TAB) << "Failed to tune phase"; + return; + } + + m_calibProgressBar->setValue(88); + + // Restore phase rotation + trxPhaseRottation(cf_ad9361_hpc, rx_phase_hpc); + + // Restore calibration switch matrix to default + callSwitchPortsEnableCb(0); + + // Re-enable quadrature tracking + iio_channel_attr_write(in0, "quadrature_tracking_en", "1"); + iio_channel_attr_write(in0B, "quadrature_tracking_en", "1"); + + m_calibProgressBar->setValue(100); + + m_calibrateBtn->setEnabled(true); + + //////TODO FIUGRE THIS OUT ????? + /// calibrate_fail: + + // osc_plot_xcorr_revert(plot_xcorr_4ch, false); + // __cal_switch_ports_enable_cb(0); + + // if (in0 && in0_slave) { + // iio_channel_attr_write(in0, "quadrature_tracking_en", "1"); + // iio_channel_attr_write(in0_slave, "quadrature_tracking_en", "1"); + // } + + // calib_failed_param = malloc(sizeof(*calib_failed_param)); + // calib_failed_param->button = button; + // calib_failed_param->ret = ret; + // gdk_threads_add_idle(calibration_failed_ui, (gpointer) calib_failed_param); + + // /* reset progress bar */ + // gtk_progress_bar_set_fraction(calib_progress, 0.0); + // gtk_progress_bar_set_text(calib_progress, "Calibration Progress"); + + // /* Disable the channels that were enabled at the beginning of the calibration */ + // struct iio_device *iio_dev; + // iio_dev = iio_context_find_device(get_context_from_osc(), CAP_DEVICE_ALT); + // if (iio_dev && cap_device_channels_enabled) { + // iio_channels_change_shadow_of_enabled(iio_dev, false); + // cap_device_channels_enabled = false; + // } +} + +void Fmcomms5Tab::trxPhaseRottation(iio_device *dev, double val) +{ + // https://github.com/analogdevicesinc/iio-oscilloscope/blob/7a672e3e3e86aeb4fea2e594acff844010afe6fa/plugins/fmcomms2_adv.c#L526 + double phase = val * 2 * M_PI / 360.0; + double vcos = std::cos(phase); + double vsin = std::sin(phase); + + bool output = (dev == m_device) || (dev == m_deviceB); + + // Correction factor for output devices + if(output) { + double corr = 1.0 / + std::fmax(std::fabs(std::sin(phase) + std::cos(phase)), + std::fabs(std::cos(phase) - std::sin(phase))); + vcos *= corr; + vsin *= corr; + } + + // Set both RX1 and RX2 + for(unsigned offset = 0; offset <= 2; offset += 2) { + iio_channel *out0 = iio_device_find_channel(dev, offset == 2 ? "voltage2" : "voltage0", output); + iio_channel *out1 = iio_device_find_channel(dev, offset == 2 ? "voltage3" : "voltage1", output); + + if(out0 && out1) { + iio_channel_attr_write_double(out0, "calibscale", vcos); + iio_channel_attr_write_double(out0, "calibphase", -1.0 * vsin); + iio_channel_attr_write_double(out1, "calibscale", vcos); + iio_channel_attr_write_double(out1, "calibphase", vsin); + } + } +} + +int Fmcomms5Tab::getCalSamples(long long calTone, long long calFreq) +{ + int samples, env_samples; + /// ???? + const char *cal_samples = getenv("CAL_SAMPLES"); + + samples = std::exp2(std::ceil(log2(calFreq / calTone)) + 2); + + if(!cal_samples) + return samples; + + env_samples = std::atoi(cal_samples); + + if(env_samples < samples) + return samples; + + return env_samples; +} + +void Fmcomms5Tab::resetCalibration() +{ + iio_channel *in0 = iio_device_find_channel(m_device, "voltage0", false); + iio_channel *in0B = iio_device_find_channel(m_deviceB, "voltage0", false); + + // Reset calibration corrections to zero/default + iio_channel_attr_write(in0, "calibphase", "0"); + iio_channel_attr_write(in0B, "calibphase", "0"); + iio_channel_attr_write(in0, "calibscale", "0"); + iio_channel_attr_write(in0B, "calibscale", "0"); + + // Optionally reset calibration switch matrix as well + iio_device_attr_write(m_device, "calibration_switch_control", "0"); +} + +void Fmcomms5Tab::callSwitchPortsEnableCb(int val) +{ + // unsigned lp_slave, lp_master, sw; + // char *rx_port, *tx_port; + + // /* + // * 0 DISABLE + // * 1 TX1B_B (HPC) -> RX1C_B (HPC) : BIST_LOOPBACK on A + // * 2 TX1B_A (LPC) -> RX1C_B (HPC) : BIST_LOOPBACK on A + // * 3 TX1B_B (HPC) -> RX1C_A (LPC) : BIST_LOOPBACK on B + // * 4 TX1B_A (LPC) -> RX1C_A (LPC) : BIST_LOOPBACK on B + // * + // */ + // switch (val) { + // default: + // case 0: + // lp_slave = 0; + // lp_master = 0; + // sw = 0; + // tx_port = "A"; + // rx_port = "A_BALANCED"; + // break; + // case 1: + // case 2: + // lp_slave = 0; + // lp_master = 1; + // sw = val - 1; + // tx_port = "B"; + // rx_port = "C_BALANCED"; + // break; + // case 3: + // case 4: + // lp_slave = 1; + // lp_master = 0; + // sw = val - 1; + // tx_port = "B"; + // rx_port = "C_BALANCED"; + // break; + // } + + // #if 0 + // iio_device_debug_attr_write_bool(dev, "loopback", lp_master); + // iio_device_debug_attr_write_bool(dev_slave, "loopback", lp_slave); + // #else + // near_end_loopback_ctrl(0, lp_slave); /* HPC */ + // near_end_loopback_ctrl(1, lp_slave); /* HPC */ + + // near_end_loopback_ctrl(4, lp_master); /* LPC */ + // near_end_loopback_ctrl(5, lp_master); /* LPC */ + // #endif + // iio_device_debug_attr_write_longlong(dev, "calibration_switch_control", sw); + // iio_channel_attr_write(iio_device_find_channel(dev, "voltage0", false), + // "rf_port_select", rx_port); + // iio_channel_attr_write(iio_device_find_channel(dev, "voltage0", true), + // "rf_port_select", tx_port); + + // if (dev_slave) { + // iio_channel_attr_write(iio_device_find_channel(dev_slave, "voltage0", false), + // "rf_port_select", rx_port); + // iio_channel_attr_write(iio_device_find_channel(dev_slave, "voltage0", true), + // "rf_port_select", tx_port); + // } +} + +double Fmcomms5Tab::tuneTrxPhaseOffset(iio_device *ldev, int *ret, long long cal_freq, long long cal_tone, double sign, + double abort, std::function tune) +{ + // https://github.com/analogdevicesinc/iio-oscilloscope/blob/7a672e3e3e86aeb4fea2e594acff844010afe6fa/plugins/fmcomms2_adv.c#L755 + double offset = 0.0, mag = 0.0; + double phase = 0.0, increment = 0.0; + + for(int i = 0; i < 10; i++) { + getMarkers(&offset, &mag); // Implement this helper + getMarkers(&offset, &mag); + + increment = calcPhaseOffset(cal_freq, cal_tone, offset, mag); // Implement this helper + increment *= sign; + + phase += increment; + + phase = scalePhase0360(phase); // Implement this helper + tune(ldev, phase); + + qDebug() << "Step:" << i << "increment" << increment << "Phase:" << phase; + + if(std::fabs(offset) < 0.001) + break; + } + + if(std::fabs(offset) > 0.1) + *ret = -EFAULT; + else + *ret = 0; + + return phase * sign; +} + +void Fmcomms5Tab::getMarkers(double *offset, double *mag) +{ + /// ???? + /// https://github.com/analogdevicesinc/iio-oscilloscope/blob/7a672e3e3e86aeb4fea2e594acff844010afe6fa/plugins/fmcomms2_adv.c#L641 +} + +double Fmcomms5Tab::scalePhase0360(double val) +{ + if(val >= 360.0) + val -= 360.0; + + if(val < 0) + val += 360.0; + + return val; +} + +double Fmcomms5Tab::calcPhaseOffset(double fsample, double dds_freq, double offset, double mag) +{ + double val = 360.0 / ((fsample / dds_freq) / offset); + + if(mag < 0) + val += 180.0; + + return scalePhase0360(val); +} From bd41816282df584e93255b368ef1177951025e0c Mon Sep 17 00:00:00 2001 From: IonutMuthi Date: Thu, 28 Aug 2025 08:22:17 +0300 Subject: [PATCH 5/6] fmcomms5: add emu Signed-off-by: IonutMuthi --- packages/ad936x/CMakeLists.txt | 1 + packages/ad936x/emu-xml/emu_setup.json | 7 +++++++ packages/ad936x/emu-xml/fmcomms5.xml | 5 +++++ 3 files changed, 13 insertions(+) create mode 100644 packages/ad936x/emu-xml/emu_setup.json create mode 100644 packages/ad936x/emu-xml/fmcomms5.xml diff --git a/packages/ad936x/CMakeLists.txt b/packages/ad936x/CMakeLists.txt index d2a1435471..251ff7641b 100644 --- a/packages/ad936x/CMakeLists.txt +++ b/packages/ad936x/CMakeLists.txt @@ -32,6 +32,7 @@ message(STATUS "building package: " ${SCOPY_MODULE}) project(scopy-package-${SCOPY_MODULE} VERSION 0.1 LANGUAGES CXX) configure_file(manifest.json.cmakein ${SCOPY_PACKAGE_BUILD_PATH}/${SCOPY_MODULE}/MANIFEST.json @ONLY) +include_emu_xml(${CMAKE_CURRENT_SOURCE_DIR}/emu-xml ${SCOPY_PACKAGE_BUILD_PATH}/${SCOPY_MODULE}/emu-xml) include_resources(${CMAKE_CURRENT_SOURCE_DIR}/resources ${SCOPY_PACKAGE_BUILD_PATH}/${SCOPY_MODULE}/resources) message("Including plugins") if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/plugins) diff --git a/packages/ad936x/emu-xml/emu_setup.json b/packages/ad936x/emu-xml/emu_setup.json new file mode 100644 index 0000000000..432f1910dc --- /dev/null +++ b/packages/ad936x/emu-xml/emu_setup.json @@ -0,0 +1,7 @@ +[ + { + "device": "fmcomms5", + "xml_path": "fmcomms5.xml", + "uri": "ip:127.0.0.1" + } +] diff --git a/packages/ad936x/emu-xml/fmcomms5.xml b/packages/ad936x/emu-xml/fmcomms5.xml new file mode 100644 index 0000000000..29f8101747 --- /dev/null +++ b/packages/ad936x/emu-xml/fmcomms5.xml @@ -0,0 +1,5 @@ +]> From 5fcdd5c9e41f999a4b425bf8bb67c9af9638b0c1 Mon Sep 17 00:00:00 2001 From: IonutMuthi Date: Thu, 28 Aug 2025 17:18:20 +0300 Subject: [PATCH 6/6] fmcomms5: refactoring Signed-off-by: IonutMuthi --- .../ad936x/fmcomms5/fmcomms5advanced.h | 5 +- .../ad936x/fmcomms5/fmcomms5calibration.h | 98 +++ .../include/ad936x/fmcomms5/fmcomms5tab.h | 20 +- .../ad936x/resources/AD_FMCOMMS5_EBZ.jpg | Bin 0 -> 255484 bytes .../plugins/ad936x/resources/resources.qrc | 1 + .../plugins/ad936x/src/ad936x/ad936x.cpp | 2 +- .../plugins/ad936x/src/fmcomms5/fmcomms5.cpp | 98 +-- .../ad936x/src/fmcomms5/fmcomms5advanced.cpp | 37 +- .../src/fmcomms5/fmcomms5calibration.cpp | 611 ++++++++++++++++++ .../ad936x/src/fmcomms5/fmcomms5tab.cpp | 469 +------------- 10 files changed, 823 insertions(+), 518 deletions(-) create mode 100644 packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5calibration.h create mode 100644 packages/ad936x/plugins/ad936x/resources/AD_FMCOMMS5_EBZ.jpg create mode 100644 packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5calibration.cpp diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h index 0f41d7ddba..dd6625bad7 100644 --- a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h +++ b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5advanced.h @@ -70,6 +70,7 @@ class SCOPY_AD936X_EXPORT Fmcomms5Advanced : public QWidget ToolTemplate *m_tool; QVBoxLayout *m_mainLayout; AnimatedRefreshBtn *m_refreshButton; + EnsmModeClocksWidget *m_ensmModeClocks; ElnaWidget *m_elna; RssiWidget *m_rssi; @@ -80,8 +81,8 @@ class SCOPY_AD936X_EXPORT Fmcomms5Advanced : public QWidget BistWidget *m_bist; Fmcomms5Tab *m_fmcomms5; - iio_device *m_plutoDevice = nullptr; - iio_device *m_plutoDeviceB = nullptr; + iio_device *m_mainDevice = nullptr; + iio_device *m_secondDevice = nullptr; QStackedWidget *m_centralWidget = nullptr; bool m_isToolInitialized; diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5calibration.h b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5calibration.h new file mode 100644 index 0000000000..8122138642 --- /dev/null +++ b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5calibration.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * 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, either 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef FMCOMMS5CALIBRATION_H +#define FMCOMMS5CALIBRATION_H + +#include +#include + +namespace scopy { +namespace ad936x { + +struct MarkerResult +{ + double magnitude; + double offset; + int chA; + int chB; +}; + +// used devices +#define CAP_DEVICE "cf-ad9361-lpc" +#define CAP_DEVICE_ALT "cf-ad9361-A" +#define CAP_SLAVE_DEVICE "cf-ad9361-B" +#define DDS_DEVICE "cf-ad9361-dds-core-lpc" +#define DDS_SLAVE_DEVICE "cf-ad9361-dds-core-B" + +/* 1MHZ tone */ +#define CAL_TONE 1000000 +#define CAL_SCALE 0.12500 +#define MARKER_AVG 3 + +class Fmcomms5Calibration : public QObject +{ + Q_OBJECT +public: + explicit Fmcomms5Calibration(iio_context *ctx, QObject *parent = nullptr); + + void calibrate(); + void resetCalibration(); + void callSwitchPortsEnableCb(int val); + +Q_SIGNALS: + void calibrationFailed(); + void updateCalibrationProgress(double progress); + +private: + iio_context *m_ctx; + iio_device *m_mainDevice = nullptr; + iio_device *m_secondDevice = nullptr; + iio_device *m_cf_ad9361_lpc = nullptr; + iio_device *m_cf_ad9361_hpc = nullptr; + iio_device *m_ddsMain = nullptr; + iio_device *m_ddsSecond = nullptr; + + const char *ddsChannelNames[8] = {"altvoltage0", "altvoltage1", "altvoltage2", "altvoltage3", + "altvoltage4", "altvoltage5", "altvoltage6", "altvoltage7"}; + + void calibrationFail(int ret); + + double tuneTrxPhaseOffset(iio_device *ldev, int *ret, long long cal_freq, long long cal_tone, double sign, + std::function tune); + + void getMarkers(double *offset, double *mag); + std::vector getMarkersFromCrossCorrelation(); + double calcPhaseOffset(double fsample, double dds_freq, double offset, double mag); + double scalePhase0360(double val); + + int defaultDds(long long freq, double scale); + void ddsTxPhaseRotation(struct iio_device *dev, double val); + void trxPhaseRottation(iio_device *dev, double val); + unsigned int getCalTone(); + + int getCalSamples(long long calTone, long long calFreq); + + void nearEndLoopbackCtrl(unsigned channel, bool enable); +}; +} // namespace ad936x +} // namespace scopy +#endif // FMCOMMS5CALIBRATION_H diff --git a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5tab.h b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5tab.h index bfaf795618..3f31f3b1e2 100644 --- a/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5tab.h +++ b/packages/ad936x/plugins/ad936x/include/ad936x/fmcomms5/fmcomms5tab.h @@ -44,27 +44,9 @@ class Fmcomms5Tab : public QWidget private: iio_context *m_ctx; QVBoxLayout *m_layout; - iio_device *m_device = nullptr; - iio_device *m_deviceB = nullptr; - QCheckBox *m_silentCalibration; QProgressBar *m_calibProgressBar; QPushButton *m_calibrateBtn; - - void initDevices(); - - void calibrate(); - void trxPhaseRottation(iio_device *dev, double val); - - void resetCalibration(); - void callSwitchPortsEnableCb(int val); - double tuneTrxPhaseOffset(iio_device *ldev, int *ret, long long cal_freq, long long cal_tone, double sign, - double abort, std::function tune); - - // helper functions TODO MOVE TO CLASS - double scalePhase0360(double val); - double calcPhaseOffset(double fsample, double dds_freq, double offset, double mag); - void getMarkers(double *offset, double *mag); - int getCalSamples(long long calTone, long long calFreq); + QPushButton *m_resetCalibrationBtn; }; } // namespace ad936x } // namespace scopy diff --git a/packages/ad936x/plugins/ad936x/resources/AD_FMCOMMS5_EBZ.jpg b/packages/ad936x/plugins/ad936x/resources/AD_FMCOMMS5_EBZ.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a14918e1f8ae49d28a3195fe9fba06db7b9c4636 GIT binary patch literal 255484 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

HcG#NzSumUK11?!w%>IDZgPJqhZ;SLeA&N1Z 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⁢pymX%tqQ2N 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 R09ad936x-filters/AD936x_LP_256kHz_768kSPS.ftr ad936x-filters/AD936x_LP_333kHz_1MSPS.ftr ad936x-filters/AD936x_LP_666kHz_2MSPS.ftr + AD_FMCOMMS5_EBZ.jpg diff --git a/packages/ad936x/plugins/ad936x/src/ad936x/ad936x.cpp b/packages/ad936x/plugins/ad936x/src/ad936x/ad936x.cpp index a8b7e6f376..7eb99b3fa4 100755 --- a/packages/ad936x/plugins/ad936x/src/ad936x/ad936x.cpp +++ b/packages/ad936x/plugins/ad936x/src/ad936x/ad936x.cpp @@ -120,7 +120,7 @@ AD936X::AD936X(iio_context *ctx, QWidget *parent) controlWidgetLayout->addWidget( generateRxChainWidget(plutoDevice, "AD9361 / AD9364 Receive Chain", controlsWidget)); - /// third is Tx (transimt chain) + /// third is Tx (transmit chain) controlWidgetLayout->addWidget( generateTxChainWidget(plutoDevice, "AD9361 / AD9364 Transmit Chain", controlsWidget)); diff --git a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp index 126a03e879..d452561673 100644 --- a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp +++ b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5.cpp @@ -96,13 +96,13 @@ FMCOMMS5::FMCOMMS5(iio_context *ctx, QWidget *parent) if(m_ctx != nullptr) { - iio_device *plutoDevice = nullptr; + iio_device *mainDevice = nullptr; int device_count = iio_context_get_devices_count(m_ctx); for(int i = 0; i < device_count; ++i) { iio_device *dev = iio_context_get_device(m_ctx, i); const char *dev_name = iio_device_get_name(dev); - if(dev_name && QString(dev_name).contains("ad936", Qt::CaseInsensitive)) { - plutoDevice = dev; + if(dev_name && QString(dev_name).compare("ad9361-phy", Qt::CaseInsensitive) == 0) { + mainDevice = dev; break; } } @@ -111,67 +111,87 @@ FMCOMMS5::FMCOMMS5(iio_context *ctx, QWidget *parent) connect(this, &FMCOMMS5::readRequested, m_helper, &AD936xHelper::readRequested); /// first widget the global settings can be created with iiowigets only - controlWidgetLayout->addWidget(m_helper->generateGlobalSettingsWidget( - plutoDevice, "FMCOMMS5 Global Settings", controlsWidget)); + controlWidgetLayout->addWidget( + m_helper->generateGlobalSettingsWidget(mainDevice, "FMCOMMS5 Global Settings", controlsWidget)); /// second is Rx ( receive chain) controlWidgetLayout->addWidget( - generateRxChainWidget(plutoDevice, "FMCOMMS5 Receive Chain", controlsWidget)); + generateRxChainWidget(mainDevice, "FMCOMMS5 Receive Chain", controlsWidget)); - /// third is Tx (transimt chain) + /// third is Tx (transmit chain) controlWidgetLayout->addWidget( - generateTxChainWidget(plutoDevice, "FMCOMMS5 Transmit Chain", controlsWidget)); + generateTxChainWidget(mainDevice, "FMCOMMS5 Transmit Chain", controlsWidget)); controlWidgetLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); } - m_blockDiagramWidget = new QWidget(this); - Style::setBackgroundColor(m_blockDiagramWidget, json::theme::background_primary); - QVBoxLayout *blockDiagramLayout = new QVBoxLayout(m_blockDiagramWidget); - m_blockDiagramWidget->setLayout(blockDiagramLayout); - - QWidget *blockDiagramWidget = new QWidget(this); - QVBoxLayout *blockDiagramWidgetLayout = new QVBoxLayout(blockDiagramWidget); - blockDiagramWidget->setLayout(blockDiagramWidgetLayout); - - QScrollArea *blockDiagramWidgetScrollArea = new QScrollArea(this); - blockDiagramWidgetScrollArea->setWidgetResizable(true); - blockDiagramWidgetScrollArea->setWidget(blockDiagramWidget); - - blockDiagramLayout->addWidget(blockDiagramWidgetScrollArea); - - QLabel *blockDiagram = new QLabel(m_blockDiagramWidget); - blockDiagramWidgetLayout->addWidget(blockDiagram); - blockDiagram->setAlignment(Qt::AlignCenter); - // todo replace - QPixmap pixmap(":/pluto/ad936x.svg"); - blockDiagram->setPixmap(pixmap); + // Block Diagram Stack Widget with Next/Prev buttons + QWidget *blockDiagramStackWidget = new QWidget(this); + Style::setBackgroundColor(blockDiagramStackWidget, json::theme::background_primary); + QVBoxLayout *blockDiagramStackLayout = new QVBoxLayout(blockDiagramStackWidget); + blockDiagramStackWidget->setLayout(blockDiagramStackLayout); + + QStackedWidget *imageStack = new QStackedWidget(blockDiagramStackWidget); + + // Add images as QLabel widgets + QLabel *imageLabel1 = new QLabel(blockDiagramStackWidget); + imageLabel1->setAlignment(Qt::AlignCenter); + imageLabel1->setPixmap(QPixmap(":/pluto/ad936x.svg")); + imageStack->addWidget(imageLabel1); + + QLabel *imageLabel2 = new QLabel(blockDiagramStackWidget); + imageLabel2->setAlignment(Qt::AlignCenter); + imageLabel2->setPixmap(QPixmap(":/pluto/AD_FMCOMMS5_EBZ.jpg")); + imageStack->addWidget(imageLabel2); + + blockDiagramStackLayout->addWidget(imageStack); + + QHBoxLayout *buttonLayout = new QHBoxLayout(); + QPushButton *prevBtn = new QPushButton("Prev", blockDiagramStackWidget); + Style::setStyle(prevBtn, style::properties::button::basicButton); + QPushButton *nextBtn = new QPushButton("Next", blockDiagramStackWidget); + Style::setStyle(nextBtn, style::properties::button::basicButton); + buttonLayout->addWidget(prevBtn); + buttonLayout->addWidget(nextBtn); + blockDiagramStackLayout->addLayout(buttonLayout); + + // Navigation logic + connect(prevBtn, &QPushButton::clicked, this, [imageStack]() { + int idx = imageStack->currentIndex(); + if(idx > 0) + imageStack->setCurrentIndex(idx - 1); + }); + connect(nextBtn, &QPushButton::clicked, this, [imageStack]() { + int idx = imageStack->currentIndex(); + if(idx < imageStack->count() - 1) + imageStack->setCurrentIndex(idx + 1); + }); centralWidget->addWidget(m_controlsWidget); - centralWidget->addWidget(m_blockDiagramWidget); + centralWidget->addWidget(blockDiagramStackWidget); m_tool->addWidgetToCentralContainerHelper(centralWidget); QButtonGroup *centralWidgetButtons = new QButtonGroup(this); centralWidgetButtons->setExclusive(true); - QPushButton *ad963xBtn = new QPushButton("Controls", this); - ad963xBtn->setCheckable(true); - ad963xBtn->setChecked(true); - Style::setStyle(ad963xBtn, style::properties::button::blueGrayButton); - connect(ad963xBtn, &QPushButton::clicked, this, + QPushButton *controlsBtn = new QPushButton("Controls", this); + controlsBtn->setCheckable(true); + controlsBtn->setChecked(true); + Style::setStyle(controlsBtn, style::properties::button::blueGrayButton); + connect(controlsBtn, &QPushButton::clicked, this, [=, this]() { centralWidget->setCurrentWidget(m_controlsWidget); }); QPushButton *blockDiagramBtn = new QPushButton("Block Diagram", this); blockDiagramBtn->setCheckable(true); Style::setStyle(blockDiagramBtn, style::properties::button::blueGrayButton); connect(blockDiagramBtn, &QPushButton::clicked, this, - [=, this]() { centralWidget->setCurrentWidget(m_blockDiagramWidget); }); + [=, this]() { centralWidget->setCurrentWidget(blockDiagramStackWidget); }); - centralWidgetButtons->addButton(ad963xBtn); + centralWidgetButtons->addButton(controlsBtn); centralWidgetButtons->addButton(blockDiagramBtn); - m_tool->addWidgetToTopContainerHelper(ad963xBtn, TTA_LEFT); + m_tool->addWidgetToTopContainerHelper(controlsBtn, TTA_LEFT); m_tool->addWidgetToTopContainerHelper(blockDiagramBtn, TTA_LEFT); } @@ -289,7 +309,7 @@ QWidget *FMCOMMS5::generateRxChainWidget(iio_device *dev, QString title, QWidget for(int i = 0; i < device_count; ++i) { iio_device *aux = iio_context_get_device(m_ctx, i); const char *dev_name = iio_device_get_name(aux); - if(dev_name && QString(dev_name).contains("ad9361-phy-b", Qt::CaseInsensitive)) { + if(dev_name && QString(dev_name).contains("ad9361-phy-B", Qt::CaseInsensitive)) { dev2 = aux; break; } diff --git a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp index 65c7a949d6..f3eeba5446 100644 --- a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp +++ b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5advanced.cpp @@ -84,20 +84,20 @@ Fmcomms5Advanced::Fmcomms5Advanced(iio_context *ctx, QWidget *parent) navigationButtons->setExclusive(true); if(m_ctx != nullptr) { - iio_device *plutoDevice = nullptr; - iio_device *plutoDeviceB = nullptr; + iio_device *mainDevice = nullptr; + iio_device *secondDevice = nullptr; int foundDevices = 0; int device_count = iio_context_get_devices_count(m_ctx); for(int i = 0; i < device_count; ++i) { iio_device *dev = iio_context_get_device(m_ctx, i); const char *dev_name = iio_device_get_name(dev); if(dev_name && QString(dev_name).compare("ad9361-phy", Qt::CaseInsensitive) == 0) { - plutoDevice = dev; + mainDevice = dev; foundDevices++; } if(dev_name && QString(dev_name).compare("ad9361-phy-B", Qt::CaseInsensitive) == 0) { - plutoDeviceB = dev; + secondDevice = dev; foundDevices++; } @@ -105,18 +105,18 @@ Fmcomms5Advanced::Fmcomms5Advanced(iio_context *ctx, QWidget *parent) break; } } - if(plutoDevice == nullptr) { + if(mainDevice == nullptr) { qWarning(CAT_FMCOMMS5_ADVANCED) << "No ad9361-phy device found in context!"; return; } - if(plutoDeviceB == nullptr) { + if(secondDevice == nullptr) { qWarning(CAT_FMCOMMS5_ADVANCED) << "No ad9361-phy-B device found in context!"; return; } - m_plutoDevice = plutoDevice; - m_plutoDeviceB = plutoDeviceB; + m_mainDevice = mainDevice; + m_secondDevice = secondDevice; m_centralWidget = centralWidget; // Create buttons @@ -145,7 +145,6 @@ Fmcomms5Advanced::Fmcomms5Advanced(iio_context *ctx, QWidget *parent) m_bistBtn = new QPushButton("BIST", this); Style::setStyle(m_bistBtn, style::properties::button::blueGrayButton); m_bistBtn->setCheckable(true); - m_fmcomms5Btn = new QPushButton("FMCOMMS5", this); Style::setStyle(m_fmcomms5Btn, style::properties::button::blueGrayButton); m_fmcomms5Btn->setCheckable(true); @@ -172,9 +171,9 @@ Fmcomms5Advanced::Fmcomms5Advanced(iio_context *ctx, QWidget *parent) m_syncBtn = new QPushButton("MSC Sync", this); Style::setStyle(m_syncBtn, style::properties::button::basicButton); - m_syncBtn->setCheckable(true); connect(m_syncBtn, &QPushButton::clicked, this, [=]() { - ad9361_multichip_sync(m_plutoDevice, &m_plutoDeviceB, 1, + // call to lib ad9361 + ad9361_multichip_sync(m_mainDevice, &m_secondDevice, 1, FIXUP_INTERFACE_TIMING | CHECK_SAMPLE_RATES); }); @@ -205,46 +204,46 @@ void Fmcomms5Advanced::init() { // ENSM Mode Clocks - m_ensmModeClocks = new EnsmModeClocksWidget(m_plutoDevice, m_centralWidget); + m_ensmModeClocks = new EnsmModeClocksWidget(m_mainDevice, m_centralWidget); m_centralWidget->addWidget(m_ensmModeClocks); connect(this, &Fmcomms5Advanced::readRequested, m_ensmModeClocks, &EnsmModeClocksWidget::readRequested); connect(m_ensmModeClocksBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_ensmModeClocks); }); // eLNA - m_elna = new ElnaWidget(m_plutoDevice, m_centralWidget); + m_elna = new ElnaWidget(m_mainDevice, m_centralWidget); connect(this, &Fmcomms5Advanced::readRequested, m_elna, &ElnaWidget::readRequested); m_centralWidget->addWidget(m_elna); connect(m_eLnaBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_elna); }); // RSSI - m_rssi = new RssiWidget(m_plutoDevice, m_centralWidget); + m_rssi = new RssiWidget(m_mainDevice, m_centralWidget); connect(this, &Fmcomms5Advanced::readRequested, m_rssi, &RssiWidget::readRequested); m_centralWidget->addWidget(m_rssi); connect(m_rssiBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_rssi); }); // GAIN - m_gainWidget = new GainWidget(m_plutoDevice, m_centralWidget); + m_gainWidget = new GainWidget(m_mainDevice, m_centralWidget); connect(this, &Fmcomms5Advanced::readRequested, m_gainWidget, &GainWidget::readRequested); m_centralWidget->addWidget(m_gainWidget); connect(m_gainBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_gainWidget); }); // TX MONITOR - m_txMonitor = new TxMonitorWidget(m_plutoDevice, m_centralWidget); + m_txMonitor = new TxMonitorWidget(m_mainDevice, m_centralWidget); connect(this, &Fmcomms5Advanced::readRequested, m_txMonitor, &TxMonitorWidget::readRequested); m_centralWidget->addWidget(m_txMonitor); connect(m_txMonitorBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_txMonitor); }); // AUX ADC/DAC/IIO - m_auxAdcDacIo = new AuxAdcDacIoWidget(m_plutoDevice, m_centralWidget); + m_auxAdcDacIo = new AuxAdcDacIoWidget(m_mainDevice, m_centralWidget); connect(this, &Fmcomms5Advanced::readRequested, m_auxAdcDacIo, &AuxAdcDacIoWidget::readRequested); m_centralWidget->addWidget(m_auxAdcDacIo); connect(m_auxAdcDacIioBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_auxAdcDacIo); }); // MISC - m_misc = new MiscWidget(m_plutoDevice, m_centralWidget); + m_misc = new MiscWidget(m_mainDevice, m_centralWidget); connect(this, &Fmcomms5Advanced::readRequested, m_misc, &MiscWidget::readRequested); m_centralWidget->addWidget(m_misc); connect(m_miscBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_misc); }); // BIST - m_bist = new BistWidget(m_plutoDevice, m_centralWidget); + m_bist = new BistWidget(m_mainDevice, m_centralWidget); connect(this, &Fmcomms5Advanced::readRequested, m_bist, &BistWidget::readRequested); m_centralWidget->addWidget(m_bist); connect(m_bistBtn, &QPushButton::clicked, this, [=, this]() { m_centralWidget->setCurrentWidget(m_bist); }); diff --git a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5calibration.cpp b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5calibration.cpp new file mode 100644 index 0000000000..b72abbf631 --- /dev/null +++ b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5calibration.cpp @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2025 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * 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, either 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "fmcomms5/fmcomms5calibration.h" +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +Q_LOGGING_CATEGORY(CAT_FMCOMMS5_CALIBRATION, "CAT_FMCOMMS5_CALIBRATION") + +using namespace scopy; +using namespace ad936x; + +Fmcomms5Calibration::Fmcomms5Calibration(iio_context *ctx, QObject *parent) + : m_ctx(ctx) + , QObject{parent} +{ + m_mainDevice = iio_context_find_device(m_ctx, "ad9361-phy"); + m_secondDevice = iio_context_find_device(m_ctx, "ad9361-phy-B"); + + if(!m_mainDevice) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "No ad9361-phy device found in context!"; + return; + } + + if(!m_secondDevice) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "No ad9361-phy-B device found in context!"; + return; + } + + // find capture core + m_cf_ad9361_lpc = iio_context_find_device(m_ctx, CAP_DEVICE_ALT); + m_cf_ad9361_hpc = iio_context_find_device(m_ctx, CAP_SLAVE_DEVICE); + + if(!m_cf_ad9361_lpc || !m_cf_ad9361_hpc) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "Could not find capture cores"; + return; + } + + // find DDS main and second devices + m_ddsMain = iio_context_find_device(m_ctx, DDS_DEVICE); + m_ddsSecond = iio_context_find_device(m_ctx, DDS_SLAVE_DEVICE); + + if(!m_ddsMain || !m_ddsSecond) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "Could not find dds cores"; + return; + } +} + +void Fmcomms5Calibration::calibrate() +{ + int ret = 0; + double rx_phase_lpc = 0, rx_phase_hpc = 0, tx_phase_hpc = 0; + long long cal_tone = 0, cal_freq = 0; + int samples = 0; + + iio_channel *in0 = iio_device_find_channel(m_mainDevice, "voltage0", false); + iio_channel *in0B = iio_device_find_channel(m_secondDevice, "voltage0", false); + + if(!in0 || !in0B) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "Could not find channels"; + ret = -ENODEV; + calibrationFail(ret); + return; + } + + if(!m_cf_ad9361_lpc || !m_cf_ad9361_hpc) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "Could not find capture cores"; + ret = -ENODEV; + calibrationFail(ret); + return; + } + + if(!m_ddsMain || !m_ddsSecond) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "Could not find dds cores"; + ret = -ENODEV; + calibrationFail(ret); + return; + } + + Q_EMIT updateCalibrationProgress(0); + + ////////////////set some logical defaults / assumptions /////////////////// + ret = defaultDds(getCalTone(), CAL_SCALE); + + if(ret < 0) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "Could not set dds cores"; + calibrationFail(ret); + } + //////////////////////////////// + + // Read calibration tone and frequency + iio_channel *dds_ch = iio_device_find_channel(m_ddsMain, "altvoltage0", true); + if(!dds_ch) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "Could not find DDS channel"; + calibrationFail(ret); + return; + } + iio_channel_attr_read_longlong(dds_ch, "frequency", &cal_tone); + iio_channel_attr_read_longlong(dds_ch, "sampling_frequency", &cal_freq); + + samples = getCalSamples(cal_tone, cal_freq); + + // Turn off quadrature tracking + iio_channel_attr_write(in0, "quadrature_tracking_en", "0"); + iio_channel_attr_write(in0B, "quadrature_tracking_en", "0"); + + // Reset any Tx rotation to zero + trxPhaseRottation(m_cf_ad9361_lpc, 0.0); + trxPhaseRottation(m_cf_ad9361_hpc, 0.0); + + Q_EMIT updateCalibrationProgress(16); + + // Calibration sequence + // 1. Calibrate RX: TX1B_B (HPC) -> RX1C_B (HPC) + callSwitchPortsEnableCb(1); + rx_phase_hpc = + tuneTrxPhaseOffset(m_cf_ad9361_hpc, // cf_ad9361_hpc + &ret, // Error/status pointer + cal_freq, // Calibration frequency + cal_tone, // Calibration tone + 1.0, // Sign + [this](iio_device *dev, double phase) { this->trxPhaseRottation(dev, phase); }); + + if(ret < 0) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "Failed to tune phase"; + calibrationFail(ret); + return; + } + + Q_EMIT updateCalibrationProgress(40); + + // 2. Calibrate RX: TX1B_B (HPC) -> RX1C_A (LPC) + callSwitchPortsEnableCb(3); + trxPhaseRottation(m_mainDevice, 0.0); + rx_phase_lpc = + tuneTrxPhaseOffset(m_cf_ad9361_lpc, // cf_ad9361_lpc + &ret, // Error/status pointer + cal_freq, // Calibration frequency + cal_tone, // Calibration tone + 1.0, // Sign + [this](iio_device *dev, double phase) { this->trxPhaseRottation(dev, phase); }); + + if(ret < 0) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "Failed to tune phase"; + calibrationFail(ret); + return; + } + + Q_EMIT updateCalibrationProgress(64); + + // 3. Calibrate TX: TX1B_A (LPC) -> RX1C_A (LPC) + callSwitchPortsEnableCb(4); + trxPhaseRottation(m_mainDevice, 0.0); + tx_phase_hpc = + tuneTrxPhaseOffset(m_ddsSecond, // m_ddsSecond + &ret, // Error/status pointer + cal_freq, // Calibration frequency + cal_tone, // Calibration tone + -1.0, // Sign + [this](iio_device *dev, double phase) { this->trxPhaseRottation(dev, phase); }); + + if(ret < 0) { + qWarning(CAT_FMCOMMS5_CALIBRATION) << "Failed to tune phase"; + calibrationFail(ret); + return; + } + + Q_EMIT updateCalibrationProgress(88); + + // Restore phase rotation + trxPhaseRottation(m_cf_ad9361_hpc, rx_phase_hpc); + + // Restore calibration switch matrix to default + callSwitchPortsEnableCb(0); + + // Re-enable quadrature tracking + iio_channel_attr_write(in0, "quadrature_tracking_en", "1"); + iio_channel_attr_write(in0B, "quadrature_tracking_en", "1"); + + Q_EMIT updateCalibrationProgress(100); +} + +void Fmcomms5Calibration::resetCalibration() +{ + iio_channel *in0 = iio_device_find_channel(m_mainDevice, "voltage0", false); + iio_channel *in0B = iio_device_find_channel(m_secondDevice, "voltage0", false); + + // Reset calibration corrections to zero/default + iio_channel_attr_write(in0, "calibphase", "0"); + iio_channel_attr_write(in0B, "calibphase", "0"); + iio_channel_attr_write(in0, "calibscale", "0"); + iio_channel_attr_write(in0B, "calibscale", "0"); + + // Reset calibration switch matrix as well + iio_device_attr_write(m_mainDevice, "calibration_switch_control", "0"); +} + +void Fmcomms5Calibration::calibrationFail(int ret) +{ + // Restore calibration switch matrix to default + callSwitchPortsEnableCb(0); + + // Re-enable quadrature tracking + iio_channel *in0 = iio_device_find_channel(m_mainDevice, "voltage0", false); + iio_channel *in0B = iio_device_find_channel(m_secondDevice, "voltage0", false); + if(in0) + iio_channel_attr_write(in0, "quadrature_tracking_en", "1"); + if(in0B) + iio_channel_attr_write(in0B, "quadrature_tracking_en", "1"); + // Reset progress + Q_EMIT updateCalibrationProgress(0); + + qWarning(CAT_FMCOMMS5_CALIBRATION) << QString("Calibration failed with error code: %1").arg(ret); + Q_EMIT calibrationFailed(); +} + +double Fmcomms5Calibration::tuneTrxPhaseOffset(iio_device *ldev, int *ret, long long cal_freq, long long cal_tone, + double sign, std::function tune) +{ + // https://github.com/analogdevicesinc/iio-oscilloscope/blob/7a672e3e3e86aeb4fea2e594acff844010afe6fa/plugins/fmcomms2_adv.c#L755 + double offset = 0.0, mag = 0.0; + double phase = 0.0, increment = 0.0; + + for(int i = 0; i < 10; i++) { + getMarkers(&offset, &mag); + getMarkers(&offset, &mag); + + increment = calcPhaseOffset(cal_freq, cal_tone, offset, mag); + increment *= sign; + + phase += increment; + + phase = scalePhase0360(phase); + tune(ldev, phase); + + qDebug(CAT_FMCOMMS5_CALIBRATION) << "Step:" << i << "increment" << increment << "Phase:" << phase; + + if(std::fabs(offset) < 0.001) + break; + } + + if(std::fabs(offset) > 0.1) + *ret = -EFAULT; + else + *ret = 0; + + return phase * sign; +} + +void Fmcomms5Calibration::getMarkers(double *offset, double *mag) +{ + /// https://github.com/analogdevicesinc/iio-oscilloscope/blob/7a672e3e3e86aeb4fea2e594acff844010afe6fa/plugins/fmcomms2_adv.c#L641 + constexpr int avg_count = MARKER_AVG; + double acc_offset = 0; + double acc_mag = 0; + + if(offset) + *offset = 0; + if(mag) + *mag = 0; + + for(int sum = 0; sum < avg_count; ++sum) { + auto markers = getMarkersFromCrossCorrelation(); + if(!markers.empty()) { + acc_offset += markers[0].offset; + acc_mag += markers[0].magnitude; + } + } + + if(offset) + *offset = acc_offset / avg_count; + if(mag) + *mag = acc_mag / avg_count; + + qDebug(CAT_FMCOMMS5_CALIBRATION) << "getMarkers: averaged offset =" << *offset << ", mag =" << *mag; +} + +std::vector Fmcomms5Calibration::getMarkersFromCrossCorrelation() +{ + const char *channel_names[] = {"voltage0", "voltage1", "voltage4", "voltage5"}; + constexpr size_t BUFFER_SAMPLES = 2048; + std::vector results; + + iio_device *dev = m_cf_ad9361_lpc; + if(!dev) { + qWarning() << "Device not found!"; + return results; + } + + // Find and enable channels + std::vector channels; + for(const char *chname : channel_names) { + iio_channel *ch = iio_device_find_channel(dev, chname, false); + if(!ch) { + qWarning() << "Channel" << chname << "not found!"; + return results; + } + iio_channel_enable(ch); + channels.push_back(ch); + } + + // Create buffer + iio_buffer *buf = iio_device_create_buffer(dev, BUFFER_SAMPLES, false); + if(!buf) { + qWarning() << "Failed to create buffer:" << strerror(errno) << "(errno:" << errno << ")"; + return results; + } + + // Refill buffer + ssize_t nbytes = iio_buffer_refill(buf); + if(nbytes <= 0) { + qWarning() << "Buffer refill failed:" << strerror(errno) << "(errno:" << errno << ")"; + iio_buffer_destroy(buf); + return results; + } + + // Extract data for each channel + std::vector> data(4, std::vector(BUFFER_SAMPLES)); + for(int c = 0; c < 4; ++c) { + int16_t *samples = (int16_t *)iio_buffer_first(buf, channels[c]); + ptrdiff_t step = iio_buffer_step(buf) / sizeof(int16_t); + for(size_t i = 0; i < BUFFER_SAMPLES; ++i) { + data[c][i] = (double)samples[i * step]; + } + } + + // Compute cross-correlation for pairs: (0,1) and (2,3) + for(int pair = 0; pair < 2; ++pair) { + const std::vector &dataA = data[pair * 2]; + const std::vector &dataB = data[pair * 2 + 1]; + std::vector xcorr(BUFFER_SAMPLES, 0.0); + + for(size_t lag = 0; lag < BUFFER_SAMPLES; ++lag) { + double sum = 0.0; + for(size_t n = 0; n < BUFFER_SAMPLES - lag; ++n) { + sum += dataA[n] * dataB[n + lag]; + } + xcorr[lag] = sum; + } + + // Find peak in cross-correlation + double max_val = 0.0; + size_t max_idx = 0; + for(size_t j = 0; j < BUFFER_SAMPLES; ++j) { + double val = std::abs(xcorr[j]); + if(val > max_val) { + max_val = val; + max_idx = j; + } + } + + double marker_offset = static_cast(max_idx) / BUFFER_SAMPLES; + double marker_mag = max_val; + + results.push_back({marker_mag, marker_offset, pair * 2, pair * 2 + 1}); + qInfo() << "Marker" << pair << ": magnitude =" << marker_mag << "offset =" << marker_offset + << "channels:" << channel_names[pair * 2] << "vs" << channel_names[pair * 2 + 1]; + } + + iio_buffer_destroy(buf); + return results; +} + +double Fmcomms5Calibration::calcPhaseOffset(double fsample, double dds_freq, double offset, double mag) +{ + double val = 360.0 / ((fsample / dds_freq) / offset); + + if(mag < 0) + val += 180.0; + + return scalePhase0360(val); +} + +double Fmcomms5Calibration::scalePhase0360(double val) +{ + if(val >= 360.0) + val -= 360.0; + + if(val < 0) + val += 360.0; + + return val; +} + +int Fmcomms5Calibration::defaultDds(long long freq, double scale) +{ + int ret = 0; + iio_device *dds_devs[2] = {m_ddsMain, m_ddsSecond}; + for(int i = 0; i < 2; ++i) { + iio_device *dev = dds_devs[i]; + if(!dev) { + qWarning(CAT_FMCOMMS5_CALIBRATION) + << "DDS device" << (i == 0 ? DDS_DEVICE : DDS_SLAVE_DEVICE) << "not found!"; + ret |= -ENODEV; + continue; + } + for(int j = 0; j < 8; ++j) { + iio_channel *ch = iio_device_find_channel(dev, ddsChannelNames[j], true); + if(!ch) { + qWarning(CAT_FMCOMMS5_CALIBRATION) + << "DDS channel" << ddsChannelNames[j] << "not found on device" + << (i == 0 ? DDS_DEVICE : DDS_SLAVE_DEVICE); + ret |= -ENODEV; + continue; + } + ret |= iio_channel_attr_write_longlong(ch, "frequency", freq); + ret |= iio_channel_attr_write_double(ch, "scale", scale); + } + ddsTxPhaseRotation(dev, 0.0); + trxPhaseRottation(dev, 0.0); + } + return ret; +} + +void Fmcomms5Calibration::ddsTxPhaseRotation(iio_device *dev, double val) +{ + // Calculate I and Q phases (scaled by 1000) + long long i = scalePhase0360(val + 90.0) * 1000; + long long q = scalePhase0360(val) * 1000; + + // Loop over 8 DDS outputs + for(int j = 0; j < 8; ++j) { + iio_channel *ch = iio_device_find_channel(dev, ddsChannelNames[j], true); + if(!ch) + continue; + + if(j == 0 || j == 1 || j == 4 || j == 5) { + iio_channel_attr_write_longlong(ch, "phase", i); + } else { + iio_channel_attr_write_longlong(ch, "phase", q); + } + } +} + +void Fmcomms5Calibration::trxPhaseRottation(iio_device *dev, double val) +{ + double phase = val * 2 * M_PI / 360.0; + double vcos = std::cos(phase); + double vsin = std::sin(phase); + + bool output = (dev == m_mainDevice) || (dev == m_secondDevice); + + // Correction factor for output devices + if(output) { + double corr = 1.0 / + std::fmax(std::fabs(std::sin(phase) + std::cos(phase)), + std::fabs(std::cos(phase) - std::sin(phase))); + vcos *= corr; + vsin *= corr; + } + + // Set both RX1 and RX2 + for(unsigned offset = 0; offset <= 2; offset += 2) { + iio_channel *out0 = iio_device_find_channel(dev, offset == 2 ? "voltage2" : "voltage0", output); + iio_channel *out1 = iio_device_find_channel(dev, offset == 2 ? "voltage3" : "voltage1", output); + + if(out0 && out1) { + iio_channel_attr_write_double(out0, "calibscale", vcos); + iio_channel_attr_write_double(out0, "calibphase", -1.0 * vsin); + iio_channel_attr_write_double(out1, "calibscale", vcos); + iio_channel_attr_write_double(out1, "calibphase", vsin); + } + } +} + +unsigned int Fmcomms5Calibration::getCalTone() +{ + unsigned freq; + const char *cal_tone = getenv("CAL_TONE"); + + if(!cal_tone) + return CAL_TONE; + + freq = atoi(cal_tone); + + if(freq > 0 && freq < 31000000) + return freq; + + return CAL_TONE; +} + +int Fmcomms5Calibration::getCalSamples(long long calTone, long long calFreq) +{ + int samples, env_samples; + const char *cal_samples = getenv("CAL_SAMPLES"); + + samples = std::exp2(std::ceil(log2(calFreq / calTone)) + 2); + + if(!cal_samples) + return samples; + + env_samples = std::atoi(cal_samples); + + if(env_samples < samples) + return samples; + + return env_samples; +} + +void Fmcomms5Calibration::callSwitchPortsEnableCb(int val) +{ + // Map input value to switch settings + unsigned lp_slave = 0, lp_master = 0, sw = 0; + QString tx_port = "A"; + QString rx_port = "A_BALANCED"; + + /* + * 0 DISABLE + * 1 TX1B_B (HPC) -> RX1C_B (HPC) : BIST_LOOPBACK on A + * 2 TX1B_A (LPC) -> RX1C_B (HPC) : BIST_LOOPBACK on A + * 3 TX1B_B (HPC) -> RX1C_A (LPC) : BIST_LOOPBACK on B + * 4 TX1B_A (LPC) -> RX1C_A (LPC) : BIST_LOOPBACK on B + * + */ + + switch(val) { + default: + case 0: + lp_slave = 0; + lp_master = 0; + sw = 0; + tx_port = "A"; + rx_port = "A_BALANCED"; + break; + case 1: + case 2: + lp_slave = 0; + lp_master = 1; + sw = val - 1; + tx_port = "B"; + rx_port = "C_BALANCED"; + break; + case 3: + case 4: + lp_slave = 1; + lp_master = 0; + sw = val - 1; + tx_port = "B"; + rx_port = "C_BALANCED"; + break; + } + + nearEndLoopbackCtrl(0, lp_slave); // HPC + nearEndLoopbackCtrl(1, lp_slave); // HPC + nearEndLoopbackCtrl(4, lp_master); // LPC + nearEndLoopbackCtrl(5, lp_master); // LPC + + // Set calibration switch control + iio_device_attr_write(m_mainDevice, "calibration_switch_control", QString::number(sw).toUtf8().constData()); + + // Set RF port select for RX and TX on master device + iio_channel *rx_ch = iio_device_find_channel(m_mainDevice, "voltage0", false); + iio_channel *tx_ch = iio_device_find_channel(m_mainDevice, "voltage0", true); + if(rx_ch) + iio_channel_attr_write(rx_ch, "rf_port_select", rx_port.toUtf8().constData()); + if(tx_ch) + iio_channel_attr_write(tx_ch, "rf_port_select", tx_port.toUtf8().constData()); + + // Set RF port select for RX and TX on slave device + if(m_secondDevice) { + iio_channel *rx_chB = iio_device_find_channel(m_secondDevice, "voltage0", false); + iio_channel *tx_chB = iio_device_find_channel(m_secondDevice, "voltage0", true); + if(rx_chB) + iio_channel_attr_write(rx_chB, "rf_port_select", rx_port.toUtf8().constData()); + if(tx_chB) + iio_channel_attr_write(tx_chB, "rf_port_select", tx_port.toUtf8().constData()); + } +} + +void Fmcomms5Calibration::nearEndLoopbackCtrl(unsigned int channel, bool enable) +{ + unsigned tmp; + struct iio_device *dev = (channel > 3) ? m_cf_ad9361_lpc : m_cf_ad9361_hpc; + if(!dev) + return; + + if(channel > 3) + channel -= 4; + + if(iio_device_reg_read(dev, 0x80000418 + channel * 0x40, &tmp)) + return; + + if(enable) + tmp |= 0x1; + else + tmp &= ~0xF; + + iio_device_reg_write(dev, 0x80000418 + channel * 0x40, tmp); +} diff --git a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5tab.cpp b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5tab.cpp index 3ab9cf9c21..c73a1b1b32 100644 --- a/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5tab.cpp +++ b/packages/ad936x/plugins/ad936x/src/fmcomms5/fmcomms5tab.cpp @@ -26,17 +26,7 @@ #include #include -#include - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -#define CAP_DEVICE "cf-ad9361-lpc" -#define CAP_DEVICE_ALT "cf-ad9361-A" -#define CAP_SLAVE_DEVICE "cf-ad9361-B" -#define DDS_DEVICE "cf-ad9361-dds-core-lpc" -#define DDS_SLAVE_DEVICE "cf-ad9361-dds-core-B" +#include Q_LOGGING_CATEGORY(CAT_FMCOMMS5_TAB, "FMCOMMS5_TAB") @@ -47,6 +37,7 @@ Fmcomms5Tab::Fmcomms5Tab(iio_context *ctx, QWidget *parent) : m_ctx(ctx) , QWidget{parent} { + Style::setBackgroundColor(this, json::theme::background_primary); m_layout = new QVBoxLayout(this); @@ -68,72 +59,54 @@ Fmcomms5Tab::Fmcomms5Tab(iio_context *ctx, QWidget *parent) Style::setStyle(title, style::properties::label::menuBig); layout->addWidget(title); - initDevices(); + Fmcomms5Calibration *calibration = new Fmcomms5Calibration(ctx, this); - QMap *calSwitchControlOptions = new QMap(); - calSwitchControlOptions->insert("0", "DISABLE"); - calSwitchControlOptions->insert("1", "TX1B_B->RX1C_B"); - calSwitchControlOptions->insert("2", "TX1B_A->RX1C_B"); - calSwitchControlOptions->insert("3", "TX1B_B->RX1C_A"); - calSwitchControlOptions->insert("4", "TX1B_A->RX1C_A"); + QComboBox *calSwitchControl = new QComboBox(widget); - auto values = calSwitchControlOptions->values(); - QString optionasData = ""; - for(int i = 0; i < values.size(); i++) { - optionasData += " " + values.at(i); - } + calSwitchControl->addItem("DISABLE"); + calSwitchControl->addItem("TX1B_B->RX1C_B"); + calSwitchControl->addItem("TX1B_A->RX1C_B"); + calSwitchControl->addItem("TX1B_B->RX1C_A"); + calSwitchControl->addItem("TX1B_A->RX1C_A"); - IIOWidget *calSwitchControl = IIOWidgetBuilder(widget) - .device(m_device) - .attribute("calibration_switch_control") - .uiStrategy(IIOWidgetBuilder::ComboUi) - .optionsValues(optionasData) - .title("") - .buildSingle(); layout->addWidget(calSwitchControl); - calSwitchControl->setUItoDataConversion([this, calSwitchControlOptions](QString data) { - return IIOWidgetUtils::comboUiToDataConversionFunction(data, calSwitchControlOptions); - }); - calSwitchControl->setDataToUIConversion([this, calSwitchControlOptions](QString data) { - return IIOWidgetUtils::comboDataToUiConversionFunction(data, calSwitchControlOptions); - }); + connect(calSwitchControl, QOverload::of(&QComboBox::currentIndexChanged), this, + [=](int idx) { calibration->callSwitchPortsEnableCb(idx); }); m_calibrateBtn = new QPushButton("Calibrate", this); Style::setStyle(m_calibrateBtn, style::properties::button::basicButton); - connect(m_calibrateBtn, &QPushButton::clicked, this, &Fmcomms5Tab::calibrate); - QPushButton *resetCalibrationBtn = new QPushButton("Reset Calibration", this); - Style::setStyle(resetCalibrationBtn, style::properties::button::basicButton); - connect(resetCalibrationBtn, &QPushButton::clicked, this, &Fmcomms5Tab::resetCalibration); + m_resetCalibrationBtn = new QPushButton("Reset Calibration", this); + Style::setStyle(m_resetCalibrationBtn, style::properties::button::basicButton); + connect(m_resetCalibrationBtn, &QPushButton::clicked, calibration, &Fmcomms5Calibration::resetCalibration); QHBoxLayout *calibBtnLayout = new QHBoxLayout(); calibBtnLayout->setSpacing(10); calibBtnLayout->addWidget(m_calibrateBtn); - calibBtnLayout->addWidget(resetCalibrationBtn); + calibBtnLayout->addWidget(m_resetCalibrationBtn); layout->addLayout(calibBtnLayout); - // ??? seems to be related to how calibration affects ui - // in iio-osc calls osc_plot_set_visible(plot_xcorr_4ch, false); - m_silentCalibration = new QCheckBox("Silent Calibration", this); - layout->addWidget(m_silentCalibration); - m_calibProgressBar = new QProgressBar(this); m_calibProgressBar->setRange(0, 100); m_calibProgressBar->setValue(0); - m_calibProgressBar->setVisible(false); - connect(m_silentCalibration, &QCheckBox::toggled, m_calibProgressBar, &QProgressBar::setVisible); + connect(calibration, &Fmcomms5Calibration::updateCalibrationProgress, m_calibProgressBar, + &QProgressBar::setValue); layout->addWidget(m_calibProgressBar); - // TODO REMOVE - m_silentCalibration->setChecked(true); + iio_device *mainDevice = iio_context_find_device(m_ctx, "ad9361-phy"); + + if(!mainDevice) { + qWarning(CAT_FMCOMMS5_TAB) << "No ad9361-phy device found in context!"; + return; + } // TX Phase IIOWidget *txPhase = IIOWidgetBuilder(widget) - .device(m_device) + .device(mainDevice) .attribute("calibration_switch_control") .uiStrategy(IIOWidgetBuilder::RangeUi) .optionsValues("[0 0.1 360]") @@ -141,394 +114,14 @@ Fmcomms5Tab::Fmcomms5Tab(iio_context *ctx, QWidget *parent) .buildSingle(); layout->addWidget(txPhase); + connect(m_calibrateBtn, &QPushButton::clicked, this, [=]() { + m_calibrateBtn->setEnabled(false); + calibration->calibrate(); + m_calibrateBtn->setEnabled(true); + txPhase->readAsync(); + }); + m_layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); } Fmcomms5Tab::~Fmcomms5Tab() {} - -void Fmcomms5Tab::initDevices() -{ - iio_device *plutoDevice = nullptr; - iio_device *plutoDeviceB = nullptr; - int foundDevices = 0; - int device_count = iio_context_get_devices_count(m_ctx); - for(int i = 0; i < device_count; ++i) { - iio_device *dev = iio_context_get_device(m_ctx, i); - const char *dev_name = iio_device_get_name(dev); - if(dev_name && QString(dev_name).compare("ad9361-phy", Qt::CaseInsensitive) == 0) { - plutoDevice = dev; - foundDevices++; - } - - if(dev_name && QString(dev_name).compare("ad9361-phy-B", Qt::CaseInsensitive) == 0) { - plutoDeviceB = dev; - foundDevices++; - } - - if(foundDevices == 2) { - break; - } - } - if(plutoDevice == nullptr) { - qWarning(CAT_FMCOMMS5_TAB) << "No ad9361-phy device found in context!"; - return; - } - - if(plutoDeviceB == nullptr) { - qWarning(CAT_FMCOMMS5_TAB) << "No ad9361-phy-B device found in context!"; - return; - } - - m_device = plutoDevice; - m_deviceB = plutoDeviceB; -} - -void Fmcomms5Tab::calibrate() -{ - // https://github.com/analogdevicesinc/iio-oscilloscope/blob/7a672e3e3e86aeb4fea2e594acff844010afe6fa/plugins/fmcomms2_adv.c#L897C13-L897C23 - - m_calibrateBtn->setEnabled(false); - int ret = 0; - double rx_phase_lpc = 0, rx_phase_hpc = 0, tx_phase_hpc = 0; - long long cal_tone = 0, cal_freq = 0; - int samples = 0; - - iio_channel *in0 = iio_device_find_channel(m_device, "voltage0", false); - iio_channel *in0B = iio_device_find_channel(m_deviceB, "voltage0", false); - - if(!in0 || !in0B) { - qWarning(CAT_FMCOMMS5_TAB) << "Could not find channels"; - return; - } - - // find caputre core - iio_device *cf_ad9361_lpc = iio_context_find_device(m_ctx, CAP_DEVICE_ALT); - iio_device *cf_ad9361_hpc = iio_context_find_device(m_ctx, CAP_SLAVE_DEVICE); - - if(!cf_ad9361_lpc || !cf_ad9361_hpc) { - qWarning(CAT_FMCOMMS5_TAB) << "Could not find capture cores"; - return; - } - - // Find DDS master/slave devices - iio_device *dev_dds_master = iio_context_find_device(m_ctx, DDS_DEVICE); - iio_device *dev_dds_B = iio_context_find_device(m_ctx, DDS_SLAVE_DEVICE); - - if(!dev_dds_master || !dev_dds_B) { - qWarning(CAT_FMCOMMS5_TAB) << "Could not find dds cores"; - return; - } - - m_calibProgressBar->setValue(0); - - // Read calibration tone and frequency - iio_channel *dds_ch = iio_device_find_channel(dev_dds_master, "altvoltage0", true); - if(!dds_ch) { - if(!m_silentCalibration->isChecked()) - qWarning(CAT_FMCOMMS5_TAB) << "Could not find DDS channel"; - return; - } - iio_channel_attr_read_longlong(dds_ch, "frequency", &cal_tone); - iio_channel_attr_read_longlong(dds_ch, "sampling_frequency", &cal_freq); - - samples = getCalSamples(cal_tone, cal_freq); // Implement this helper - - // Turn off quadrature tracking - iio_channel_attr_write(in0, "quadrature_tracking_en", "0"); - iio_channel_attr_write(in0B, "quadrature_tracking_en", "0"); - - // Reset any Tx rotation to zero - trxPhaseRottation(cf_ad9361_lpc, 0.0); - trxPhaseRottation(cf_ad9361_hpc, 0.0); - - m_calibProgressBar->setValue(16); - - // Calibration sequence - // 1. Calibrate RX: TX1B_B (HPC) -> RX1C_B (HPC) - callSwitchPortsEnableCb(1); - rx_phase_hpc = - tuneTrxPhaseOffset(cf_ad9361_hpc, // cf_ad9361_hpc - &ret, // Error/status pointer - cal_freq, // Calibration frequency - cal_tone, // Calibration tone - 1.0, // Sign - 0.01, // Abort threshold - [this](iio_device *dev, double phase) { this->trxPhaseRottation(dev, phase); }); - - if(ret < 0) { - qWarning(CAT_FMCOMMS5_TAB) << "Failed to tune phase"; - return; - } - - m_calibProgressBar->setValue(40); - - // 2. Calibrate RX: TX1B_B (HPC) -> RX1C_A (LPC) - callSwitchPortsEnableCb(3); - trxPhaseRottation(m_device, 0.0); - rx_phase_lpc = - tuneTrxPhaseOffset(cf_ad9361_lpc, // cf_ad9361_lpc - &ret, // Error/status pointer - cal_freq, // Calibration frequency - cal_tone, // Calibration tone - 1.0, // Sign - 0.01, // Abort threshold - [this](iio_device *dev, double phase) { this->trxPhaseRottation(dev, phase); }); - - if(ret < 0) { - qWarning(CAT_FMCOMMS5_TAB) << "Failed to tune phase"; - return; - } - - m_calibProgressBar->setValue(64); - - // 3. Calibrate TX: TX1B_A (LPC) -> RX1C_A (LPC) - callSwitchPortsEnableCb(4); - trxPhaseRottation(m_device, 0.0); - tx_phase_hpc = - tuneTrxPhaseOffset(dev_dds_B, // dev_dds_B - &ret, // Error/status pointer - cal_freq, // Calibration frequency - cal_tone, // Calibration tone - -1.0, // Sign - 0.001, // Abort threshold - [this](iio_device *dev, double phase) { this->trxPhaseRottation(dev, phase); }); - - if(ret < 0) { - qWarning(CAT_FMCOMMS5_TAB) << "Failed to tune phase"; - return; - } - - m_calibProgressBar->setValue(88); - - // Restore phase rotation - trxPhaseRottation(cf_ad9361_hpc, rx_phase_hpc); - - // Restore calibration switch matrix to default - callSwitchPortsEnableCb(0); - - // Re-enable quadrature tracking - iio_channel_attr_write(in0, "quadrature_tracking_en", "1"); - iio_channel_attr_write(in0B, "quadrature_tracking_en", "1"); - - m_calibProgressBar->setValue(100); - - m_calibrateBtn->setEnabled(true); - - //////TODO FIUGRE THIS OUT ????? - /// calibrate_fail: - - // osc_plot_xcorr_revert(plot_xcorr_4ch, false); - // __cal_switch_ports_enable_cb(0); - - // if (in0 && in0_slave) { - // iio_channel_attr_write(in0, "quadrature_tracking_en", "1"); - // iio_channel_attr_write(in0_slave, "quadrature_tracking_en", "1"); - // } - - // calib_failed_param = malloc(sizeof(*calib_failed_param)); - // calib_failed_param->button = button; - // calib_failed_param->ret = ret; - // gdk_threads_add_idle(calibration_failed_ui, (gpointer) calib_failed_param); - - // /* reset progress bar */ - // gtk_progress_bar_set_fraction(calib_progress, 0.0); - // gtk_progress_bar_set_text(calib_progress, "Calibration Progress"); - - // /* Disable the channels that were enabled at the beginning of the calibration */ - // struct iio_device *iio_dev; - // iio_dev = iio_context_find_device(get_context_from_osc(), CAP_DEVICE_ALT); - // if (iio_dev && cap_device_channels_enabled) { - // iio_channels_change_shadow_of_enabled(iio_dev, false); - // cap_device_channels_enabled = false; - // } -} - -void Fmcomms5Tab::trxPhaseRottation(iio_device *dev, double val) -{ - // https://github.com/analogdevicesinc/iio-oscilloscope/blob/7a672e3e3e86aeb4fea2e594acff844010afe6fa/plugins/fmcomms2_adv.c#L526 - double phase = val * 2 * M_PI / 360.0; - double vcos = std::cos(phase); - double vsin = std::sin(phase); - - bool output = (dev == m_device) || (dev == m_deviceB); - - // Correction factor for output devices - if(output) { - double corr = 1.0 / - std::fmax(std::fabs(std::sin(phase) + std::cos(phase)), - std::fabs(std::cos(phase) - std::sin(phase))); - vcos *= corr; - vsin *= corr; - } - - // Set both RX1 and RX2 - for(unsigned offset = 0; offset <= 2; offset += 2) { - iio_channel *out0 = iio_device_find_channel(dev, offset == 2 ? "voltage2" : "voltage0", output); - iio_channel *out1 = iio_device_find_channel(dev, offset == 2 ? "voltage3" : "voltage1", output); - - if(out0 && out1) { - iio_channel_attr_write_double(out0, "calibscale", vcos); - iio_channel_attr_write_double(out0, "calibphase", -1.0 * vsin); - iio_channel_attr_write_double(out1, "calibscale", vcos); - iio_channel_attr_write_double(out1, "calibphase", vsin); - } - } -} - -int Fmcomms5Tab::getCalSamples(long long calTone, long long calFreq) -{ - int samples, env_samples; - /// ???? - const char *cal_samples = getenv("CAL_SAMPLES"); - - samples = std::exp2(std::ceil(log2(calFreq / calTone)) + 2); - - if(!cal_samples) - return samples; - - env_samples = std::atoi(cal_samples); - - if(env_samples < samples) - return samples; - - return env_samples; -} - -void Fmcomms5Tab::resetCalibration() -{ - iio_channel *in0 = iio_device_find_channel(m_device, "voltage0", false); - iio_channel *in0B = iio_device_find_channel(m_deviceB, "voltage0", false); - - // Reset calibration corrections to zero/default - iio_channel_attr_write(in0, "calibphase", "0"); - iio_channel_attr_write(in0B, "calibphase", "0"); - iio_channel_attr_write(in0, "calibscale", "0"); - iio_channel_attr_write(in0B, "calibscale", "0"); - - // Optionally reset calibration switch matrix as well - iio_device_attr_write(m_device, "calibration_switch_control", "0"); -} - -void Fmcomms5Tab::callSwitchPortsEnableCb(int val) -{ - // unsigned lp_slave, lp_master, sw; - // char *rx_port, *tx_port; - - // /* - // * 0 DISABLE - // * 1 TX1B_B (HPC) -> RX1C_B (HPC) : BIST_LOOPBACK on A - // * 2 TX1B_A (LPC) -> RX1C_B (HPC) : BIST_LOOPBACK on A - // * 3 TX1B_B (HPC) -> RX1C_A (LPC) : BIST_LOOPBACK on B - // * 4 TX1B_A (LPC) -> RX1C_A (LPC) : BIST_LOOPBACK on B - // * - // */ - // switch (val) { - // default: - // case 0: - // lp_slave = 0; - // lp_master = 0; - // sw = 0; - // tx_port = "A"; - // rx_port = "A_BALANCED"; - // break; - // case 1: - // case 2: - // lp_slave = 0; - // lp_master = 1; - // sw = val - 1; - // tx_port = "B"; - // rx_port = "C_BALANCED"; - // break; - // case 3: - // case 4: - // lp_slave = 1; - // lp_master = 0; - // sw = val - 1; - // tx_port = "B"; - // rx_port = "C_BALANCED"; - // break; - // } - - // #if 0 - // iio_device_debug_attr_write_bool(dev, "loopback", lp_master); - // iio_device_debug_attr_write_bool(dev_slave, "loopback", lp_slave); - // #else - // near_end_loopback_ctrl(0, lp_slave); /* HPC */ - // near_end_loopback_ctrl(1, lp_slave); /* HPC */ - - // near_end_loopback_ctrl(4, lp_master); /* LPC */ - // near_end_loopback_ctrl(5, lp_master); /* LPC */ - // #endif - // iio_device_debug_attr_write_longlong(dev, "calibration_switch_control", sw); - // iio_channel_attr_write(iio_device_find_channel(dev, "voltage0", false), - // "rf_port_select", rx_port); - // iio_channel_attr_write(iio_device_find_channel(dev, "voltage0", true), - // "rf_port_select", tx_port); - - // if (dev_slave) { - // iio_channel_attr_write(iio_device_find_channel(dev_slave, "voltage0", false), - // "rf_port_select", rx_port); - // iio_channel_attr_write(iio_device_find_channel(dev_slave, "voltage0", true), - // "rf_port_select", tx_port); - // } -} - -double Fmcomms5Tab::tuneTrxPhaseOffset(iio_device *ldev, int *ret, long long cal_freq, long long cal_tone, double sign, - double abort, std::function tune) -{ - // https://github.com/analogdevicesinc/iio-oscilloscope/blob/7a672e3e3e86aeb4fea2e594acff844010afe6fa/plugins/fmcomms2_adv.c#L755 - double offset = 0.0, mag = 0.0; - double phase = 0.0, increment = 0.0; - - for(int i = 0; i < 10; i++) { - getMarkers(&offset, &mag); // Implement this helper - getMarkers(&offset, &mag); - - increment = calcPhaseOffset(cal_freq, cal_tone, offset, mag); // Implement this helper - increment *= sign; - - phase += increment; - - phase = scalePhase0360(phase); // Implement this helper - tune(ldev, phase); - - qDebug() << "Step:" << i << "increment" << increment << "Phase:" << phase; - - if(std::fabs(offset) < 0.001) - break; - } - - if(std::fabs(offset) > 0.1) - *ret = -EFAULT; - else - *ret = 0; - - return phase * sign; -} - -void Fmcomms5Tab::getMarkers(double *offset, double *mag) -{ - /// ???? - /// https://github.com/analogdevicesinc/iio-oscilloscope/blob/7a672e3e3e86aeb4fea2e594acff844010afe6fa/plugins/fmcomms2_adv.c#L641 -} - -double Fmcomms5Tab::scalePhase0360(double val) -{ - if(val >= 360.0) - val -= 360.0; - - if(val < 0) - val += 360.0; - - return val; -} - -double Fmcomms5Tab::calcPhaseOffset(double fsample, double dds_freq, double offset, double mag) -{ - double val = 360.0 / ((fsample / dds_freq) / offset); - - if(mag < 0) - val += 180.0; - - return scalePhase0360(val); -}