mirror of
https://github.com/jankae/LibreVNA.git
synced 2026-04-05 14:35:23 +00:00
WIP: device synchronization
This commit is contained in:
parent
047f6ce981
commit
58918f81c1
90 changed files with 8970 additions and 310 deletions
|
|
@ -75,3 +75,13 @@ 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, int device, int port)
|
||||
{
|
||||
for(unsigned int i=0;i<map.size();i++) {
|
||||
if(map[i].device == device && map[i].port == port) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return map.size();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ public:
|
|||
public:
|
||||
unsigned int device;
|
||||
unsigned int port;
|
||||
static int findActiveStage(std::vector<PortMapping> map, int device, int port);
|
||||
};
|
||||
|
||||
enum class Synchronization {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ USBInBuffer::USBInBuffer(libusb_device_handle *handle, unsigned char endpoint, i
|
|||
{
|
||||
buffer = new unsigned char[buffer_size];
|
||||
transfer = libusb_alloc_transfer(0);
|
||||
libusb_fill_bulk_transfer(transfer, handle, endpoint, buffer, buffer_size, CallbackTrampoline, this, 100);
|
||||
libusb_fill_bulk_transfer(transfer, handle, endpoint, buffer, buffer_size, CallbackTrampoline, this, 0);
|
||||
libusb_submit_transfer(transfer);
|
||||
}
|
||||
|
||||
|
|
@ -66,9 +66,11 @@ int USBInBuffer::getReceived() const
|
|||
|
||||
void USBInBuffer::Callback(libusb_transfer *transfer)
|
||||
{
|
||||
// qDebug() << libusb_error_name(transfer->status);
|
||||
switch(transfer->status) {
|
||||
case LIBUSB_TRANSFER_COMPLETED:
|
||||
received_size += transfer->actual_length;
|
||||
// qDebug() << transfer->actual_length <<"total:" << received_size;
|
||||
inCallback = true;
|
||||
emit DataReceived();
|
||||
inCallback = false;
|
||||
|
|
@ -249,6 +251,7 @@ bool Device::SendPacket(const Protocol::PacketInfo& packet, std::function<void(T
|
|||
t.packet = packet;
|
||||
t.timeout = timeout;
|
||||
t.callback = cb;
|
||||
lock_guard<mutex> lock(transmissionMutex);
|
||||
transmissionQueue.enqueue(t);
|
||||
// qDebug() << "Enqueued packet, queue at " << transmissionQueue.size();
|
||||
if(!transmissionActive) {
|
||||
|
|
@ -321,6 +324,16 @@ std::set<QString> Device::GetDevices()
|
|||
return serials;
|
||||
}
|
||||
|
||||
void Device::SetTrigger(bool set)
|
||||
{
|
||||
qDebug() << "Trigger" << set << "to" << this;
|
||||
if(set) {
|
||||
SendCommandWithoutPayload(Protocol::PacketType::SetTrigger);
|
||||
} else {
|
||||
SendCommandWithoutPayload(Protocol::PacketType::ClearTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
void Device::USBHandleThread()
|
||||
{
|
||||
qDebug() << "Receive thread started";
|
||||
|
|
@ -460,7 +473,9 @@ void Device::ReceivedData()
|
|||
uint16_t handled_len;
|
||||
// qDebug() << "Received data";
|
||||
do {
|
||||
// qDebug() << "Decoding" << dataBuffer->getReceived() << "Bytes";
|
||||
handled_len = Protocol::DecodeBuffer(dataBuffer->getBuffer(), dataBuffer->getReceived(), &packet);
|
||||
// qDebug() << "Handled" << handled_len << "Bytes, type:" << (int) packet.type;
|
||||
dataBuffer->removeBytes(handled_len);
|
||||
switch(packet.type) {
|
||||
case Protocol::PacketType::VNADatapoint:
|
||||
|
|
@ -502,6 +517,14 @@ void Device::ReceivedData()
|
|||
case Protocol::PacketType::FrequencyCorrection:
|
||||
emit FrequencyCorrectionReceived(packet.frequencyCorrection.ppm);
|
||||
break;
|
||||
case Protocol::PacketType::SetTrigger:
|
||||
qDebug() << "Trigger" << true << "from" << this;
|
||||
emit TriggerReceived(true);
|
||||
break;
|
||||
case Protocol::PacketType::ClearTrigger:
|
||||
qDebug() << "Trigger" << false << "from" << this;
|
||||
emit TriggerReceived(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -557,8 +580,9 @@ bool Device::startNextTransmission()
|
|||
|
||||
void Device::transmissionFinished(TransmissionResult result)
|
||||
{
|
||||
lock_guard<mutex> lock(transmissionMutex);
|
||||
// remove transmitted packet
|
||||
// qDebug() << "Transmission finsished (" << result << "), queue at " << transmissionQueue.size();
|
||||
// qDebug() << "Transmission finsished (" << result << "), queue at " << transmissionQueue.size() << " Outstanding ACKs:"<<outstandingAckCount;
|
||||
if(transmissionQueue.empty()) {
|
||||
qWarning() << "transmissionFinished with empty transmission queue, stray Ack?";
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <set>
|
||||
#include <QQueue>
|
||||
#include <QTimer>
|
||||
#include <mutex>
|
||||
|
||||
Q_DECLARE_METATYPE(Protocol::Datapoint)
|
||||
Q_DECLARE_METATYPE(Protocol::ManualStatusV1)
|
||||
|
|
@ -87,8 +88,11 @@ signals:
|
|||
void ConnectionLost();
|
||||
void AckReceived();
|
||||
void NackReceived();
|
||||
void TriggerReceived(bool set);
|
||||
void LogLineReceived(QString line);
|
||||
void NeedsFirmwareUpdate(int usedProtocol, int requiredProtocol);
|
||||
public slots:
|
||||
void SetTrigger(bool set);
|
||||
private slots:
|
||||
void ReceivedData();
|
||||
void ReceivedLog();
|
||||
|
|
@ -120,6 +124,7 @@ private:
|
|||
std::function<void(TransmissionResult)> callback;
|
||||
};
|
||||
|
||||
std::mutex transmissionMutex;
|
||||
QQueue<Transmission> transmissionQueue;
|
||||
bool startNextTransmission();
|
||||
QTimer transmissionTimer;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "virtualdevice.h"
|
||||
|
||||
#include "preferences.h"
|
||||
#include "CustomWidgets/informationbox.h"
|
||||
#include "../VNA_embedded/Application/Communication/Protocol.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
|
@ -99,130 +100,74 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
static constexpr VirtualDevice::Info defaultInfo = {
|
||||
.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,
|
||||
.maxdBm = 100,
|
||||
.minRBW = 1,
|
||||
.maxRBW = 1000000,
|
||||
}
|
||||
};
|
||||
|
||||
static const VirtualDevice::Status defaultStatus = {
|
||||
.statusString = "",
|
||||
.overload = false,
|
||||
.unlocked = false,
|
||||
.unlevel = false,
|
||||
.extRef = false,
|
||||
};
|
||||
|
||||
VirtualDevice::VirtualDevice(QString serial)
|
||||
: QObject(),
|
||||
info{},
|
||||
status{}
|
||||
{
|
||||
cdev = nullptr;
|
||||
isCompound = false;
|
||||
cdev = nullptr;
|
||||
zerospan = false;
|
||||
auto dev = new Device(serial);
|
||||
devices.push_back(dev);
|
||||
|
||||
// Check if this is a compound device
|
||||
auto pref = Preferences::getInstance();
|
||||
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);
|
||||
connect(dev, &Device::ConnectionLost, this, &VirtualDevice::ConnectionLost);
|
||||
connect(dev, &Device::DeviceInfoUpdated, [&](){
|
||||
auto i = devices[0]->Info();
|
||||
info.ProtocolVersion = i.ProtocolVersion;
|
||||
info.FW_major = i.FW_major;
|
||||
info.FW_minor = i.FW_minor;
|
||||
info.FW_patch = i.FW_patch;
|
||||
info.hardware_version = i.hardware_version;
|
||||
info.HW_Revision = i.HW_Revision;
|
||||
info.ports = 2;
|
||||
info.supportsVNAmode = true;
|
||||
info.supportsSAmode = true;
|
||||
info.supportsSGmode = true;
|
||||
info.supportsExtRef = true;
|
||||
info.Limits.minFreq = i.limits_minFreq;
|
||||
info.Limits.maxFreq = i.limits_maxFreq;
|
||||
info.Limits.maxFreqHarmonic = i.limits_maxFreqHarmonic;
|
||||
info.Limits.minIFBW = i.limits_minIFBW;
|
||||
info.Limits.maxIFBW = i.limits_maxIFBW;
|
||||
info.Limits.maxPoints = i.limits_maxPoints;
|
||||
info.Limits.mindBm = (double) i.limits_cdbm_min / 100;
|
||||
info.Limits.maxdBm = (double) i.limits_cdbm_max / 100;
|
||||
info.Limits.minRBW = i.limits_minRBW;
|
||||
info.Limits.maxRBW = i.limits_minRBW;
|
||||
connect(dev, &Device::DeviceInfoUpdated, [=](){
|
||||
info = Info(devices[0]);
|
||||
emit InfoUpdated();
|
||||
});
|
||||
connect(dev, &Device::LogLineReceived, this, &VirtualDevice::LogLineReceived);
|
||||
connect(dev, &Device::DeviceStatusUpdated, [&](){
|
||||
status.statusString = devices[0]->getLastDeviceInfoString();
|
||||
status.overload = devices[0]->StatusV1().ADC_overload;
|
||||
status.unlevel = devices[0]->StatusV1().unlevel;
|
||||
status.unlocked = !devices[0]->StatusV1().LO1_locked || !devices[0]->StatusV1().source_locked;
|
||||
status.extRef = devices[0]->StatusV1().extRefInUse;
|
||||
connect(dev, &Device::DeviceStatusUpdated, [=](){
|
||||
status = Status(devices[0]);
|
||||
emit StatusUpdated(status);
|
||||
});
|
||||
connect(dev, &Device::NeedsFirmwareUpdate, this, &VirtualDevice::NeedsFirmwareUpdate);
|
||||
|
||||
connect(dev, &Device::SpectrumResultReceived, [&](Protocol::SpectrumAnalyzerResult res){
|
||||
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);
|
||||
});
|
||||
connect(dev, &Device::DatapointReceived, [&](Protocol::VNADatapoint<32> *res){
|
||||
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);
|
||||
for(int i=0;i<2;i++) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete res;
|
||||
emit VNAmeasurementReceived(m);
|
||||
});
|
||||
connect(dev, &Device::SpectrumResultReceived, this, &VirtualDevice::singleSpectrumResultReceived);
|
||||
connect(dev, &Device::DatapointReceived, this, &VirtualDevice::singleDatapointReceived);
|
||||
} else {
|
||||
// TODO
|
||||
// Connect to the actual devices
|
||||
for(auto devSerial : cdev->deviceSerials) {
|
||||
auto dev = new Device(devSerial);
|
||||
devices.push_back(dev);
|
||||
// Create device connections
|
||||
connect(dev, &Device::ConnectionLost, this, &VirtualDevice::ConnectionLost);
|
||||
connect(dev, &Device::NeedsFirmwareUpdate, this, &VirtualDevice::NeedsFirmwareUpdate);
|
||||
connect(dev, &Device::LogLineReceived, [=](QString line){
|
||||
emit LogLineReceived(line.prepend(dev->serial()+": "));
|
||||
});
|
||||
connect(dev, &Device::DeviceInfoUpdated, [=](){
|
||||
compoundInfoUpdated(dev);
|
||||
});
|
||||
connect(dev, &Device::DeviceStatusUpdated, [=](){
|
||||
compoundStatusUpdated(dev);
|
||||
});
|
||||
connect(dev, &Device::DatapointReceived, [=](Protocol::VNADatapoint<32> *data){
|
||||
compoundDatapointReceivecd(data, dev);
|
||||
});
|
||||
connect(dev, &Device::SpectrumResultReceived, [=](Protocol::SpectrumAnalyzerResult res) {
|
||||
compoundSpectrumResultReceived(res, dev);
|
||||
});
|
||||
}
|
||||
if(cdev->sync == CompoundDevice::Synchronization::USB) {
|
||||
// create trigger connections for USB synchronization
|
||||
for(int i=0;i<devices.size() - 1;i++) {
|
||||
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;
|
||||
}
|
||||
|
|
@ -244,12 +189,12 @@ void VirtualDevice::RegisterTypes()
|
|||
|
||||
bool VirtualDevice::isCompoundDevice() const
|
||||
{
|
||||
return isCompound;
|
||||
return cdev != nullptr;
|
||||
}
|
||||
|
||||
Device *VirtualDevice::getDevice()
|
||||
{
|
||||
if(isCompound || devices.size() < 1) {
|
||||
if(isCompoundDevice() || devices.size() < 1) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return devices[0];
|
||||
|
|
@ -271,12 +216,12 @@ const VirtualDevice::Info &VirtualDevice::getInfo() const
|
|||
return info;
|
||||
}
|
||||
|
||||
const VirtualDevice::Info &VirtualDevice::getInfo(VirtualDevice *vdev)
|
||||
VirtualDevice::Info VirtualDevice::getInfo(VirtualDevice *vdev)
|
||||
{
|
||||
if(vdev) {
|
||||
return vdev->info;
|
||||
} else {
|
||||
return defaultInfo;
|
||||
return Info();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -285,12 +230,12 @@ const VirtualDevice::Status &VirtualDevice::getStatus() const
|
|||
return status;
|
||||
}
|
||||
|
||||
const VirtualDevice::Status &VirtualDevice::getStatus(VirtualDevice *vdev)
|
||||
VirtualDevice::Status VirtualDevice::getStatus(VirtualDevice *vdev)
|
||||
{
|
||||
if(vdev) {
|
||||
return vdev->status;
|
||||
} else {
|
||||
return defaultStatus;
|
||||
return Status();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -316,26 +261,27 @@ bool VirtualDevice::setVNA(const VirtualDevice::VNASettings &s, std::function<vo
|
|||
|
||||
// create port->stage mapping
|
||||
portStageMapping.clear();
|
||||
for(int i=0;i<s.excitedPorts.size();i++) {
|
||||
for(unsigned int i=0;i<s.excitedPorts.size();i++) {
|
||||
portStageMapping[s.excitedPorts[i]] = i;
|
||||
}
|
||||
|
||||
zerospan = (s.freqStart == s.freqStop) && (s.dBmStart == s.dBmStop);
|
||||
auto pref = Preferences::getInstance();
|
||||
Protocol::SweepSettings sd;
|
||||
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()) {
|
||||
Protocol::SweepSettings sd;
|
||||
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.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.suppressPeaks = pref.Acquisition.suppressPeaks ? 1 : 0;
|
||||
sd.fixedPowerSetting = pref.Acquisition.adjustPowerLevel || s.dBmStart != s.dBmStop ? 0 : 1;
|
||||
sd.logSweep = s.logSweep ? 1 : 0;
|
||||
sd.syncMode = 0;
|
||||
return devices[0]->Configure(sd, [=](Device::TransmissionResult r){
|
||||
if(cb) {
|
||||
|
|
@ -343,8 +289,31 @@ bool VirtualDevice::setVNA(const VirtualDevice::VNASettings &s, std::function<vo
|
|||
}
|
||||
});
|
||||
} else {
|
||||
// TODO
|
||||
return false;
|
||||
// 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;
|
||||
}
|
||||
// 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);
|
||||
success &= devices[i]->Configure(sd, [=](Device::TransmissionResult r){
|
||||
if(cb) {
|
||||
results[devices[i]] = r;
|
||||
checkIfAllTransmissionsComplete(cb);
|
||||
}
|
||||
});
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -353,8 +322,7 @@ QString VirtualDevice::serial()
|
|||
if(!isCompoundDevice()) {
|
||||
return devices[0]->serial();
|
||||
} else {
|
||||
// TODO
|
||||
return "";
|
||||
return cdev->name;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -420,18 +388,30 @@ bool VirtualDevice::setSG(const SGSettings &s)
|
|||
return false;
|
||||
}
|
||||
auto pref = Preferences::getInstance();
|
||||
Protocol::PacketInfo packet;
|
||||
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()) {
|
||||
Protocol::PacketInfo packet;
|
||||
packet.type = Protocol::PacketType::Generator;
|
||||
Protocol::GeneratorSettings &sd = packet.generator;
|
||||
sd.frequency = s.freq;
|
||||
sd.cdbm_level = s.dBm * 100;
|
||||
sd.activePort = s.port;
|
||||
sd.applyAmplitudeCorrection = 1;
|
||||
return devices[0]->SendPacket(packet);
|
||||
} else {
|
||||
// TODO
|
||||
return false;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -443,17 +423,7 @@ bool VirtualDevice::setIdle(std::function<void (bool)> cb)
|
|||
success &= dev->SetIdle([=](Device::TransmissionResult r){
|
||||
if(cb) {
|
||||
results[dev] = r;
|
||||
if(results.size() == devices.size()) {
|
||||
// got all responses
|
||||
bool success = true;
|
||||
for(auto res : results) {
|
||||
if(res.second != Device::TransmissionResult::Ack) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cb(success);
|
||||
}
|
||||
checkIfAllTransmissionsComplete(cb);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -523,10 +493,25 @@ bool VirtualDevice::setExtRef(QString option_in, QString option_out)
|
|||
return success;
|
||||
}
|
||||
|
||||
std::set<QString> VirtualDevice::GetDevices()
|
||||
std::set<QString> VirtualDevice::GetAvailableVirtualDevices()
|
||||
{
|
||||
auto pref = Preferences::getInstance();
|
||||
auto ret = Device::GetDevices();
|
||||
// TODO check if compound devices are configured and add them if all sub-devices are available
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
@ -535,6 +520,164 @@ VirtualDevice *VirtualDevice::getConnected()
|
|||
return connected;
|
||||
}
|
||||
|
||||
void VirtualDevice::singleDatapointReceived(Protocol::VNADatapoint<32> *res)
|
||||
{
|
||||
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);
|
||||
for(int i=0;i<2;i++) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete res;
|
||||
emit VNAmeasurementReceived(m);
|
||||
}
|
||||
|
||||
void VirtualDevice::compoundDatapointReceivecd(Protocol::VNADatapoint<32> *data, Device *dev)
|
||||
{
|
||||
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);
|
||||
m.measurements[name] = input / ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit VNAmeasurementReceived(m);
|
||||
|
||||
// Clear this and all incomplete older datapoint buffers
|
||||
for(auto p : compoundVNABuffer) {
|
||||
for(auto d : p.second) {
|
||||
delete d.second;
|
||||
}
|
||||
}
|
||||
compoundVNABuffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualDevice::singleSpectrumResultReceived(Protocol::SpectrumAnalyzerResult res)
|
||||
{
|
||||
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(Protocol::SpectrumAnalyzerResult res, Device *dev)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void VirtualDevice::compoundInfoUpdated(Device *dev)
|
||||
{
|
||||
compoundInfoBuffer[dev] = dev->Info();
|
||||
if(compoundInfoBuffer.size() == devices.size()) {
|
||||
// got information of all devices
|
||||
info = Info(devices[0]);
|
||||
for(int i=1;i<devices.size();i++) {
|
||||
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]);
|
||||
for(int i=1;i<devices.size();i++) {
|
||||
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)
|
||||
{
|
||||
Sparam S;
|
||||
|
|
@ -579,3 +722,109 @@ VirtualDevice::VNAMeasurement VirtualDevice::VNAMeasurement::interpolateTo(const
|
|||
}
|
||||
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,
|
||||
.maxdBm = 100,
|
||||
.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_minRBW;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ public:
|
|||
|
||||
class Info {
|
||||
public:
|
||||
Info();
|
||||
Info(Device *dev);
|
||||
|
||||
void subset(const Info &merge);
|
||||
|
||||
uint16_t ProtocolVersion;
|
||||
uint8_t FW_major;
|
||||
uint8_t FW_minor;
|
||||
|
|
@ -42,6 +47,11 @@ public:
|
|||
|
||||
class Status {
|
||||
public:
|
||||
Status();
|
||||
Status(Device *dev);
|
||||
|
||||
void merge(const Status &merge);
|
||||
|
||||
QString statusString;
|
||||
bool overload;
|
||||
bool unlocked;
|
||||
|
|
@ -56,9 +66,9 @@ public:
|
|||
CompoundDevice *getCompoundDevice();
|
||||
std::vector<Device*> getDevices();
|
||||
const Info& getInfo() const;
|
||||
static const VirtualDevice::Info &getInfo(VirtualDevice *vdev);
|
||||
static VirtualDevice::Info getInfo(VirtualDevice *vdev);
|
||||
const Status &getStatus() const;
|
||||
static const VirtualDevice::Status &getStatus(VirtualDevice *vdev);
|
||||
static VirtualDevice::Status getStatus(VirtualDevice *vdev);
|
||||
|
||||
class VNASettings {
|
||||
public:
|
||||
|
|
@ -148,7 +158,7 @@ public:
|
|||
public:
|
||||
double freq;
|
||||
double dBm;
|
||||
int port;
|
||||
int port; // starts at one, set to zero to disable all ports
|
||||
};
|
||||
|
||||
QStringList availableSGPorts();
|
||||
|
|
@ -161,7 +171,7 @@ public:
|
|||
|
||||
bool setExtRef(QString option_in, QString option_out);
|
||||
|
||||
static std::set<QString> GetDevices();
|
||||
static std::set<QString> GetAvailableVirtualDevices();
|
||||
static VirtualDevice* getConnected();
|
||||
|
||||
signals:
|
||||
|
|
@ -172,10 +182,19 @@ signals:
|
|||
void StatusUpdated(Status status);
|
||||
void LogLineReceived(QString line);
|
||||
void NeedsFirmwareUpdate(int usedProtocol, int requiredProtocol);
|
||||
|
||||
private slots:
|
||||
void singleDatapointReceived(Protocol::VNADatapoint<32> *res);
|
||||
void compoundDatapointReceivecd(Protocol::VNADatapoint<32> *data, Device *dev);
|
||||
void singleSpectrumResultReceived(Protocol::SpectrumAnalyzerResult res);
|
||||
void compoundSpectrumResultReceived(Protocol::SpectrumAnalyzerResult res, Device *dev);
|
||||
void compoundInfoUpdated(Device *dev);
|
||||
void compoundStatusUpdated(Device *dev);
|
||||
private:
|
||||
void checkIfAllTransmissionsComplete(std::function<void(bool)> cb = nullptr);
|
||||
|
||||
Info info;
|
||||
Status status;
|
||||
bool isCompound;
|
||||
std::vector<Device*> devices;
|
||||
bool zerospan;
|
||||
|
||||
|
|
@ -183,7 +202,10 @@ private:
|
|||
|
||||
CompoundDevice *cdev;
|
||||
|
||||
std::map<int, std::vector<Protocol::VNADatapoint<32>*>> compoundDataBuffer;
|
||||
std::map<int, std::map<Device*, Protocol::VNADatapoint<32>*>> compoundVNABuffer;
|
||||
std::map<int, std::map<Device*, Protocol::SpectrumAnalyzerResult>> compoundSABuffer;
|
||||
std::map<Device*, Protocol::DeviceInfo> compoundInfoBuffer;
|
||||
std::map<Device*, Protocol::DeviceStatusV1> compoundStatusBuffer;
|
||||
|
||||
std::map<int, int> portStageMapping; // maps from excitedPort (count starts at zero) to stage (count starts at zero)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public:
|
|||
virtual nlohmann::json toJSON() override;
|
||||
virtual void fromJSON(nlohmann::json j) override;
|
||||
|
||||
void setAveragingMode(Averaging::Mode mode) override {Q_UNUSED(mode)};
|
||||
void setAveragingMode(Averaging::Mode mode) override {Q_UNUSED(mode)}
|
||||
|
||||
private slots:
|
||||
void updateDevice();
|
||||
|
|
|
|||
|
|
@ -94,18 +94,10 @@ SignalgeneratorWidget::SignalgeneratorWidget(VirtualDevice *dev, QWidget *parent
|
|||
connect(ui->levelSlider, &QSlider::valueChanged, [=](int value) {
|
||||
setLevel((double) value / 100.0);
|
||||
});
|
||||
connect(ui->EnablePort1, &QCheckBox::toggled, [=](){
|
||||
if(ui->EnablePort1->isChecked() && ui->EnablePort2->isChecked()) {
|
||||
ui->EnablePort2->setCheckState(Qt::CheckState::Unchecked);
|
||||
}
|
||||
emit SettingsChanged();
|
||||
});
|
||||
connect(ui->EnablePort2, &QCheckBox::toggled, [=](){
|
||||
if(ui->EnablePort1->isChecked() && ui->EnablePort2->isChecked()) {
|
||||
ui->EnablePort1->setCheckState(Qt::CheckState::Unchecked);
|
||||
}
|
||||
emit SettingsChanged();
|
||||
});
|
||||
connect(ui->EnablePort1, &QCheckBox::toggled, this, &SignalgeneratorWidget::SettingsChanged);
|
||||
connect(ui->EnablePort2, &QCheckBox::toggled, this, &SignalgeneratorWidget::SettingsChanged);
|
||||
connect(ui->EnablePort3, &QCheckBox::toggled, this, &SignalgeneratorWidget::SettingsChanged);
|
||||
connect(ui->EnablePort4, &QCheckBox::toggled, this, &SignalgeneratorWidget::SettingsChanged);
|
||||
connect(ui->EnabledSweep, &QCheckBox::toggled, [=](bool enabled){
|
||||
ui->current->setEnabled(enabled);
|
||||
if(enabled) {
|
||||
|
|
@ -152,6 +144,10 @@ VirtualDevice::SGSettings SignalgeneratorWidget::getDeviceStatus()
|
|||
s.port = 1;
|
||||
} else if(ui->EnablePort2->isChecked()) {
|
||||
s.port = 2;
|
||||
} else if(ui->EnablePort3->isChecked()) {
|
||||
s.port = 3;
|
||||
} else if(ui->EnablePort4->isChecked()) {
|
||||
s.port = 4;
|
||||
} else {
|
||||
s.port = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,6 +142,9 @@
|
|||
<property name="text">
|
||||
<string>Port 1</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
|
@ -149,6 +152,29 @@
|
|||
<property name="text">
|
||||
<string>Port 2</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="EnablePort3">
|
||||
<property name="text">
|
||||
<string>Port 3</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="EnablePort4">
|
||||
<property name="text">
|
||||
<string>Port 4</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
@ -277,4 +303,7 @@
|
|||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<buttongroups>
|
||||
<buttongroup name="buttonGroup"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ void AppWindow::SetupSCPI()
|
|||
}));
|
||||
scpi_dev->add(new SCPICommand("LIST", nullptr, [=](QStringList) -> QString {
|
||||
QString ret;
|
||||
for(auto d : Device::GetDevices()) {
|
||||
for(auto d : VirtualDevice::GetAvailableVirtualDevices()) {
|
||||
ret += d + ",";
|
||||
}
|
||||
// remove last comma
|
||||
|
|
@ -854,7 +854,7 @@ int AppWindow::UpdateDeviceList()
|
|||
{
|
||||
deviceActionGroup->setExclusive(true);
|
||||
ui->menuConnect_to->clear();
|
||||
auto devices = Device::GetDevices();
|
||||
auto devices = VirtualDevice::GetAvailableVirtualDevices();
|
||||
if(vdevice) {
|
||||
devices.insert(vdevice->serial());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue