partial driver for SNA5000A

This commit is contained in:
Jan Käberich 2023-10-31 18:49:08 +01:00
parent 4c0d18b5f4
commit 772ef44d49
11 changed files with 841 additions and 41 deletions

View File

@ -0,0 +1,546 @@
#include "sna5000adriver.h"
#include "CustomWidgets/informationbox.h"
#include "Util/util.h"
#include <QTcpSocket>
#include <QDateTime>
#include <QApplication>
SNA5000ADriver::SNA5000ADriver()
: DeviceTCPDriver("SNA5000A")
{
diffGen = new TraceDifferenceGenerator<VNAPoint>([=](const VNAPoint &p){
VNAMeasurement m;
m.Z0 = 50.0;
m.pointNum = p.index;
m.frequency = p.frequency;
m.dBm = excitationPower;
m.measurements = p.data;
emit VNAmeasurementReceived(m);
});
traceReader.waitingForResponse = false;
// connect(&traceTimer, &QTimer::timeout, this, &SNA5000ADriver::extractTracePoints);
// traceTimer.setSingleShot(true);
}
SNA5000ADriver::~SNA5000ADriver()
{
delete diffGen;
}
std::set<QString> SNA5000ADriver::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 : getSearchAddresses()) {
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 SNA5")) {
// 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();
}
}
return ret;
}
bool SNA5000ADriver::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;
}
connect(&dataSocket, qOverload<QAbstractSocket::SocketError>(&QTcpSocket::errorOccurred), this, [this](QAbstractSocket::SocketError err) {
if(err == QAbstractSocket::SocketTimeoutError) {
// ignore, these are triggered by the query function
} else {
emit ConnectionLost();
}
}, Qt::QueuedConnection);
// 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();
const QStringList supportedDevices = {"SNA5002A", "SNA5004A", "SNA5012A", "SNA5014A"};
if(!supportedDevices.contains(info.hardware_version)) {
dataSocket.close();
InformationBox::ShowError("Error", "Invalid hardware version: " + info.hardware_version);
return false;
}
info.supportedFeatures.insert(DeviceDriver::Feature::VNA);
info.supportedFeatures.insert(DeviceDriver::Feature::VNAFrequencySweep);
info.supportedFeatures.insert(DeviceDriver::Feature::Generator);
// Extract limits
info.Limits.VNA.ports = queryInt(":SERVICE:PORT:COUNT?");
info.Limits.VNA.minFreq = queryInt(":SERVICE:SWEEP:FREQENCY:MINIMUM?");
info.Limits.VNA.maxFreq = queryInt(":SERVICE:SWEEP:FREQENCY:MAXIMUM?");
info.Limits.VNA.maxPoints = queryInt(":SERVICE:SWEEP:POINTS?");
info.Limits.VNA.minIFBW = 10;
info.Limits.VNA.maxIFBW = 3000000;
info.Limits.VNA.mindBm = -55;
info.Limits.VNA.maxdBm = 10;
info.Limits.Generator.ports = info.Limits.VNA.ports;
info.Limits.Generator.minFreq = info.Limits.VNA.minFreq;
info.Limits.Generator.maxFreq = info.Limits.VNA.maxFreq;
info.Limits.Generator.mindBm = info.Limits.VNA.mindBm;
info.Limits.Generator.maxdBm = info.Limits.VNA.maxdBm;
connected = true;
// reset to default configuration
dataSocket.write("*RST\r\n");
emit InfoUpdated();
return true;
}
void SNA5000ADriver::disconnect()
{
traceReaderStop();
connected = false;
dataSocket.close();
}
DeviceDriver::Info SNA5000ADriver::getInfo()
{
return info;
}
std::set<DeviceDriver::Flag> SNA5000ADriver::getFlags()
{
return std::set<DeviceDriver::Flag>();
}
QString SNA5000ADriver::getStatus()
{
return "";
}
QStringList SNA5000ADriver::availableVNAMeasurements()
{
switch(info.Limits.VNA.ports) {
case 2:
return {"S11", "S12", "S21", "S22"};
case 4:
return {"S11", "S12", "S13", "S14", "S21", "S22", "S23", "S24", "S31", "S32", "S33", "S34", "S41", "S42", "S43", "S44"};
default:
return {""};
}
}
bool SNA5000ADriver::setVNA(const VNASettings &s, std::function<void (bool)> cb)
{
excitationPower = s.dBmStart;
excitedPorts = s.excitedPorts;
if(!traceReaderStop()) {
emit ConnectionLost();
return false;
}
// disable unused traces
for(unsigned int i=1;i<=info.Limits.VNA.ports;i++) {
write(":DISP:WIND:TRAC"+QString::number(i)+" 0");
}
// enable a trace for every active port
for(auto p : s.excitedPorts) {
write(":DISP:WIND:TRAC"+QString::number(p)+" 1");
// set the parameter to force the stimulus active at the port
write(":CALC:PAR"+QString::number(p)+":DEF S"+QString::number(p)+QString::number(p));
}
// configure the sweep
write(":SENS:FREQ:STAR "+QString::number(s.freqStart));
write(":SENS:FREQ:STOP "+QString::number(s.freqStop));
write(":SENS:BWID "+QString::number(s.IFBW));
write(":SOUR:POW "+QString::number(s.dBmStart));
write(":SENS:SWEEP:POINTS "+QString::number(s.points));
// traceTimer.start(100);
if(cb) {
cb(true);
}
traceReaderRestart();
return true;
}
QStringList SNA5000ADriver::availableSGPorts()
{
switch(info.Limits.Generator.ports) {
case 2:
return {"PORT1", "PORT2"};
case 4:
return {"PORT1", "PORT2", "PORT3", "PORT4"};
default:
return {""};
}
}
bool SNA5000ADriver::setSG(const SGSettings &s)
{
// enable SA mode (generator control is only available there)
write(":CALC:CUST:DEF \"SA\"");
if(s.port == 0) {
// turn off all ports
for(unsigned int i=0;i<info.Limits.Generator.ports;i++) {
write(":SENS:SA:SOUR"+QString::number(i)+":STAT OFF");
}
} else {
// set the frequency
write(":SENS:SA:SOUR"+QString::number(s.port)+":FREQ:CW "+QString::number(s.freq));
// set the power
write(":SENS:SA:SOUR"+QString::number(s.port)+":POW:VAL "+QString::number(s.dBm));
// enable the port
write(":SENS:SA:SOUR"+QString::number(s.port)+":STAT ON");
}
return true;
}
bool SNA5000ADriver::setIdle(std::function<void (bool)> cb)
{
if(!connected) {
return false;
}
if(!traceReaderStop()) {
emit ConnectionLost();
return false;
}
// traceTimer.stop();
write("*RST\r\n");
if(cb) {
cb(true);
}
return true;
}
QStringList SNA5000ADriver::availableExtRefInSettings()
{
return {""};
}
QStringList SNA5000ADriver::availableExtRefOutSettings()
{
return {""};
}
bool SNA5000ADriver::setExtRef(QString option_in, QString option_out)
{
Q_UNUSED(option_in)
Q_UNUSED(option_out)
return false;
}
void SNA5000ADriver::write(QString s)
{
dataSocket.write(QString(s + "\r\n").toLocal8Bit());
dataSocket.readAll();
}
QString SNA5000ADriver::query(QString s, unsigned int timeout)
{
dataSocket.write(QString(s + "\r\n").toLocal8Bit());
if(!waitForLine(timeout)) {
return QString();
} else {
return QString(dataSocket.readLine());
}
}
long long SNA5000ADriver::queryInt(QString s)
{
auto resp = query(s);
if(resp.isEmpty()) {
return 0;
} else {
return resp.toLongLong();
}
}
std::vector<double> SNA5000ADriver::queryDoubleList(QString s)
{
std::vector<double> ret;
auto resp = query(s, 1000);
if(!resp.isEmpty()) {
QStringList values = resp.split(",");
for(auto v : values) {
ret.push_back(v.toDouble());
}
}
return ret;
}
void SNA5000ADriver::extractTracePoints()
{
// while(connected) {
// qDebug() << "SNA5000 Thread";
// std::vector<double> xcoord = queryDoubleList(":CALC:DATA:XAXIS?");
// std::map<QString, std::vector<double>> data;
// for(auto i : excitedPorts) {
// for(auto j : excitedPorts) {
// QString name = "S"+QString::number(i)+QString::number(j);
// std::vector<double> Sij = queryDoubleList(":SENS:DATA:RAWD? "+name);
// if(Sij.size() != xcoord.size() * 2) {
// // invalid size, abort
// return;
// }
// data[name] = Sij;
// }
// }
// // Compile VNApoints
// std::vector<VNAPoint> trace;
// trace.resize(xcoord.size());
// for(unsigned int i=0;i<xcoord.size();i++) {
// trace[i].index = i;
// trace[i].frequency = xcoord[i];
// std::map<QString, std::complex<double>> tracedata;
// for(auto d : data) {
// tracedata[d.first] = std::complex(d.second[i*2], d.second[i*2+1]);
// }
// trace[i].data = tracedata;
// }
// diffGen->newTrace(trace);
// QThread::msleep(100);
// }
// traceTimer.start(500);
}
void SNA5000ADriver::handleIncomingData()
{
if(!dataSocket.canReadLine()) {
// no complete response yet, ignore
return;
}
traceReader.waitingForResponse = false;
std::vector<double> data;
QStringList values = QString(dataSocket.readLine()).split(",");
for(auto v : values) {
data.push_back(v.toDouble());
}
if(traceReader.state == 0) {
traceReader.xaxis = data;
} else {
unsigned int comp = 0;
bool handled = false;
for(auto i : excitedPorts) {
for(auto j : excitedPorts) {
comp++;
if(traceReader.state == comp) {
QString name = "S"+QString::number(i)+QString::number(j);
traceReader.data[name] = data;
handled = true;
break;
}
}
if(handled) {
break;
}
}
}
if(traceReader.state >= excitedPorts.size()*excitedPorts.size()) {
// Check size, abort if wrong
bool sizeOkay = true;
for(auto d : traceReader.data) {
if(d.second.size() != traceReader.xaxis.size() * 2) {
sizeOkay = false;
break;
}
}
if(sizeOkay) {
/*
* The SNA5000A performs the measurements in multiple sweeps (with the stimulus active at one port per sweep).
* E.g. measuring S11/S21 in the first sweep and then S12/S22. The LibreVNA-GUI expects each point of each sweep
* to containg all measured parameters (S11/S12/S21/S22).
*
* Values that are not measured yet are reported as very small values by the SNA5000A. Ignore all datapoints
* where at least one parameter is too small (=not measured yet). This will cause a delay in the displaying of
* measurements but at least we can process complete datapoints afterwards.
*/
// threshold equals -196dB, we can safely assume that no real measurement will ever below that
constexpr double threshold = 1e-10;
int lastIndex = -1;
for(unsigned int i=0;i<traceReader.xaxis.size();i++) {
for(auto d : traceReader.data) {
if(abs(d.second[i*2]) < threshold && abs(d.second[i*2+1]) < threshold) {
lastIndex = i;
break;
}
}
if(lastIndex >= 0) {
// deteceted incomplete measurements
break;
}
}
if(lastIndex == -1) {
// all measurements complete
lastIndex = traceReader.xaxis.size();
}
if(lastIndex > 0) {
// Compile VNApoints
std::vector<VNAPoint> trace;
trace.resize(lastIndex);
for(int i=0;i<lastIndex;i++) {
trace[i].index = i;
trace[i].frequency = traceReader.xaxis[i];
std::map<QString, std::complex<double>> tracedata;
for(auto d : traceReader.data) {
tracedata[d.first] = std::complex(d.second[i*2], d.second[i*2+1]);
}
trace[i].data = tracedata;
}
diffGen->newTrace(trace);
}
}
traceReader.state = 0;
} else {
// move on to next trace
traceReader.state++;
}
traceReaderStatemachine();
}
bool SNA5000ADriver::traceReaderStop(unsigned int timeout)
{
traceReader.enabled = false;
if(traceReader.waitingForResponse) {
// already issued a command, needs to wait for the response parsing
auto start = QDateTime::currentDateTimeUtc();
while(traceReader.waitingForResponse) {
if(start.msecsTo(QDateTime::currentDateTimeUtc()) >= timeout) {
// timed out
qWarning() << "Timed out waiting for trace reader to stop";
return false;
}
QApplication::processEvents();
}
QObject::disconnect(&dataSocket, &QTcpSocket::readyRead, this, &SNA5000ADriver::handleIncomingData);
return true;
} else {
// already stopped
QObject::disconnect(&dataSocket, &QTcpSocket::readyRead, this, &SNA5000ADriver::handleIncomingData);
return true;
}
}
void SNA5000ADriver::traceReaderRestart()
{
traceReader.enabled = true;
traceReader.data.clear();
traceReader.xaxis.clear();
traceReader.state = 0;
dataSocket.readAll();
traceReaderStatemachine();
}
void SNA5000ADriver::traceReaderStatemachine()
{
if(!traceReader.enabled) {
return;
}
if(traceReader.state == 0) {
write(":CALC:DATA:XAXIS?");
traceReader.waitingForResponse = true;
QObject::connect(&dataSocket, &QTcpSocket::readyRead, this, &SNA5000ADriver::handleIncomingData, Qt::UniqueConnection);
} else {
unsigned int comp = 0;
for(auto i : excitedPorts) {
for(auto j : excitedPorts) {
comp++;
if(traceReader.state == comp) {
QString name = "S"+QString::number(i)+QString::number(j);
write(":SENS:DATA:RAWD? "+name);
traceReader.waitingForResponse = true;
QObject::connect(&dataSocket, &QTcpSocket::readyRead, this, &SNA5000ADriver::handleIncomingData, Qt::UniqueConnection);
return;
}
}
}
}
}
bool SNA5000ADriver::waitForLine(unsigned int timeout)
{
auto start = QDateTime::currentDateTimeUtc();
while(!dataSocket.canReadLine()) {
if(start.msecsTo(QDateTime::currentDateTimeUtc()) >= timeout) {
// timed out
qWarning() << "Timed out waiting for response";
return false;
}
dataSocket.waitForReadyRead(10);
}
return true;
}

View File

@ -0,0 +1,195 @@
#ifndef SNA5000ADRIVER_H
#define SNA5000ADRIVER_H
#include "../devicetcpdriver.h"
#include "../tracedifferencegenerator.h"
#include <QHostAddress>
#include <QTcpSocket>
#include <QThread>
class SNA5000ADriver : public DeviceTCPDriver
{
Q_OBJECT
public:
SNA5000ADriver();
virtual ~SNA5000ADriver();
/**
* @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 "SNA5000A";}
/**
* @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 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 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 slots:
void extractTracePoints();
signals:
private slots:
void handleIncomingData();
private:
struct {
bool enabled;
unsigned int state;
std::vector<double> xaxis;
std::map<QString, std::vector<double>> data;
bool waitingForResponse;
} traceReader;
bool traceReaderStop(unsigned int timeout = 1000);
void traceReaderRestart();
void traceReaderStatemachine();
bool waitForLine(unsigned int timeout);
void write(QString s);
QString query(QString s, unsigned int timeout = 100);
long long queryInt(QString s);
std::vector<double> queryDoubleList(QString s);
QString serial;
QTcpSocket dataSocket;
bool connected;
Info info;
std::vector<int> excitedPorts;
double excitationPower;
class VNAPoint {
public:
unsigned int index;
double frequency;
std::map<QString, std::complex<double>> data;
bool operator==(const VNAPoint& rhs) {
if(index != rhs.index || frequency != rhs.frequency || data.size() != rhs.data.size()) {
return false;
}
if(data.size() == 0) {
return true;
} else {
return std::prev(data.end())->second == std::prev(rhs.data.end())->second;
}
// return index == rhs.index && frequency == rhs.frequency && data.size() == rhs.data.size() && std::equal(data.begin(), data.end(), rhs.data.begin());
}
};
TraceDifferenceGenerator<VNAPoint> *diffGen;
std::map<QString, QHostAddress> detectedDevices;
};
#endif // SNA5000ADRIVER_H

View File

@ -9,7 +9,7 @@
SSA3000XDriver::SSA3000XDriver()
: DeviceTCPDriver("SSA3000X")
{
diffGen = new TraceDifferenceGenerator<SpectrumPoint, 10>([=](const SpectrumPoint &p){
diffGen = new TraceDifferenceGenerator<SpectrumPoint>([=](const SpectrumPoint &p){
SAMeasurement m;
m.pointNum = p.index;
m.frequency = p.frequency;
@ -128,7 +128,7 @@ bool SSA3000XDriver::connectTo(QString serial)
info.Limits.SA.minFreq = 0;
info.Limits.SA.maxFreq = maxFreq;
info.Limits.SA.minRBW = 1;
info.Limits.SA.maxRBW = 1000000;
info.Limits.SA.maxRBW = 3000000;
info.Limits.SA.mindBm = -20;
info.Limits.SA.maxdBm = 0;
info.Limits.Generator.ports = 1;

View File

@ -165,7 +165,7 @@ private:
};
QTimer traceTimer;
TraceDifferenceGenerator<SpectrumPoint, 10> *diffGen;
TraceDifferenceGenerator<SpectrumPoint> *diffGen;
std::map<QString, QHostAddress> detectedDevices;
};

View File

@ -4,6 +4,7 @@
#include "LibreVNA/librevnausbdriver.h"
#include "LibreVNA/Compound/compounddriver.h"
#include "SSA3000X/ssa3000xdriver.h"
#include "SNA5000A/sna5000adriver.h"
DeviceDriver *DeviceDriver::activeDriver = nullptr;
@ -23,6 +24,7 @@ std::vector<DeviceDriver *> DeviceDriver::getDrivers()
ret.push_back(new LibreVNATCPDriver);
ret.push_back(new CompoundDriver);
ret.push_back(new SSA3000XDriver);
ret.push_back(new SNA5000ADriver);
}
return ret;
}
@ -110,26 +112,26 @@ DeviceDriver::Info::Info()
hardware_version = "missing";
Limits.VNA.ports = 2;
Limits.VNA.minFreq = 0;
Limits.VNA.maxFreq = 6000000000;
Limits.VNA.maxFreq = 100000000000;
Limits.VNA.mindBm = -100;
Limits.VNA.maxdBm = 30;
Limits.VNA.minIFBW = 1;
Limits.VNA.maxIFBW = 1000000;
Limits.VNA.maxIFBW = 100000000;
Limits.VNA.maxPoints = 65535;
Limits.Generator.ports = 2;
Limits.Generator.minFreq = 0;
Limits.Generator.maxFreq = 6000000000;
Limits.Generator.maxFreq = 100000000000;
Limits.Generator.mindBm = -100;
Limits.Generator.maxdBm = 30;
Limits.SA.ports = 2;
Limits.SA.minFreq = 0;
Limits.SA.maxFreq = 6000000000;
Limits.SA.maxFreq = 100000000000;
Limits.SA.mindBm = -100;
Limits.SA.maxdBm = 30;
Limits.SA.minRBW = 1;
Limits.SA.maxRBW = 1000000;
Limits.SA.maxRBW = 100000000;
}
void DeviceDriver::Info::subset(const DeviceDriver::Info &info)

View File

@ -6,7 +6,7 @@
#include <QDebug>
template<typename T, int minUnchanged>
template<typename T>
class TraceDifferenceGenerator {
public:
TraceDifferenceGenerator(std::function<void(const T&)> changeCallback) :
@ -21,47 +21,92 @@ public:
}
void newTrace(const std::vector<T> &trace) {
if(trace.size() > last.size()) {
// definitely got more points than last time. Find first point that is hasn't been transmitted and generate callbacks for it and all subsequent points
unsigned int i=nextCallbackIndex;
while(i < trace.size()) {
callback(trace[i]);
i++;
}
nextCallbackIndex = 0;
} else if(trace.size() < last.size()) {
if(trace.size() < last.size()) {
// got less points than last time. This must be a completely new trace, generate callbacks for all points
for(auto &i : trace) {
callback(i);
}
nextCallbackIndex = 0;
} else {
// still the same amount of points.
unsigned int i = nextCallbackIndex;
unsigned int changedPoints = 0;
// same amount or more points -> find first difference going backwards from next scheduled update point
unsigned int lastDiff = nextCallbackIndex;
do {
if(i > 0) {
i--;
if(lastDiff > 0) {
lastDiff--;
} else {
i = trace.size() - 1;
}
bool unchanged = last[i] == trace[i];
if(!unchanged) {
changedPoints = (i + trace.size() - nextCallbackIndex + 1);
if(changedPoints > trace.size()) {
changedPoints -= trace.size();
// reached the beginning of the trace
if(last.size() == trace.size()) {
if(nextCallbackIndex == trace.size()) {
// nothing has changed in the whole trace, abort
break;
} else {
// wrap around to the end
lastDiff = trace.size() - 1;
}
} else {
// trace must be longer than last -> update all the way to the end
lastDiff = trace.size() - 1;
for(;nextCallbackIndex!=trace.size();nextCallbackIndex++) {
callback(trace[nextCallbackIndex]);
}
break;
}
}
if(!(last[lastDiff] == trace[lastDiff])) {
do {
if(nextCallbackIndex >= trace.size()) {
nextCallbackIndex = 0;
}
callback(trace[nextCallbackIndex]);
nextCallbackIndex++;
} while(nextCallbackIndex != lastDiff + 1);
break;
}
} while (i != nextCallbackIndex);
i = nextCallbackIndex;
while(changedPoints--) {
callback(trace[i]);
i = (i + 1) % trace.size();
}
nextCallbackIndex = i;
} while(lastDiff != nextCallbackIndex);
}
last = trace;
// if(trace.size() > last.size()) {
// // definitely got more points than last time. Find first point that hasn't been transmitted and generate callbacks for it and all subsequent points
// unsigned int i=nextCallbackIndex;
// while(i < trace.size()) {
// callback(trace[i]);
// i++;
// }
// nextCallbackIndex = trace.size();
// } else i else {
// // still the same amount of points.
// unsigned int i = nextCallbackIndex;
// unsigned int changedPoints = 0;
// do {
// if(i > 0) {
// i--;
// } else {
// // reached the end
// if(nextCallbackIndex == trace.size()) {
// // checked the whole trace
// break;
// } else {
// // last callback was within trace, wrap around
// i = trace.size() - 1;
// }
// }
// bool unchanged = last[i] == trace[i];
// if(!unchanged) {
// changedPoints = (i + trace.size() - nextCallbackIndex + 1);
// if(changedPoints > trace.size()) {
// changedPoints -= trace.size();
// }
// break;
// }
// } while (i != nextCallbackIndex);
// i = nextCallbackIndex;
// while(changedPoints--) {
// callback(trace[i]);
// i = (i + 1) % trace.size();
// }
// nextCallbackIndex = i;
// }
// last = trace;
}
private:

View File

@ -189,7 +189,8 @@ void SignalgeneratorWidget::deviceInfoUpdated()
delete cb;
}
portCheckboxes.clear();
for(unsigned int i=1;i<=DeviceDriver::getInfo(window->getDevice()).Limits.Generator.ports;i++) {
auto info = DeviceDriver::getInfo(window->getDevice());
for(unsigned int i=1;i<=info.Limits.Generator.ports;i++) {
auto cb = new QCheckBox("Port "+QString::number(i));
ui->portBox->layout()->addWidget(cb);
portCheckboxes.push_back(cb);
@ -207,6 +208,11 @@ void SignalgeneratorWidget::deviceInfoUpdated()
});
}
setPort(port);
ui->levelSlider->setMaximum(info.Limits.Generator.maxdBm * 100);
ui->levelSlider->setMinimum(info.Limits.Generator.mindBm * 100);
ui->levelSpin->setMaximum(info.Limits.Generator.maxdBm);
ui->levelSpin->setMinimum(info.Limits.Generator.mindBm);
}
void SignalgeneratorWidget::setLevel(double level)

View File

@ -36,6 +36,7 @@ HEADERS += \
Device/LibreVNA/manualcontroldialogvff.h \
Device/LibreVNA/receivercaldialog.h \
Device/LibreVNA/sourcecaldialog.h \
Device/SNA5000A/sna5000adriver.h \
Device/SSA3000X/ssa3000xdriver.h \
Device/devicedriver.h \
Device/devicelog.h \
@ -193,6 +194,7 @@ SOURCES += \
Device/LibreVNA/manualcontroldialogvff.cpp \
Device/LibreVNA/receivercaldialog.cpp \
Device/LibreVNA/sourcecaldialog.cpp \
Device/SNA5000A/sna5000adriver.cpp \
Device/SSA3000X/ssa3000xdriver.cpp \
Device/devicedriver.cpp \
Device/devicelog.cpp \

View File

@ -39,6 +39,7 @@ void TraceModel::addTrace(Trace *t)
endInsertRows();
t->setModel(this);
emit traceAdded(t);
emit requiredExcitation();
}
void TraceModel::removeTrace(unsigned int index)
@ -50,6 +51,7 @@ void TraceModel::removeTrace(unsigned int index)
traces.erase(traces.begin() + index);
endRemoveRows();
emit traceRemoved(trace);
emit requiredExcitation();
}
}

View File

@ -401,9 +401,6 @@ bool AppWindow::ConnectToDevice(QString serial, DeviceDriver *driver)
break;
}
}
for(auto m : modeHandler->getModes()) {
connect(device, &DeviceDriver::InfoUpdated, m, &Mode::deviceInfoUpdated);
}
// vdevice->initialize();
@ -1135,6 +1132,9 @@ void AppWindow::DeviceInfoUpdated()
modeHandler->getActiveMode()->initializeDevice();
}
UpdateReferenceToolbar();
for(auto m : modeHandler->getModes()) {
m->deviceInfoUpdated();
}
}
//void AppWindow::SourceCalibrationDialog()

View File

@ -41,6 +41,7 @@ SOURCES += \
../LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.cpp \
../LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.cpp \
../LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.cpp \
../LibreVNA-GUI/Device/SNA5000A/sna5000adriver.cpp \
../LibreVNA-GUI/Device/devicedriver.cpp \
../LibreVNA-GUI/Device/devicelog.cpp \
../LibreVNA-GUI/Device/LibreVNA/devicepacketlog.cpp \
@ -216,6 +217,7 @@ HEADERS += \
../LibreVNA-GUI/Device/LibreVNA/Compound/compounddriver.h \
../LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.h \
../LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.h \
../LibreVNA-GUI/Device/SNA5000A/sna5000adriver.h \
../LibreVNA-GUI/Device/devicedriver.h \
../LibreVNA-GUI/Device/devicelog.h \
../LibreVNA-GUI/Device/LibreVNA/devicepacketlog.h \