2022-11-14 07:08:45 +08:00
|
|
|
|
#include "virtualdevice.h"
|
2022-10-01 23:10:44 +08:00
|
|
|
|
|
|
|
|
|
#include "preferences.h"
|
|
|
|
|
#include "CustomWidgets/informationbox.h"
|
|
|
|
|
#include "../../VNA_embedded/Application/Communication/Protocol.hpp"
|
|
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
static VirtualDevice *connected = nullptr;
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
class Reference
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
enum class TypeIn {
|
|
|
|
|
Internal,
|
|
|
|
|
External,
|
|
|
|
|
Auto,
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
enum class OutFreq {
|
|
|
|
|
MHZ10,
|
|
|
|
|
MHZ100,
|
|
|
|
|
Off,
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static QString OutFreqToLabel(Reference::OutFreq t)
|
|
|
|
|
{
|
|
|
|
|
switch(t) {
|
|
|
|
|
case OutFreq::MHZ10: return "10 MHz";
|
|
|
|
|
case OutFreq::MHZ100: return "100 MHz";
|
|
|
|
|
case OutFreq::Off: return "Off";
|
|
|
|
|
default: return "Invalid";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString OutFreqToKey(Reference::OutFreq f)
|
|
|
|
|
{
|
|
|
|
|
switch(f) {
|
|
|
|
|
case OutFreq::MHZ10: return "10 MHz";
|
|
|
|
|
case OutFreq::MHZ100: return "100 MHz";
|
|
|
|
|
case OutFreq::Off: return "Off";
|
|
|
|
|
default: return "Invalid";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Reference::OutFreq KeyToOutFreq(QString key)
|
|
|
|
|
{
|
|
|
|
|
for (auto r: Reference::getOutFrequencies()) {
|
|
|
|
|
if(OutFreqToKey(r) == key|| OutFreqToLabel(r) == key) {
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// not found
|
|
|
|
|
return Reference::OutFreq::None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static QString TypeToLabel(TypeIn t)
|
|
|
|
|
{
|
|
|
|
|
switch(t) {
|
|
|
|
|
case TypeIn::Internal: return "Internal";
|
|
|
|
|
case TypeIn::External: return "External";
|
|
|
|
|
case TypeIn::Auto: return "Auto";
|
|
|
|
|
default: return "Invalid";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const QString TypeToKey(TypeIn t)
|
|
|
|
|
{
|
|
|
|
|
switch(t) {
|
|
|
|
|
case TypeIn::Internal: return "Int";
|
|
|
|
|
case TypeIn::External: return "Ext";
|
|
|
|
|
case TypeIn::Auto: return "Auto";
|
|
|
|
|
default: return "Invalid";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static TypeIn KeyToType(QString key)
|
|
|
|
|
{
|
|
|
|
|
for (auto r: Reference::getReferencesIn()) {
|
|
|
|
|
if(TypeToKey(r) == key || TypeToLabel(r) == key) {
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// not found
|
|
|
|
|
return TypeIn::None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::vector<Reference::TypeIn> getReferencesIn()
|
|
|
|
|
{
|
|
|
|
|
return {TypeIn::Internal, TypeIn::External, TypeIn::Auto};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::vector<Reference::OutFreq> getOutFrequencies()
|
|
|
|
|
{
|
|
|
|
|
return {OutFreq::Off, OutFreq::MHZ10, OutFreq::MHZ100};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
VirtualDevice::VirtualDevice(QString serial)
|
|
|
|
|
: QObject(),
|
|
|
|
|
info{},
|
|
|
|
|
status{}
|
|
|
|
|
{
|
|
|
|
|
cdev = nullptr;
|
|
|
|
|
zerospan = false;
|
|
|
|
|
|
|
|
|
|
// Check if this is a compound device
|
2022-10-06 04:17:53 +08:00
|
|
|
|
auto& pref = Preferences::getInstance();
|
2022-10-01 23:10:44 +08:00
|
|
|
|
for(auto cd : pref.compoundDevices) {
|
|
|
|
|
if(cd->name == serial) {
|
|
|
|
|
// connect request to this compound device
|
|
|
|
|
cdev = cd;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!isCompoundDevice()) {
|
|
|
|
|
// just acting as a wrapper for device, pass on signals
|
|
|
|
|
auto dev = new Device(serial);
|
|
|
|
|
devices.push_back(dev);
|
2022-10-24 03:19:53 +08:00
|
|
|
|
connect(dev, &Device::ConnectionLost, this, &VirtualDevice::ConnectionLost, Qt::QueuedConnection);
|
|
|
|
|
connect(dev, &Device::DeviceInfoUpdated, this, [=](){
|
2022-10-01 23:10:44 +08:00
|
|
|
|
info = Info(devices[0]);
|
|
|
|
|
emit InfoUpdated();
|
2022-10-24 03:19:53 +08:00
|
|
|
|
}, Qt::QueuedConnection);
|
|
|
|
|
connect(dev, &Device::LogLineReceived, this, &VirtualDevice::LogLineReceived, Qt::QueuedConnection);
|
|
|
|
|
connect(dev, &Device::DeviceStatusUpdated, this, [=](){
|
2022-10-01 23:10:44 +08:00
|
|
|
|
status = Status(devices[0]);
|
|
|
|
|
emit StatusUpdated(status);
|
2022-10-24 03:19:53 +08:00
|
|
|
|
}, Qt::QueuedConnection);
|
|
|
|
|
connect(dev, &Device::NeedsFirmwareUpdate, this, &VirtualDevice::NeedsFirmwareUpdate, Qt::QueuedConnection);
|
|
|
|
|
connect(dev, &Device::SpectrumResultReceived, this, &VirtualDevice::singleSpectrumResultReceived, Qt::QueuedConnection);
|
|
|
|
|
connect(dev, &Device::DatapointReceived, this, &VirtualDevice::singleDatapointReceived, Qt::QueuedConnection);
|
2022-10-01 23:10:44 +08:00
|
|
|
|
} else {
|
|
|
|
|
// Connect to the actual devices
|
|
|
|
|
for(auto devSerial : cdev->deviceSerials) {
|
2022-12-13 18:32:21 +08:00
|
|
|
|
auto dev = new Device(devSerial, true);
|
2022-10-01 23:10:44 +08:00
|
|
|
|
devices.push_back(dev);
|
|
|
|
|
// Create device connections
|
2022-10-24 03:19:53 +08:00
|
|
|
|
connect(dev, &Device::ConnectionLost, this, &VirtualDevice::ConnectionLost, Qt::QueuedConnection);
|
|
|
|
|
connect(dev, &Device::NeedsFirmwareUpdate, this, &VirtualDevice::NeedsFirmwareUpdate, Qt::QueuedConnection);
|
|
|
|
|
connect(dev, &Device::LogLineReceived, this, [=](QString line){
|
2022-10-01 23:10:44 +08:00
|
|
|
|
emit LogLineReceived(line.prepend(dev->serial()+": "));
|
2022-10-24 03:19:53 +08:00
|
|
|
|
}, Qt::QueuedConnection);
|
2022-10-01 23:10:44 +08:00
|
|
|
|
connect(dev, &Device::DeviceInfoUpdated, this, &VirtualDevice::compoundInfoUpdated, Qt::QueuedConnection);
|
|
|
|
|
connect(dev, &Device::DeviceStatusUpdated, this, &VirtualDevice::compoundStatusUpdated, Qt::QueuedConnection);
|
|
|
|
|
connect(dev, &Device::DatapointReceived, this, &VirtualDevice::compoundDatapointReceivecd, Qt::QueuedConnection);
|
|
|
|
|
connect(dev, &Device::SpectrumResultReceived, this, &VirtualDevice::compoundSpectrumResultReceived, Qt::QueuedConnection);
|
|
|
|
|
}
|
|
|
|
|
if(cdev->sync == CompoundDevice::Synchronization::USB) {
|
|
|
|
|
// create trigger connections for USB synchronization
|
2022-10-14 06:27:22 +08:00
|
|
|
|
for(unsigned int i=0;i<devices.size() - 1;i++) {
|
2022-10-01 23:10:44 +08:00
|
|
|
|
connect(devices[i], &Device::TriggerReceived, devices[i+1], &Device::SetTrigger, Qt::QueuedConnection);
|
|
|
|
|
}
|
|
|
|
|
connect(devices.back(), &Device::TriggerReceived, devices.front(), &Device::SetTrigger, Qt::QueuedConnection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
connected = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VirtualDevice::~VirtualDevice()
|
|
|
|
|
{
|
|
|
|
|
connected = nullptr;
|
|
|
|
|
for(auto dev : devices) {
|
|
|
|
|
delete dev;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VirtualDevice::RegisterTypes()
|
|
|
|
|
{
|
|
|
|
|
qRegisterMetaType<VirtualDevice::Status>("Status");
|
|
|
|
|
qRegisterMetaType<VirtualDevice::VNAMeasurement>("VNAMeasurement");
|
|
|
|
|
qRegisterMetaType<VirtualDevice::SAMeasurement>("SAMeasurement");
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 18:25:33 +08:00
|
|
|
|
void VirtualDevice::initialize()
|
|
|
|
|
{
|
|
|
|
|
for(auto dev : devices) {
|
|
|
|
|
dev->SendCommandWithoutPayload(Protocol::PacketType::RequestDeviceInfo);
|
|
|
|
|
dev->SendCommandWithoutPayload(Protocol::PacketType::RequestDeviceStatus);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-01 23:10:44 +08:00
|
|
|
|
bool VirtualDevice::isCompoundDevice() const
|
|
|
|
|
{
|
|
|
|
|
return cdev != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Device *VirtualDevice::getDevice()
|
|
|
|
|
{
|
|
|
|
|
if(isCompoundDevice() || devices.size() < 1) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
} else {
|
|
|
|
|
return devices[0];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CompoundDevice *VirtualDevice::getCompoundDevice()
|
|
|
|
|
{
|
|
|
|
|
return cdev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<Device *> VirtualDevice::getDevices()
|
|
|
|
|
{
|
|
|
|
|
return devices;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const VirtualDevice::Info &VirtualDevice::getInfo() const
|
|
|
|
|
{
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VirtualDevice::Info VirtualDevice::getInfo(VirtualDevice *vdev)
|
|
|
|
|
{
|
|
|
|
|
if(vdev) {
|
|
|
|
|
return vdev->info;
|
|
|
|
|
} else {
|
|
|
|
|
return Info();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const VirtualDevice::Status &VirtualDevice::getStatus() const
|
|
|
|
|
{
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VirtualDevice::Status VirtualDevice::getStatus(VirtualDevice *vdev)
|
|
|
|
|
{
|
|
|
|
|
if(vdev) {
|
|
|
|
|
return vdev->status;
|
|
|
|
|
} else {
|
|
|
|
|
return Status();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList VirtualDevice::availableVNAMeasurements()
|
|
|
|
|
{
|
|
|
|
|
QStringList ret;
|
2022-10-14 06:27:22 +08:00
|
|
|
|
for(unsigned int i=1;i<=info.ports;i++) {
|
|
|
|
|
for(unsigned int j=1;j<=info.ports;j++) {
|
2022-10-01 23:10:44 +08:00
|
|
|
|
ret.push_back("S"+QString::number(i)+QString::number(j));
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-16 19:28:46 +08:00
|
|
|
|
auto &pref = Preferences::getInstance();
|
|
|
|
|
if(pref.Debug.makeRawReceiverValuesAvailable) {
|
|
|
|
|
for(unsigned int i=1;i<=info.ports;i++) {
|
|
|
|
|
for(unsigned int j=0;j<info.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");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-01 23:10:44 +08:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool VirtualDevice::setVNA(const VirtualDevice::VNASettings &s, std::function<void (bool)> cb)
|
|
|
|
|
{
|
|
|
|
|
if(!info.supportsVNAmode) {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-06 04:17:53 +08:00
|
|
|
|
auto& pref = Preferences::getInstance();
|
|
|
|
|
Protocol::SweepSettings sd = {};
|
2022-10-01 23:10:44 +08:00
|
|
|
|
sd.f_start = s.freqStart;
|
|
|
|
|
sd.f_stop = s.freqStop;
|
|
|
|
|
sd.points = s.points;
|
|
|
|
|
sd.if_bandwidth = s.IFBW;
|
|
|
|
|
sd.cdbm_excitation_start = s.dBmStart * 100;
|
|
|
|
|
sd.cdbm_excitation_stop = s.dBmStop * 100;
|
|
|
|
|
sd.stages = s.excitedPorts.size() - 1;
|
|
|
|
|
sd.suppressPeaks = pref.Acquisition.suppressPeaks ? 1 : 0;
|
|
|
|
|
sd.fixedPowerSetting = pref.Acquisition.adjustPowerLevel || s.dBmStart != s.dBmStop ? 0 : 1;
|
|
|
|
|
sd.logSweep = s.logSweep ? 1 : 0;
|
|
|
|
|
|
|
|
|
|
zerospan = (s.freqStart == s.freqStop) && (s.dBmStart == s.dBmStop);
|
|
|
|
|
if(!isCompoundDevice()) {
|
|
|
|
|
sd.port1Stage = find(s.excitedPorts.begin(), s.excitedPorts.end(), 0) - s.excitedPorts.begin();
|
|
|
|
|
sd.port2Stage = find(s.excitedPorts.begin(), s.excitedPorts.end(), 1) - s.excitedPorts.begin();
|
|
|
|
|
sd.syncMode = 0;
|
|
|
|
|
sd.syncMaster = 0;
|
|
|
|
|
return devices[0]->Configure(sd, [=](Device::TransmissionResult r){
|
|
|
|
|
if(cb) {
|
|
|
|
|
cb(r == Device::TransmissionResult::Ack);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// set the synchronization mode
|
|
|
|
|
switch(cdev->sync) {
|
|
|
|
|
case CompoundDevice::Synchronization::USB: sd.syncMode = 1; break;
|
|
|
|
|
case CompoundDevice::Synchronization::ExtRef: sd.syncMode = 2; break;
|
|
|
|
|
case CompoundDevice::Synchronization::Trigger: sd.syncMode = 3; break;
|
2022-10-14 06:27:22 +08:00
|
|
|
|
case CompoundDevice::Synchronization::Last: sd.syncMode = 1; break; // should never get here
|
2022-10-01 23:10:44 +08:00
|
|
|
|
}
|
|
|
|
|
// create vector of currently used stimulus ports
|
|
|
|
|
vector<CompoundDevice::PortMapping> activeMapping;
|
|
|
|
|
for(auto p : s.excitedPorts) {
|
|
|
|
|
activeMapping.push_back(cdev->portMapping[p]);
|
|
|
|
|
}
|
|
|
|
|
// Configure the devices
|
|
|
|
|
results.clear();
|
|
|
|
|
bool success = true;
|
|
|
|
|
for(unsigned int i=0;i<devices.size();i++) {
|
|
|
|
|
sd.port1Stage = CompoundDevice::PortMapping::findActiveStage(activeMapping, i, 0);
|
|
|
|
|
sd.port2Stage = CompoundDevice::PortMapping::findActiveStage(activeMapping, i, 1);
|
|
|
|
|
sd.syncMaster = i == 0 ? 1 : 0;
|
|
|
|
|
success &= devices[i]->Configure(sd, [=](Device::TransmissionResult r){
|
|
|
|
|
if(cb) {
|
|
|
|
|
results[devices[i]] = r;
|
|
|
|
|
checkIfAllTransmissionsComplete(cb);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString VirtualDevice::serial()
|
|
|
|
|
{
|
|
|
|
|
if(!isCompoundDevice()) {
|
|
|
|
|
return devices[0]->serial();
|
|
|
|
|
} else {
|
|
|
|
|
return cdev->name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList VirtualDevice::availableSAMeasurements()
|
|
|
|
|
{
|
|
|
|
|
QStringList ret;
|
2022-10-14 06:27:22 +08:00
|
|
|
|
for(unsigned int i=1;i<=info.ports;i++) {
|
2022-10-01 23:10:44 +08:00
|
|
|
|
ret.push_back("PORT"+QString::number(i));
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool VirtualDevice::setSA(const VirtualDevice::SASettings &s, std::function<void (bool)> cb)
|
|
|
|
|
{
|
|
|
|
|
if(!info.supportsSAmode) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
zerospan = s.freqStart == s.freqStop;
|
2022-10-06 04:17:53 +08:00
|
|
|
|
auto& pref = Preferences::getInstance();
|
|
|
|
|
Protocol::SpectrumAnalyzerSettings sd = {};
|
2022-10-01 23:10:44 +08:00
|
|
|
|
sd.f_start = s.freqStart;
|
|
|
|
|
sd.f_stop = s.freqStop;
|
|
|
|
|
sd.pointNum = s.points;
|
|
|
|
|
sd.RBW = s.RBW;
|
|
|
|
|
sd.WindowType = (int) s.window;
|
|
|
|
|
sd.SignalID = s.signalID ? 1 : 0;
|
|
|
|
|
sd.Detector = (int) s.detector;
|
|
|
|
|
sd.UseDFT = 0;
|
|
|
|
|
if(!s.trackingGenerator && pref.Acquisition.useDFTinSAmode && s.RBW <= pref.Acquisition.RBWLimitForDFT) {
|
|
|
|
|
sd.UseDFT = 1;
|
|
|
|
|
}
|
|
|
|
|
sd.applyReceiverCorrection = 1;
|
|
|
|
|
sd.trackingGeneratorOffset = s.trackingOffset;
|
|
|
|
|
sd.trackingPower = s.trackingPower;
|
|
|
|
|
|
|
|
|
|
if(!isCompoundDevice()) {
|
|
|
|
|
sd.trackingGenerator = s.trackingGenerator ? 1 : 0;
|
|
|
|
|
sd.trackingGeneratorPort = s.trackingPort;
|
|
|
|
|
sd.syncMode = 0;
|
|
|
|
|
sd.syncMaster = 0;
|
|
|
|
|
return devices[0]->Configure(sd, [=](Device::TransmissionResult r){
|
|
|
|
|
if(cb) {
|
|
|
|
|
cb(r == Device::TransmissionResult::Ack);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// set the synchronization mode
|
|
|
|
|
switch(cdev->sync) {
|
|
|
|
|
case CompoundDevice::Synchronization::USB: sd.syncMode = 1; break;
|
|
|
|
|
case CompoundDevice::Synchronization::ExtRef: sd.syncMode = 2; break;
|
|
|
|
|
case CompoundDevice::Synchronization::Trigger: sd.syncMode = 3; break;
|
2022-10-14 06:27:22 +08:00
|
|
|
|
case CompoundDevice::Synchronization::Last: sd.syncMode = 1; break; // should never get here
|
2022-10-01 23:10:44 +08:00
|
|
|
|
}
|
|
|
|
|
// Configure the devices
|
|
|
|
|
results.clear();
|
|
|
|
|
bool success = true;
|
|
|
|
|
for(unsigned int i=0;i<devices.size();i++) {
|
|
|
|
|
sd.trackingGenerator = 0;
|
|
|
|
|
sd.trackingGeneratorPort = 0;
|
|
|
|
|
if(s.trackingGenerator) {
|
|
|
|
|
if(CompoundDevice::PortMapping::findActiveStage(cdev->portMapping, i, 0) == s.trackingPort) {
|
|
|
|
|
sd.trackingGenerator = 1;
|
|
|
|
|
sd.trackingGeneratorPort = 0;
|
|
|
|
|
} else if(CompoundDevice::PortMapping::findActiveStage(cdev->portMapping, i, 1) == s.trackingPort) {
|
|
|
|
|
sd.trackingGenerator = 1;
|
|
|
|
|
sd.trackingGeneratorPort = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sd.syncMaster = i == 0 ? 1 : 0;
|
|
|
|
|
success &= devices[i]->Configure(sd, [=](Device::TransmissionResult r){
|
|
|
|
|
if(cb) {
|
|
|
|
|
results[devices[i]] = r;
|
|
|
|
|
checkIfAllTransmissionsComplete(cb);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList VirtualDevice::availableSGPorts()
|
|
|
|
|
{
|
|
|
|
|
QStringList ret;
|
2022-10-14 06:27:22 +08:00
|
|
|
|
for(unsigned int i=1;i<info.ports;i++) {
|
2022-10-01 23:10:44 +08:00
|
|
|
|
ret.push_back("PORT"+QString::number(i));
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool VirtualDevice::setSG(const SGSettings &s)
|
|
|
|
|
{
|
|
|
|
|
if(!info.supportsSGmode) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-10-06 04:17:53 +08:00
|
|
|
|
Protocol::PacketInfo packet = {};
|
2022-10-01 23:10:44 +08:00
|
|
|
|
packet.type = Protocol::PacketType::Generator;
|
|
|
|
|
Protocol::GeneratorSettings &sd = packet.generator;
|
|
|
|
|
sd.frequency = s.freq;
|
|
|
|
|
sd.cdbm_level = s.dBm * 100;
|
|
|
|
|
sd.applyAmplitudeCorrection = 1;
|
|
|
|
|
|
|
|
|
|
if(!isCompoundDevice()) {
|
|
|
|
|
sd.activePort = s.port;
|
|
|
|
|
return devices[0]->SendPacket(packet);
|
|
|
|
|
} else {
|
|
|
|
|
// configure all devices
|
|
|
|
|
bool success = true;
|
|
|
|
|
for(unsigned int i=0;i<devices.size();i++) {
|
|
|
|
|
sd.activePort = 0;
|
|
|
|
|
if(s.port > 0) {
|
|
|
|
|
if(cdev->portMapping[s.port-1].device == i) {
|
|
|
|
|
// this device has the active port
|
|
|
|
|
sd.activePort = cdev->portMapping[s.port-1].port+1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
success &= devices[i]->SendPacket(packet);
|
|
|
|
|
}
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool VirtualDevice::setIdle(std::function<void (bool)> cb)
|
|
|
|
|
{
|
|
|
|
|
auto success = true;
|
|
|
|
|
results.clear();
|
|
|
|
|
for(auto dev : devices) {
|
|
|
|
|
success &= dev->SetIdle([=](Device::TransmissionResult r){
|
|
|
|
|
if(cb) {
|
|
|
|
|
results[dev] = r;
|
|
|
|
|
checkIfAllTransmissionsComplete(cb);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList VirtualDevice::availableExtRefInSettings()
|
|
|
|
|
{
|
|
|
|
|
QStringList ret;
|
|
|
|
|
for(auto r : Reference::getReferencesIn()) {
|
|
|
|
|
ret.push_back(Reference::TypeToLabel(r));
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList VirtualDevice::availableExtRefOutSettings()
|
|
|
|
|
{
|
|
|
|
|
QStringList ret;
|
|
|
|
|
for(auto r : Reference::getOutFrequencies()) {
|
|
|
|
|
ret.push_back(Reference::OutFreqToLabel(r));
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool VirtualDevice::setExtRef(QString option_in, QString option_out)
|
|
|
|
|
{
|
|
|
|
|
if(!info.supportsExtRef) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
auto refIn = Reference::KeyToType(option_in);
|
|
|
|
|
if(refIn == Reference::TypeIn::None) {
|
|
|
|
|
refIn = Reference::TypeIn::Internal;
|
|
|
|
|
}
|
|
|
|
|
auto refOut = Reference::KeyToOutFreq(option_out);
|
|
|
|
|
if(refOut == Reference::OutFreq::None) {
|
|
|
|
|
refOut = Reference::OutFreq::Off;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-06 04:17:53 +08:00
|
|
|
|
Protocol::PacketInfo p = {};
|
2022-10-01 23:10:44 +08:00
|
|
|
|
p.type = Protocol::PacketType::Reference;
|
|
|
|
|
switch(refIn) {
|
|
|
|
|
case Reference::TypeIn::Internal:
|
|
|
|
|
case Reference::TypeIn::None:
|
|
|
|
|
p.reference.UseExternalRef = 0;
|
|
|
|
|
p.reference.AutomaticSwitch = 0;
|
|
|
|
|
break;
|
|
|
|
|
case Reference::TypeIn::Auto:
|
|
|
|
|
p.reference.UseExternalRef = 0;
|
|
|
|
|
p.reference.AutomaticSwitch = 1;
|
|
|
|
|
break;
|
|
|
|
|
case Reference::TypeIn::External:
|
|
|
|
|
p.reference.UseExternalRef = 1;
|
|
|
|
|
p.reference.AutomaticSwitch = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
switch(refOut) {
|
|
|
|
|
case Reference::OutFreq::None:
|
|
|
|
|
case Reference::OutFreq::Off: p.reference.ExtRefOuputFreq = 0; break;
|
|
|
|
|
case Reference::OutFreq::MHZ10: p.reference.ExtRefOuputFreq = 10000000; break;
|
|
|
|
|
case Reference::OutFreq::MHZ100: p.reference.ExtRefOuputFreq = 100000000; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool success = true;
|
|
|
|
|
for(auto dev : devices) {
|
|
|
|
|
success &= dev->SendPacket(p);
|
|
|
|
|
}
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::set<QString> VirtualDevice::GetAvailableVirtualDevices()
|
|
|
|
|
{
|
2022-10-06 04:17:53 +08:00
|
|
|
|
auto& pref = Preferences::getInstance();
|
2022-10-01 23:10:44 +08:00
|
|
|
|
auto ret = Device::GetDevices();
|
|
|
|
|
// Add compound devices as well
|
|
|
|
|
for(auto vdev : pref.compoundDevices) {
|
|
|
|
|
// check if all serial number required for this compound device are available
|
|
|
|
|
bool serialMissing = false;
|
|
|
|
|
for(auto s : vdev->deviceSerials) {
|
|
|
|
|
if(ret.count(s) == 0) {
|
|
|
|
|
serialMissing = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(!serialMissing) {
|
|
|
|
|
// this compound device is available
|
|
|
|
|
ret.insert(vdev->name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VirtualDevice *VirtualDevice::getConnected()
|
|
|
|
|
{
|
|
|
|
|
return connected;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VirtualDevice::singleDatapointReceived(Device *dev, Protocol::VNADatapoint<32> *res)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(dev)
|
2022-11-16 19:28:46 +08:00
|
|
|
|
auto &pref = Preferences::getInstance();
|
2022-10-01 23:10:44 +08:00
|
|
|
|
VNAMeasurement m;
|
|
|
|
|
m.pointNum = res->pointNum;
|
|
|
|
|
m.Z0 = 50.0;
|
|
|
|
|
if(zerospan) {
|
|
|
|
|
m.us = res->us;
|
|
|
|
|
} else {
|
|
|
|
|
m.frequency = res->frequency;
|
|
|
|
|
m.dBm = (double) res->cdBm / 100;
|
|
|
|
|
}
|
|
|
|
|
for(auto map : portStageMapping) {
|
|
|
|
|
// map.first is the port (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);
|
2022-12-15 07:00:15 +08:00
|
|
|
|
for(unsigned int i=0;i<info.ports;i++) {
|
2022-10-01 23:10:44 +08:00
|
|
|
|
complex<double> input = res->getValue(map.second, i, 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);
|
|
|
|
|
m.measurements[name] = input / ref;
|
|
|
|
|
}
|
2022-11-16 19:28:46 +08:00
|
|
|
|
if(pref.Debug.makeRawReceiverValuesAvailable) {
|
|
|
|
|
QString name = "RawPort"+QString::number(i+1)+"Stage"+QString::number(map.first);
|
|
|
|
|
m.measurements[name] = input;
|
|
|
|
|
name = "RawPort"+QString::number(i+1)+"Stage"+QString::number(map.first)+"Ref";
|
|
|
|
|
m.measurements[name] = res->getValue(map.second, i, true);
|
|
|
|
|
}
|
2022-10-01 23:10:44 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete res;
|
|
|
|
|
emit VNAmeasurementReceived(m);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VirtualDevice::compoundDatapointReceivecd(Device *dev, Protocol::VNADatapoint<32> *data)
|
|
|
|
|
{
|
|
|
|
|
if(!compoundVNABuffer.count(data->pointNum)) {
|
|
|
|
|
compoundVNABuffer[data->pointNum] = std::map<Device*, Protocol::VNADatapoint<32>*>();
|
|
|
|
|
}
|
|
|
|
|
auto &buf = compoundVNABuffer[data->pointNum];
|
|
|
|
|
buf[dev] = 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 zero)
|
|
|
|
|
// 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[cdev->portMapping[map.first].device];
|
|
|
|
|
// ...and which device port was used for the stimulus...
|
|
|
|
|
auto stimulusDevPort = cdev->portMapping[map.first].port;
|
|
|
|
|
// ...grab the reference receiver data
|
|
|
|
|
complex<double> ref = buf[stimulusDev]->getValue(map.second, stimulusDevPort, true);
|
|
|
|
|
|
|
|
|
|
// for all ports of the compound device...
|
|
|
|
|
for(unsigned int i=0;i<cdev->portMapping.size();i++) {
|
|
|
|
|
// ...figure out which physical device and port was used for this input...
|
|
|
|
|
auto inputDevice = devices[cdev->portMapping[i].device];
|
|
|
|
|
// ...and grab the data
|
|
|
|
|
auto inputPort = cdev->portMapping[i].port;
|
|
|
|
|
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+1);
|
|
|
|
|
auto S = input / ref;
|
|
|
|
|
if(inputDevice != stimulusDev) {
|
|
|
|
|
// can't use phase information when measuring across devices
|
|
|
|
|
S = abs(S);
|
|
|
|
|
}
|
|
|
|
|
m.measurements[name] = S;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 VirtualDevice::singleSpectrumResultReceived(Device *dev, Protocol::SpectrumAnalyzerResult res)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(dev)
|
|
|
|
|
SAMeasurement m;
|
|
|
|
|
m.pointNum = res.pointNum;
|
|
|
|
|
if(zerospan) {
|
|
|
|
|
m.us = res.us;
|
|
|
|
|
} else {
|
|
|
|
|
m.frequency = res.frequency;
|
|
|
|
|
}
|
|
|
|
|
m.measurements["PORT1"] = res.port1;
|
|
|
|
|
m.measurements["PORT2"] = res.port2;
|
|
|
|
|
emit SAmeasurementReceived(m);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VirtualDevice::compoundSpectrumResultReceived(Device *dev, Protocol::SpectrumAnalyzerResult res)
|
|
|
|
|
{
|
|
|
|
|
if(!compoundSABuffer.count(res.pointNum)) {
|
|
|
|
|
compoundSABuffer[res.pointNum] = std::map<Device*, 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<cdev->portMapping.size();port++) {
|
|
|
|
|
auto device = devices[cdev->portMapping[port].device];
|
|
|
|
|
auto devicePort = cdev->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 VirtualDevice::compoundInfoUpdated(Device *dev)
|
|
|
|
|
{
|
|
|
|
|
compoundInfoBuffer[dev] = dev->Info();
|
|
|
|
|
if(compoundInfoBuffer.size() == devices.size()) {
|
|
|
|
|
// got information of all devices
|
|
|
|
|
info = Info(devices[0]);
|
2022-10-14 06:27:22 +08:00
|
|
|
|
for(unsigned int i=1;i<devices.size();i++) {
|
2022-10-01 23:10:44 +08:00
|
|
|
|
try {
|
|
|
|
|
info.subset(Info(devices[i]));
|
|
|
|
|
} catch (exception &e) {
|
|
|
|
|
InformationBox::ShowError("Failed to get device information", e.what());
|
|
|
|
|
emit ConnectionLost();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(cdev->sync == CompoundDevice::Synchronization::ExtRef) {
|
|
|
|
|
// can't use the external reference if it is used for synchronization
|
|
|
|
|
info.supportsExtRef = false;
|
|
|
|
|
}
|
|
|
|
|
info.ports = cdev->portMapping.size();
|
|
|
|
|
emit InfoUpdated();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VirtualDevice::compoundStatusUpdated(Device *dev)
|
|
|
|
|
{
|
|
|
|
|
compoundStatusBuffer[dev] = dev->StatusV1();
|
|
|
|
|
if(compoundStatusBuffer.size() == devices.size()) {
|
|
|
|
|
// got status of all devices
|
|
|
|
|
status = Status(devices[0]);
|
2022-10-14 06:27:22 +08:00
|
|
|
|
for(unsigned int i=1;i<devices.size();i++) {
|
2022-10-01 23:10:44 +08:00
|
|
|
|
status.merge(Status(devices[i]));
|
|
|
|
|
}
|
|
|
|
|
emit StatusUpdated(status);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VirtualDevice::checkIfAllTransmissionsComplete(std::function<void (bool)> cb)
|
|
|
|
|
{
|
|
|
|
|
if(results.size() == devices.size()) {
|
|
|
|
|
// got all responses
|
|
|
|
|
bool success = true;
|
|
|
|
|
for(auto res : results) {
|
|
|
|
|
if(res.second != Device::TransmissionResult::Ack) {
|
|
|
|
|
success = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(cb) {
|
|
|
|
|
cb(success);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Sparam VirtualDevice::VNAMeasurement::toSparam(int port1, int port2) const
|
|
|
|
|
{
|
|
|
|
|
Sparam S;
|
|
|
|
|
S.m11 = measurements.at("S"+QString::number(port1)+QString::number(port1));
|
|
|
|
|
S.m12 = measurements.at("S"+QString::number(port1)+QString::number(port2));
|
|
|
|
|
S.m21 = measurements.at("S"+QString::number(port2)+QString::number(port1));
|
|
|
|
|
S.m22 = measurements.at("S"+QString::number(port2)+QString::number(port2));
|
|
|
|
|
return S;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VirtualDevice::VNAMeasurement::fromSparam(Sparam S, int port1, int port2)
|
|
|
|
|
{
|
|
|
|
|
QString s11 = "S"+QString::number(port1)+QString::number(port1);
|
|
|
|
|
QString s12 = "S"+QString::number(port1)+QString::number(port2);
|
|
|
|
|
QString s21 = "S"+QString::number(port2)+QString::number(port1);
|
|
|
|
|
QString s22 = "S"+QString::number(port2)+QString::number(port2);
|
|
|
|
|
if(measurements.count(s11)) {
|
|
|
|
|
measurements[s11] = S.m11;
|
|
|
|
|
}
|
|
|
|
|
if(measurements.count(s12)) {
|
|
|
|
|
measurements[s12] = S.m12;
|
|
|
|
|
}
|
|
|
|
|
if(measurements.count(s21)) {
|
|
|
|
|
measurements[s21] = S.m21;
|
|
|
|
|
}
|
|
|
|
|
if(measurements.count(s22)) {
|
|
|
|
|
measurements[s22] = S.m22;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VirtualDevice::VNAMeasurement VirtualDevice::VNAMeasurement::interpolateTo(const VirtualDevice::VNAMeasurement &to, double a)
|
|
|
|
|
{
|
|
|
|
|
VNAMeasurement ret;
|
|
|
|
|
ret.frequency = frequency * (1.0 - a) + to.frequency * a;
|
|
|
|
|
ret.dBm = dBm * (1.0 - a) + to.dBm * a;
|
|
|
|
|
ret.Z0 = Z0 * (1.0 - a) + to.Z0 * a;
|
|
|
|
|
for(auto m : measurements) {
|
|
|
|
|
if(to.measurements.count(m.first) == 0) {
|
|
|
|
|
throw runtime_error("Nothing to interpolate to, expected measurement +\""+m.first.toStdString()+"\"");
|
|
|
|
|
}
|
|
|
|
|
ret.measurements[m.first] = measurements[m.first] * (1.0 - a) + to.measurements.at(m.first) * a;
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VirtualDevice::Info::Info()
|
|
|
|
|
{
|
|
|
|
|
ProtocolVersion = Protocol::Version;
|
|
|
|
|
FW_major = 0;
|
|
|
|
|
FW_minor = 0;
|
|
|
|
|
FW_patch = 0;
|
|
|
|
|
hardware_version = 1;
|
|
|
|
|
HW_Revision = '0';
|
|
|
|
|
ports = 2;
|
|
|
|
|
supportsVNAmode = true;
|
|
|
|
|
supportsSAmode = true;
|
|
|
|
|
supportsSGmode = true;
|
|
|
|
|
supportsExtRef = true;
|
|
|
|
|
Limits = {
|
|
|
|
|
.minFreq = 0,
|
|
|
|
|
.maxFreq = 6000000000,
|
|
|
|
|
.maxFreqHarmonic = 18000000000,
|
|
|
|
|
.minIFBW = 10,
|
|
|
|
|
.maxIFBW = 1000000,
|
|
|
|
|
.maxPoints = 10000,
|
|
|
|
|
.mindBm = -100,
|
2022-10-25 19:23:08 +08:00
|
|
|
|
.maxdBm = 30,
|
2022-10-01 23:10:44 +08:00
|
|
|
|
.minRBW = 1,
|
|
|
|
|
.maxRBW = 1000000,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VirtualDevice::Info::Info(Device *dev)
|
|
|
|
|
{
|
|
|
|
|
auto info = dev->Info();
|
|
|
|
|
ProtocolVersion = info.ProtocolVersion;
|
|
|
|
|
FW_major = info.FW_major;
|
|
|
|
|
FW_minor = info.FW_minor;
|
|
|
|
|
FW_patch = info.FW_patch;
|
|
|
|
|
hardware_version = info.hardware_version;
|
|
|
|
|
HW_Revision = info.HW_Revision;
|
|
|
|
|
ports = 2;
|
|
|
|
|
supportsVNAmode = true;
|
|
|
|
|
supportsSAmode = true;
|
|
|
|
|
supportsSGmode = true;
|
|
|
|
|
supportsExtRef = true;
|
|
|
|
|
Limits.minFreq = info.limits_minFreq;
|
|
|
|
|
Limits.maxFreq = info.limits_maxFreq;
|
|
|
|
|
Limits.maxFreqHarmonic = info.limits_maxFreqHarmonic;
|
|
|
|
|
Limits.minIFBW = info.limits_minIFBW;
|
|
|
|
|
Limits.maxIFBW = info.limits_maxIFBW;
|
|
|
|
|
Limits.maxPoints = info.limits_maxPoints;
|
|
|
|
|
Limits.mindBm = (double) info.limits_cdbm_min / 100;
|
|
|
|
|
Limits.maxdBm = (double) info.limits_cdbm_max / 100;
|
|
|
|
|
Limits.minRBW = info.limits_minRBW;
|
|
|
|
|
Limits.maxRBW = info.limits_maxRBW;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VirtualDevice::Info::subset(const VirtualDevice::Info &merge)
|
|
|
|
|
{
|
|
|
|
|
if((merge.ProtocolVersion != ProtocolVersion)
|
|
|
|
|
|| (merge.FW_major != FW_major)
|
|
|
|
|
|| (merge.FW_minor != FW_minor)
|
|
|
|
|
|| (merge.FW_patch != FW_patch)) {
|
|
|
|
|
throw runtime_error("Incompatible device, unable to create compound device. All devices must run the same firmware version.");
|
|
|
|
|
}
|
|
|
|
|
ports += merge.ports;
|
|
|
|
|
supportsVNAmode &= merge.supportsVNAmode;
|
|
|
|
|
supportsSGmode &= merge.supportsSGmode;
|
|
|
|
|
supportsSAmode &= merge.supportsSAmode;
|
|
|
|
|
supportsExtRef &= merge.supportsExtRef;
|
|
|
|
|
Limits.minFreq = max(Limits.minFreq, merge.Limits.minFreq);
|
|
|
|
|
Limits.maxFreq = min(Limits.maxFreq, merge.Limits.maxFreq);
|
|
|
|
|
Limits.maxFreqHarmonic = min(Limits.maxFreqHarmonic, merge.Limits.maxFreqHarmonic);
|
|
|
|
|
Limits.minIFBW = max(Limits.minIFBW, merge.Limits.minIFBW);
|
|
|
|
|
Limits.maxIFBW = min(Limits.maxIFBW, merge.Limits.maxIFBW);
|
|
|
|
|
Limits.maxPoints = min(Limits.maxPoints, merge.Limits.maxPoints);
|
|
|
|
|
Limits.mindBm = max(Limits.mindBm, merge.Limits.mindBm);
|
|
|
|
|
Limits.maxdBm = min(Limits.maxdBm, merge.Limits.maxdBm);
|
|
|
|
|
Limits.minRBW = max(Limits.minRBW, merge.Limits.minRBW);
|
|
|
|
|
Limits.maxRBW = min(Limits.maxRBW, merge.Limits.maxRBW);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VirtualDevice::Status::Status()
|
|
|
|
|
{
|
|
|
|
|
statusString = "";
|
|
|
|
|
overload = false;
|
|
|
|
|
unlocked = false;
|
|
|
|
|
unlevel = false;
|
|
|
|
|
extRef = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VirtualDevice::Status::Status(Device *dev)
|
|
|
|
|
{
|
|
|
|
|
auto status = dev->StatusV1();
|
|
|
|
|
statusString = dev->getLastDeviceInfoString();
|
|
|
|
|
overload = status.ADC_overload;
|
|
|
|
|
unlevel = status.unlevel;
|
|
|
|
|
unlocked = !status.LO1_locked || !status.source_locked;
|
|
|
|
|
extRef = status.extRefInUse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VirtualDevice::Status::merge(const VirtualDevice::Status &merge)
|
|
|
|
|
{
|
|
|
|
|
statusString += " / "+merge.statusString;
|
|
|
|
|
overload |= merge.overload;
|
|
|
|
|
unlevel |= merge.unlevel;
|
|
|
|
|
unlocked |= merge.unlocked;
|
|
|
|
|
extRef &= merge.extRef;
|
|
|
|
|
}
|