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

View File

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

View File

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

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

View File

@ -9,6 +9,7 @@
class LibreVNADriver : public DeviceDriver
{
friend class CompoundDriver;
Q_OBJECT
public:
enum class TransmissionResult {
@ -161,6 +162,16 @@ public:
*/
virtual void registerTypes();
enum class Synchronization {
Disabled = 0,
GUI = 1,
Reserved = 2,
ExternalTrigger = 3,
Last = 4,
};
void setSynchronization(Synchronization s, bool master);
public:
signals:
// Required for the compound device driver
@ -191,7 +202,10 @@ protected:
bool zerospan;
unsigned int SApoints;
std::map<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
bool captureRawReceiverValues;

View File

@ -26,16 +26,26 @@ LibreVNATCPDriver::LibreVNATCPDriver()
auto interfaces = QNetworkInterface::allInterfaces();
for(auto i : interfaces) {
qDebug() << "Creating socket for interface" << i.name();
auto socket = new QUdpSocket();
socket->bind(QHostAddress::AnyIPv4, 0, QUdpSocket::ShareAddress);
socket->setMulticastInterface(i);
qDebug() << socket->joinMulticastGroup(SSDPaddress, i);
socket->joinMulticastGroup(SSDPaddress, i);
connect(socket, &QUdpSocket::readyRead, this, [=](){
SSDPreceived(socket);
});
ssdpSockets.push_back(socket);
}
specificSettings.push_back(Savable::SettingDescription(&captureRawReceiverValues, "LibreVNATCPDriver.captureRawReceiverValues", false));
specificSettings.push_back(Savable::SettingDescription(&harmonicMixing, "LibreVNATCPDriver.harmonicMixing", false));
specificSettings.push_back(Savable::SettingDescription(&SASignalID, "LibreVNATCPDriver.signalID", true));
specificSettings.push_back(Savable::SettingDescription(&VNASuppressInvalidPeaks, "LibreVNATCPDriver.suppressInvalidPeaks", true));
specificSettings.push_back(Savable::SettingDescription(&VNAAdjustPowerLevel, "LibreVNATCPDriver.adjustPowerLevel", false));
specificSettings.push_back(Savable::SettingDescription(&SAUseDFT, "LibreVNATCPDriver.useDFT", true));
specificSettings.push_back(Savable::SettingDescription(&SARBWLimitForDFT, "LibreVNATCPDriver.RBWlimitDFT", 3000));
specificSettings.push_back(Savable::SettingDescription(&IF1, "LibreVNATCPDriver.IF1", 62000000));
specificSettings.push_back(Savable::SettingDescription(&ADCprescaler, "LibreVNATCPDriver.ADCprescaler", 128));
specificSettings.push_back(Savable::SettingDescription(&DFTPhaseInc, "LibreVNATCPDriver.DFTPhaseInc", 1280));
}
QString LibreVNATCPDriver::getDriverName()

View File

@ -126,6 +126,17 @@ LibreVNAUSBDriver::LibreVNAUSBDriver()
dataBuffer = nullptr;
logBuffer = nullptr;
m_receiveThread = nullptr;
specificSettings.push_back(Savable::SettingDescription(&captureRawReceiverValues, "LibreVNAUSBDriver.captureRawReceiverValues", false));
specificSettings.push_back(Savable::SettingDescription(&harmonicMixing, "LibreVNAUSBDriver.harmonicMixing", false));
specificSettings.push_back(Savable::SettingDescription(&SASignalID, "LibreVNAUSBDriver.signalID", true));
specificSettings.push_back(Savable::SettingDescription(&VNASuppressInvalidPeaks, "LibreVNAUSBDriver.suppressInvalidPeaks", true));
specificSettings.push_back(Savable::SettingDescription(&VNAAdjustPowerLevel, "LibreVNAUSBDriver.adjustPowerLevel", false));
specificSettings.push_back(Savable::SettingDescription(&SAUseDFT, "LibreVNAUSBDriver.useDFT", true));
specificSettings.push_back(Savable::SettingDescription(&SARBWLimitForDFT, "LibreVNAUSBDriver.RBWlimitDFT", 3000));
specificSettings.push_back(Savable::SettingDescription(&IF1, "LibreVNAUSBDriver.IF1", 62000000));
specificSettings.push_back(Savable::SettingDescription(&ADCprescaler, "LibreVNAUSBDriver.ADCprescaler", 128));
specificSettings.push_back(Savable::SettingDescription(&DFTPhaseInc, "LibreVNAUSBDriver.DFTPhaseInc", 1280));
}
QString LibreVNAUSBDriver::getDriverName()

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/librevnausbdriver.h"
#include "LibreVNA/Compound/compounddriver.h"
#include "SSA3000X/ssa3000xdriver.h"
DeviceDriver *DeviceDriver::activeDriver = nullptr;
@ -19,17 +21,23 @@ std::vector<DeviceDriver *> DeviceDriver::getDrivers()
// first function call
ret.push_back(new LibreVNAUSBDriver);
ret.push_back(new LibreVNATCPDriver);
ret.push_back(new CompoundDriver);
ret.push_back(new SSA3000XDriver);
}
return ret;
}
bool DeviceDriver::connectDevice(QString serial)
bool DeviceDriver::connectDevice(QString serial, bool isIndepedentDriver)
{
if(activeDriver && activeDriver != this) {
activeDriver->disconnect();
if(!isIndepedentDriver) {
if(activeDriver && activeDriver != this) {
activeDriver->disconnect();
}
}
if(connectTo(serial)) {
activeDriver = this;
if(!isIndepedentDriver) {
activeDriver = this;
}
return true;
} else {
return false;
@ -123,3 +131,41 @@ DeviceDriver::Info::Info()
Limits.SA.minRBW = 1;
Limits.SA.maxRBW = 1000000;
}
void DeviceDriver::Info::subset(const DeviceDriver::Info &info)
{
if (info.firmware_version != firmware_version) {
firmware_version = "Mixed";
}
if (info.hardware_version != hardware_version) {
hardware_version = "Mixed";
}
Limits.VNA.ports += info.Limits.VNA.ports;
Limits.VNA.minFreq = std::max(Limits.VNA.minFreq, info.Limits.VNA.minFreq);
Limits.VNA.maxFreq = std::min(Limits.VNA.maxFreq, info.Limits.VNA.maxFreq);
Limits.VNA.mindBm = std::max(Limits.VNA.mindBm, info.Limits.VNA.mindBm);
Limits.VNA.maxdBm = std::min(Limits.VNA.maxdBm, info.Limits.VNA.maxdBm);
Limits.VNA.minIFBW = std::max(Limits.VNA.minIFBW, info.Limits.VNA.minIFBW);
Limits.VNA.maxIFBW = std::min(Limits.VNA.maxIFBW, info.Limits.VNA.maxIFBW);
Limits.VNA.maxPoints = std::min(Limits.VNA.maxPoints, info.Limits.VNA.maxPoints);
Limits.Generator.ports += info.Limits.Generator.ports;
Limits.Generator.minFreq = std::max(Limits.Generator.minFreq, info.Limits.Generator.minFreq);
Limits.Generator.maxFreq = std::min(Limits.Generator.maxFreq, info.Limits.Generator.maxFreq);
Limits.Generator.mindBm = std::max(Limits.Generator.mindBm, info.Limits.Generator.mindBm);
Limits.Generator.maxdBm = std::min(Limits.Generator.maxdBm, info.Limits.Generator.maxdBm);
Limits.SA.ports += info.Limits.SA.ports;
Limits.SA.minFreq = std::max(Limits.SA.minFreq, info.Limits.SA.minFreq);
Limits.SA.maxFreq = std::min(Limits.SA.maxFreq, info.Limits.SA.maxFreq);
Limits.SA.mindBm = std::max(Limits.SA.mindBm, info.Limits.SA.mindBm);
Limits.SA.maxdBm = std::min(Limits.SA.maxdBm, info.Limits.SA.maxdBm);
Limits.SA.minRBW = std::max(Limits.SA.minRBW, info.Limits.SA.minRBW);
Limits.SA.maxRBW = std::min(Limits.SA.maxRBW, info.Limits.SA.maxRBW);
std::set<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
* @return true if connection successful, otherwise false
*/
virtual bool connectTo(QString getSerial) = 0;
virtual bool connectTo(QString serial) = 0;
/**
* @brief Disconnects from device. Has no effect if no device was connected
*/
@ -120,6 +120,7 @@ public:
double mindBm, maxdBm;
} SA;
} Limits;
void subset(const Info &info);
};
/**
@ -486,7 +487,7 @@ signals:
void releaseControl();
public:
bool connectDevice(QString serial);
bool connectDevice(QString serial, bool isIndepedentDriver = false);
void disconnectDevice();
static DeviceDriver* getActiveDriver() {return activeDriver;}
static unsigned int SApoints();

View File

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

View File

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

View File

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

View File

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

View File

@ -403,7 +403,7 @@ VNA::VNA(AppWindow *window, QString name)
tb_acq->addWidget(new QLabel("Points:"));
tb_acq->addWidget(points);
auto eBandwidth = new SIUnitEdit("Hz", " k", 3);
auto eBandwidth = new SIUnitEdit("Hz", " kM", 3);
eBandwidth->setFixedWidth(70);
eBandwidth->setToolTip("IF bandwidth");
connect(eBandwidth, &SIUnitEdit::valueChanged, this, &VNA::SetIFBandwidth);
@ -675,6 +675,10 @@ void VNA::deactivate()
void VNA::initializeDevice()
{
if(!window->getDevice()->supports(DeviceDriver::Feature::VNA)) {
InformationBox::ShowError("Unsupported", "The connected device does not support VNA mode");
return;
}
defaultCalMenu->setEnabled(true);
connect(window->getDevice(), &DeviceDriver::VNAmeasurementReceived, this, &VNA::NewDatapoint, Qt::UniqueConnection);
// Check if default calibration exists and attempt to load it
@ -1680,11 +1684,11 @@ void VNA::ConfigureDevice(bool resetTraces, std::function<void(bool)> cb)
DeviceDriver::VNASettings s = {};
s.IFBW = settings.bandwidth;
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);
}
} 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))
s.excitedPorts.push_back(i);
}

View File

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

View File

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

View File

@ -3,7 +3,7 @@
#include "ui_preferencesdialog.h"
#include "CustomWidgets/informationbox.h"
#include "appwindow.h"
#include "Device/compounddeviceeditdialog.h"
#include "Device/LibreVNA/Compound/compounddeviceeditdialog.h"
#include <QSettings>
#include <QPushButton>
@ -137,56 +137,6 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
ui->MarkerShowP1dB->setEnabled(enabled);
});
// Compound device page
// connect(ui->compoundList, &QListWidget::currentRowChanged, [=](){
// if(VirtualDevice::getConnected() && VirtualDevice::getConnected()->getCompoundDevice() == p->compoundDevices[ui->compoundList->currentRow()]) {
// // can't remove the device we are connected to
// ui->compoundDelete->setEnabled(false);
// } else {
// ui->compoundDelete->setEnabled(true);
// }
// });
connect(ui->compoundList, &QListWidget::doubleClicked, [=](){
auto index = ui->compoundList->currentRow();
if(index >= 0 && index < (int) p->compoundDevices.size()) {
auto d = new CompoundDeviceEditDialog(p->compoundDevices[index]);
connect(d, &QDialog::accepted, [=](){
ui->compoundList->item(index)->setText(p->compoundDevices[index]->getDesription());
p->nonTrivialWriting();
});
d->show();
}
});
connect(ui->compoundAdd, &QPushButton::clicked, [=](){
auto cd = new CompoundDevice;
auto d = new CompoundDeviceEditDialog(cd);
connect(d, &QDialog::accepted, [=](){
p->compoundDevices.push_back(cd);
ui->compoundList->addItem(cd->getDesription());
p->nonTrivialWriting();
});
connect(d, &QDialog::rejected, [=](){
delete cd;
});
d->show();
});
// connect(ui->compoundDelete, &QPushButton::clicked, [=](){
// auto index = ui->compoundList->currentRow();
// if(index >= 0 && index < (int) p->compoundDevices.size()) {
// // delete the actual compound device
// if(VirtualDevice::getConnected() && VirtualDevice::getConnected()->getCompoundDevice() == p->compoundDevices[index]) {
// // can't remove the device we are currently connected to
// return;
// }
// delete p->compoundDevices[index];
// // delete the line in the GUI list
// delete ui->compoundList->takeItem(index);
// // remove compound device from list
// p->compoundDevices.erase(p->compoundDevices.begin() + index);
// p->nonTrivialWriting();
// }
// });
// Debug page
ui->DebugMaxUSBlogSize->setUnit("B");
ui->DebugMaxUSBlogSize->setPrefixes(" kMG");
@ -362,10 +312,6 @@ void PreferencesDialog::setInitialGUIState()
ui->DebugMaxUSBlogSize->setValue(p->Debug.USBlogSizeLimit);
ui->DebugSaveTraceData->setChecked(p->Debug.saveTraceData);
for(auto cd : p->compoundDevices) {
ui->compoundList->addItem(cd->getDesription());
}
QTreeWidgetItem *item = ui->treeWidget->topLevelItem(0);
if (item != nullptr) {
ui->treeWidget->setCurrentItem(item); // visually select first item
@ -466,9 +412,7 @@ void PreferencesDialog::updateFromGUI()
Preferences::~Preferences()
{
for(auto cd : compoundDevices) {
delete cd;
}
}
void Preferences::load()
@ -476,6 +420,10 @@ void Preferences::load()
// load settings, using default values if not present
qInfo() << "Loading preferences";
load(descr);
for(auto driver : DeviceDriver::getDrivers()) {
driver->registerTypes();
load(driver->driverSpecificSettings());
}
nonTrivialParsing();
}
@ -495,6 +443,9 @@ void Preferences::store()
{
nonTrivialWriting();
store(descr);
for(auto driver : DeviceDriver::getDrivers()) {
store(driver->driverSpecificSettings());
}
}
void Preferences::store(std::vector<Savable::SettingDescription> descr)
@ -515,6 +466,16 @@ void Preferences::edit()
}
void Preferences::setDefault()
{
for(auto d : descr) {
d.var.setValue(d.def);
}
for(auto driver : DeviceDriver::getDrivers()) {
setDefault(driver->driverSpecificSettings());
}
}
void Preferences::setDefault(std::vector<Savable::SettingDescription> descr)
{
for(auto d : descr) {
d.var.setValue(d.def);
@ -524,39 +485,42 @@ void Preferences::setDefault()
void Preferences::fromJSON(nlohmann::json j)
{
parseJSON(j, descr);
for(auto driver : DeviceDriver::getDrivers()) {
Savable::parseJSON(j, driver->driverSpecificSettings());
}
nonTrivialParsing();
}
static nlohmann::json merge(const nlohmann::json &a, const nlohmann::json &b)
{
nlohmann::json result = a.flatten();
nlohmann::json tmp = b.flatten();
for (nlohmann::json::iterator it = tmp.begin(); it != tmp.end(); ++it)
{
result[it.key()] = it.value();
}
return result.unflatten();
}
nlohmann::json Preferences::toJSON()
{
nonTrivialWriting();
return createJSON(descr);
auto j = createJSON(descr);
for(auto driver : DeviceDriver::getDrivers()) {
j = merge(j, Savable::createJSON(driver->driverSpecificSettings()));
}
return j;
}
void Preferences::nonTrivialParsing()
{
try {
compoundDevices.clear();
nlohmann::json jc = nlohmann::json::parse(compoundDeviceJSON.toStdString());
for(auto j : jc) {
auto cd = new CompoundDevice();
cd->fromJSON(j);
compoundDevices.push_back(cd);
}
} catch(const exception& e){
qDebug() << "Failed to parse compound device string: " << e.what();
}
}
void Preferences::nonTrivialWriting()
{
if(compoundDevices.size() > 0) {
nlohmann::json j;
for(auto cd : compoundDevices) {
j.push_back(cd->toJSON());
}
compoundDeviceJSON = QString::fromStdString(j.dump());
} else {
compoundDeviceJSON = "[]";
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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