From b701479e870cfacfee1ca4d5de8916b50babf52f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Tue, 7 Feb 2023 18:30:05 +0100 Subject: [PATCH] WIP: working compound driver, partial SSA3000X as a demonstration --- .../Compound}/compounddevice.cpp | 26 +- .../{ => LibreVNA/Compound}/compounddevice.h | 20 +- .../Compound}/compounddeviceeditdialog.cpp | 12 +- .../Compound}/compounddeviceeditdialog.h | 0 .../Compound}/compounddeviceeditdialog.ui | 0 .../LibreVNA/Compound/compounddriver.cpp | 706 ++++ .../Device/LibreVNA/Compound/compounddriver.h | 210 + .../Compound/compounddriversettingswidget.ui | 87 + .../Device/LibreVNA/librevnadriver.cpp | 54 +- .../Device/LibreVNA/librevnadriver.h | 16 +- .../Device/LibreVNA/librevnatcpdriver.cpp | 14 +- .../Device/LibreVNA/librevnausbdriver.cpp | 11 + .../Device/SSA3000X/ssa3000xdriver.cpp | 296 ++ .../Device/SSA3000X/ssa3000xdriver.h | 154 + .../LibreVNA-GUI/Device/devicedriver.cpp | 54 +- .../LibreVNA-GUI/Device/devicedriver.h | 5 +- .../LibreVNA-GUI/Generator/generator.cpp | 6 + .../LibreVNA-GUI/LibreVNA-GUI.pro | 15 +- .../SpectrumAnalyzer/spectrumanalyzer.cpp | 45 +- .../LibreVNA-GUI/Traces/tracemodel.cpp | 1 - .../PC_Application/LibreVNA-GUI/VNA/vna.cpp | 10 +- .../PC_Application/LibreVNA-GUI/appwindow.cpp | 21 +- Software/PC_Application/LibreVNA-GUI/mode.h | 2 + .../LibreVNA-GUI/preferences.cpp | 122 +- .../PC_Application/LibreVNA-GUI/preferences.h | 7 +- .../LibreVNA-GUI/preferencesdialog.ui | 3479 ++++++++--------- .../PC_Application/LibreVNA-GUI/savable.h | 2 + .../LibreVNA-Test/LibreVNA-Test.pro | 14 +- 28 files changed, 3418 insertions(+), 1971 deletions(-) rename Software/PC_Application/LibreVNA-GUI/Device/{ => LibreVNA/Compound}/compounddevice.cpp (68%) rename Software/PC_Application/LibreVNA-GUI/Device/{ => LibreVNA/Compound}/compounddevice.h (55%) rename Software/PC_Application/LibreVNA-GUI/Device/{ => LibreVNA/Compound}/compounddeviceeditdialog.cpp (97%) rename Software/PC_Application/LibreVNA-GUI/Device/{ => LibreVNA/Compound}/compounddeviceeditdialog.h (100%) rename Software/PC_Application/LibreVNA-GUI/Device/{ => LibreVNA/Compound}/compounddeviceeditdialog.ui (100%) create mode 100644 Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.cpp create mode 100644 Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.h create mode 100644 Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriversettingswidget.ui create mode 100644 Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.cpp create mode 100644 Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.h diff --git a/Software/PC_Application/LibreVNA-GUI/Device/compounddevice.cpp b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddevice.cpp similarity index 68% rename from Software/PC_Application/LibreVNA-GUI/Device/compounddevice.cpp rename to Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddevice.cpp index 2d7a1d3..379bc55 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/compounddevice.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddevice.cpp @@ -3,7 +3,7 @@ CompoundDevice::CompoundDevice() { name = ""; - sync = Synchronization::USB; + sync = LibreVNADriver::Synchronization::GUI; } nlohmann::json CompoundDevice::toJSON() @@ -49,26 +49,26 @@ void CompoundDevice::fromJSON(nlohmann::json j) } } -QString CompoundDevice::SyncToString(CompoundDevice::Synchronization sync) +QString CompoundDevice::SyncToString(LibreVNADriver::Synchronization sync) { switch(sync) { - case Synchronization::USB: return "USB"; - case Synchronization::ExtRef: return "Ext. Ref."; - case Synchronization::Trigger: return "Trigger"; + case LibreVNADriver::Synchronization::Disabled: return "Disabled"; + case LibreVNADriver::Synchronization::GUI: return "GUI"; + case LibreVNADriver::Synchronization::ExternalTrigger: return "Trigger"; default: - case Synchronization::Last: return "Invalid"; + return "Invalid"; } } -CompoundDevice::Synchronization CompoundDevice::SyncFromString(QString s) +LibreVNADriver::Synchronization CompoundDevice::SyncFromString(QString s) { - for(int i=0;i<(int) Synchronization::Last;i++) { - if(SyncToString((Synchronization)i) == s) { - return (Synchronization) i; + for(int i=0;i<(int) LibreVNADriver::Synchronization::Last;i++) { + if(SyncToString((LibreVNADriver::Synchronization)i) == s) { + return (LibreVNADriver::Synchronization) i; } } - // default to USB - return Synchronization::USB; + // default to GUI + return LibreVNADriver::Synchronization::GUI; } QString CompoundDevice::getDesription() @@ -76,7 +76,7 @@ QString CompoundDevice::getDesription() return name + ", "+QString::number(deviceSerials.size())+" devices, "+QString::number(portMapping.size())+" ports in total"; } -int CompoundDevice::PortMapping::findActiveStage(std::vector map, unsigned int device, unsigned int port) +unsigned int CompoundDevice::PortMapping::findActiveStage(std::vector map, unsigned int device, unsigned int port) { for(unsigned int i=0;i @@ -18,25 +19,18 @@ public: class PortMapping { public: - unsigned int device; - unsigned int port; - static int findActiveStage(std::vector map, unsigned int device, unsigned int port); + unsigned int device; // starts at zero + unsigned int port; // starts at zero + static unsigned int findActiveStage(std::vector map, unsigned int device, unsigned int port); }; - enum class Synchronization { - USB, - ExtRef, - Trigger, - Last - }; - - static QString SyncToString(Synchronization sync); - static Synchronization SyncFromString(QString s); + static QString SyncToString(LibreVNADriver::Synchronization sync); + static LibreVNADriver::Synchronization SyncFromString(QString s); QString getDesription(); QString name; - Synchronization sync; + LibreVNADriver::Synchronization sync; std::vector deviceSerials; std::vector portMapping; }; diff --git a/Software/PC_Application/LibreVNA-GUI/Device/compounddeviceeditdialog.cpp b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.cpp similarity index 97% rename from Software/PC_Application/LibreVNA-GUI/Device/compounddeviceeditdialog.cpp rename to Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.cpp index 82b184e..3b4f222 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/compounddeviceeditdialog.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.cpp @@ -1,7 +1,7 @@ #include "compounddeviceeditdialog.h" #include "ui_compounddeviceeditdialog.h" -#include "device.h" +#include "../../device.h" #include #include @@ -25,9 +25,9 @@ CompoundDeviceEditDialog::CompoundDeviceEditDialog(CompoundDevice *cdev, QWidget ldev.name = ui->name->text(); checkIfOkay(); }); - for(int i=0;i<(int)CompoundDevice::Synchronization::Last;i++) { - ui->sync->addItem(CompoundDevice::SyncToString((CompoundDevice::Synchronization) i)); - if((CompoundDevice::Synchronization) i == CompoundDevice::Synchronization::Trigger) { + for(int i=0;i<(int)LibreVNADriver::Synchronization::Last;i++) { + ui->sync->addItem(CompoundDevice::SyncToString((LibreVNADriver::Synchronization) i)); + if((LibreVNADriver::Synchronization) i == LibreVNADriver::Synchronization::ExternalTrigger) { // Disable for now auto *model = qobject_cast(ui->sync->model()); Q_ASSERT(model != nullptr); @@ -522,9 +522,9 @@ void DeviceFrame::update() // } serial->setCurrentText(s); - if(dev->sync == CompoundDevice::Synchronization::USB) { + if(dev->sync == LibreVNADriver::Synchronization::GUI) { setStyleSheet("image: url(:/icons/compound_V1_USB.png);"); - } else if(dev->sync == CompoundDevice::Synchronization::ExtRef) { + } else if(dev->sync == LibreVNADriver::Synchronization::Reserved) { if(position == 0) { setStyleSheet("image: url(:/icons/compound_V1_Ref_Left.png);"); } else if(position == dev->deviceSerials.size() - 1) { diff --git a/Software/PC_Application/LibreVNA-GUI/Device/compounddeviceeditdialog.h b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.h similarity index 100% rename from Software/PC_Application/LibreVNA-GUI/Device/compounddeviceeditdialog.h rename to Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.h diff --git a/Software/PC_Application/LibreVNA-GUI/Device/compounddeviceeditdialog.ui b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.ui similarity index 100% rename from Software/PC_Application/LibreVNA-GUI/Device/compounddeviceeditdialog.ui rename to Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.ui diff --git a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.cpp b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.cpp new file mode 100644 index 0000000..713fd79 --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.cpp @@ -0,0 +1,706 @@ +#include "compounddriver.h" + +#include "../librevnatcpdriver.h" +#include "../librevnausbdriver.h" + +#include "ui_compounddriversettingswidget.h" +#include "compounddeviceeditdialog.h" +#include "preferences.h" + +#include + +CompoundDriver::CompoundDriver() +{ + connected = false; + + drivers.push_back(new LibreVNAUSBDriver); + drivers.push_back(new LibreVNATCPDriver); + + auto &p = Preferences::getInstance(); + for(auto d : drivers) { + p.load(d->driverSpecificSettings()); + } + + specificSettings.push_back(Savable::SettingDescription(&compoundJSONString, "compoundDriver.compoundDeviceJSON", "")); + specificSettings.push_back(Savable::SettingDescription(&captureRawReceiverValues, "compoundDriver.captureRawReceiverValues", false)); +} + +CompoundDriver::~CompoundDriver() +{ + disconnect(); + for(auto d : drivers) { + delete d; + } +} + +std::set CompoundDriver::GetAvailableDevices() +{ + parseCompoundJSON(); + + std::set availableSerials; + for(auto d : drivers) { + availableSerials.merge(d->GetAvailableDevices()); + } + + std::set ret; + + for(auto cd : configuredDevices) { + bool allAvailable = true; + for(auto s : cd->deviceSerials) { + if(availableSerials.count(s) == 0) { + allAvailable = false; + break; + } + } + if(allAvailable) { + ret.insert(cd->name); + } + } + + return ret; +} + +bool CompoundDriver::connectTo(QString getSerial) +{ + if(connected) { + disconnect(); + } + bool found = false; + for(auto cd : configuredDevices) { + if(cd->name == getSerial) { + activeDevice = *cd; + found = true; + break; + } + } + if(!found) { + qWarning() << "Attempted to connect to an unknown compound device"; + return false; + } + std::vector> availableSerials; + for(auto d : drivers) { + availableSerials.push_back(d->GetAvailableDevices()); + } + // attempt to connect to all individual devices + for(auto s : activeDevice.deviceSerials) { + LibreVNADriver *device = nullptr; + for(unsigned int i=0;i 0) { + // this driver can connect to the requested device + if (i == 0) { + device = new LibreVNAUSBDriver(); + break; + } else if(i == 1) { + device = new LibreVNATCPDriver(); + break; + } + } + } + if(!device) { + qWarning() << "Unable to find required serial for compound device:" <driverSpecificSettings()); + if(!device->connectDevice(s, true)) { + qWarning() << "Unable to connect to required serial for compound device:" <getSerial()+": ")); + }); + connect(dev, &LibreVNADriver::InfoUpdated, this, [=]() { + updatedInfo(dev); + }); + connect(dev, &LibreVNADriver::passOnReceivedPacket, this, [=](const Protocol::PacketInfo& packet) { + incomingPacket(dev, packet); + }); + } + + connected = true; + + return true; +} + +void CompoundDriver::disconnect() +{ + for(auto d : devices) { + QObject::disconnect(d, nullptr, this, nullptr); + d->disconnect(); + delete d; + } + devices.clear(); + deviceInfos.clear(); + deviceStatus.clear(); + compoundSABuffer.clear(); + compoundVNABuffer.clear(); + connected = false; +} + +QString CompoundDriver::getSerial() +{ + if(connected) { + return activeDevice.name; + } else { + return ""; + } +} + +std::set CompoundDriver::getFlags() +{ + std::set ret; + if(lastStatus.extRefInUse) { + ret.insert(Flag::ExtRef); + } + if(!lastStatus.source_locked || !lastStatus.LO1_locked) { + ret.insert(Flag::Unlocked); + } + if(lastStatus.unlevel) { + ret.insert(Flag::Unlevel); + } + if(lastStatus.ADC_overload) { + ret.insert(Flag::Overload); + } + return ret; +} + +QString CompoundDriver::getStatus() +{ + QString ret; + ret.append("HW Rev."); + ret.append(info.hardware_version); + ret.append(" FW "+info.firmware_version); + ret.append(" Temps: "+QString::number(lastStatus.temp_source)+"°C/"+QString::number(lastStatus.temp_LO1)+"°C/"+QString::number(lastStatus.temp_MCU)+"°C"); + ret.append(" Reference:"); + if(lastStatus.extRefInUse) { + ret.append("External"); + } else { + ret.append("Internal"); + if(lastStatus.extRefAvailable) { + ret.append(" (External available)"); + } + } + return ret; +} + +QWidget *CompoundDriver::createSettingsWidget() +{ + auto w = new QWidget(); + auto ui = new Ui::CompoundDriverSettingsWidget; + ui->setupUi(w); + + // Set initial values + ui->CaptureRawReceiverValues->setChecked(captureRawReceiverValues); + + // make connections + connect(ui->CaptureRawReceiverValues, &QCheckBox::toggled, this, [=](){ + captureRawReceiverValues = ui->CaptureRawReceiverValues->isChecked(); + }); + + connect(ui->compoundList, &QListWidget::doubleClicked, [=](){ + auto index = ui->compoundList->currentRow(); + if(index >= 0 && index < (int) configuredDevices.size()) { + auto d = new CompoundDeviceEditDialog(configuredDevices[index]); + connect(d, &QDialog::accepted, [=](){ + ui->compoundList->item(index)->setText(configuredDevices[index]->getDesription()); + createCompoundJSON(); + }); + d->show(); + } + }); + connect(ui->compoundAdd, &QPushButton::clicked, [=](){ + auto cd = new CompoundDevice; + auto d = new CompoundDeviceEditDialog(cd); + connect(d, &QDialog::accepted, [=](){ + configuredDevices.push_back(cd); + ui->compoundList->addItem(cd->getDesription()); + createCompoundJSON(); + }); + connect(d, &QDialog::rejected, [=](){ + delete cd; + }); + d->show(); + }); + connect(ui->compoundDelete, &QPushButton::clicked, [=](){ + auto index = ui->compoundList->currentRow(); + if(index >= 0 && index < (int) configuredDevices.size()) { + // delete the actual compound device + delete configuredDevices[index]; + // delete the line in the GUI list + delete ui->compoundList->takeItem(index); + // remove compound device from list + configuredDevices.erase(configuredDevices.begin() + index); + createCompoundJSON(); + } + }); + + for(auto cd : configuredDevices) { + ui->compoundList->addItem(cd->getDesription()); + } + + return w; +} + +QStringList CompoundDriver::availableVNAMeasurements() +{ + QStringList ret; + for(unsigned int i=1;i<=info.Limits.VNA.ports;i++) { + for(unsigned int j=1;j<=info.Limits.VNA.ports;j++) { + ret.push_back("S"+QString::number(i)+QString::number(j)); + } + } + if(captureRawReceiverValues) { + for(unsigned int i=1;i<=info.Limits.VNA.ports;i++) { + for(unsigned int j=0;j cb) +{ + if(!supports(Feature::VNA)) { + return false; + } + if(s.excitedPorts.size() == 0) { + return setIdle(cb); + } + + // create port->stage mapping + portStageMapping.clear(); + for(unsigned int i=0;i activeMapping; + for(auto p : s.excitedPorts) { + activeMapping.push_back(activeDevice.portMapping[p-1]); + } + // Configure the devices + results.clear(); + bool success = true; + for(unsigned int i=0;isetSynchronization(activeDevice.sync, i == 0); + auto devSetting = s; + // indicate the number of stages + devSetting.excitedPorts = std::vector(s.excitedPorts.size(), 0); + // activate the ports of this specific device at the correct stage + auto p1Stage = CompoundDevice::PortMapping::findActiveStage(activeMapping, i, 0); + if(p1Stage < s.excitedPorts.size()) { + devSetting.excitedPorts[p1Stage] = 1; + } + auto p2Stage = CompoundDevice::PortMapping::findActiveStage(activeMapping, i, 1); + if(p2Stage < s.excitedPorts.size()) { + devSetting.excitedPorts[p2Stage] = 2; + } + success &= devices[i]->setVNA(devSetting, [=](bool success){ + if(cb) { + results[devices[i]] = success; + checkIfAllTransmissionsComplete(cb); + } + }); + } + return success; +} + +QStringList CompoundDriver::availableSAMeasurements() +{ + QStringList ret; + for(unsigned int i=1;i<=info.Limits.SA.ports;i++) { + ret.push_back("PORT"+QString::number(i)); + } + return ret; +} + +bool CompoundDriver::setSA(const DeviceDriver::SASettings &s, std::function cb) +{ + if(!supports(Feature::SA)) { + return false; + } + zerospan = s.freqStart == s.freqStop; + + // Configure the devices + results.clear(); + bool success = true; + for(unsigned int i=0;isetSynchronization(activeDevice.sync, i == 0); + auto devSettings = s; + devSettings.trackingGenerator = false; + devSettings.trackingPort = 0; + if(s.trackingGenerator) { + if(activeDevice.portMapping[s.trackingPort-1].device == i) { + // tracking generator active on this device + devSettings.trackingGenerator = true; + devSettings.trackingPort = activeDevice.portMapping[s.trackingPort-1].port + 1; + } + } + success &= devices[i]->setSA(devSettings, [=](bool success){ + if(cb) { + results[devices[i]] = success; + checkIfAllTransmissionsComplete(cb); + } + }); + } + SApoints = devices[0]->getSApoints(); + for(unsigned int i=1;igetSApoints() != SApoints) { + qWarning() << "Individual devices report different number of SA points, unable to start compound SA sweep"; + setIdle(); + success = false; + break; + } + } + return success; +} + +QStringList CompoundDriver::availableSGPorts() +{ + QStringList ret; + for(unsigned int i=1;i 0) { + if(activeDevice.portMapping[s.port-1].device == i) { + // this device has the active port + devSettings.port = activeDevice.portMapping[s.port-1].port+1; + } + } + success &= devices[i]->setSG(devSettings); + } + return success; +} + +bool CompoundDriver::setIdle(std::function cb) +{ + auto success = true; + results.clear(); + for(auto dev : devices) { + success &= dev->setIdle([=](bool success){ + if(cb) { + results[dev] = success; + checkIfAllTransmissionsComplete(cb); + } + }); + } + return success; +} + +QStringList CompoundDriver::availableExtRefInSettings() +{ + if(!connected) { + return QStringList(); + } + auto set = devices[0]->availableExtRefInSettings().toSet(); + for(unsigned int i=1;iavailableExtRefInSettings().toSet()); + } + return QStringList(set.toList()); +} + +QStringList CompoundDriver::availableExtRefOutSettings() +{ + if(!connected) { + return QStringList(); + } + auto set = devices[0]->availableExtRefOutSettings().toSet(); + for(unsigned int i=1;iavailableExtRefOutSettings().toSet()); + } + return QStringList(set.toList()); +} + +bool CompoundDriver::setExtRef(QString option_in, QString option_out) +{ + auto success = true; + for(auto dev : devices) { + success &= dev->setExtRef(option_in, option_out); + } + return success; +} + +void CompoundDriver::parseCompoundJSON() +{ + try { + configuredDevices.clear(); + nlohmann::json jc = nlohmann::json::parse(compoundJSONString.toStdString()); + for(auto j : jc) { + auto cd = new CompoundDevice(); + cd->fromJSON(j); + configuredDevices.push_back(cd); + } + } catch(const std::exception& e){ + qDebug() << "Failed to parse compound device string: " << e.what(); + } +} + +void CompoundDriver::createCompoundJSON() +{ + if(configuredDevices.size() > 0) { + nlohmann::json j; + for(auto cd : configuredDevices) { + j.push_back(cd->toJSON()); + } + compoundJSONString = QString::fromStdString(j.dump()); + } else { + compoundJSONString = "[]"; + } +} + +void CompoundDriver::incomingPacket(LibreVNADriver *device, const Protocol::PacketInfo &p) +{ + switch(p.type) { + case Protocol::PacketType::DeviceStatusV1: + updatedStatus(device, p.statusV1); + break; + case Protocol::PacketType::VNADatapoint: + datapointReceivecd(device, p.VNAdatapoint); + break; + case Protocol::PacketType::SpectrumAnalyzerResult: + spectrumResultReceived(device, p.spectrumResult); + break; + case Protocol::PacketType::SetTrigger: + if(activeDevice.sync == LibreVNADriver::Synchronization::GUI) { + for(unsigned int i=0;isendWithoutPayload(Protocol::PacketType::SetTrigger); + } else { + devices[0]->sendWithoutPayload(Protocol::PacketType::SetTrigger); + } + break; + } + } + } + break; + case Protocol::PacketType::ClearTrigger: + if(activeDevice.sync == LibreVNADriver::Synchronization::GUI) { + for(unsigned int i=0;isendWithoutPayload(Protocol::PacketType::ClearTrigger); + } else { + devices[0]->sendWithoutPayload(Protocol::PacketType::ClearTrigger); + } + break; + } + } + } + break; + default: + // nothing to do for other packet types + break; + } +} + +void CompoundDriver::updatedInfo(LibreVNADriver *device) +{ + deviceInfos[device] = device->getInfo(); + if(deviceInfos.size() == devices.size()) { + // got infos from all devices + info = devices[0]->getInfo(); + for(unsigned int i=1;igetInfo()); + } + // overwrite number of ports (not all physical ports may be configured for this compound device) + info.Limits.VNA.ports = activeDevice.portMapping.size(); + info.Limits.Generator.ports = activeDevice.portMapping.size(); + info.Limits.SA.ports = activeDevice.portMapping.size(); + emit InfoUpdated(); + } +} + +void CompoundDriver::updatedStatus(LibreVNADriver *device, const Protocol::DeviceStatusV1 &status) +{ + deviceStatus[device] = status; + if(deviceStatus.size() == devices.size()) { + // got status from all devices + for(unsigned int i=0;i(); + } + auto &buf = compoundSABuffer[res.pointNum]; + buf[dev] = res; + if(buf.size() == devices.size()) { + // Got datapoints from all devices, can create merged VNA result + SAMeasurement m; + m.pointNum = res.pointNum; + if(zerospan) { + m.us = res.us; + } else { + m.frequency = res.frequency; + } + // assemble data + for(unsigned int port=0;portfirst <= res.pointNum) { + it = compoundSABuffer.erase(it); + } else { + it++; + } + } + } +} + +void CompoundDriver::datapointReceivecd(LibreVNADriver *dev, Protocol::VNADatapoint<32> *data) +{ + if(!compoundVNABuffer.count(data->pointNum)) { + compoundVNABuffer[data->pointNum] = std::map*>(); + } + auto &buf = compoundVNABuffer[data->pointNum]; + // create copy of datapoint as it will be deleted by the device driver + buf[dev] = new Protocol::VNADatapoint<32>(*data); + if(buf.size() == devices.size()) { + // Got datapoints from all devices, can create merged VNA result + VNAMeasurement m; + m.pointNum = data->pointNum; + m.Z0 = 50.0; + if(zerospan) { + m.us = data->us; + } else { + m.frequency = data->frequency; + m.dBm = (double) data->cdBm / 100; + } + // assemble data + for(auto map : portStageMapping) { + // map.first is the port (starts at one) + // map.second is the stage at which this port had the stimulus (starts at zero) + + // figure out which device had the stimulus for the port... + auto stimulusDev = devices[activeDevice.portMapping[map.first-1].device]; + // ...and which device port was used for the stimulus... + auto stimulusDevPort = activeDevice.portMapping[map.first-1].port; + // ...grab the reference receiver data + std::complex ref = buf[stimulusDev]->getValue(map.second, stimulusDevPort, true); + + // for all ports of the compound device... + for(unsigned int i=0;i input = buf[inputDevice]->getValue(map.second, inputPort, false); + if(!std::isnan(ref.real()) && !std::isnan(input.real())) { + // got both required measurements + QString name = "S"+QString::number(i+1)+QString::number(map.first); + auto S = input / ref; + if(inputDevice != stimulusDev) { + // can't use phase information when measuring across devices + S = abs(S); + } + m.measurements[name] = S; + } + + if(captureRawReceiverValues) { + QString name = "RawPort"+QString::number(inputPort+1)+"Stage"+QString::number(map.second); + m.measurements[name] = input; + name = "RawPort"+QString::number(inputPort+1)+"Stage"+QString::number(map.second)+"Ref"; + m.measurements[name] = buf[inputDevice]->getValue(map.second, inputPort, true); + } + } + } + + emit VNAmeasurementReceived(m); + + // Clear this and all (incomplete) older datapoint buffers + int pointNum = data->pointNum; + auto it = compoundVNABuffer.begin(); + while(it != compoundVNABuffer.end()) { + if(it->first <= pointNum) { + for(auto d : it->second) { + delete d.second; + } + it = compoundVNABuffer.erase(it); + } else { + it++; + } + } + } +} + +void CompoundDriver::checkIfAllTransmissionsComplete(std::function cb) +{ + if(results.size() == devices.size()) { + // got all responses + bool success = true; + for(auto res : results) { + if(res.second != true) { + success = false; + break; + } + } + if(cb) { + cb(success); + } + } +} diff --git a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.h b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.h new file mode 100644 index 0000000..b87c831 --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.h @@ -0,0 +1,210 @@ +#ifndef COMPOUNDDRIVER_H +#define COMPOUNDDRIVER_H + +#include "../../devicedriver.h" +#include "compounddevice.h" + +class CompoundDriver : public DeviceDriver +{ +public: + CompoundDriver(); + ~CompoundDriver(); + + /** + * @brief Returns the driver name. It must be unique across all implemented drivers and is used to identify the driver + * @return driver name + */ + virtual QString getDriverName() override {return "LibreVNA/Compound";} + /** + * @brief Lists all available devices by their serial numbers + * @return Serial numbers of detected devices + */ + virtual std::set GetAvailableDevices() override; + +protected: + /** + * @brief Connects to a device, given by its serial number + * + * @param serial Serial number of device that should be connected to + * @return true if connection successful, otherwise false + */ + virtual bool connectTo(QString getSerial) override; + /** + * @brief Disconnects from device. Has no effect if no device was connected + */ + virtual void disconnect() override; + +public: + /** + * @brief Returns the serial number of the connected device + * @return Serial number of connected device (empty string if no device is connected) + */ + virtual QString getSerial() override; + + /** + * @brief Returns the device information. This function will be called when a device has been connected. Its return value must be valid + * directly after returning from DeviceDriver::connectTo() + * + * Emit the InfoUpdate() signal whenever the return value of this function changes. + * + * @return Device information + */ + virtual Info getInfo() override {return info;} + + /** + * @brief Returns a set of all active flags + * + * There is also a convenience function to check a specific flag, see DeviceDriver::asserted() + * + * @return Set of active flags + */ + virtual std::set getFlags() override; + + /** + * @brief Returns the device status string. It will be displayed in the status bar of the application + * + * Emit the StatusUpdated() signal whenever the return value of this function changes + * + * @return Status string + */ + virtual QString getStatus() override; + + /** + * @brief Returns a widget to edit the driver specific settings. + * + * The widget is displayed in the global settings dialog and allows the user to edit the settings + * specific to this driver. The application takes ownership of the widget after returning, + * create a new widget for every call to this function. If the driver has no specific settings + * or the settings do not need to be editable by the user, return a nullptr. In this case, no + * page for this driver is created in the settings dialog + * @return newly constructed settings widget or nullptr + */ + virtual QWidget* createSettingsWidget() override; + + /** + * @brief Names of available measurements. + * + * The names must be identical to the names used in the returned VNAMeasurement. + * Typically the S parameters, e.g. this function may return {"S11","S12","S21","S22"} but any other names are also allowed. + * + * @return List of available VNA measurement parameters + */ + virtual QStringList availableVNAMeasurements() override; + + /** + * @brief Configures the VNA and starts a sweep + * @param s VNA settings + * @param cb Callback, must be called after the VNA has been configured + * @return true if configuration successful, false otherwise + */ + virtual bool setVNA(const VNASettings &s, std::function cb = nullptr) override; + + /** + * @brief Names of available measurements. + * + * The names must be identical to the names used in the returned SAMeasurement. + * Typically the port names, e.g. this function may return {"PORT1","PORT2"} but any other names are also allowed. + * + * @return List of available SA measurement parameters + */ + virtual QStringList availableSAMeasurements() override; + /** + * @brief Configures the SA and starts a sweep + * @param s SA settings + * @param cb Callback, must be called after the SA has been configured + * @return true if configuration successful, false otherwise + */ + virtual bool setSA(const SASettings &s, std::function cb = nullptr) override; + + /** + * @brief Returns the number of points in one spectrum analyzer sweep (as configured by the last setSA() call) + * @return Number of points in the sweep + */ + virtual unsigned int getSApoints() override {return SApoints;} + + /** + * @brief Names of available generator ports. + * + * Typically the port names, e.g. this function may return {"PORT1","PORT2"} but any other names are also allowed. + * + * @return List of available SA measurement parameters + */ + virtual QStringList availableSGPorts() override; + /** + * @brief Configures the generator + * @param s Generator settings + * @return true if configuration successful, false otherwise + */ + virtual bool setSG(const SGSettings &s) override; + + /** + * @brief Sets the device to idle + * + * Stops all sweeps and signal generation + * + * @param cb Callback, must be called after the device has stopped all operations + * @return true if configuration successful, false otherwise + */ + virtual bool setIdle(std::function cb = nullptr) override; + + /** + * @brief Returns the available options for the external reference input + * @return External reference input options + */ + virtual QStringList availableExtRefInSettings() override; + + /** + * @brief Returns the available options for the external reference output + * @return External reference output options + */ + virtual QStringList availableExtRefOutSettings() override; + + /** + * @brief Configures the external reference input/output + * @param option_in Reference input option (one of the options returned by availableExtRefInSettings()) + * @param option_out Reference output option (one of the options returned by availableExtRefOutSettings()) + * @return true if configuration successful, false otherwise + */ + virtual bool setExtRef(QString option_in, QString option_out) override; + +private: + void parseCompoundJSON(); + void createCompoundJSON(); + void incomingPacket(LibreVNADriver *device, const Protocol::PacketInfo &p); + void updatedInfo(LibreVNADriver *device); + void updatedStatus(LibreVNADriver *device, const Protocol::DeviceStatusV1 &status); + void datapointReceivecd(LibreVNADriver *dev, Protocol::VNADatapoint<32> *data); + void spectrumResultReceived(LibreVNADriver *dev, Protocol::SpectrumAnalyzerResult res); + + Info info; + std::map deviceInfos; + std::map deviceStatus; + std::map*>> compoundVNABuffer; + std::map> compoundSABuffer; + Protocol::DeviceStatusV1 lastStatus; + + // Parsed configuration of compound devices (as extracted from compoundJSONString + std::vector configuredDevices; + + std::map portStageMapping; // maps from excitedPort (count starts at one) to stage (count starts at zero) + + // All possible drivers to interact with a LibreVNA + std::vector drivers; + + // Configuration of the device we are connected to + CompoundDevice activeDevice; + bool connected; + std::vector devices; + bool zerospan; + unsigned int SApoints; + + // Driver specific settings + bool captureRawReceiverValues; + QString compoundJSONString; + + // Buffers for storing individual device answers + std::map results; + void checkIfAllTransmissionsComplete(std::function cb = nullptr); +}; + +#endif // COMPOUNDDRIVER_H diff --git a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriversettingswidget.ui b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriversettingswidget.ui new file mode 100644 index 0000000..f71cf08 --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/Compound/compounddriversettingswidget.ui @@ -0,0 +1,87 @@ + + + CompoundDriverSettingsWidget + + + + 0 + 0 + 893 + 559 + + + + CompoundDriverSettingsWidget + + + + + + + + + + + + + + + + + :/icons/add.png + :/icons/add.png:/icons/add.png + + + + + + + + + + + :/icons/remove.png + :/icons/remove.png:/icons/remove.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Debug - Acquisition + + + + + + Capture raw receiver values + + + + + + + + + + + + + diff --git a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.cpp b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.cpp index 59692fb..81206b6 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.cpp @@ -107,6 +107,7 @@ LibreVNADriver::LibreVNADriver() connected = false; skipOwnPacketHandling = false; SApoints = 0; + setSynchronization(Synchronization::Disabled, false); auto manual = new QAction("Manual Control"); connect(manual, &QAction::triggered, this, [=](){ @@ -146,17 +147,6 @@ LibreVNADriver::LibreVNADriver() d->show(); }); specificActions.push_back(freqcal); - - specificSettings.push_back(Savable::SettingDescription(&captureRawReceiverValues, "captureRawReceiverValues", false)); - specificSettings.push_back(Savable::SettingDescription(&harmonicMixing, "harmonicMixing", false)); - specificSettings.push_back(Savable::SettingDescription(&SASignalID, "signalID", true)); - specificSettings.push_back(Savable::SettingDescription(&VNASuppressInvalidPeaks, "suppressInvalidPeaks", true)); - specificSettings.push_back(Savable::SettingDescription(&VNAAdjustPowerLevel, "adjustPowerLevel", false)); - specificSettings.push_back(Savable::SettingDescription(&SAUseDFT, "useDFT", true)); - specificSettings.push_back(Savable::SettingDescription(&SARBWLimitForDFT, "RBWlimitDFT", 3000)); - specificSettings.push_back(Savable::SettingDescription(&IF1, "IF1", 62000000)); - specificSettings.push_back(Savable::SettingDescription(&ADCprescaler, "ADCprescaler", 128)); - specificSettings.push_back(Savable::SettingDescription(&DFTPhaseInc, "DFTPhaseInc", 1280)); } std::set LibreVNADriver::getFlags() @@ -232,6 +222,12 @@ QWidget *LibreVNADriver::createSettingsWidget() ui->IF1->setUnit("Hz"); ui->IF1->setPrefixes(" kM"); ui->IF1->setPrecision(5); + ui->ADCRate->setUnit("Hz"); + ui->ADCRate->setPrefixes(" kM"); + ui->ADCRate->setPrecision(5); + ui->IF2->setUnit("Hz"); + ui->IF2->setPrefixes(" kM"); + ui->IF2->setPrecision(5); ui->IF1->setValue(IF1); ui->ADCpresc->setValue(ADCprescaler); ui->ADCphaseInc->setValue(DFTPhaseInc); @@ -333,10 +329,10 @@ bool LibreVNADriver::setVNA(const DeviceDriver::VNASettings &s, std::function= 25000000) { // Check point spacing. @@ -518,6 +514,12 @@ void LibreVNADriver::registerTypes() qRegisterMetaType(); } +void LibreVNADriver::setSynchronization(LibreVNADriver::Synchronization s, bool master) +{ + sync = s; + syncMaster = master; +} + void LibreVNADriver::handleReceivedPacket(const Protocol::PacketInfo &packet) { emit passOnReceivedPacket(packet); @@ -591,20 +593,20 @@ void LibreVNADriver::handleReceivedPacket(const Protocol::PacketInfo &packet) m.dBm = (double) res->cdBm / 100; } for(auto map : portStageMapping) { - // map.first is the port (starts at zero) + // map.first is the port (starts at one) // map.second is the stage at which this port had the stimulus (starts at zero) - complex ref = res->getValue(map.second, map.first, true); - for(unsigned int i=0;i input = res->getValue(map.second, i, false); + complex ref = res->getValue(map.second, map.first-1, true); + for(unsigned int i=1;i<=info.Limits.VNA.ports;i++) { + complex input = res->getValue(map.second, i-1, false); if(!std::isnan(ref.real()) && !std::isnan(input.real())) { // got both required measurements - QString name = "S"+QString::number(i+1)+QString::number(map.first+1); + QString name = "S"+QString::number(i)+QString::number(map.first); m.measurements[name] = input / ref; } if(captureRawReceiverValues) { - QString name = "RawPort"+QString::number(i+1)+"Stage"+QString::number(map.first); + QString name = "RawPort"+QString::number(i)+"Stage"+QString::number(map.second); m.measurements[name] = input; - name = "RawPort"+QString::number(i+1)+"Stage"+QString::number(map.first)+"Ref"; + name = "RawPort"+QString::number(i)+"Stage"+QString::number(map.second)+"Ref"; m.measurements[name] = res->getValue(map.second, i, true); } } diff --git a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.h b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.h index e807a30..0edf7e1 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.h +++ b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.h @@ -9,6 +9,7 @@ class LibreVNADriver : public DeviceDriver { + friend class CompoundDriver; Q_OBJECT public: enum class TransmissionResult { @@ -161,6 +162,16 @@ public: */ virtual void registerTypes(); + enum class Synchronization { + Disabled = 0, + GUI = 1, + Reserved = 2, + ExternalTrigger = 3, + Last = 4, + }; + + void setSynchronization(Synchronization s, bool master); + public: signals: // Required for the compound device driver @@ -191,7 +202,10 @@ protected: bool zerospan; unsigned int SApoints; - std::map portStageMapping; // maps from excitedPort (count starts at zero) to stage (count starts at zero) + Synchronization sync; + bool syncMaster; + + std::map portStageMapping; // maps from excitedPort (count starts at one) to stage (count starts at zero) // Driver specific settings bool captureRawReceiverValues; diff --git a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnatcpdriver.cpp b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnatcpdriver.cpp index fe80b4b..c018699 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnatcpdriver.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnatcpdriver.cpp @@ -26,16 +26,26 @@ LibreVNATCPDriver::LibreVNATCPDriver() auto interfaces = QNetworkInterface::allInterfaces(); for(auto i : interfaces) { - qDebug() << "Creating socket for interface" << i.name(); auto socket = new QUdpSocket(); socket->bind(QHostAddress::AnyIPv4, 0, QUdpSocket::ShareAddress); socket->setMulticastInterface(i); - qDebug() << socket->joinMulticastGroup(SSDPaddress, i); + socket->joinMulticastGroup(SSDPaddress, i); connect(socket, &QUdpSocket::readyRead, this, [=](){ SSDPreceived(socket); }); ssdpSockets.push_back(socket); } + + specificSettings.push_back(Savable::SettingDescription(&captureRawReceiverValues, "LibreVNATCPDriver.captureRawReceiverValues", false)); + specificSettings.push_back(Savable::SettingDescription(&harmonicMixing, "LibreVNATCPDriver.harmonicMixing", false)); + specificSettings.push_back(Savable::SettingDescription(&SASignalID, "LibreVNATCPDriver.signalID", true)); + specificSettings.push_back(Savable::SettingDescription(&VNASuppressInvalidPeaks, "LibreVNATCPDriver.suppressInvalidPeaks", true)); + specificSettings.push_back(Savable::SettingDescription(&VNAAdjustPowerLevel, "LibreVNATCPDriver.adjustPowerLevel", false)); + specificSettings.push_back(Savable::SettingDescription(&SAUseDFT, "LibreVNATCPDriver.useDFT", true)); + specificSettings.push_back(Savable::SettingDescription(&SARBWLimitForDFT, "LibreVNATCPDriver.RBWlimitDFT", 3000)); + specificSettings.push_back(Savable::SettingDescription(&IF1, "LibreVNATCPDriver.IF1", 62000000)); + specificSettings.push_back(Savable::SettingDescription(&ADCprescaler, "LibreVNATCPDriver.ADCprescaler", 128)); + specificSettings.push_back(Savable::SettingDescription(&DFTPhaseInc, "LibreVNATCPDriver.DFTPhaseInc", 1280)); } QString LibreVNATCPDriver::getDriverName() diff --git a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnausbdriver.cpp b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnausbdriver.cpp index d7635ff..6ae110c 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnausbdriver.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnausbdriver.cpp @@ -126,6 +126,17 @@ LibreVNAUSBDriver::LibreVNAUSBDriver() dataBuffer = nullptr; logBuffer = nullptr; m_receiveThread = nullptr; + + specificSettings.push_back(Savable::SettingDescription(&captureRawReceiverValues, "LibreVNAUSBDriver.captureRawReceiverValues", false)); + specificSettings.push_back(Savable::SettingDescription(&harmonicMixing, "LibreVNAUSBDriver.harmonicMixing", false)); + specificSettings.push_back(Savable::SettingDescription(&SASignalID, "LibreVNAUSBDriver.signalID", true)); + specificSettings.push_back(Savable::SettingDescription(&VNASuppressInvalidPeaks, "LibreVNAUSBDriver.suppressInvalidPeaks", true)); + specificSettings.push_back(Savable::SettingDescription(&VNAAdjustPowerLevel, "LibreVNAUSBDriver.adjustPowerLevel", false)); + specificSettings.push_back(Savable::SettingDescription(&SAUseDFT, "LibreVNAUSBDriver.useDFT", true)); + specificSettings.push_back(Savable::SettingDescription(&SARBWLimitForDFT, "LibreVNAUSBDriver.RBWlimitDFT", 3000)); + specificSettings.push_back(Savable::SettingDescription(&IF1, "LibreVNAUSBDriver.IF1", 62000000)); + specificSettings.push_back(Savable::SettingDescription(&ADCprescaler, "LibreVNAUSBDriver.ADCprescaler", 128)); + specificSettings.push_back(Savable::SettingDescription(&DFTPhaseInc, "LibreVNAUSBDriver.DFTPhaseInc", 1280)); } QString LibreVNAUSBDriver::getDriverName() diff --git a/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.cpp b/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.cpp new file mode 100644 index 0000000..9bf154a --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.cpp @@ -0,0 +1,296 @@ +#include "ssa3000xdriver.h" + +#include "CustomWidgets/informationbox.h" + +#include + +SSA3000XDriver::SSA3000XDriver() +{ + searchAddresses.push_back(QHostAddress("192.168.22.2")); +} + +SSA3000XDriver::~SSA3000XDriver() +{ + +} + +std::set SSA3000XDriver::GetAvailableDevices() +{ + std::set ret; + + // attempt to establish a connection to check if the device is available and extract the serial number + detectedDevices.clear(); + auto sock = QTcpSocket(); + for(auto address : searchAddresses) { + sock.connectToHost(address, 5024); + if(sock.waitForConnected(50)) { + // connection successful + sock.waitForReadyRead(100); + auto line = QString(sock.readLine()); + if(line.startsWith("Welcome to the SCPI instrument 'Siglent SSA3")) { + // throw away command prompt ">>" + sock.readLine(); + // looks like we are connected to the correct instrument, request serial number + sock.write("*IDN?\r\n"); + sock.waitForReadyRead(100); + sock.read(1); + sock.waitForReadyRead(100); + line = QString(sock.readLine()); + auto fields = line.split(","); + if(fields.size() == 4) { + detectedDevices[fields[2]] = address; + ret.insert(fields[2]); + } + } + sock.disconnect(); + } else { + qDebug() << "SSA3000X failed to connect"; + } + } + return ret; +} + +bool SSA3000XDriver::connectTo(QString serial) +{ + if(connected) { + disconnect(); + } + + // check if this device is actually available + QHostAddress address; + bool available = false; + for(auto d : detectedDevices) { + if(d.first == serial) { + address = d.second; + available = true; + break; + } + } + if(!available) { + // no location information about this device available + return false; + } + + dataSocket.connectToHost(address, 5025); + + // check if connection succeeds + if(!dataSocket.waitForConnected(1000)) { + // socket failed + dataSocket.close(); + InformationBox::ShowError("Error", "TCP connection timed out"); + return false; + } + + // grab model information + dataSocket.write("*IDN?\r\n"); + dataSocket.waitForReadyRead(100); + auto line = QString(dataSocket.readLine()); + auto fields = line.split(","); + if(fields.size() != 4) { + dataSocket.close(); + InformationBox::ShowError("Error", "Invalid *IDN? response"); + return false; + } + + this->serial = fields[2]; + + info = Info(); + info.hardware_version = fields[1]; + info.firmware_version = fields[3].trimmed(); + info.supportedFeatures.insert(DeviceDriver::Feature::SA); + info.supportedFeatures.insert(DeviceDriver::Feature::SATrackingGenerator); + info.supportedFeatures.insert(DeviceDriver::Feature::Generator); + info.supportedFeatures.insert(DeviceDriver::Feature::ExtRefIn); + + double maxFreq = 0; + if(info.hardware_version == "SSA3032X") { + maxFreq = 3200000000; + } else if(info.hardware_version == "SSA3021X") { + maxFreq = 2100000000; + } else { + dataSocket.close(); + InformationBox::ShowError("Error", "Invalid hardware version: " + info.hardware_version); + return false; + } + + info.Limits.SA.ports = 1; + info.Limits.SA.minFreq = 0; + info.Limits.SA.maxFreq = maxFreq; + info.Limits.SA.minRBW = 1; + info.Limits.SA.maxRBW = 1000000; + info.Limits.SA.mindBm = -20; + info.Limits.SA.maxdBm = 0; + info.Limits.Generator.ports = 1; + info.Limits.Generator.minFreq = 0; + info.Limits.Generator.maxFreq = maxFreq; + info.Limits.Generator.mindBm = -20; + info.Limits.Generator.maxdBm = 0; + + connected = true; + + // reset to default configuration + dataSocket.write("*RST\r\n"); + + emit InfoUpdated(); + +// // sockets are connected now +// dataBuffer.clear(); +// logBuffer.clear(); +// connect(&dataSocket, qOverload(&QTcpSocket::error), this, &LibreVNATCPDriver::ConnectionLost, Qt::QueuedConnection); +// connect(&logSocket, qOverload(&QTcpSocket::error), this, &LibreVNATCPDriver::ConnectionLost, Qt::QueuedConnection); + +// qInfo() << "TCP connection established" << flush; +// this->serial = serial; +// connected = true; + +// connect(&dataSocket, &QTcpSocket::readyRead, this, &LibreVNATCPDriver::ReceivedData, Qt::UniqueConnection); +// connect(&logSocket, &QTcpSocket::readyRead, this, &LibreVNATCPDriver::ReceivedLog, Qt::UniqueConnection); +// connect(&transmissionTimer, &QTimer::timeout, this, &LibreVNATCPDriver::transmissionTimeout, Qt::UniqueConnection); +// connect(this, &LibreVNATCPDriver::receivedAnswer, this, &LibreVNATCPDriver::transmissionFinished, static_cast(Qt::QueuedConnection | Qt::UniqueConnection)); +// connect(this, &LibreVNATCPDriver::receivedPacket, this, &LibreVNATCPDriver::handleReceivedPacket, static_cast(Qt::QueuedConnection | Qt::UniqueConnection)); +// transmissionTimer.setSingleShot(true); +// transmissionActive = false; + +// sendWithoutPayload(Protocol::PacketType::RequestDeviceInfo); +// sendWithoutPayload(Protocol::PacketType::RequestDeviceStatus); +// updateIFFrequencies(); + return true; +} + +void SSA3000XDriver::disconnect() +{ + dataSocket.close(); + connected = false; +} + +DeviceDriver::Info SSA3000XDriver::getInfo() +{ + return info; +} + +std::set SSA3000XDriver::getFlags() +{ + return std::set(); +} + +QString SSA3000XDriver::getStatus() +{ + return ""; +} + +QStringList SSA3000XDriver::availableSAMeasurements() +{ + return {"PORT1"}; +} + +bool SSA3000XDriver::setSA(const DeviceDriver::SASettings &s, std::function cb) +{ + if(!connected) { + return false; + } + write(":FREQ:STAR "+QString::number(s.freqStart)); + write(":FREQ:STOP "+QString::number(s.freqStop)); + write(":BWID "+QString::number(s.RBW)); + write(":FREQ:STAR "+QString::number(s.freqStart)); + write(":FREQ:STAR "+QString::number(s.freqStart)); + write(":FREQ:STAR "+QString::number(s.freqStart)); + + QString windowName = ""; + switch(s.window) { + case SASettings::Window::FlatTop: + windowName = "FLATtop"; + break; + case SASettings::Window::Hann: + windowName = "HANNing"; + break; + case SASettings::Window::Kaiser: + windowName = "HAMMing"; // kaiser is not available + break; + case SASettings::Window::None: + windowName = "RECTangular"; + break; + } + write(":DDEM:FFT:WIND "+windowName); + + QString detName = ""; + switch(s.detector) { + case SASettings::Detector::Average: + detName = "AVERage"; + break; + case SASettings::Detector::Normal: + detName = "NORMAL"; + break; + case SASettings::Detector::NPeak: + detName = "NEGative"; + break; + case SASettings::Detector::PPeak: + detName = "POSitive"; + break; + case SASettings::Detector::Sample: + detName = "SAMPle"; + break; + } + write(":DET:TRAC:FUNC "+detName); + + write(":OUTP:STAT " + (s.trackingGenerator ? QString("ON") : QString("OFF"))); + write(":SOUR:POW "+QString::number((int) s.trackingPower)); + + return true; +} + +unsigned int SSA3000XDriver::getSApoints() +{ + return 0; +} + +QStringList SSA3000XDriver::availableSGPorts() +{ + return {"PORT1"}; +} + +bool SSA3000XDriver::setSG(const DeviceDriver::SGSettings &s) +{ + if(!connected) { + return false; + } + if(s.port == 1) { + write(":FREQ:SPAN:ZERO"); + write(":FREQ:CENT "+QString::number(s.freq)); + write(":OUTP:STAT ON"); + write(":SOUR:POW "+QString::number((int) s.dBm)); + } else { + write(":OUTP:STAT OFF"); + } + return true; +} + +bool SSA3000XDriver::setIdle(std::function cb) +{ + if(!connected) { + return false; + } + write("*RST\r\n"); + if(cb) { + cb(true); + } +} + +QStringList SSA3000XDriver::availableExtRefInSettings() +{ + return {""}; +} + +QStringList SSA3000XDriver::availableExtRefOutSettings() +{ + return {""}; +} + +bool SSA3000XDriver::setExtRef(QString option_in, QString option_out) +{ + return false; +} + +void SSA3000XDriver::write(QString s) +{ + dataSocket.write(QString(s + "\r\n").toLocal8Bit()); +} diff --git a/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.h b/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.h new file mode 100644 index 0000000..4c07214 --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.h @@ -0,0 +1,154 @@ +#ifndef SSA3000XDRIVER_H +#define SSA3000XDRIVER_H + +#include "../devicedriver.h" + +#include +#include + +class SSA3000XDriver : public DeviceDriver +{ +public: + SSA3000XDriver(); + virtual ~SSA3000XDriver(); + + /** + * @brief Returns the driver name. It must be unique across all implemented drivers and is used to identify the driver + * @return driver name + */ + virtual QString getDriverName() override {return "SSA3000X";} + /** + * @brief Lists all available devices by their serial numbers + * @return Serial numbers of detected devices + */ + virtual std::set GetAvailableDevices() override; + +protected: + /** + * @brief Connects to a device, given by its serial number + * + * @param serial Serial number of device that should be connected to + * @return true if connection successful, otherwise false + */ + virtual bool connectTo(QString serial) override; + /** + * @brief Disconnects from device. Has no effect if no device was connected + */ + virtual void disconnect() override; + +public: + /** + * @brief Returns the serial number of the connected device + * @return Serial number of connected device (empty string if no device is connected) + */ + virtual QString getSerial() override {return serial;} + + /** + * @brief Returns the device information. This function will be called when a device has been connected. Its return value must be valid + * directly after returning from DeviceDriver::connectTo() + * + * Emit the InfoUpdate() signal whenever the return value of this function changes. + * + * @return Device information + */ + virtual Info getInfo() override; + + /** + * @brief Returns a set of all active flags + * + * There is also a convenience function to check a specific flag, see DeviceDriver::asserted() + * + * @return Set of active flags + */ + virtual std::set getFlags() override; + + /** + * @brief Returns the device status string. It will be displayed in the status bar of the application + * + * Emit the StatusUpdated() signal whenever the return value of this function changes + * + * @return Status string + */ + virtual QString getStatus() override; + + /** + * @brief Names of available measurements. + * + * The names must be identical to the names used in the returned SAMeasurement. + * Typically the port names, e.g. this function may return {"PORT1","PORT2"} but any other names are also allowed. + * + * @return List of available SA measurement parameters + */ + virtual QStringList availableSAMeasurements() override; + /** + * @brief Configures the SA and starts a sweep + * @param s SA settings + * @param cb Callback, must be called after the SA has been configured + * @return true if configuration successful, false otherwise + */ + virtual bool setSA(const SASettings &s, std::function cb = nullptr) override; + + /** + * @brief Returns the number of points in one spectrum analyzer sweep (as configured by the last setSA() call) + * @return Number of points in the sweep + */ + virtual unsigned int getSApoints() override; + + /** + * @brief Names of available generator ports. + * + * Typically the port names, e.g. this function may return {"PORT1","PORT2"} but any other names are also allowed. + * + * @return List of available SA measurement parameters + */ + virtual QStringList availableSGPorts() override; + /** + * @brief Configures the generator + * @param s Generator settings + * @return true if configuration successful, false otherwise + */ + virtual bool setSG(const SGSettings &s) override; + + /** + * @brief Sets the device to idle + * + * Stops all sweeps and signal generation + * + * @param cb Callback, must be called after the device has stopped all operations + * @return true if configuration successful, false otherwise + */ + virtual bool setIdle(std::function cb = nullptr) override; + + /** + * @brief Returns the available options for the external reference input + * @return External reference input options + */ + virtual QStringList availableExtRefInSettings() override; + + /** + * @brief Returns the available options for the external reference output + * @return External reference output options + */ + virtual QStringList availableExtRefOutSettings() override; + + /** + * @brief Configures the external reference input/output + * @param option_in Reference input option (one of the options returned by availableExtRefInSettings()) + * @param option_out Reference output option (one of the options returned by availableExtRefOutSettings()) + * @return true if configuration successful, false otherwise + */ + virtual bool setExtRef(QString option_in, QString option_out) override; + +private: + void write(QString s); + QString serial; + QTcpSocket dataSocket; + + bool connected; + Info info; + + std::vector searchAddresses; + std::map detectedDevices; +}; + +#endif // SSA3000XDRIVER_H diff --git a/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.cpp b/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.cpp index 3471753..4748f0c 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.cpp @@ -2,6 +2,8 @@ #include "LibreVNA/librevnatcpdriver.h" #include "LibreVNA/librevnausbdriver.h" +#include "LibreVNA/Compound/compounddriver.h" +#include "SSA3000X/ssa3000xdriver.h" DeviceDriver *DeviceDriver::activeDriver = nullptr; @@ -19,17 +21,23 @@ std::vector DeviceDriver::getDrivers() // first function call ret.push_back(new LibreVNAUSBDriver); ret.push_back(new LibreVNATCPDriver); + ret.push_back(new CompoundDriver); + ret.push_back(new SSA3000XDriver); } return ret; } -bool DeviceDriver::connectDevice(QString serial) +bool DeviceDriver::connectDevice(QString serial, bool isIndepedentDriver) { - if(activeDriver && activeDriver != this) { - activeDriver->disconnect(); + if(!isIndepedentDriver) { + if(activeDriver && activeDriver != this) { + activeDriver->disconnect(); + } } if(connectTo(serial)) { - activeDriver = this; + if(!isIndepedentDriver) { + activeDriver = this; + } return true; } else { return false; @@ -123,3 +131,41 @@ DeviceDriver::Info::Info() Limits.SA.minRBW = 1; Limits.SA.maxRBW = 1000000; } + +void DeviceDriver::Info::subset(const DeviceDriver::Info &info) +{ + if (info.firmware_version != firmware_version) { + firmware_version = "Mixed"; + } + if (info.hardware_version != hardware_version) { + hardware_version = "Mixed"; + } + + Limits.VNA.ports += info.Limits.VNA.ports; + Limits.VNA.minFreq = std::max(Limits.VNA.minFreq, info.Limits.VNA.minFreq); + Limits.VNA.maxFreq = std::min(Limits.VNA.maxFreq, info.Limits.VNA.maxFreq); + Limits.VNA.mindBm = std::max(Limits.VNA.mindBm, info.Limits.VNA.mindBm); + Limits.VNA.maxdBm = std::min(Limits.VNA.maxdBm, info.Limits.VNA.maxdBm); + Limits.VNA.minIFBW = std::max(Limits.VNA.minIFBW, info.Limits.VNA.minIFBW); + Limits.VNA.maxIFBW = std::min(Limits.VNA.maxIFBW, info.Limits.VNA.maxIFBW); + Limits.VNA.maxPoints = std::min(Limits.VNA.maxPoints, info.Limits.VNA.maxPoints); + + Limits.Generator.ports += info.Limits.Generator.ports; + Limits.Generator.minFreq = std::max(Limits.Generator.minFreq, info.Limits.Generator.minFreq); + Limits.Generator.maxFreq = std::min(Limits.Generator.maxFreq, info.Limits.Generator.maxFreq); + Limits.Generator.mindBm = std::max(Limits.Generator.mindBm, info.Limits.Generator.mindBm); + Limits.Generator.maxdBm = std::min(Limits.Generator.maxdBm, info.Limits.Generator.maxdBm); + + Limits.SA.ports += info.Limits.SA.ports; + Limits.SA.minFreq = std::max(Limits.SA.minFreq, info.Limits.SA.minFreq); + Limits.SA.maxFreq = std::min(Limits.SA.maxFreq, info.Limits.SA.maxFreq); + Limits.SA.mindBm = std::max(Limits.SA.mindBm, info.Limits.SA.mindBm); + Limits.SA.maxdBm = std::min(Limits.SA.maxdBm, info.Limits.SA.maxdBm); + Limits.SA.minRBW = std::max(Limits.SA.minRBW, info.Limits.SA.minRBW); + Limits.SA.maxRBW = std::min(Limits.SA.maxRBW, info.Limits.SA.maxRBW); + + std::set intersectFeatures; + std::set_intersection(supportedFeatures.begin(), supportedFeatures.end(), info.supportedFeatures.begin(), info.supportedFeatures.end(), + std::inserter(intersectFeatures, intersectFeatures.begin())); + supportedFeatures = intersectFeatures; +} diff --git a/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.h b/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.h index 6cc6bf0..b04c99c 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.h +++ b/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.h @@ -51,7 +51,7 @@ protected: * @param serial Serial number of device that should be connected to * @return true if connection successful, otherwise false */ - virtual bool connectTo(QString getSerial) = 0; + virtual bool connectTo(QString serial) = 0; /** * @brief Disconnects from device. Has no effect if no device was connected */ @@ -120,6 +120,7 @@ public: double mindBm, maxdBm; } SA; } Limits; + void subset(const Info &info); }; /** @@ -486,7 +487,7 @@ signals: void releaseControl(); public: - bool connectDevice(QString serial); + bool connectDevice(QString serial, bool isIndepedentDriver = false); void disconnectDevice(); static DeviceDriver* getActiveDriver() {return activeDriver;} static unsigned int SApoints(); diff --git a/Software/PC_Application/LibreVNA-GUI/Generator/generator.cpp b/Software/PC_Application/LibreVNA-GUI/Generator/generator.cpp index 2853331..b7bac7e 100644 --- a/Software/PC_Application/LibreVNA-GUI/Generator/generator.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Generator/generator.cpp @@ -1,5 +1,7 @@ #include "generator.h" +#include "CustomWidgets/informationbox.h" + #include Generator::Generator(AppWindow *window, QString name) @@ -37,6 +39,10 @@ void Generator::deactivate() void Generator::initializeDevice() { + if(!window->getDevice()->supports(DeviceDriver::Feature::Generator)) { + InformationBox::ShowError("Unsupported", "The connected device does not support generator mode"); + return; + } updateDevice(); } diff --git a/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro b/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro index 1a8c999..b2d6b7f 100644 --- a/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro +++ b/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro @@ -19,6 +19,9 @@ HEADERS += \ CustomWidgets/toggleswitch.h \ CustomWidgets/touchstoneimport.h \ CustomWidgets/tracesetselector.h \ + Device/LibreVNA/Compound/compounddevice.h \ + Device/LibreVNA/Compound/compounddeviceeditdialog.h \ + Device/LibreVNA/Compound/compounddriver.h \ Device/LibreVNA/amplitudecaldialog.h \ Device/LibreVNA/firmwareupdatedialog.h \ Device/LibreVNA/frequencycaldialog.h \ @@ -28,8 +31,7 @@ HEADERS += \ Device/LibreVNA/manualcontroldialog.h \ Device/LibreVNA/receivercaldialog.h \ Device/LibreVNA/sourcecaldialog.h \ - Device/compounddevice.h \ - Device/compounddeviceeditdialog.h \ + Device/SSA3000X/ssa3000xdriver.h \ Device/device.h \ Device/devicedriver.h \ Device/devicelog.h \ @@ -171,6 +173,9 @@ SOURCES += \ CustomWidgets/toggleswitch.cpp \ CustomWidgets/touchstoneimport.cpp \ CustomWidgets/tracesetselector.cpp \ + Device/LibreVNA/Compound/compounddevice.cpp \ + Device/LibreVNA/Compound/compounddeviceeditdialog.cpp \ + Device/LibreVNA/Compound/compounddriver.cpp \ Device/LibreVNA/amplitudecaldialog.cpp \ Device/LibreVNA/firmwareupdatedialog.cpp \ Device/LibreVNA/frequencycaldialog.cpp \ @@ -180,8 +185,7 @@ SOURCES += \ Device/LibreVNA/manualcontroldialog.cpp \ Device/LibreVNA/receivercaldialog.cpp \ Device/LibreVNA/sourcecaldialog.cpp \ - Device/compounddevice.cpp \ - Device/compounddeviceeditdialog.cpp \ + Device/SSA3000X/ssa3000xdriver.cpp \ Device/device.cpp \ Device/devicedriver.cpp \ Device/devicelog.cpp \ @@ -313,6 +317,8 @@ FORMS += \ CustomWidgets/jsonpickerdialog.ui \ CustomWidgets/tilewidget.ui \ CustomWidgets/touchstoneimport.ui \ + Device/LibreVNA/Compound/compounddeviceeditdialog.ui \ + Device/LibreVNA/Compound/compounddriversettingswidget.ui \ Device/LibreVNA/addamplitudepointsdialog.ui \ Device/LibreVNA/amplitudecaldialog.ui \ Device/LibreVNA/automaticamplitudedialog.ui \ @@ -320,7 +326,6 @@ FORMS += \ Device/LibreVNA/frequencycaldialog.ui \ Device/LibreVNA/librevnadriversettingswidget.ui \ Device/LibreVNA/manualcontroldialog.ui \ - Device/compounddeviceeditdialog.ui \ Device/devicelog.ui \ Device/deviceusblogview.ui \ Generator/signalgenwidget.ui \ diff --git a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp index 34b7897..50bb925 100644 --- a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -173,7 +173,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name) // Acquisition toolbar auto tb_acq = new QToolBar("Acquisition"); - auto eBandwidth = new SIUnitEdit("Hz", " k", 3); + auto eBandwidth = new SIUnitEdit("Hz", " kM", 3); eBandwidth->setFixedWidth(70); eBandwidth->setToolTip("RBW"); connect(eBandwidth, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetRBW); @@ -221,7 +221,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name) toolbars.insert(tb_acq); // Tracking generator toolbar - auto tb_trackgen = new QToolBar("Tracking Generator"); + tb_trackgen = new QToolBar("Tracking Generator"); auto cbTrackGenEnable = new QCheckBox("Tracking Generator"); connect(cbTrackGenEnable, &QCheckBox::toggled, this, &SpectrumAnalyzer::SetTGEnabled); connect(this, &SpectrumAnalyzer::TGStateChanged, cbTrackGenEnable, &QCheckBox::setChecked); @@ -231,8 +231,12 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name) cbTrackGenPort->addItem("Port 1"); cbTrackGenPort->addItem("Port 2"); cbTrackGenPort->setCurrentIndex(0); - connect(cbTrackGenPort, qOverload(&QComboBox::currentIndexChanged), this, &SpectrumAnalyzer::SetTGPort); - connect(this, &SpectrumAnalyzer::TGPortChanged, cbTrackGenPort, qOverload(&QComboBox::setCurrentIndex)); + connect(cbTrackGenPort, qOverload(&QComboBox::currentIndexChanged), this, [=](int index) { + SetTGPort(index + 1); + }); + connect(this, &SpectrumAnalyzer::TGPortChanged, this, [=](int port) { + cbTrackGenPort->setCurrentIndex(port - 1); + }); tb_trackgen->addWidget(cbTrackGenPort); auto dbm = new QDoubleSpinBox(); @@ -247,7 +251,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name) tb_trackgen->addWidget(new QLabel("Level:")); tb_trackgen->addWidget(dbm); - auto tgOffset = new SIUnitEdit("Hz", " kMG", 6); + tgOffset = new SIUnitEdit("Hz", " kMG", 6); tgOffset->setFixedWidth(width); tgOffset->setToolTip("Tracking generator offset"); connect(tgOffset, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetTGOffset); @@ -330,6 +334,20 @@ void SpectrumAnalyzer::deactivate() void SpectrumAnalyzer::initializeDevice() { + if(!window->getDevice()->supports(DeviceDriver::Feature::SA)) { + InformationBox::ShowError("Unsupported", "The connected device does not support spectrum analyzer mode"); + return; + } + bool hasTG = window->getDevice()->supports(DeviceDriver::Feature::SATrackingGenerator); + bool hasTGoffset = window->getDevice()->supports(DeviceDriver::Feature::SATrackingOffset); + tb_trackgen->setEnabled(hasTG); + if(hasTG) { + SetTGEnabled(settings.trackingGenerator); + if(!hasTGoffset) { + tgOffset->setValue(0); + tgOffset->setEnabled(false); + } + } connect(window->getDevice(), &DeviceDriver::SAmeasurementReceived, this, &SpectrumAnalyzer::NewDatapoint, Qt::UniqueConnection); // Configure initial state of device @@ -358,9 +376,9 @@ nlohmann::json SpectrumAnalyzer::toJSON() sweep["acquisition"] = acq; nlohmann::json tracking; tracking["enabled"] = settings.trackingGenerator ? true : false; - tracking["port"] = settings.trackingPort ? 2 : 1; + tracking["port"] = settings.trackingPort; tracking["offset"] = settings.trackingOffset; - tracking["power"] = (double) settings.trackingPower / 100.0; // convert to dBm + tracking["power"] = settings.trackingPower; sweep["trackingGenerator"] = tracking; if(normalize.active) { @@ -426,8 +444,7 @@ void SpectrumAnalyzer::fromJSON(nlohmann::json j) auto tracking = sweep["trackingGenerator"]; SetTGEnabled(tracking.value("enabled", settings.trackingGenerator ? true : false)); int port = tracking.value("port", 1); - // Function expects 0 for port1, 1 for port2 - SetTGPort(port - 1); + SetTGPort(port); SetTGLevel(tracking.value("power", settings.trackingPower)); SetTGOffset(tracking.value("offset", settings.trackingOffset)); } @@ -699,7 +716,7 @@ void SpectrumAnalyzer::SetTGEnabled(bool enabled) void SpectrumAnalyzer::SetTGPort(int port) { - if(port < 0 || port >= cbTrackGenPort->count()) { + if(port < 1 || port > cbTrackGenPort->count()) { return; } if(port != settings.trackingPort) { @@ -719,7 +736,7 @@ void SpectrumAnalyzer::SetTGLevel(double level) level = DeviceDriver::getInfo(window->getDevice()).Limits.SA.mindBm; } emit TGLevelChanged(level); - settings.trackingPower = level * 100; + settings.trackingPower = level; if(settings.trackingGenerator) { SettingsChanged(); } @@ -1058,7 +1075,7 @@ void SpectrumAnalyzer::SetupSCPI() if(!SCPI::paramToULongLong(params, 0, newval)) { return SCPI::getResultName(SCPI::Result::Error); } else if(newval > 0 && newval <= DeviceDriver::getInfo(window->getDevice()).Limits.SA.ports){ - SetTGPort(newval-1); + SetTGPort(newval); return SCPI::getResultName(SCPI::Result::Empty); } else { // invalid port number @@ -1066,7 +1083,7 @@ void SpectrumAnalyzer::SetupSCPI() } return SCPI::getResultName(SCPI::Result::Empty); }, [=](QStringList) -> QString { - return QString::number(settings.trackingPort+1); + return QString::number(settings.trackingPort); })); scpi_tg->add(new SCPICommand("LVL", [=](QStringList params) -> QString { double newval; @@ -1077,7 +1094,7 @@ void SpectrumAnalyzer::SetupSCPI() return SCPI::getResultName(SCPI::Result::Empty); } }, [=](QStringList) -> QString { - return QString::number(settings.trackingPower / 100.0); + return QString::number(settings.trackingPower); })); scpi_tg->add(new SCPICommand("OFFset", [=](QStringList params) -> QString { long newval; diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/tracemodel.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/tracemodel.cpp index 5386f16..c57c172 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/tracemodel.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/tracemodel.cpp @@ -224,7 +224,6 @@ std::vector TraceModel::getLiveTraces() const bool TraceModel::PortExcitationRequired(int port) { - port++; for(auto t : traces) { if(t->getSource() == Trace::Source::Live && !t->isPaused()) { // this trace needs measurements from VNA, check if port has to be excited for its measurement diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp b/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp index f5c39ed..b68857e 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp +++ b/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp @@ -403,7 +403,7 @@ VNA::VNA(AppWindow *window, QString name) tb_acq->addWidget(new QLabel("Points:")); tb_acq->addWidget(points); - auto eBandwidth = new SIUnitEdit("Hz", " k", 3); + auto eBandwidth = new SIUnitEdit("Hz", " kM", 3); eBandwidth->setFixedWidth(70); eBandwidth->setToolTip("IF bandwidth"); connect(eBandwidth, &SIUnitEdit::valueChanged, this, &VNA::SetIFBandwidth); @@ -675,6 +675,10 @@ void VNA::deactivate() void VNA::initializeDevice() { + if(!window->getDevice()->supports(DeviceDriver::Feature::VNA)) { + InformationBox::ShowError("Unsupported", "The connected device does not support VNA mode"); + return; + } defaultCalMenu->setEnabled(true); connect(window->getDevice(), &DeviceDriver::VNAmeasurementReceived, this, &VNA::NewDatapoint, Qt::UniqueConnection); // Check if default calibration exists and attempt to load it @@ -1680,11 +1684,11 @@ void VNA::ConfigureDevice(bool resetTraces, std::function cb) DeviceDriver::VNASettings s = {}; s.IFBW = settings.bandwidth; if(Preferences::getInstance().Acquisition.alwaysExciteAllPorts) { - for(unsigned int i=0;igetDevice()).Limits.VNA.ports;i++) { + for(unsigned int i=1;i<=DeviceDriver::getInfo(window->getDevice()).Limits.VNA.ports;i++) { s.excitedPorts.push_back(i); } } else { - for(unsigned int i=0;igetDevice()).Limits.VNA.ports;i++) { + for(unsigned int i=1;i<=DeviceDriver::getInfo(window->getDevice()).Limits.VNA.ports;i++) { if(traceModel.PortExcitationRequired(i)) s.excitedPorts.push_back(i); } diff --git a/Software/PC_Application/LibreVNA-GUI/appwindow.cpp b/Software/PC_Application/LibreVNA-GUI/appwindow.cpp index 2c0f776..7491902 100644 --- a/Software/PC_Application/LibreVNA-GUI/appwindow.cpp +++ b/Software/PC_Application/LibreVNA-GUI/appwindow.cpp @@ -100,11 +100,6 @@ AppWindow::AppWindow(QWidget *parent) Preferences::getInstance().load(); } - for(auto driver : DeviceDriver::getDrivers()) { - driver->registerTypes(); - Preferences::getInstance().load(driver->driverSpecificSettings()); - } - device = nullptr; // vdevice = nullptr; modeHandler = nullptr; @@ -254,6 +249,8 @@ void AppWindow::SetupMenu() auto SCPIenabled = p.SCPIServer.enabled; auto SCPIport = p.SCPIServer.port; p.edit(); + // store the updated settings + p.store(); if(SCPIenabled != p.SCPIServer.enabled || SCPIport != p.SCPIServer.port) { StopTCPServer(); if(p.SCPIServer.enabled) { @@ -321,9 +318,6 @@ void AppWindow::closeEvent(QCloseEvent *event) delete modeHandler; modeHandler = nullptr; pref.store(); - for(auto driver : DeviceDriver::getDrivers()) { - Preferences::getInstance().store(driver->driverSpecificSettings()); - } for(auto driver : DeviceDriver::getDrivers()) { delete driver; } @@ -350,9 +344,11 @@ bool AppWindow::ConnectToDevice(QString serial, DeviceDriver *driver) } if(d->GetAvailableDevices().count(serial)) { // this driver can connect to the device + connect(d, &DeviceDriver::InfoUpdated, this, &AppWindow::DeviceInfoUpdated, Qt::QueuedConnection); if(d->connectDevice(serial)) { device = d; } else { + disconnect(d, nullptr, this, nullptr); break; } } @@ -363,7 +359,6 @@ bool AppWindow::ConnectToDevice(QString serial, DeviceDriver *driver) return false; } UpdateStatusBar(AppWindow::DeviceStatusBar::Connected); - connect(device, &DeviceDriver::InfoUpdated, this, &AppWindow::DeviceInfoUpdated); connect(device, &DeviceDriver::LogLineReceived, &deviceLog, &DeviceLog::addLine); connect(device, &DeviceDriver::ConnectionLost, this, &AppWindow::DeviceConnectionLost); connect(device, &DeviceDriver::StatusUpdated, this, &AppWindow::DeviceStatusUpdated); @@ -420,9 +415,9 @@ bool AppWindow::ConnectToDevice(QString serial, DeviceDriver *driver) // vdevice->initialize(); // UpdateAcquisitionFrequencies(); - if (modeHandler->getActiveMode()) { - modeHandler->getActiveMode()->initializeDevice(); - } +// if (modeHandler->getActiveMode()) { +// modeHandler->getActiveMode()->initializeDevice(); +// } return true; } catch (const runtime_error &e) { qWarning() << "Failed to connect:" << e.what(); @@ -1370,7 +1365,7 @@ void AppWindow::UpdateStatusBar(DeviceStatusBar status) break; case DeviceStatusBar::Disconnected: lConnectionStatus.setText("No device connected"); - lDeviceInfo.setText("No device information available yet"); + lDeviceInfo.setText("No status information available yet"); break; default: // invalid status diff --git a/Software/PC_Application/LibreVNA-GUI/mode.h b/Software/PC_Application/LibreVNA-GUI/mode.h index ee863f1..22a77a7 100644 --- a/Software/PC_Application/LibreVNA-GUI/mode.h +++ b/Software/PC_Application/LibreVNA-GUI/mode.h @@ -62,6 +62,8 @@ protected: AppWindow *window; std::set actions; std::set toolbars; + QToolBar *tb_trackgen; + SIUnitEdit *tgOffset; std::set docks; private: diff --git a/Software/PC_Application/LibreVNA-GUI/preferences.cpp b/Software/PC_Application/LibreVNA-GUI/preferences.cpp index ca9e9ca..77f03fb 100644 --- a/Software/PC_Application/LibreVNA-GUI/preferences.cpp +++ b/Software/PC_Application/LibreVNA-GUI/preferences.cpp @@ -3,7 +3,7 @@ #include "ui_preferencesdialog.h" #include "CustomWidgets/informationbox.h" #include "appwindow.h" -#include "Device/compounddeviceeditdialog.h" +#include "Device/LibreVNA/Compound/compounddeviceeditdialog.h" #include #include @@ -137,56 +137,6 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) : ui->MarkerShowP1dB->setEnabled(enabled); }); - // Compound device page -// connect(ui->compoundList, &QListWidget::currentRowChanged, [=](){ -// if(VirtualDevice::getConnected() && VirtualDevice::getConnected()->getCompoundDevice() == p->compoundDevices[ui->compoundList->currentRow()]) { -// // can't remove the device we are connected to -// ui->compoundDelete->setEnabled(false); -// } else { -// ui->compoundDelete->setEnabled(true); -// } -// }); - connect(ui->compoundList, &QListWidget::doubleClicked, [=](){ - auto index = ui->compoundList->currentRow(); - if(index >= 0 && index < (int) p->compoundDevices.size()) { - auto d = new CompoundDeviceEditDialog(p->compoundDevices[index]); - connect(d, &QDialog::accepted, [=](){ - ui->compoundList->item(index)->setText(p->compoundDevices[index]->getDesription()); - p->nonTrivialWriting(); - }); - d->show(); - } - }); - connect(ui->compoundAdd, &QPushButton::clicked, [=](){ - auto cd = new CompoundDevice; - auto d = new CompoundDeviceEditDialog(cd); - connect(d, &QDialog::accepted, [=](){ - p->compoundDevices.push_back(cd); - ui->compoundList->addItem(cd->getDesription()); - p->nonTrivialWriting(); - }); - connect(d, &QDialog::rejected, [=](){ - delete cd; - }); - d->show(); - }); -// connect(ui->compoundDelete, &QPushButton::clicked, [=](){ -// auto index = ui->compoundList->currentRow(); -// if(index >= 0 && index < (int) p->compoundDevices.size()) { -// // delete the actual compound device -// if(VirtualDevice::getConnected() && VirtualDevice::getConnected()->getCompoundDevice() == p->compoundDevices[index]) { -// // can't remove the device we are currently connected to -// return; -// } -// delete p->compoundDevices[index]; -// // delete the line in the GUI list -// delete ui->compoundList->takeItem(index); -// // remove compound device from list -// p->compoundDevices.erase(p->compoundDevices.begin() + index); -// p->nonTrivialWriting(); -// } -// }); - // Debug page ui->DebugMaxUSBlogSize->setUnit("B"); ui->DebugMaxUSBlogSize->setPrefixes(" kMG"); @@ -362,10 +312,6 @@ void PreferencesDialog::setInitialGUIState() ui->DebugMaxUSBlogSize->setValue(p->Debug.USBlogSizeLimit); ui->DebugSaveTraceData->setChecked(p->Debug.saveTraceData); - for(auto cd : p->compoundDevices) { - ui->compoundList->addItem(cd->getDesription()); - } - QTreeWidgetItem *item = ui->treeWidget->topLevelItem(0); if (item != nullptr) { ui->treeWidget->setCurrentItem(item); // visually select first item @@ -466,9 +412,7 @@ void PreferencesDialog::updateFromGUI() Preferences::~Preferences() { - for(auto cd : compoundDevices) { - delete cd; - } + } void Preferences::load() @@ -476,6 +420,10 @@ void Preferences::load() // load settings, using default values if not present qInfo() << "Loading preferences"; load(descr); + for(auto driver : DeviceDriver::getDrivers()) { + driver->registerTypes(); + load(driver->driverSpecificSettings()); + } nonTrivialParsing(); } @@ -495,6 +443,9 @@ void Preferences::store() { nonTrivialWriting(); store(descr); + for(auto driver : DeviceDriver::getDrivers()) { + store(driver->driverSpecificSettings()); + } } void Preferences::store(std::vector descr) @@ -515,6 +466,16 @@ void Preferences::edit() } void Preferences::setDefault() +{ + for(auto d : descr) { + d.var.setValue(d.def); + } + for(auto driver : DeviceDriver::getDrivers()) { + setDefault(driver->driverSpecificSettings()); + } +} + +void Preferences::setDefault(std::vector descr) { for(auto d : descr) { d.var.setValue(d.def); @@ -524,39 +485,42 @@ void Preferences::setDefault() void Preferences::fromJSON(nlohmann::json j) { parseJSON(j, descr); + for(auto driver : DeviceDriver::getDrivers()) { + Savable::parseJSON(j, driver->driverSpecificSettings()); + } nonTrivialParsing(); } +static nlohmann::json merge(const nlohmann::json &a, const nlohmann::json &b) +{ + nlohmann::json result = a.flatten(); + nlohmann::json tmp = b.flatten(); + + for (nlohmann::json::iterator it = tmp.begin(); it != tmp.end(); ++it) + { + result[it.key()] = it.value(); + } + + return result.unflatten(); +} + nlohmann::json Preferences::toJSON() { nonTrivialWriting(); - return createJSON(descr); + auto j = createJSON(descr); + for(auto driver : DeviceDriver::getDrivers()) { + j = merge(j, Savable::createJSON(driver->driverSpecificSettings())); + } + + return j; } void Preferences::nonTrivialParsing() { - try { - compoundDevices.clear(); - nlohmann::json jc = nlohmann::json::parse(compoundDeviceJSON.toStdString()); - for(auto j : jc) { - auto cd = new CompoundDevice(); - cd->fromJSON(j); - compoundDevices.push_back(cd); - } - } catch(const exception& e){ - qDebug() << "Failed to parse compound device string: " << e.what(); - } + } void Preferences::nonTrivialWriting() { - if(compoundDevices.size() > 0) { - nlohmann::json j; - for(auto cd : compoundDevices) { - j.push_back(cd->toJSON()); - } - compoundDeviceJSON = QString::fromStdString(j.dump()); - } else { - compoundDeviceJSON = "[]"; - } + } diff --git a/Software/PC_Application/LibreVNA-GUI/preferences.h b/Software/PC_Application/LibreVNA-GUI/preferences.h index a261ab8..83116a5 100644 --- a/Software/PC_Application/LibreVNA-GUI/preferences.h +++ b/Software/PC_Application/LibreVNA-GUI/preferences.h @@ -4,7 +4,7 @@ #include "Util/qpointervariant.h" #include "savable.h" -#include "Device/compounddevice.h" +#include "Device/LibreVNA/Compound/compounddevice.h" #include #include @@ -56,6 +56,7 @@ public: void store(std::vector descr); void edit(); void setDefault(); + void setDefault(std::vector descr); void manualTCPport() { TCPoverride = true; } @@ -159,9 +160,6 @@ public: bool TCPoverride; // in case of manual port specification via command line - QString compoundDeviceJSON; - std::vector compoundDevices; - void fromJSON(nlohmann::json j) override; nlohmann::json toJSON() override; @@ -258,7 +256,6 @@ private: {&SCPIServer.port, "SCPIServer.port", 19542}, {&Debug.USBlogSizeLimit, "Debug.USBlogSizeLimit", 10000000.0}, {&Debug.saveTraceData, "Debug.saveTraceData", false}, - {&compoundDeviceJSON, "compoundDeviceJSON", "[]"}, }}; }; diff --git a/Software/PC_Application/LibreVNA-GUI/preferencesdialog.ui b/Software/PC_Application/LibreVNA-GUI/preferencesdialog.ui index 0d2b694..a73de49 100644 --- a/Software/PC_Application/LibreVNA-GUI/preferencesdialog.ui +++ b/Software/PC_Application/LibreVNA-GUI/preferencesdialog.ui @@ -13,1869 +13,1792 @@ Preferences - + - - - - - - 0 - 0 - + + + Qt::Horizontal + + + + + 0 + 0 + + + + + 250 + 16777215 + + + + true + + + false + + + + 1 - - - 180 - 16777215 - + + + + Startup - - true + + + + Acquisition - - false - - - - 1 - - - - - Startup - - - - - Acquisition - - - - - Graphs - - - - - Marker - - - - - SCPI Server - - - - - Compound Devices - - - - - Debug - - - - - Device Drivers - - - - - - - - - 0 - 0 - + + + + Graphs - - - 16777215 - 16777215 - + + + + Marker - - 6 + + + + SCPI Server - - - - - - true + + + + Debug + + + + + Device Drivers + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + 3 + + + + + + + true + + + + + 0 + 0 + 683 + 920 + - - - - 0 - 0 - 749 - 920 - - - - - - - - - When starting the application... - - - - - - Autoconnect to the first device available - - - - - - - Set to... - - - - - - - - - - - Last used - - - - - - - Default values - - - - - - - Setup file - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - 2 - - - - - - - The last used sweep settings (e.g. span, bandwidth,...) will be remembered. The graphs, traces and markers will not be remembered and the default configuration for them is used on startup. - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - All settings (sweep settings as well as graphs, markers and traces) will be taken from the specified file. If the autosave option is checked, the current state of the GUI is stored in this file during shutdown as well. - - - true - - - - - - - - - File location: - - - - - - - - - - - 0 - 0 - - - - - 20 - 16777215 - - - - ... - - - - - - - - - Autosave to this file on shutdown - - - - - - - Qt::Vertical - - - - 20 - 545 - - - - - - - - - - - - These default values for the sweep settings will be used. The graphs, traces and markers will not be remembered and the default configuration for them is used on startup. - - - true - - - - - - - Vector Network Analyzer - - - - - - - - Type: - - - - - - - - Frequency Sweep - - - - - Power Sweep - - - - - - - - - - - - Frequency Sweep: - - - - - - Start: - - - - - - - - - - Stop: - - - - - - - - - - Simulus level: - - - - - - - dbm - - - -42.000000000000000 - - - 0.000000000000000 - - - 0.250000000000000 - - - - - - - - - - Power Sweep: - - - - - - Start: - - - - - - - dbm - - - -42.000000000000000 - - - 0.000000000000000 - - - 0.250000000000000 - - - - - - - Stop: - - - - - - - dbm - - - -42.000000000000000 - - - 0.000000000000000 - - - 0.250000000000000 - - - - - - - Frequency: - - - - - - - - - - - - - - - - - Points: - - - - - - - 1 - - - 4501 - - - 501 - - - - - - - IF bandwitdh: - - - - - - - - - - Averaging: - - - - - - - 1 - - - 99 - - - - - - - - - - - - Signal Generator - - - - - - Frequency: - - - - - - - - - - Output level: - - - - - - - dbm - - - -42.000000000000000 - - - 0.000000000000000 - - - 0.250000000000000 - - - - - - - - - - Spectrum Analyzer - - - - - - Start: - - - - - - - - - - Stop: - - - - - - - - - - RBW: - - - - - - - - - - Window: - - - - - - - - None - - - - - Kaiser - - - - - Hann - - - - - Flat Top - - - - - - - - Detector: - - - - - - - - +Peak - - - - - -Peak - - - - - Sample - - - - - Normal - - - - - Average - - - - - - - - Signal Identification: - - - - - - - - - - - - - - Averaging: - - - - - - - 1 - - - 99 - - - - - - - - - - - - - - - - - Qt::Vertical - - - - 17 - 37 - - - - - - - - - - - - - - - - - - true - - - - - 0 - 0 - 763 - 561 - - - - - - - Vector Network Analyzer - - - - - - <html><head/><body><p>If only S11/S21 or S22/S12 are enabled, faster sweeps are possible by only exciting one port. Checking this option forces the device to always excite both ports even when the measurements from one port will not be used.</p></body></html> - - - Always create the stimulus signal at all ports - - - - - - - Allow segmented sweep (increases maximum number of points) - - - - - - - - - - Common - - - - - - - - Averaging mode: - - - - - - - - Mean - - - - - Median - - - - - - - - - - Full Span - - + + + + + + + When starting the application... + + + + + + Autoconnect to the first device available + + + + + + + Set to... + + + + + - - - + + + - Behavior: + Last used - - - - - Use maximum possible span as advertised by device - - - - - Use manually defined full span - - - - - - + + - Start frequency: + Default values - - - - - + + - Stop frequency: + Setup file - - + + + + Qt::Horizontal + + + + 40 + 20 + + + - - - - Use calibrated range as full span when calibrated - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - true - - - - - 0 - 0 - 749 - 952 - - - - - - - - - Show unit on graph axes - - - - - - - Size - - - - - - Line Width: - - - - - - - 0.100000000000000 - - - - - - - Font (axes): - - - - - - - 1 - - - - - - - 1 - - - - - - - 1 - - - - - - - 1 - - - - - - - Font (trace names): - - - - - - - Font (marker data): - - - - - - - Font (cursor overlay): - - - - - - - - - - Colors - - - - - - Background: - - - - - - - - - - - - - - Axis: - - - - - - - - - - - - - - Ticks - - - false - - - - + + + + + 2 + + + + + - + The last used sweep settings (e.g. span, bandwidth,...) will be remembered. The graphs, traces and markers will not be remembered and the default configuration for them is used on startup. - - false - - - false - - - - - - - Tick lines: - - - - - - - - - - - - - - Different background for every 2nd Y tick - - + true + + + + Qt::Vertical + + + + 20 + 40 + + + + - - + + + + + + All settings (sweep settings as well as graphs, markers and traces) will be taken from the specified file. If the autosave option is checked, the current state of the GUI is stored in this file during shutdown as well. + + + true + + + + + + + + + File location: + + + + + + + + + + + 0 + 0 + + + + + 20 + 16777215 + + + + ... + + + + + + + + + Autosave to this file on shutdown + + + + + + + Qt::Vertical + + + + 20 + 545 + + + + + + + + + + + + These default values for the sweep settings will be used. The graphs, traces and markers will not be remembered and the default configuration for them is used on startup. + + + true + + + + + + + Vector Network Analyzer + + + + + + + + Type: + + + + + + + + Frequency Sweep + + + + + Power Sweep + + + + + + + + + + + + Frequency Sweep: + + + + + + Start: + + + + + + + + + + Stop: + + + + + + + + + + Simulus level: + + + + + + + dbm + + + -42.000000000000000 + + + 0.000000000000000 + + + 0.250000000000000 + + + + + + + + + + Power Sweep: + + + + + + Start: + + + + + + + dbm + + + -42.000000000000000 + + + 0.000000000000000 + + + 0.250000000000000 + + + + + + + Stop: + + + + + + + dbm + + + -42.000000000000000 + + + 0.000000000000000 + + + 0.250000000000000 + + + + + + + Frequency: + + + + + + + + + + + + + + + + + Points: + + + + + + + 1 + + + 4501 + + + 501 + + + + + + + IF bandwitdh: + + + + + + + + + + Averaging: + + + + + + + 1 + + + 99 + + + + + + + + + + + + Signal Generator + + + + + + Frequency: + + + + + + + + + + Output level: + + + + + + + dbm + + + -42.000000000000000 + + + 0.000000000000000 + + + 0.250000000000000 + + + + + + + + + + Spectrum Analyzer + + + + + + Start: + + + + + + + + + + Stop: + + + + + + + + + + RBW: + + + + + + + + + + Window: + + + + + + + + None + + + + + Kaiser + + + + + Hann + + + + + Flat Top + + + + + + + + Detector: + + + + + + + + +Peak + + + + + -Peak + + + + + Sample + + + + + Normal + + + + + Average + + + + + + + + Signal Identification: + + + + + + + + + + + + + + Averaging: + + + + + + + 1 + + + 99 + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 17 + 37 + + + + + + + + + + + + + + + + + + true + + + + + 0 + 0 + 565 + 365 + + + + + + + Vector Network Analyzer + + + + + + <html><head/><body><p>If only S11/S21 or S22/S12 are enabled, faster sweeps are possible by only exciting one port. Checking this option forces the device to always excite both ports even when the measurements from one port will not be used.</p></body></html> + + + Always create the stimulus signal at all ports + - - - Sweep Indicator + + + Allow segmented sweep (increases maximum number of points) - - - - - - - Show triangle of size - - - - - - - 1 - - - - - - - below X axis - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Show vertical line in graph - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Hide - - - - - - - - - - of displayed data after the sweep point - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Pan and Zoom - - - - - - Enable: - - - - - - - - - - - - - - Zoom factor: - - - - - - - - - - - - - Trace Domain Handling - - - - - - <html><head/><body><p>A trace may change its output format/domain if certain settings are changed (e.g. from frequency to time domain when enabling TDR). Depending on the type of change, the graphs settings also have to be adjusted. Select the the behavior of the graphs when this happens:</p></body></html> - - - true - - - - - - - - Remove changed trace from all unsupported graphs - - - - - Adjust graphs to support the changed trace (may remove other traces) - - - - - Adjust graph only if it contains no other traces - - - - - - - - - - - Limit checking - - - - - - Behavior: - - - - - - - - Don't show any indication - - - - - PASS/FAIL text in graph corner - - - - - Overlay over whole graph on FAIL - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - true - - - - - 0 - -44 - 749 - 605 - - - - - + + + + + + Common + + - - - Default Behavior - - - - - - Show data on graphs - - - - - - - Select formats to be shown by default: - - - - - - - - - Standard formats - - - - - - dB - - - - - - - dBm - - - - - - - dBUv - - - - - - - dB+Angle - - - - - - - Real+Imaginary - - - - - - - VSWR - - - - - - - Impedance - - - - - - - Resistance - - - - - - - Capacitance - - - - - - - Inductance - - - - - - - Quality factor - - - - - - - - - - Special marker formats - - - - - - P1dB - - - - - - - Noise - - - - - - - Phasenoise - - - - - - - Insertion loss - - - - - - - Cut-off frequency - - - - - - - Center + Bandwidth - - - - - - - TOI - - - - - - - Average Tone - - - - - - - Average Modulation - - - - - - - Flatness - - - - - - - Max. Delta Negative - - - - - - - Max. Delta Positive - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - + - + - Positioning: + Averaging mode: - - - QComboBox::AdjustToContents - + - Snap to measurement points + Mean - Interpolate between points + Median - - + + + + + + Full Span + + + + + + + + Behavior: + + + + + + + + Use maximum possible span as advertised by device + + + + + Use manually defined full span + + + + + + + + Start frequency: + + + + + + + + + + Stop frequency: + + + + + + + + + + + + Use calibrated range as full span when calibrated + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + true + + + + + 0 + 0 + 553 + 969 + + + + + + + + + Show unit on graph axes + + + + + + + Size + + + + - Sort order on graphs: + Line Width: + + + + + + + 0.100000000000000 + + + + + + + Font (axes): - - - - X-Coordinate (Frequency/Time/Power) - - - - - Number - - - - - Creation timestamp - - - - - - - - Symbol Style: + + + 1 - - - - Filled triangle - Marker number inside - - - - - Filled triangle - Marker number above - - - - - Empty triangle - Marker number above - - + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + + + Font (trace names): + + + + + + + Font (marker data): + + + + + + + Font (cursor overlay): + - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - - true - - - - - 0 - 0 - 763 - 561 - - - - - - - - - SCPI Control - - - - - - Enable server - - - - - - - + + + + + + Colors + + + + + + Background: + + + + + + + + + + + + + + Axis: + + + + + + + + + + + + + + Ticks + + + false + + + + - Port: + + + + false + + + false - - - - 1 + + + + Tick lines: - - 65535 + + + + + + + + + + + + + Different background for every 2nd Y tick + + + true - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - - 473 - 20 - - - - - - - - - - - - - - - - true - - - - - 0 - 0 - 763 - 561 - - - - - - - - - - - - - - - - - - :/icons/add.png - :/icons/add.png:/icons/add.png - - - - - - - - - :/icons/remove.png - :/icons/remove.png:/icons/remove.png - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - - - true - - - - - 0 - 0 - 763 - 561 - - - - - - - - - USB logging - - - - + + + + + + Sweep Indicator + + + + + + + + Show triangle of size + + + + + + + 1 + + + + + + + below X axis + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Show vertical line in graph + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Hide + + + + + + + + + + of displayed data after the sweep point + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Pan and Zoom + + + + + + Enable: + + + + + + + + + + + + + + Zoom factor: + + + + + + + + + + + + + Trace Domain Handling + + + + + + <html><head/><body><p>A trace may change its output format/domain if certain settings are changed (e.g. from frequency to time domain when enabling TDR). Depending on the type of change, the graphs settings also have to be adjusted. Select the the behavior of the graphs when this happens:</p></body></html> + + + true + + + + + + - Maximum size: + Remove changed trace from all unsupported graphs - - - - - - - - - - - - Trace saving - - - - + + - Include datapoints + Adjust graphs to support the changed trace (may remove other traces) - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - - 366 - 20 - - - - - - + + + + Adjust graph only if it contains no other traces + + + + + + + + + + + Limit checking + + + + + + Behavior: + + + + + + + + Don't show any indication + + + + + PASS/FAIL text in graph corner + + + + + Overlay over whole graph on FAIL + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + - - - + + + - - + + + + + + true + + + + + 0 + 0 + 683 + 605 + + + + + + + + + Default Behavior + + + + + + Show data on graphs + + + + + + + + + Standard formats + + + + + + dB + + + + + + + dBm + + + + + + + dBUv + + + + + + + dB+Angle + + + + + + + Real+Imaginary + + + + + + + VSWR + + + + + + + Impedance + + + + + + + Resistance + + + + + + + Capacitance + + + + + + + Inductance + + + + + + + Quality factor + + + + + + + + + + Special marker formats + + + + + + Select formats to be shown by default: + + + + + + + P1dB + + + + + + + Noise + + + + + + + Phasenoise + + + + + + + Insertion loss + + + + + + + Cut-off frequency + + + + + + + Center + Bandwidth + + + + + + + TOI + + + + + + + Average Tone + + + + + + + Average Modulation + + + + + + + Flatness + + + + + + + Max. Delta Negative + + + + + + + Max. Delta Positive + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Positioning: + + + + + + + QComboBox::AdjustToContents + + + + Snap to measurement points + + + + + Interpolate between points + + + + + + + + Sort order on graphs: + + + + + + + + X-Coordinate (Frequency/Time/Power) + + + + + Number + + + + + Creation timestamp + + + + + + + + Symbol Style: + + + + + + + + Filled triangle - Marker number inside + + + + + Filled triangle - Marker number above + + + + + Empty triangle - Marker number above + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + true + + + + + 0 + 0 + 168 + 127 + + + + + + + + + SCPI Control + + + + + + Enable server + + + + + + + + + Port: + + + + + + + 1 + + + 65535 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 473 + 20 + + + + + + + + + + + + + + + + true + + + + + 0 + 0 + 211 + 168 + + + + + + + + + USB logging + + + + + + Maximum size: + + + + + + + + + + + + + Trace saving + + + + + + Include datapoints + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 366 + 20 + + + + + + + + + + + + @@ -1904,8 +1827,6 @@
CustomWidgets/siunitedit.h
- - - + diff --git a/Software/PC_Application/LibreVNA-GUI/savable.h b/Software/PC_Application/LibreVNA-GUI/savable.h index b96305e..6d8b526 100644 --- a/Software/PC_Application/LibreVNA-GUI/savable.h +++ b/Software/PC_Application/LibreVNA-GUI/savable.h @@ -53,6 +53,7 @@ public: switch(static_cast(val.type())) { case QMetaType::Double: e.var.setValue((*json_entry).get()); break; case QMetaType::Int: e.var.setValue((*json_entry).get()); break; + case QMetaType::UInt: e.var.setValue((*json_entry).get()); break; case QMetaType::Bool: e.var.setValue((*json_entry).get()); break; case QMetaType::QString: { auto s = QString::fromStdString((*json_entry).get()); @@ -82,6 +83,7 @@ public: switch(static_cast(val.type())) { case QMetaType::Double: *json_entry = val.toDouble(); break; case QMetaType::Int: *json_entry = val.toInt(); break; + case QMetaType::UInt: *json_entry = val.toUInt(); break; case QMetaType::Bool: *json_entry = val.toBool(); break; case QMetaType::QString: *json_entry = val.toString().toStdString(); break; case QMetaType::QColor: *json_entry = val.value().name().toStdString(); break; diff --git a/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro b/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro index fb4537d..de1780c 100644 --- a/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro +++ b/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro @@ -34,8 +34,10 @@ SOURCES += \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialog.cpp \ ../LibreVNA-GUI/Device/LibreVNA/receivercaldialog.cpp \ ../LibreVNA-GUI/Device/LibreVNA/sourcecaldialog.cpp \ - ../LibreVNA-GUI/Device/compounddevice.cpp \ - ../LibreVNA-GUI/Device/compounddeviceeditdialog.cpp \ + ../LibreVNA-GUI/Device/LibreVNA/Compound/compounddevice.cpp \ + ../LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.cpp \ + ../LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.cpp \ + ../LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.cpp \ ../LibreVNA-GUI/Device/device.cpp \ ../LibreVNA-GUI/Device/devicedriver.cpp \ ../LibreVNA-GUI/Device/devicelog.cpp \ @@ -205,8 +207,10 @@ HEADERS += \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialog.h \ ../LibreVNA-GUI/Device/LibreVNA/receivercaldialog.h \ ../LibreVNA-GUI/Device/LibreVNA/sourcecaldialog.h \ - ../LibreVNA-GUI/Device/compounddevice.h \ - ../LibreVNA-GUI/Device/compounddeviceeditdialog.h \ + ../LibreVNA-GUI/Device/LibreVNA/Compound/compounddevice.h \ + ../LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.h \ + ../LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.h \ + ../LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.h \ ../LibreVNA-GUI/Device/device.h \ ../LibreVNA-GUI/Device/devicedriver.h \ ../LibreVNA-GUI/Device/devicelog.h \ @@ -355,7 +359,7 @@ FORMS += \ ../LibreVNA-GUI/Device/LibreVNA/firmwareupdatedialog.ui \ ../LibreVNA-GUI/Device/LibreVNA/frequencycaldialog.ui \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialog.ui \ - ../LibreVNA-GUI/Device/compounddeviceeditdialog.ui \ + ../LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.ui \ ../LibreVNA-GUI/Device/devicelog.ui \ ../LibreVNA-GUI/Device/deviceusblogview.ui \ ../LibreVNA-GUI/Generator/signalgenwidget.ui \