From 772ef44d495601cc1619e42c65ccc70a2da380fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Tue, 31 Oct 2023 18:49:08 +0100 Subject: [PATCH] partial driver for SNA5000A --- .../Device/SNA5000A/sna5000adriver.cpp | 546 ++++++++++++++++++ .../Device/SNA5000A/sna5000adriver.h | 195 +++++++ .../Device/SSA3000X/ssa3000xdriver.cpp | 4 +- .../Device/SSA3000X/ssa3000xdriver.h | 2 +- .../LibreVNA-GUI/Device/devicedriver.cpp | 12 +- .../Device/tracedifferencegenerator.h | 103 +++- .../Generator/signalgenwidget.cpp | 8 +- .../LibreVNA-GUI/LibreVNA-GUI.pro | 2 + .../LibreVNA-GUI/Traces/tracemodel.cpp | 2 + .../PC_Application/LibreVNA-GUI/appwindow.cpp | 6 +- .../LibreVNA-Test/LibreVNA-Test.pro | 2 + 11 files changed, 841 insertions(+), 41 deletions(-) create mode 100644 Software/PC_Application/LibreVNA-GUI/Device/SNA5000A/sna5000adriver.cpp create mode 100644 Software/PC_Application/LibreVNA-GUI/Device/SNA5000A/sna5000adriver.h diff --git a/Software/PC_Application/LibreVNA-GUI/Device/SNA5000A/sna5000adriver.cpp b/Software/PC_Application/LibreVNA-GUI/Device/SNA5000A/sna5000adriver.cpp new file mode 100644 index 0000000..54f54ae --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/Device/SNA5000A/sna5000adriver.cpp @@ -0,0 +1,546 @@ +#include "sna5000adriver.h" + +#include "CustomWidgets/informationbox.h" +#include "Util/util.h" + +#include +#include +#include + +SNA5000ADriver::SNA5000ADriver() + : DeviceTCPDriver("SNA5000A") +{ + diffGen = new TraceDifferenceGenerator([=](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 SNA5000ADriver::GetAvailableDevices() +{ + std::set 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(&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 SNA5000ADriver::getFlags() +{ + return std::set(); +} + +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 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 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 SNA5000ADriver::queryDoubleList(QString s) +{ + std::vector 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 xcoord = queryDoubleList(":CALC:DATA:XAXIS?"); +// std::map> data; +// for(auto i : excitedPorts) { +// for(auto j : excitedPorts) { +// QString name = "S"+QString::number(i)+QString::number(j); +// std::vector Sij = queryDoubleList(":SENS:DATA:RAWD? "+name); +// if(Sij.size() != xcoord.size() * 2) { +// // invalid size, abort +// return; +// } +// data[name] = Sij; +// } +// } + +// // Compile VNApoints +// std::vector trace; +// trace.resize(xcoord.size()); +// for(unsigned int i=0;i> 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 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= 0) { + // deteceted incomplete measurements + break; + } + } + if(lastIndex == -1) { + // all measurements complete + lastIndex = traceReader.xaxis.size(); + } + + if(lastIndex > 0) { + // Compile VNApoints + std::vector trace; + trace.resize(lastIndex); + for(int i=0;i> 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; +} + diff --git a/Software/PC_Application/LibreVNA-GUI/Device/SNA5000A/sna5000adriver.h b/Software/PC_Application/LibreVNA-GUI/Device/SNA5000A/sna5000adriver.h new file mode 100644 index 0000000..d40a3e0 --- /dev/null +++ b/Software/PC_Application/LibreVNA-GUI/Device/SNA5000A/sna5000adriver.h @@ -0,0 +1,195 @@ +#ifndef SNA5000ADRIVER_H +#define SNA5000ADRIVER_H + +#include "../devicetcpdriver.h" + +#include "../tracedifferencegenerator.h" + +#include +#include +#include + +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 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 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 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 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 xaxis; + std::map> 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 queryDoubleList(QString s); + QString serial; + QTcpSocket dataSocket; + + bool connected; + Info info; + + std::vector excitedPorts; + double excitationPower; + + class VNAPoint { + public: + unsigned int index; + double frequency; + std::map> 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 *diffGen; + + std::map detectedDevices; +}; + + +#endif // SNA5000ADRIVER_H diff --git a/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.cpp b/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.cpp index 0cd8102..3ac8d0f 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.cpp @@ -9,7 +9,7 @@ SSA3000XDriver::SSA3000XDriver() : DeviceTCPDriver("SSA3000X") { - diffGen = new TraceDifferenceGenerator([=](const SpectrumPoint &p){ + diffGen = new TraceDifferenceGenerator([=](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; diff --git a/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.h b/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.h index 4214fd3..36486e0 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.h +++ b/Software/PC_Application/LibreVNA-GUI/Device/SSA3000X/ssa3000xdriver.h @@ -165,7 +165,7 @@ private: }; QTimer traceTimer; - TraceDifferenceGenerator *diffGen; + TraceDifferenceGenerator *diffGen; std::map detectedDevices; }; diff --git a/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.cpp b/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.cpp index 4748f0c..ef4a8d9 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Device/devicedriver.cpp @@ -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::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) diff --git a/Software/PC_Application/LibreVNA-GUI/Device/tracedifferencegenerator.h b/Software/PC_Application/LibreVNA-GUI/Device/tracedifferencegenerator.h index fca726f..4ad52b2 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/tracedifferencegenerator.h +++ b/Software/PC_Application/LibreVNA-GUI/Device/tracedifferencegenerator.h @@ -6,7 +6,7 @@ #include -template +template class TraceDifferenceGenerator { public: TraceDifferenceGenerator(std::function changeCallback) : @@ -21,47 +21,92 @@ public: } void newTrace(const std::vector &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: diff --git a/Software/PC_Application/LibreVNA-GUI/Generator/signalgenwidget.cpp b/Software/PC_Application/LibreVNA-GUI/Generator/signalgenwidget.cpp index 9263b67..af3908d 100644 --- a/Software/PC_Application/LibreVNA-GUI/Generator/signalgenwidget.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Generator/signalgenwidget.cpp @@ -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) diff --git a/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro b/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro index 3595a70..7be9128 100644 --- a/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro +++ b/Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI.pro @@ -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 \ diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/tracemodel.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/tracemodel.cpp index c57c172..d2bc673 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/tracemodel.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/tracemodel.cpp @@ -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(); } } diff --git a/Software/PC_Application/LibreVNA-GUI/appwindow.cpp b/Software/PC_Application/LibreVNA-GUI/appwindow.cpp index a358019..487fcf7 100644 --- a/Software/PC_Application/LibreVNA-GUI/appwindow.cpp +++ b/Software/PC_Application/LibreVNA-GUI/appwindow.cpp @@ -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() diff --git a/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro b/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro index 80b8565..a6c1a62 100644 --- a/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro +++ b/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro @@ -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 \