WIP: working compound driver, partial SSA3000X as a demonstration

This commit is contained in:
Jan Käberich 2023-02-07 18:30:05 +01:00
parent 89e46057a5
commit b701479e87
28 changed files with 3418 additions and 1971 deletions

View File

@ -3,7 +3,7 @@
CompoundDevice::CompoundDevice() CompoundDevice::CompoundDevice()
{ {
name = ""; name = "";
sync = Synchronization::USB; sync = LibreVNADriver::Synchronization::GUI;
} }
nlohmann::json CompoundDevice::toJSON() 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) { switch(sync) {
case Synchronization::USB: return "USB"; case LibreVNADriver::Synchronization::Disabled: return "Disabled";
case Synchronization::ExtRef: return "Ext. Ref."; case LibreVNADriver::Synchronization::GUI: return "GUI";
case Synchronization::Trigger: return "Trigger"; case LibreVNADriver::Synchronization::ExternalTrigger: return "Trigger";
default: 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++) { for(int i=0;i<(int) LibreVNADriver::Synchronization::Last;i++) {
if(SyncToString((Synchronization)i) == s) { if(SyncToString((LibreVNADriver::Synchronization)i) == s) {
return (Synchronization) i; return (LibreVNADriver::Synchronization) i;
} }
} }
// default to USB // default to GUI
return Synchronization::USB; return LibreVNADriver::Synchronization::GUI;
} }
QString CompoundDevice::getDesription() QString CompoundDevice::getDesription()
@ -76,7 +76,7 @@ QString CompoundDevice::getDesription()
return name + ", "+QString::number(deviceSerials.size())+" devices, "+QString::number(portMapping.size())+" ports in total"; return name + ", "+QString::number(deviceSerials.size())+" devices, "+QString::number(portMapping.size())+" ports in total";
} }
int CompoundDevice::PortMapping::findActiveStage(std::vector<CompoundDevice::PortMapping> map, unsigned int device, unsigned int port) unsigned int CompoundDevice::PortMapping::findActiveStage(std::vector<CompoundDevice::PortMapping> map, unsigned int device, unsigned int port)
{ {
for(unsigned int i=0;i<map.size();i++) { for(unsigned int i=0;i<map.size();i++) {
if(map[i].device == device && map[i].port == port) { if(map[i].device == device && map[i].port == port) {

View File

@ -2,6 +2,7 @@
#define COMPOUNDDEVICE_H #define COMPOUNDDEVICE_H
#include "savable.h" #include "savable.h"
#include "../librevnadriver.h"
#include <vector> #include <vector>
@ -18,25 +19,18 @@ public:
class PortMapping { class PortMapping {
public: public:
unsigned int device; unsigned int device; // starts at zero
unsigned int port; unsigned int port; // starts at zero
static int findActiveStage(std::vector<PortMapping> map, unsigned int device, unsigned int port); static unsigned int findActiveStage(std::vector<PortMapping> map, unsigned int device, unsigned int port);
}; };
enum class Synchronization { static QString SyncToString(LibreVNADriver::Synchronization sync);
USB, static LibreVNADriver::Synchronization SyncFromString(QString s);
ExtRef,
Trigger,
Last
};
static QString SyncToString(Synchronization sync);
static Synchronization SyncFromString(QString s);
QString getDesription(); QString getDesription();
QString name; QString name;
Synchronization sync; LibreVNADriver::Synchronization sync;
std::vector<QString> deviceSerials; std::vector<QString> deviceSerials;
std::vector<PortMapping> portMapping; std::vector<PortMapping> portMapping;
}; };

View File

@ -1,7 +1,7 @@
#include "compounddeviceeditdialog.h" #include "compounddeviceeditdialog.h"
#include "ui_compounddeviceeditdialog.h" #include "ui_compounddeviceeditdialog.h"
#include "device.h" #include "../../device.h"
#include <QPushButton> #include <QPushButton>
#include <QDrag> #include <QDrag>
@ -25,9 +25,9 @@ CompoundDeviceEditDialog::CompoundDeviceEditDialog(CompoundDevice *cdev, QWidget
ldev.name = ui->name->text(); ldev.name = ui->name->text();
checkIfOkay(); checkIfOkay();
}); });
for(int i=0;i<(int)CompoundDevice::Synchronization::Last;i++) { for(int i=0;i<(int)LibreVNADriver::Synchronization::Last;i++) {
ui->sync->addItem(CompoundDevice::SyncToString((CompoundDevice::Synchronization) i)); ui->sync->addItem(CompoundDevice::SyncToString((LibreVNADriver::Synchronization) i));
if((CompoundDevice::Synchronization) i == CompoundDevice::Synchronization::Trigger) { if((LibreVNADriver::Synchronization) i == LibreVNADriver::Synchronization::ExternalTrigger) {
// Disable for now // Disable for now
auto *model = qobject_cast<QStandardItemModel *>(ui->sync->model()); auto *model = qobject_cast<QStandardItemModel *>(ui->sync->model());
Q_ASSERT(model != nullptr); Q_ASSERT(model != nullptr);
@ -522,9 +522,9 @@ void DeviceFrame::update()
// } // }
serial->setCurrentText(s); serial->setCurrentText(s);
if(dev->sync == CompoundDevice::Synchronization::USB) { if(dev->sync == LibreVNADriver::Synchronization::GUI) {
setStyleSheet("image: url(:/icons/compound_V1_USB.png);"); 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) { if(position == 0) {
setStyleSheet("image: url(:/icons/compound_V1_Ref_Left.png);"); setStyleSheet("image: url(:/icons/compound_V1_Ref_Left.png);");
} else if(position == dev->deviceSerials.size() - 1) { } else if(position == dev->deviceSerials.size() - 1) {

View File

@ -0,0 +1,706 @@
#include "compounddriver.h"
#include "../librevnatcpdriver.h"
#include "../librevnausbdriver.h"
#include "ui_compounddriversettingswidget.h"
#include "compounddeviceeditdialog.h"
#include "preferences.h"
#include <exception>
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<QString> CompoundDriver::GetAvailableDevices()
{
parseCompoundJSON();
std::set<QString> availableSerials;
for(auto d : drivers) {
availableSerials.merge(d->GetAvailableDevices());
}
std::set<QString> 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<std::set<QString>> 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<availableSerials.size();i++) {
if(availableSerials[i].count(s) > 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:" <<s;
disconnect();
return false;
}
auto &p = Preferences::getInstance();
p.load(device->driverSpecificSettings());
if(!device->connectDevice(s, true)) {
qWarning() << "Unable to connect to required serial for compound device:" <<s;
delete device;
disconnect();
return false;
} else {
devices.push_back(device);
}
}
// make device connections
for(auto dev : devices) {
// Create device connections
connect(dev, &LibreVNADriver::ConnectionLost, this, &CompoundDriver::ConnectionLost, Qt::QueuedConnection);
connect(dev, &LibreVNADriver::LogLineReceived, this, [=](QString line){
emit LogLineReceived(line.prepend(dev->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<DeviceDriver::Flag> CompoundDriver::getFlags()
{
std::set<DeviceDriver::Flag> 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<info.Limits.VNA.ports;j++) {
ret.push_back("RawPort"+QString::number(i)+"Stage"+QString::number(j));
ret.push_back("RawPort"+QString::number(i)+"Stage"+QString::number(j)+"Ref");
}
}
}
return ret;
}
bool CompoundDriver::setVNA(const DeviceDriver::VNASettings &s, std::function<void (bool)> 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<s.excitedPorts.size();i++) {
portStageMapping[s.excitedPorts[i]] = i;
}
zerospan = (s.freqStart == s.freqStop) && (s.dBmStart == s.dBmStop);
// create vector of currently used stimulus ports
std::vector<CompoundDevice::PortMapping> 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;i<devices.size();i++) {
auto dev = devices[i];
dev->setSynchronization(activeDevice.sync, i == 0);
auto devSetting = s;
// indicate the number of stages
devSetting.excitedPorts = std::vector<int>(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<void (bool)> 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;i<devices.size();i++) {
auto dev = devices[i];
dev->setSynchronization(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;i<devices.size();i++) {
if(devices[i]->getSApoints() != 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<info.Limits.Generator.ports;i++) {
ret.push_back("PORT"+QString::number(i));
}
return ret;
}
bool CompoundDriver::setSG(const DeviceDriver::SGSettings &s)
{
if(!supports(Feature::Generator)) {
return false;
}
// configure all devices
bool success = true;
for(unsigned int i=0;i<devices.size();i++) {
auto devSettings = s;
devSettings.port = 0;
if(s.port > 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<void (bool)> 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;i<devices.size();i++) {
set = set.intersect(devices[i]->availableExtRefInSettings().toSet());
}
return QStringList(set.toList());
}
QStringList CompoundDriver::availableExtRefOutSettings()
{
if(!connected) {
return QStringList();
}
auto set = devices[0]->availableExtRefOutSettings().toSet();
for(unsigned int i=1;i<devices.size();i++) {
set = set.intersect(devices[i]->availableExtRefOutSettings().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;i<devices.size();i++) {
if(devices[i] == device) {
// pass on to the next device
if(i < devices.size() - 1) {
devices[i+1]->sendWithoutPayload(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;i<devices.size();i++) {
if(devices[i] == device) {
// pass on to the next device
if(i < devices.size() - 1) {
devices[i+1]->sendWithoutPayload(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;i<devices.size();i++) {
info.subset(devices[i]->getInfo());
}
// 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<devices.size();i++) {
auto devStat = deviceStatus[devices[i]];
if(i==0) {
lastStatus = devStat;
} else {
lastStatus.extRefAvailable &= devStat.extRefAvailable;
lastStatus.extRefInUse |= devStat.extRefInUse;
lastStatus.FPGA_configured &= devStat.FPGA_configured;
lastStatus.source_locked &= devStat.source_locked;
lastStatus.LO1_locked &= devStat.LO1_locked;
lastStatus.ADC_overload |= devStat.ADC_overload;
lastStatus.unlevel |= devStat.unlevel;
lastStatus.temp_source = std::max(lastStatus.temp_source, devStat.temp_source);
lastStatus.temp_LO1 = std::max(lastStatus.temp_LO1, devStat.temp_LO1);
lastStatus.temp_MCU = std::max(lastStatus.temp_MCU, devStat.temp_MCU);
}
}
emit StatusUpdated();
emit FlagsUpdated();
}
}
void CompoundDriver::spectrumResultReceived(LibreVNADriver *dev, Protocol::SpectrumAnalyzerResult res)
{
if(!compoundSABuffer.count(res.pointNum)) {
compoundSABuffer[res.pointNum] = std::map<LibreVNADriver*, Protocol::SpectrumAnalyzerResult>();
}
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;port<activeDevice.portMapping.size();port++) {
auto device = devices[activeDevice.portMapping[port].device];
auto devicePort = activeDevice.portMapping[port].port;
QString name = "PORT"+QString::number(port+1);
if(devicePort == 0) {
m.measurements[name] = buf[device].port1;
} else {
m.measurements[name] = buf[device].port2;
}
}
emit SAmeasurementReceived(m);
// Clear this and all (incomplete) older datapoint buffers
auto it = compoundSABuffer.begin();
while(it != compoundSABuffer.end()) {
if(it->first <= 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<LibreVNADriver*, Protocol::VNADatapoint<32>*>();
}
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<double> ref = buf[stimulusDev]->getValue(map.second, stimulusDevPort, true);
// for all ports of the compound device...
for(unsigned int i=0;i<activeDevice.portMapping.size();i++) {
// ...figure out which physical device and port was used for this input...
auto inputDevice = devices[activeDevice.portMapping[i].device];
// ...and grab the data
auto inputPort = activeDevice.portMapping[i].port;
std::complex<double> 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<void (bool)> 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);
}
}
}

View File

@ -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<QString> 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<Flag> 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<void(bool)> 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<void(bool)> 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<void(bool)> 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<LibreVNADriver*, Info> deviceInfos;
std::map<LibreVNADriver*, Protocol::DeviceStatusV1> deviceStatus;
std::map<int, std::map<LibreVNADriver*, Protocol::VNADatapoint<32>*>> compoundVNABuffer;
std::map<int, std::map<LibreVNADriver*, Protocol::SpectrumAnalyzerResult>> compoundSABuffer;
Protocol::DeviceStatusV1 lastStatus;
// Parsed configuration of compound devices (as extracted from compoundJSONString
std::vector<CompoundDevice*> configuredDevices;
std::map<int, int> portStageMapping; // maps from excitedPort (count starts at one) to stage (count starts at zero)
// All possible drivers to interact with a LibreVNA
std::vector<LibreVNADriver*> drivers;
// Configuration of the device we are connected to
CompoundDevice activeDevice;
bool connected;
std::vector<LibreVNADriver*> devices;
bool zerospan;
unsigned int SApoints;
// Driver specific settings
bool captureRawReceiverValues;
QString compoundJSONString;
// Buffers for storing individual device answers
std::map<LibreVNADriver*, bool> results;
void checkIfAllTransmissionsComplete(std::function<void(bool)> cb = nullptr);
};
#endif // COMPOUNDDRIVER_H

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CompoundDriverSettingsWidget</class>
<widget class="QWidget" name="CompoundDriverSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>893</width>
<height>559</height>
</rect>
</property>
<property name="windowTitle">
<string>CompoundDriverSettingsWidget</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_13">
<item>
<widget class="QListWidget" name="compoundList"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_19">
<item>
<widget class="QPushButton" name="compoundAdd">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="list-add" resource="../../../icons.qrc">
<normaloff>:/icons/add.png</normaloff>
<normalon>:/icons/add.png</normalon>:/icons/add.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="compoundDelete">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="list-remove" resource="../../../icons.qrc">
<normaloff>:/icons/remove.png</normaloff>
<normalon>:/icons/remove.png</normalon>:/icons/remove.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_8">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_25">
<property name="title">
<string>Debug - Acquisition</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_32">
<item>
<widget class="QCheckBox" name="CaptureRawReceiverValues">
<property name="text">
<string>Capture raw receiver values</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -107,6 +107,7 @@ LibreVNADriver::LibreVNADriver()
connected = false; connected = false;
skipOwnPacketHandling = false; skipOwnPacketHandling = false;
SApoints = 0; SApoints = 0;
setSynchronization(Synchronization::Disabled, false);
auto manual = new QAction("Manual Control"); auto manual = new QAction("Manual Control");
connect(manual, &QAction::triggered, this, [=](){ connect(manual, &QAction::triggered, this, [=](){
@ -146,17 +147,6 @@ LibreVNADriver::LibreVNADriver()
d->show(); d->show();
}); });
specificActions.push_back(freqcal); 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<DeviceDriver::Flag> LibreVNADriver::getFlags() std::set<DeviceDriver::Flag> LibreVNADriver::getFlags()
@ -232,6 +222,12 @@ QWidget *LibreVNADriver::createSettingsWidget()
ui->IF1->setUnit("Hz"); ui->IF1->setUnit("Hz");
ui->IF1->setPrefixes(" kM"); ui->IF1->setPrefixes(" kM");
ui->IF1->setPrecision(5); 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->IF1->setValue(IF1);
ui->ADCpresc->setValue(ADCprescaler); ui->ADCpresc->setValue(ADCprescaler);
ui->ADCphaseInc->setValue(DFTPhaseInc); ui->ADCphaseInc->setValue(DFTPhaseInc);
@ -333,10 +329,10 @@ bool LibreVNADriver::setVNA(const DeviceDriver::VNASettings &s, std::function<vo
p.settings.logSweep = s.logSweep ? 1 : 0; p.settings.logSweep = s.logSweep ? 1 : 0;
zerospan = (s.freqStart == s.freqStop) && (s.dBmStart == s.dBmStop); zerospan = (s.freqStart == s.freqStop) && (s.dBmStart == s.dBmStop);
p.settings.port1Stage = find(s.excitedPorts.begin(), s.excitedPorts.end(), 0) - s.excitedPorts.begin(); p.settings.port1Stage = find(s.excitedPorts.begin(), s.excitedPorts.end(), 1) - s.excitedPorts.begin();
p.settings.port2Stage = find(s.excitedPorts.begin(), s.excitedPorts.end(), 1) - s.excitedPorts.begin(); p.settings.port2Stage = find(s.excitedPorts.begin(), s.excitedPorts.end(), 2) - s.excitedPorts.begin();
p.settings.syncMode = 0; p.settings.syncMode = (int) sync;
p.settings.syncMaster = 0; p.settings.syncMaster = syncMaster ? 1 : 0;
return SendPacket(p, [=](TransmissionResult r){ return SendPacket(p, [=](TransmissionResult r){
if(cb) { if(cb) {
@ -385,12 +381,12 @@ bool LibreVNADriver::setSA(const DeviceDriver::SASettings &s, std::function<void
} }
p.spectrumSettings.applyReceiverCorrection = 1; p.spectrumSettings.applyReceiverCorrection = 1;
p.spectrumSettings.trackingGeneratorOffset = s.trackingOffset; p.spectrumSettings.trackingGeneratorOffset = s.trackingOffset;
p.spectrumSettings.trackingPower = s.trackingPower; p.spectrumSettings.trackingPower = s.trackingPower * 100;
p.spectrumSettings.trackingGenerator = s.trackingGenerator ? 1 : 0; p.spectrumSettings.trackingGenerator = s.trackingGenerator ? 1 : 0;
p.spectrumSettings.trackingGeneratorPort = s.trackingPort; p.spectrumSettings.trackingGeneratorPort = s.trackingPort == 2 ? 1 : 0;
p.spectrumSettings.syncMode = 0; p.spectrumSettings.syncMode = (int) sync;
p.spectrumSettings.syncMaster = 0; p.spectrumSettings.syncMaster = syncMaster ? 1 : 0;
if(p.spectrumSettings.trackingGenerator && p.spectrumSettings.f_stop >= 25000000) { if(p.spectrumSettings.trackingGenerator && p.spectrumSettings.f_stop >= 25000000) {
// Check point spacing. // Check point spacing.
@ -518,6 +514,12 @@ void LibreVNADriver::registerTypes()
qRegisterMetaType<TransmissionResult>(); qRegisterMetaType<TransmissionResult>();
} }
void LibreVNADriver::setSynchronization(LibreVNADriver::Synchronization s, bool master)
{
sync = s;
syncMaster = master;
}
void LibreVNADriver::handleReceivedPacket(const Protocol::PacketInfo &packet) void LibreVNADriver::handleReceivedPacket(const Protocol::PacketInfo &packet)
{ {
emit passOnReceivedPacket(packet); emit passOnReceivedPacket(packet);
@ -591,20 +593,20 @@ void LibreVNADriver::handleReceivedPacket(const Protocol::PacketInfo &packet)
m.dBm = (double) res->cdBm / 100; m.dBm = (double) res->cdBm / 100;
} }
for(auto map : portStageMapping) { 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) // map.second is the stage at which this port had the stimulus (starts at zero)
complex<double> ref = res->getValue(map.second, map.first, true); complex<double> ref = res->getValue(map.second, map.first-1, true);
for(unsigned int i=0;i<info.Limits.VNA.ports;i++) { for(unsigned int i=1;i<=info.Limits.VNA.ports;i++) {
complex<double> input = res->getValue(map.second, i, false); complex<double> input = res->getValue(map.second, i-1, false);
if(!std::isnan(ref.real()) && !std::isnan(input.real())) { if(!std::isnan(ref.real()) && !std::isnan(input.real())) {
// got both required measurements // 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; m.measurements[name] = input / ref;
} }
if(captureRawReceiverValues) { 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; 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); m.measurements[name] = res->getValue(map.second, i, true);
} }
} }

View File

@ -9,6 +9,7 @@
class LibreVNADriver : public DeviceDriver class LibreVNADriver : public DeviceDriver
{ {
friend class CompoundDriver;
Q_OBJECT Q_OBJECT
public: public:
enum class TransmissionResult { enum class TransmissionResult {
@ -161,6 +162,16 @@ public:
*/ */
virtual void registerTypes(); virtual void registerTypes();
enum class Synchronization {
Disabled = 0,
GUI = 1,
Reserved = 2,
ExternalTrigger = 3,
Last = 4,
};
void setSynchronization(Synchronization s, bool master);
public: public:
signals: signals:
// Required for the compound device driver // Required for the compound device driver
@ -191,7 +202,10 @@ protected:
bool zerospan; bool zerospan;
unsigned int SApoints; unsigned int SApoints;
std::map<int, int> portStageMapping; // maps from excitedPort (count starts at zero) to stage (count starts at zero) Synchronization sync;
bool syncMaster;
std::map<int, int> portStageMapping; // maps from excitedPort (count starts at one) to stage (count starts at zero)
// Driver specific settings // Driver specific settings
bool captureRawReceiverValues; bool captureRawReceiverValues;

View File

@ -26,16 +26,26 @@ LibreVNATCPDriver::LibreVNATCPDriver()
auto interfaces = QNetworkInterface::allInterfaces(); auto interfaces = QNetworkInterface::allInterfaces();
for(auto i : interfaces) { for(auto i : interfaces) {
qDebug() << "Creating socket for interface" << i.name();
auto socket = new QUdpSocket(); auto socket = new QUdpSocket();
socket->bind(QHostAddress::AnyIPv4, 0, QUdpSocket::ShareAddress); socket->bind(QHostAddress::AnyIPv4, 0, QUdpSocket::ShareAddress);
socket->setMulticastInterface(i); socket->setMulticastInterface(i);
qDebug() << socket->joinMulticastGroup(SSDPaddress, i); socket->joinMulticastGroup(SSDPaddress, i);
connect(socket, &QUdpSocket::readyRead, this, [=](){ connect(socket, &QUdpSocket::readyRead, this, [=](){
SSDPreceived(socket); SSDPreceived(socket);
}); });
ssdpSockets.push_back(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() QString LibreVNATCPDriver::getDriverName()

View File

@ -126,6 +126,17 @@ LibreVNAUSBDriver::LibreVNAUSBDriver()
dataBuffer = nullptr; dataBuffer = nullptr;
logBuffer = nullptr; logBuffer = nullptr;
m_receiveThread = 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() QString LibreVNAUSBDriver::getDriverName()

View File

@ -0,0 +1,296 @@
#include "ssa3000xdriver.h"
#include "CustomWidgets/informationbox.h"
#include <QTcpSocket>
SSA3000XDriver::SSA3000XDriver()
{
searchAddresses.push_back(QHostAddress("192.168.22.2"));
}
SSA3000XDriver::~SSA3000XDriver()
{
}
std::set<QString> SSA3000XDriver::GetAvailableDevices()
{
std::set<QString> 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<QAbstractSocket::SocketError>(&QTcpSocket::error), this, &LibreVNATCPDriver::ConnectionLost, Qt::QueuedConnection);
// connect(&logSocket, qOverload<QAbstractSocket::SocketError>(&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::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
// connect(this, &LibreVNATCPDriver::receivedPacket, this, &LibreVNATCPDriver::handleReceivedPacket, static_cast<Qt::ConnectionType>(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<DeviceDriver::Flag> SSA3000XDriver::getFlags()
{
return std::set<DeviceDriver::Flag>();
}
QString SSA3000XDriver::getStatus()
{
return "";
}
QStringList SSA3000XDriver::availableSAMeasurements()
{
return {"PORT1"};
}
bool SSA3000XDriver::setSA(const DeviceDriver::SASettings &s, std::function<void (bool)> 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<void (bool)> 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());
}

View File

@ -0,0 +1,154 @@
#ifndef SSA3000XDRIVER_H
#define SSA3000XDRIVER_H
#include "../devicedriver.h"
#include <QHostAddress>
#include <QTcpSocket>
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<QString> 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<Flag> 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<void(bool)> 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<void(bool)> 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<QHostAddress> searchAddresses;
std::map<QString, QHostAddress> detectedDevices;
};
#endif // SSA3000XDRIVER_H

View File

@ -2,6 +2,8 @@
#include "LibreVNA/librevnatcpdriver.h" #include "LibreVNA/librevnatcpdriver.h"
#include "LibreVNA/librevnausbdriver.h" #include "LibreVNA/librevnausbdriver.h"
#include "LibreVNA/Compound/compounddriver.h"
#include "SSA3000X/ssa3000xdriver.h"
DeviceDriver *DeviceDriver::activeDriver = nullptr; DeviceDriver *DeviceDriver::activeDriver = nullptr;
@ -19,17 +21,23 @@ std::vector<DeviceDriver *> DeviceDriver::getDrivers()
// first function call // first function call
ret.push_back(new LibreVNAUSBDriver); ret.push_back(new LibreVNAUSBDriver);
ret.push_back(new LibreVNATCPDriver); ret.push_back(new LibreVNATCPDriver);
ret.push_back(new CompoundDriver);
ret.push_back(new SSA3000XDriver);
} }
return ret; return ret;
} }
bool DeviceDriver::connectDevice(QString serial) bool DeviceDriver::connectDevice(QString serial, bool isIndepedentDriver)
{ {
if(!isIndepedentDriver) {
if(activeDriver && activeDriver != this) { if(activeDriver && activeDriver != this) {
activeDriver->disconnect(); activeDriver->disconnect();
} }
}
if(connectTo(serial)) { if(connectTo(serial)) {
if(!isIndepedentDriver) {
activeDriver = this; activeDriver = this;
}
return true; return true;
} else { } else {
return false; return false;
@ -123,3 +131,41 @@ DeviceDriver::Info::Info()
Limits.SA.minRBW = 1; Limits.SA.minRBW = 1;
Limits.SA.maxRBW = 1000000; 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<Feature> intersectFeatures;
std::set_intersection(supportedFeatures.begin(), supportedFeatures.end(), info.supportedFeatures.begin(), info.supportedFeatures.end(),
std::inserter(intersectFeatures, intersectFeatures.begin()));
supportedFeatures = intersectFeatures;
}

View File

@ -51,7 +51,7 @@ protected:
* @param serial Serial number of device that should be connected to * @param serial Serial number of device that should be connected to
* @return true if connection successful, otherwise false * @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 * @brief Disconnects from device. Has no effect if no device was connected
*/ */
@ -120,6 +120,7 @@ public:
double mindBm, maxdBm; double mindBm, maxdBm;
} SA; } SA;
} Limits; } Limits;
void subset(const Info &info);
}; };
/** /**
@ -486,7 +487,7 @@ signals:
void releaseControl(); void releaseControl();
public: public:
bool connectDevice(QString serial); bool connectDevice(QString serial, bool isIndepedentDriver = false);
void disconnectDevice(); void disconnectDevice();
static DeviceDriver* getActiveDriver() {return activeDriver;} static DeviceDriver* getActiveDriver() {return activeDriver;}
static unsigned int SApoints(); static unsigned int SApoints();

View File

@ -1,5 +1,7 @@
#include "generator.h" #include "generator.h"
#include "CustomWidgets/informationbox.h"
#include <QSettings> #include <QSettings>
Generator::Generator(AppWindow *window, QString name) Generator::Generator(AppWindow *window, QString name)
@ -37,6 +39,10 @@ void Generator::deactivate()
void Generator::initializeDevice() void Generator::initializeDevice()
{ {
if(!window->getDevice()->supports(DeviceDriver::Feature::Generator)) {
InformationBox::ShowError("Unsupported", "The connected device does not support generator mode");
return;
}
updateDevice(); updateDevice();
} }

View File

@ -19,6 +19,9 @@ HEADERS += \
CustomWidgets/toggleswitch.h \ CustomWidgets/toggleswitch.h \
CustomWidgets/touchstoneimport.h \ CustomWidgets/touchstoneimport.h \
CustomWidgets/tracesetselector.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/amplitudecaldialog.h \
Device/LibreVNA/firmwareupdatedialog.h \ Device/LibreVNA/firmwareupdatedialog.h \
Device/LibreVNA/frequencycaldialog.h \ Device/LibreVNA/frequencycaldialog.h \
@ -28,8 +31,7 @@ HEADERS += \
Device/LibreVNA/manualcontroldialog.h \ Device/LibreVNA/manualcontroldialog.h \
Device/LibreVNA/receivercaldialog.h \ Device/LibreVNA/receivercaldialog.h \
Device/LibreVNA/sourcecaldialog.h \ Device/LibreVNA/sourcecaldialog.h \
Device/compounddevice.h \ Device/SSA3000X/ssa3000xdriver.h \
Device/compounddeviceeditdialog.h \
Device/device.h \ Device/device.h \
Device/devicedriver.h \ Device/devicedriver.h \
Device/devicelog.h \ Device/devicelog.h \
@ -171,6 +173,9 @@ SOURCES += \
CustomWidgets/toggleswitch.cpp \ CustomWidgets/toggleswitch.cpp \
CustomWidgets/touchstoneimport.cpp \ CustomWidgets/touchstoneimport.cpp \
CustomWidgets/tracesetselector.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/amplitudecaldialog.cpp \
Device/LibreVNA/firmwareupdatedialog.cpp \ Device/LibreVNA/firmwareupdatedialog.cpp \
Device/LibreVNA/frequencycaldialog.cpp \ Device/LibreVNA/frequencycaldialog.cpp \
@ -180,8 +185,7 @@ SOURCES += \
Device/LibreVNA/manualcontroldialog.cpp \ Device/LibreVNA/manualcontroldialog.cpp \
Device/LibreVNA/receivercaldialog.cpp \ Device/LibreVNA/receivercaldialog.cpp \
Device/LibreVNA/sourcecaldialog.cpp \ Device/LibreVNA/sourcecaldialog.cpp \
Device/compounddevice.cpp \ Device/SSA3000X/ssa3000xdriver.cpp \
Device/compounddeviceeditdialog.cpp \
Device/device.cpp \ Device/device.cpp \
Device/devicedriver.cpp \ Device/devicedriver.cpp \
Device/devicelog.cpp \ Device/devicelog.cpp \
@ -313,6 +317,8 @@ FORMS += \
CustomWidgets/jsonpickerdialog.ui \ CustomWidgets/jsonpickerdialog.ui \
CustomWidgets/tilewidget.ui \ CustomWidgets/tilewidget.ui \
CustomWidgets/touchstoneimport.ui \ CustomWidgets/touchstoneimport.ui \
Device/LibreVNA/Compound/compounddeviceeditdialog.ui \
Device/LibreVNA/Compound/compounddriversettingswidget.ui \
Device/LibreVNA/addamplitudepointsdialog.ui \ Device/LibreVNA/addamplitudepointsdialog.ui \
Device/LibreVNA/amplitudecaldialog.ui \ Device/LibreVNA/amplitudecaldialog.ui \
Device/LibreVNA/automaticamplitudedialog.ui \ Device/LibreVNA/automaticamplitudedialog.ui \
@ -320,7 +326,6 @@ FORMS += \
Device/LibreVNA/frequencycaldialog.ui \ Device/LibreVNA/frequencycaldialog.ui \
Device/LibreVNA/librevnadriversettingswidget.ui \ Device/LibreVNA/librevnadriversettingswidget.ui \
Device/LibreVNA/manualcontroldialog.ui \ Device/LibreVNA/manualcontroldialog.ui \
Device/compounddeviceeditdialog.ui \
Device/devicelog.ui \ Device/devicelog.ui \
Device/deviceusblogview.ui \ Device/deviceusblogview.ui \
Generator/signalgenwidget.ui \ Generator/signalgenwidget.ui \

View File

@ -173,7 +173,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name)
// Acquisition toolbar // Acquisition toolbar
auto tb_acq = new QToolBar("Acquisition"); auto tb_acq = new QToolBar("Acquisition");
auto eBandwidth = new SIUnitEdit("Hz", " k", 3); auto eBandwidth = new SIUnitEdit("Hz", " kM", 3);
eBandwidth->setFixedWidth(70); eBandwidth->setFixedWidth(70);
eBandwidth->setToolTip("RBW"); eBandwidth->setToolTip("RBW");
connect(eBandwidth, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetRBW); connect(eBandwidth, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetRBW);
@ -221,7 +221,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name)
toolbars.insert(tb_acq); toolbars.insert(tb_acq);
// Tracking generator toolbar // Tracking generator toolbar
auto tb_trackgen = new QToolBar("Tracking Generator"); tb_trackgen = new QToolBar("Tracking Generator");
auto cbTrackGenEnable = new QCheckBox("Tracking Generator"); auto cbTrackGenEnable = new QCheckBox("Tracking Generator");
connect(cbTrackGenEnable, &QCheckBox::toggled, this, &SpectrumAnalyzer::SetTGEnabled); connect(cbTrackGenEnable, &QCheckBox::toggled, this, &SpectrumAnalyzer::SetTGEnabled);
connect(this, &SpectrumAnalyzer::TGStateChanged, cbTrackGenEnable, &QCheckBox::setChecked); connect(this, &SpectrumAnalyzer::TGStateChanged, cbTrackGenEnable, &QCheckBox::setChecked);
@ -231,8 +231,12 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name)
cbTrackGenPort->addItem("Port 1"); cbTrackGenPort->addItem("Port 1");
cbTrackGenPort->addItem("Port 2"); cbTrackGenPort->addItem("Port 2");
cbTrackGenPort->setCurrentIndex(0); cbTrackGenPort->setCurrentIndex(0);
connect(cbTrackGenPort, qOverload<int>(&QComboBox::currentIndexChanged), this, &SpectrumAnalyzer::SetTGPort); connect(cbTrackGenPort, qOverload<int>(&QComboBox::currentIndexChanged), this, [=](int index) {
connect(this, &SpectrumAnalyzer::TGPortChanged, cbTrackGenPort, qOverload<int>(&QComboBox::setCurrentIndex)); SetTGPort(index + 1);
});
connect(this, &SpectrumAnalyzer::TGPortChanged, this, [=](int port) {
cbTrackGenPort->setCurrentIndex(port - 1);
});
tb_trackgen->addWidget(cbTrackGenPort); tb_trackgen->addWidget(cbTrackGenPort);
auto dbm = new QDoubleSpinBox(); auto dbm = new QDoubleSpinBox();
@ -247,7 +251,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name)
tb_trackgen->addWidget(new QLabel("Level:")); tb_trackgen->addWidget(new QLabel("Level:"));
tb_trackgen->addWidget(dbm); tb_trackgen->addWidget(dbm);
auto tgOffset = new SIUnitEdit("Hz", " kMG", 6); tgOffset = new SIUnitEdit("Hz", " kMG", 6);
tgOffset->setFixedWidth(width); tgOffset->setFixedWidth(width);
tgOffset->setToolTip("Tracking generator offset"); tgOffset->setToolTip("Tracking generator offset");
connect(tgOffset, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetTGOffset); connect(tgOffset, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetTGOffset);
@ -330,6 +334,20 @@ void SpectrumAnalyzer::deactivate()
void SpectrumAnalyzer::initializeDevice() 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); connect(window->getDevice(), &DeviceDriver::SAmeasurementReceived, this, &SpectrumAnalyzer::NewDatapoint, Qt::UniqueConnection);
// Configure initial state of device // Configure initial state of device
@ -358,9 +376,9 @@ nlohmann::json SpectrumAnalyzer::toJSON()
sweep["acquisition"] = acq; sweep["acquisition"] = acq;
nlohmann::json tracking; nlohmann::json tracking;
tracking["enabled"] = settings.trackingGenerator ? true : false; tracking["enabled"] = settings.trackingGenerator ? true : false;
tracking["port"] = settings.trackingPort ? 2 : 1; tracking["port"] = settings.trackingPort;
tracking["offset"] = settings.trackingOffset; tracking["offset"] = settings.trackingOffset;
tracking["power"] = (double) settings.trackingPower / 100.0; // convert to dBm tracking["power"] = settings.trackingPower;
sweep["trackingGenerator"] = tracking; sweep["trackingGenerator"] = tracking;
if(normalize.active) { if(normalize.active) {
@ -426,8 +444,7 @@ void SpectrumAnalyzer::fromJSON(nlohmann::json j)
auto tracking = sweep["trackingGenerator"]; auto tracking = sweep["trackingGenerator"];
SetTGEnabled(tracking.value("enabled", settings.trackingGenerator ? true : false)); SetTGEnabled(tracking.value("enabled", settings.trackingGenerator ? true : false));
int port = tracking.value("port", 1); int port = tracking.value("port", 1);
// Function expects 0 for port1, 1 for port2 SetTGPort(port);
SetTGPort(port - 1);
SetTGLevel(tracking.value("power", settings.trackingPower)); SetTGLevel(tracking.value("power", settings.trackingPower));
SetTGOffset(tracking.value("offset", settings.trackingOffset)); SetTGOffset(tracking.value("offset", settings.trackingOffset));
} }
@ -699,7 +716,7 @@ void SpectrumAnalyzer::SetTGEnabled(bool enabled)
void SpectrumAnalyzer::SetTGPort(int port) void SpectrumAnalyzer::SetTGPort(int port)
{ {
if(port < 0 || port >= cbTrackGenPort->count()) { if(port < 1 || port > cbTrackGenPort->count()) {
return; return;
} }
if(port != settings.trackingPort) { if(port != settings.trackingPort) {
@ -719,7 +736,7 @@ void SpectrumAnalyzer::SetTGLevel(double level)
level = DeviceDriver::getInfo(window->getDevice()).Limits.SA.mindBm; level = DeviceDriver::getInfo(window->getDevice()).Limits.SA.mindBm;
} }
emit TGLevelChanged(level); emit TGLevelChanged(level);
settings.trackingPower = level * 100; settings.trackingPower = level;
if(settings.trackingGenerator) { if(settings.trackingGenerator) {
SettingsChanged(); SettingsChanged();
} }
@ -1058,7 +1075,7 @@ void SpectrumAnalyzer::SetupSCPI()
if(!SCPI::paramToULongLong(params, 0, newval)) { if(!SCPI::paramToULongLong(params, 0, newval)) {
return SCPI::getResultName(SCPI::Result::Error); return SCPI::getResultName(SCPI::Result::Error);
} else if(newval > 0 && newval <= DeviceDriver::getInfo(window->getDevice()).Limits.SA.ports){ } else if(newval > 0 && newval <= DeviceDriver::getInfo(window->getDevice()).Limits.SA.ports){
SetTGPort(newval-1); SetTGPort(newval);
return SCPI::getResultName(SCPI::Result::Empty); return SCPI::getResultName(SCPI::Result::Empty);
} else { } else {
// invalid port number // invalid port number
@ -1066,7 +1083,7 @@ void SpectrumAnalyzer::SetupSCPI()
} }
return SCPI::getResultName(SCPI::Result::Empty); return SCPI::getResultName(SCPI::Result::Empty);
}, [=](QStringList) -> QString { }, [=](QStringList) -> QString {
return QString::number(settings.trackingPort+1); return QString::number(settings.trackingPort);
})); }));
scpi_tg->add(new SCPICommand("LVL", [=](QStringList params) -> QString { scpi_tg->add(new SCPICommand("LVL", [=](QStringList params) -> QString {
double newval; double newval;
@ -1077,7 +1094,7 @@ void SpectrumAnalyzer::SetupSCPI()
return SCPI::getResultName(SCPI::Result::Empty); return SCPI::getResultName(SCPI::Result::Empty);
} }
}, [=](QStringList) -> QString { }, [=](QStringList) -> QString {
return QString::number(settings.trackingPower / 100.0); return QString::number(settings.trackingPower);
})); }));
scpi_tg->add(new SCPICommand("OFFset", [=](QStringList params) -> QString { scpi_tg->add(new SCPICommand("OFFset", [=](QStringList params) -> QString {
long newval; long newval;

View File

@ -224,7 +224,6 @@ std::vector<Trace *> TraceModel::getLiveTraces() const
bool TraceModel::PortExcitationRequired(int port) bool TraceModel::PortExcitationRequired(int port)
{ {
port++;
for(auto t : traces) { for(auto t : traces) {
if(t->getSource() == Trace::Source::Live && !t->isPaused()) { if(t->getSource() == Trace::Source::Live && !t->isPaused()) {
// this trace needs measurements from VNA, check if port has to be excited for its measurement // this trace needs measurements from VNA, check if port has to be excited for its measurement

View File

@ -403,7 +403,7 @@ VNA::VNA(AppWindow *window, QString name)
tb_acq->addWidget(new QLabel("Points:")); tb_acq->addWidget(new QLabel("Points:"));
tb_acq->addWidget(points); tb_acq->addWidget(points);
auto eBandwidth = new SIUnitEdit("Hz", " k", 3); auto eBandwidth = new SIUnitEdit("Hz", " kM", 3);
eBandwidth->setFixedWidth(70); eBandwidth->setFixedWidth(70);
eBandwidth->setToolTip("IF bandwidth"); eBandwidth->setToolTip("IF bandwidth");
connect(eBandwidth, &SIUnitEdit::valueChanged, this, &VNA::SetIFBandwidth); connect(eBandwidth, &SIUnitEdit::valueChanged, this, &VNA::SetIFBandwidth);
@ -675,6 +675,10 @@ void VNA::deactivate()
void VNA::initializeDevice() 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); defaultCalMenu->setEnabled(true);
connect(window->getDevice(), &DeviceDriver::VNAmeasurementReceived, this, &VNA::NewDatapoint, Qt::UniqueConnection); connect(window->getDevice(), &DeviceDriver::VNAmeasurementReceived, this, &VNA::NewDatapoint, Qt::UniqueConnection);
// Check if default calibration exists and attempt to load it // Check if default calibration exists and attempt to load it
@ -1680,11 +1684,11 @@ void VNA::ConfigureDevice(bool resetTraces, std::function<void(bool)> cb)
DeviceDriver::VNASettings s = {}; DeviceDriver::VNASettings s = {};
s.IFBW = settings.bandwidth; s.IFBW = settings.bandwidth;
if(Preferences::getInstance().Acquisition.alwaysExciteAllPorts) { if(Preferences::getInstance().Acquisition.alwaysExciteAllPorts) {
for(unsigned int i=0;i<DeviceDriver::getInfo(window->getDevice()).Limits.VNA.ports;i++) { for(unsigned int i=1;i<=DeviceDriver::getInfo(window->getDevice()).Limits.VNA.ports;i++) {
s.excitedPorts.push_back(i); s.excitedPorts.push_back(i);
} }
} else { } else {
for(unsigned int i=0;i<DeviceDriver::getInfo(window->getDevice()).Limits.VNA.ports;i++) { for(unsigned int i=1;i<=DeviceDriver::getInfo(window->getDevice()).Limits.VNA.ports;i++) {
if(traceModel.PortExcitationRequired(i)) if(traceModel.PortExcitationRequired(i))
s.excitedPorts.push_back(i); s.excitedPorts.push_back(i);
} }

View File

@ -100,11 +100,6 @@ AppWindow::AppWindow(QWidget *parent)
Preferences::getInstance().load(); Preferences::getInstance().load();
} }
for(auto driver : DeviceDriver::getDrivers()) {
driver->registerTypes();
Preferences::getInstance().load(driver->driverSpecificSettings());
}
device = nullptr; device = nullptr;
// vdevice = nullptr; // vdevice = nullptr;
modeHandler = nullptr; modeHandler = nullptr;
@ -254,6 +249,8 @@ void AppWindow::SetupMenu()
auto SCPIenabled = p.SCPIServer.enabled; auto SCPIenabled = p.SCPIServer.enabled;
auto SCPIport = p.SCPIServer.port; auto SCPIport = p.SCPIServer.port;
p.edit(); p.edit();
// store the updated settings
p.store();
if(SCPIenabled != p.SCPIServer.enabled || SCPIport != p.SCPIServer.port) { if(SCPIenabled != p.SCPIServer.enabled || SCPIport != p.SCPIServer.port) {
StopTCPServer(); StopTCPServer();
if(p.SCPIServer.enabled) { if(p.SCPIServer.enabled) {
@ -321,9 +318,6 @@ void AppWindow::closeEvent(QCloseEvent *event)
delete modeHandler; delete modeHandler;
modeHandler = nullptr; modeHandler = nullptr;
pref.store(); pref.store();
for(auto driver : DeviceDriver::getDrivers()) {
Preferences::getInstance().store(driver->driverSpecificSettings());
}
for(auto driver : DeviceDriver::getDrivers()) { for(auto driver : DeviceDriver::getDrivers()) {
delete driver; delete driver;
} }
@ -350,9 +344,11 @@ bool AppWindow::ConnectToDevice(QString serial, DeviceDriver *driver)
} }
if(d->GetAvailableDevices().count(serial)) { if(d->GetAvailableDevices().count(serial)) {
// this driver can connect to the device // this driver can connect to the device
connect(d, &DeviceDriver::InfoUpdated, this, &AppWindow::DeviceInfoUpdated, Qt::QueuedConnection);
if(d->connectDevice(serial)) { if(d->connectDevice(serial)) {
device = d; device = d;
} else { } else {
disconnect(d, nullptr, this, nullptr);
break; break;
} }
} }
@ -363,7 +359,6 @@ bool AppWindow::ConnectToDevice(QString serial, DeviceDriver *driver)
return false; return false;
} }
UpdateStatusBar(AppWindow::DeviceStatusBar::Connected); UpdateStatusBar(AppWindow::DeviceStatusBar::Connected);
connect(device, &DeviceDriver::InfoUpdated, this, &AppWindow::DeviceInfoUpdated);
connect(device, &DeviceDriver::LogLineReceived, &deviceLog, &DeviceLog::addLine); connect(device, &DeviceDriver::LogLineReceived, &deviceLog, &DeviceLog::addLine);
connect(device, &DeviceDriver::ConnectionLost, this, &AppWindow::DeviceConnectionLost); connect(device, &DeviceDriver::ConnectionLost, this, &AppWindow::DeviceConnectionLost);
connect(device, &DeviceDriver::StatusUpdated, this, &AppWindow::DeviceStatusUpdated); connect(device, &DeviceDriver::StatusUpdated, this, &AppWindow::DeviceStatusUpdated);
@ -420,9 +415,9 @@ bool AppWindow::ConnectToDevice(QString serial, DeviceDriver *driver)
// vdevice->initialize(); // vdevice->initialize();
// UpdateAcquisitionFrequencies(); // UpdateAcquisitionFrequencies();
if (modeHandler->getActiveMode()) { // if (modeHandler->getActiveMode()) {
modeHandler->getActiveMode()->initializeDevice(); // modeHandler->getActiveMode()->initializeDevice();
} // }
return true; return true;
} catch (const runtime_error &e) { } catch (const runtime_error &e) {
qWarning() << "Failed to connect:" << e.what(); qWarning() << "Failed to connect:" << e.what();
@ -1370,7 +1365,7 @@ void AppWindow::UpdateStatusBar(DeviceStatusBar status)
break; break;
case DeviceStatusBar::Disconnected: case DeviceStatusBar::Disconnected:
lConnectionStatus.setText("No device connected"); lConnectionStatus.setText("No device connected");
lDeviceInfo.setText("No device information available yet"); lDeviceInfo.setText("No status information available yet");
break; break;
default: default:
// invalid status // invalid status

View File

@ -62,6 +62,8 @@ protected:
AppWindow *window; AppWindow *window;
std::set<QAction*> actions; std::set<QAction*> actions;
std::set<QToolBar*> toolbars; std::set<QToolBar*> toolbars;
QToolBar *tb_trackgen;
SIUnitEdit *tgOffset;
std::set<QDockWidget*> docks; std::set<QDockWidget*> docks;
private: private:

View File

@ -3,7 +3,7 @@
#include "ui_preferencesdialog.h" #include "ui_preferencesdialog.h"
#include "CustomWidgets/informationbox.h" #include "CustomWidgets/informationbox.h"
#include "appwindow.h" #include "appwindow.h"
#include "Device/compounddeviceeditdialog.h" #include "Device/LibreVNA/Compound/compounddeviceeditdialog.h"
#include <QSettings> #include <QSettings>
#include <QPushButton> #include <QPushButton>
@ -137,56 +137,6 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
ui->MarkerShowP1dB->setEnabled(enabled); 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 // Debug page
ui->DebugMaxUSBlogSize->setUnit("B"); ui->DebugMaxUSBlogSize->setUnit("B");
ui->DebugMaxUSBlogSize->setPrefixes(" kMG"); ui->DebugMaxUSBlogSize->setPrefixes(" kMG");
@ -362,10 +312,6 @@ void PreferencesDialog::setInitialGUIState()
ui->DebugMaxUSBlogSize->setValue(p->Debug.USBlogSizeLimit); ui->DebugMaxUSBlogSize->setValue(p->Debug.USBlogSizeLimit);
ui->DebugSaveTraceData->setChecked(p->Debug.saveTraceData); ui->DebugSaveTraceData->setChecked(p->Debug.saveTraceData);
for(auto cd : p->compoundDevices) {
ui->compoundList->addItem(cd->getDesription());
}
QTreeWidgetItem *item = ui->treeWidget->topLevelItem(0); QTreeWidgetItem *item = ui->treeWidget->topLevelItem(0);
if (item != nullptr) { if (item != nullptr) {
ui->treeWidget->setCurrentItem(item); // visually select first item ui->treeWidget->setCurrentItem(item); // visually select first item
@ -466,9 +412,7 @@ void PreferencesDialog::updateFromGUI()
Preferences::~Preferences() Preferences::~Preferences()
{ {
for(auto cd : compoundDevices) {
delete cd;
}
} }
void Preferences::load() void Preferences::load()
@ -476,6 +420,10 @@ void Preferences::load()
// load settings, using default values if not present // load settings, using default values if not present
qInfo() << "Loading preferences"; qInfo() << "Loading preferences";
load(descr); load(descr);
for(auto driver : DeviceDriver::getDrivers()) {
driver->registerTypes();
load(driver->driverSpecificSettings());
}
nonTrivialParsing(); nonTrivialParsing();
} }
@ -495,6 +443,9 @@ void Preferences::store()
{ {
nonTrivialWriting(); nonTrivialWriting();
store(descr); store(descr);
for(auto driver : DeviceDriver::getDrivers()) {
store(driver->driverSpecificSettings());
}
} }
void Preferences::store(std::vector<Savable::SettingDescription> descr) void Preferences::store(std::vector<Savable::SettingDescription> descr)
@ -515,6 +466,16 @@ void Preferences::edit()
} }
void Preferences::setDefault() 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<Savable::SettingDescription> descr)
{ {
for(auto d : descr) { for(auto d : descr) {
d.var.setValue(d.def); d.var.setValue(d.def);
@ -524,39 +485,42 @@ void Preferences::setDefault()
void Preferences::fromJSON(nlohmann::json j) void Preferences::fromJSON(nlohmann::json j)
{ {
parseJSON(j, descr); parseJSON(j, descr);
for(auto driver : DeviceDriver::getDrivers()) {
Savable::parseJSON(j, driver->driverSpecificSettings());
}
nonTrivialParsing(); 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() nlohmann::json Preferences::toJSON()
{ {
nonTrivialWriting(); 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() 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() 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 = "[]";
}
} }

View File

@ -4,7 +4,7 @@
#include "Util/qpointervariant.h" #include "Util/qpointervariant.h"
#include "savable.h" #include "savable.h"
#include "Device/compounddevice.h" #include "Device/LibreVNA/Compound/compounddevice.h"
#include <QDialog> #include <QDialog>
#include <QVariant> #include <QVariant>
@ -56,6 +56,7 @@ public:
void store(std::vector<Savable::SettingDescription> descr); void store(std::vector<Savable::SettingDescription> descr);
void edit(); void edit();
void setDefault(); void setDefault();
void setDefault(std::vector<Savable::SettingDescription> descr);
void manualTCPport() { TCPoverride = true; } void manualTCPport() { TCPoverride = true; }
@ -159,9 +160,6 @@ public:
bool TCPoverride; // in case of manual port specification via command line bool TCPoverride; // in case of manual port specification via command line
QString compoundDeviceJSON;
std::vector<CompoundDevice*> compoundDevices;
void fromJSON(nlohmann::json j) override; void fromJSON(nlohmann::json j) override;
nlohmann::json toJSON() override; nlohmann::json toJSON() override;
@ -258,7 +256,6 @@ private:
{&SCPIServer.port, "SCPIServer.port", 19542}, {&SCPIServer.port, "SCPIServer.port", 19542},
{&Debug.USBlogSizeLimit, "Debug.USBlogSizeLimit", 10000000.0}, {&Debug.USBlogSizeLimit, "Debug.USBlogSizeLimit", 10000000.0},
{&Debug.saveTraceData, "Debug.saveTraceData", false}, {&Debug.saveTraceData, "Debug.saveTraceData", false},
{&compoundDeviceJSON, "compoundDeviceJSON", "[]"},
}}; }};
}; };

View File

@ -13,20 +13,22 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Preferences</string> <string>Preferences</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_25"> <layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTreeWidget" name="treeWidget"> <widget class="QTreeWidget" name="treeWidget">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>180</width> <width>250</width>
<height>16777215</height> <height>16777215</height>
</size> </size>
</property> </property>
@ -66,11 +68,6 @@
<string>SCPI Server</string> <string>SCPI Server</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>Compound Devices</string>
</property>
</item>
<item> <item>
<property name="text"> <property name="text">
<string>Debug</string> <string>Debug</string>
@ -82,8 +79,6 @@
</property> </property>
</item> </item>
</widget> </widget>
</item>
<item>
<widget class="QStackedWidget" name="pageWidget"> <widget class="QStackedWidget" name="pageWidget">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -98,7 +93,7 @@
</size> </size>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>6</number> <number>3</number>
</property> </property>
<widget class="QWidget" name="Startup"> <widget class="QWidget" name="Startup">
<layout class="QHBoxLayout" name="horizontalLayout_4"> <layout class="QHBoxLayout" name="horizontalLayout_4">
@ -112,7 +107,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>749</width> <width>683</width>
<height>920</height> <height>920</height>
</rect> </rect>
</property> </property>
@ -717,8 +712,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>763</width> <width>565</width>
<height>561</height> <height>365</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_21"> <layout class="QVBoxLayout" name="verticalLayout_21">
@ -874,8 +869,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>749</width> <width>553</width>
<height>952</height> <height>969</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_22"> <layout class="QVBoxLayout" name="verticalLayout_22">
@ -1286,7 +1281,7 @@
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="Marker"> <widget class="QWidget" name="Marker">
<layout class="QHBoxLayout" name="horizontalLayout_6"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QScrollArea" name="scrollArea_4"> <widget class="QScrollArea" name="scrollArea_4">
<property name="widgetResizable"> <property name="widgetResizable">
@ -1296,12 +1291,12 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>-44</y> <y>0</y>
<width>749</width> <width>683</width>
<height>605</height> <height>605</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_23"> <layout class="QVBoxLayout" name="verticalLayout_7">
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_9" stretch="0,0,0"> <layout class="QVBoxLayout" name="verticalLayout_9" stretch="0,0,0">
<item> <item>
@ -1317,13 +1312,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="label_51">
<property name="text">
<string>Select formats to be shown by default:</string>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_18"> <layout class="QHBoxLayout" name="horizontalLayout_18">
<item> <item>
@ -1418,6 +1406,13 @@
<string>Special marker formats</string> <string>Special marker formats</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_27"> <layout class="QVBoxLayout" name="verticalLayout_27">
<item>
<widget class="QLabel" name="label_51">
<property name="text">
<string>Select formats to be shown by default:</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QCheckBox" name="MarkerShowP1dB"> <widget class="QCheckBox" name="MarkerShowP1dB">
<property name="text"> <property name="text">
@ -1636,8 +1631,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>763</width> <width>168</width>
<height>561</height> <height>127</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_12"> <layout class="QHBoxLayout" name="horizontalLayout_12">
@ -1714,77 +1709,6 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="CompoundDevices">
<layout class="QHBoxLayout" name="horizontalLayout_14">
<item>
<widget class="QScrollArea" name="scrollArea_6">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_7">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>763</width>
<height>561</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_24">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_13">
<item>
<widget class="QListWidget" name="compoundList"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_19">
<item>
<widget class="QPushButton" name="compoundAdd">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="list-add" resource="icons.qrc">
<normaloff>:/icons/add.png</normaloff>
<normalon>:/icons/add.png</normalon>:/icons/add.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="compoundDelete">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="list-remove" resource="icons.qrc">
<normaloff>:/icons/remove.png</normaloff>
<normalon>:/icons/remove.png</normalon>:/icons/remove.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_8">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="Debug"> <widget class="QWidget" name="Debug">
<layout class="QVBoxLayout" name="verticalLayout_29"> <layout class="QVBoxLayout" name="verticalLayout_29">
<item> <item>
@ -1797,8 +1721,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>763</width> <width>211</width>
<height>561</height> <height>168</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_19"> <layout class="QHBoxLayout" name="horizontalLayout_19">
@ -1874,8 +1798,7 @@
</layout> </layout>
</widget> </widget>
</widget> </widget>
</item> </widget>
</layout>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
@ -1904,8 +1827,6 @@
<header>CustomWidgets/siunitedit.h</header> <header>CustomWidgets/siunitedit.h</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources> <resources/>
<include location="icons.qrc"/>
</resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -53,6 +53,7 @@ public:
switch(static_cast<QMetaType::Type>(val.type())) { switch(static_cast<QMetaType::Type>(val.type())) {
case QMetaType::Double: e.var.setValue((*json_entry).get<double>()); break; case QMetaType::Double: e.var.setValue((*json_entry).get<double>()); break;
case QMetaType::Int: e.var.setValue((*json_entry).get<int>()); break; case QMetaType::Int: e.var.setValue((*json_entry).get<int>()); break;
case QMetaType::UInt: e.var.setValue((*json_entry).get<unsigned int>()); break;
case QMetaType::Bool: e.var.setValue((*json_entry).get<bool>()); break; case QMetaType::Bool: e.var.setValue((*json_entry).get<bool>()); break;
case QMetaType::QString: { case QMetaType::QString: {
auto s = QString::fromStdString((*json_entry).get<std::string>()); auto s = QString::fromStdString((*json_entry).get<std::string>());
@ -82,6 +83,7 @@ public:
switch(static_cast<QMetaType::Type>(val.type())) { switch(static_cast<QMetaType::Type>(val.type())) {
case QMetaType::Double: *json_entry = val.toDouble(); break; case QMetaType::Double: *json_entry = val.toDouble(); break;
case QMetaType::Int: *json_entry = val.toInt(); 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::Bool: *json_entry = val.toBool(); break;
case QMetaType::QString: *json_entry = val.toString().toStdString(); break; case QMetaType::QString: *json_entry = val.toString().toStdString(); break;
case QMetaType::QColor: *json_entry = val.value<QColor>().name().toStdString(); break; case QMetaType::QColor: *json_entry = val.value<QColor>().name().toStdString(); break;

View File

@ -34,8 +34,10 @@ SOURCES += \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialog.cpp \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialog.cpp \
../LibreVNA-GUI/Device/LibreVNA/receivercaldialog.cpp \ ../LibreVNA-GUI/Device/LibreVNA/receivercaldialog.cpp \
../LibreVNA-GUI/Device/LibreVNA/sourcecaldialog.cpp \ ../LibreVNA-GUI/Device/LibreVNA/sourcecaldialog.cpp \
../LibreVNA-GUI/Device/compounddevice.cpp \ ../LibreVNA-GUI/Device/LibreVNA/Compound/compounddevice.cpp \
../LibreVNA-GUI/Device/compounddeviceeditdialog.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/device.cpp \
../LibreVNA-GUI/Device/devicedriver.cpp \ ../LibreVNA-GUI/Device/devicedriver.cpp \
../LibreVNA-GUI/Device/devicelog.cpp \ ../LibreVNA-GUI/Device/devicelog.cpp \
@ -205,8 +207,10 @@ HEADERS += \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialog.h \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialog.h \
../LibreVNA-GUI/Device/LibreVNA/receivercaldialog.h \ ../LibreVNA-GUI/Device/LibreVNA/receivercaldialog.h \
../LibreVNA-GUI/Device/LibreVNA/sourcecaldialog.h \ ../LibreVNA-GUI/Device/LibreVNA/sourcecaldialog.h \
../LibreVNA-GUI/Device/compounddevice.h \ ../LibreVNA-GUI/Device/LibreVNA/Compound/compounddevice.h \
../LibreVNA-GUI/Device/compounddeviceeditdialog.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/device.h \
../LibreVNA-GUI/Device/devicedriver.h \ ../LibreVNA-GUI/Device/devicedriver.h \
../LibreVNA-GUI/Device/devicelog.h \ ../LibreVNA-GUI/Device/devicelog.h \
@ -355,7 +359,7 @@ FORMS += \
../LibreVNA-GUI/Device/LibreVNA/firmwareupdatedialog.ui \ ../LibreVNA-GUI/Device/LibreVNA/firmwareupdatedialog.ui \
../LibreVNA-GUI/Device/LibreVNA/frequencycaldialog.ui \ ../LibreVNA-GUI/Device/LibreVNA/frequencycaldialog.ui \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialog.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/devicelog.ui \
../LibreVNA-GUI/Device/deviceusblogview.ui \ ../LibreVNA-GUI/Device/deviceusblogview.ui \
../LibreVNA-GUI/Generator/signalgenwidget.ui \ ../LibreVNA-GUI/Generator/signalgenwidget.ui \