Merge branch 'master' into prototype_testing

This commit is contained in:
Jan Käberich 2025-05-18 13:31:58 +02:00
commit f76e3a209d
189 changed files with 979384 additions and 264 deletions

View file

@ -237,7 +237,7 @@ void LibreCALDialog::updateCalibrationStartStatus()
ui->start->setEnabled(canStart);
if(canStart) {
ui->lCalibrationStatus->setText("Ready to start");
ui->lCalibrationStatus->setStyleSheet("QLabel { color : black; }");
ui->lCalibrationStatus->setStyleSheet("");
}
}
@ -254,7 +254,7 @@ void LibreCALDialog::updateDeviceStatus()
}
if(device->stabilized()) {
ui->lDeviceStatus->setText("LibreCAL ready for calibration");
ui->lDeviceStatus->setStyleSheet("QLabel { color : black; }");
ui->lDeviceStatus->setStyleSheet("");
} else {
ui->lDeviceStatus->setText("Heating up, please wait with calibration");
ui->lDeviceStatus->setStyleSheet("QLabel { color : orange; }");
@ -263,6 +263,7 @@ void LibreCALDialog::updateDeviceStatus()
void LibreCALDialog::determineAutoPorts()
{
disableUI();
ui->progressCal->setValue(0);
ui->lCalibrationStatus->setText("Autodetecting port connections...");
ui->lCalibrationStatus->setStyleSheet("QLabel { color : green; }");
@ -422,7 +423,7 @@ void LibreCALDialog::startCalibration()
ui->progressCal->setValue(0);
ui->lCalibrationStatus->setText("Creating calibration kit from coefficients...");
ui->lCalibrationStatus->setStyleSheet("QLabel { color : black; }");
ui->lCalibrationStatus->setStyleSheet("");
auto& kit = cal->getKit();
kit.clearStandards();
kit.manufacturer = "LibreCAL ("+coeffSet.name+")";
@ -606,7 +607,9 @@ void LibreCALDialog::startCalibration()
disconnect(cal, &Calibration::measurementsUpdated, this, nullptr);
connect(cal, &Calibration::measurementsUpdated, this, startNextCalibrationStep, Qt::QueuedConnection);
connect(cal, &Calibration::measurementsAborted, this, [=](){
setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::None));
enableUI();
ui->lCalibrationStatus->setText("Ready to start");
});
startNextCalibrationStep();

View file

@ -35,6 +35,7 @@ public:
double getLowerFreq() const;
double getUpperFreq() const;
double getReferenceImpedance() const;
TraceModel* getModel() const {return model;}
signals:
void selectionChanged();

View file

@ -13,6 +13,9 @@
CompoundDriver::CompoundDriver()
{
connected = false;
isIdle = true;
triggerForwarding = false;
SApoints = 0;
drivers.push_back(new LibreVNAUSBDriver);
drivers.push_back(new LibreVNATCPDriver);
@ -295,6 +298,11 @@ bool CompoundDriver::setVNA(const DeviceDriver::VNASettings &s, std::function<vo
return setIdle(cb);
}
setIdle([this](bool){
enableTriggerForwarding();
qDebug() << "Start trigger forwarding";
});
// create port->stage mapping
portStageMapping.clear();
for(unsigned int i=0;i<s.excitedPorts.size();i++) {
@ -333,6 +341,11 @@ bool CompoundDriver::setVNA(const DeviceDriver::VNASettings &s, std::function<vo
}
});
}
lastNonIdleSettings.type = Types::VNA;
lastNonIdleSettings.vna = s;
isIdle = false;
return success;
}
@ -352,6 +365,11 @@ bool CompoundDriver::setSA(const DeviceDriver::SASettings &s, std::function<void
}
zerospan = s.freqStart == s.freqStop;
setIdle([this](bool){
enableTriggerForwarding();
qDebug() << "Start trigger forwarding";
});
// Configure the devices
results.clear();
bool success = true;
@ -384,6 +402,11 @@ bool CompoundDriver::setSA(const DeviceDriver::SASettings &s, std::function<void
break;
}
}
lastNonIdleSettings.type = Types::SA;
lastNonIdleSettings.sa = s;
isIdle = false;
return success;
}
@ -414,14 +437,22 @@ bool CompoundDriver::setSG(const DeviceDriver::SGSettings &s)
}
success &= devices[i]->setSG(devSettings);
}
lastNonIdleSettings.type = Types::SG;
lastNonIdleSettings.sg = s;
isIdle = false;
return success;
}
bool CompoundDriver::setIdle(std::function<void (bool)> cb)
{
disableTriggerForwarding();
qDebug() << "Stop trigger forwarding";
auto success = true;
results.clear();
for(auto dev : devices) {
dev->sendWithoutPayload(Protocol::PacketType::ClearTrigger);
success &= dev->setIdle([=](bool success){
if(cb) {
results[dev] = success;
@ -429,6 +460,9 @@ bool CompoundDriver::setIdle(std::function<void (bool)> cb)
}
});
}
isIdle = true;
return success;
}
@ -463,9 +497,36 @@ QStringList CompoundDriver::availableExtRefOutSettings()
bool CompoundDriver::setExtRef(QString option_in, QString option_out)
{
auto success = true;
for(auto dev : devices) {
success &= dev->setExtRef(option_in, option_out);
qDebug() << "Ref change start";
if(isIdle) {
// can immediately switch reference settings
for(auto dev : devices) {
success &= dev->setExtRef(option_in, option_out);
}
} else {
// can not switch during a sweep
// set to idle first
setIdle();
// change reference
for(auto dev : devices) {
success &= dev->setExtRef(option_in, option_out);
}
// restore last non idle state
switch(lastNonIdleSettings.type) {
case Types::VNA:
setVNA(lastNonIdleSettings.vna);
break;
case Types::SA:
setSA(lastNonIdleSettings.sa);
break;
case Types::SG:
setSG(lastNonIdleSettings.sg);
break;
}
}
qDebug() << "Ref change stop";
return success;
}
@ -484,6 +545,27 @@ std::set<QString> CompoundDriver::getIndividualDeviceSerials()
return ret;
}
void CompoundDriver::triggerReceived(LibreVNADriver *device, bool set)
{
triggerMutex.lock();
if(activeDevice.sync == LibreVNADriver::Synchronization::GUI && triggerForwarding) {
for(unsigned int i=0;i<devices.size();i++) {
if(devices[i] == device) {
// pass on to the next device
if(i < devices.size() - 1) {
qDebug() << "Passing on trigger" << set << "from" << device->getSerial() << "to" << devices[i+1]->getSerial();
devices[i+1]->sendWithoutPayload(set ? Protocol::PacketType::SetTrigger : Protocol::PacketType::ClearTrigger);
} else {
qDebug() << "Passing on trigger" << set << "from" << device->getSerial() << "to" << devices[0]->getSerial();
devices[0]->sendWithoutPayload(set ? Protocol::PacketType::SetTrigger : Protocol::PacketType::ClearTrigger);
}
break;
}
}
}
triggerMutex.unlock();
}
void CompoundDriver::parseCompoundJSON()
{
try {
@ -528,36 +610,6 @@ void CompoundDriver::incomingPacket(LibreVNADriver *device, const Protocol::Pack
case Protocol::PacketType::SpectrumAnalyzerResult:
spectrumResultReceived(device, p.spectrumResult);
break;
case Protocol::PacketType::SetTrigger:
if(activeDevice.sync == LibreVNADriver::Synchronization::GUI) {
for(unsigned int i=0;i<devices.size();i++) {
if(devices[i] == device) {
// pass on to the next device
if(i < devices.size() - 1) {
devices[i+1]->sendWithoutPayload(Protocol::PacketType::SetTrigger);
} else {
devices[0]->sendWithoutPayload(Protocol::PacketType::SetTrigger);
}
break;
}
}
}
break;
case Protocol::PacketType::ClearTrigger:
if(activeDevice.sync == LibreVNADriver::Synchronization::GUI) {
for(unsigned int i=0;i<devices.size();i++) {
if(devices[i] == device) {
// pass on to the next device
if(i < devices.size() - 1) {
devices[i+1]->sendWithoutPayload(Protocol::PacketType::ClearTrigger);
} else {
devices[0]->sendWithoutPayload(Protocol::PacketType::ClearTrigger);
}
break;
}
}
}
break;
default:
// nothing to do for other packet types
break;
@ -651,6 +703,26 @@ void CompoundDriver::spectrumResultReceived(LibreVNADriver *dev, Protocol::Spect
}
}
void CompoundDriver::enableTriggerForwarding()
{
triggerMutex.lock();
for(auto d : devices) {
connect(d, &LibreVNADriver::receivedTrigger, this, &CompoundDriver::triggerReceived, Qt::UniqueConnection);
}
triggerForwarding = true;
triggerMutex.unlock();
}
void CompoundDriver::disableTriggerForwarding()
{
triggerMutex.lock();
triggerForwarding = false;
for(auto d : devices) {
QObject::disconnect(d, &LibreVNADriver::receivedTrigger, this, &CompoundDriver::triggerReceived);
}
triggerMutex.unlock();
}
void CompoundDriver::datapointReceivecd(LibreVNADriver *dev, Protocol::VNADatapoint<32> *data)
{
if(!compoundVNABuffer.count(data->pointNum)) {

View file

@ -4,6 +4,8 @@
#include "../../devicedriver.h"
#include "compounddevice.h"
#include <QMutex>
class CompoundDriver : public DeviceDriver
{
public:
@ -169,6 +171,8 @@ public:
static std::set<QString> getIndividualDeviceSerials();
private slots:
void triggerReceived(LibreVNADriver *device, bool set);
private:
void parseCompoundJSON();
void createCompoundJSON();
@ -177,6 +181,8 @@ private:
void updatedStatus(LibreVNADriver *device, const Protocol::DeviceStatus &status);
void datapointReceivecd(LibreVNADriver *dev, Protocol::VNADatapoint<32> *data);
void spectrumResultReceived(LibreVNADriver *dev, Protocol::SpectrumAnalyzerResult res);
void enableTriggerForwarding();
void disableTriggerForwarding();
Info info;
std::map<LibreVNADriver*, Info> deviceInfos;
@ -196,8 +202,18 @@ private:
// Configuration of the device we are connected to
CompoundDevice activeDevice;
bool connected;
bool triggerForwarding;
QMutex triggerMutex;
std::vector<LibreVNADriver*> devices;
bool zerospan;
bool isIdle;
enum class Types{VNA, SA, SG};
struct {
VNASettings vna;
SASettings sa;
SGSettings sg;
Types type;
} lastNonIdleSettings;
unsigned int VNApoints;
unsigned int SApoints;

View file

@ -191,6 +191,8 @@ signals:
void receivedAnswer(const LibreVNADriver::TransmissionResult &result);
void receivedPacket(const Protocol::PacketInfo& packet);
void receivedTrigger(LibreVNADriver *driver, bool set);
protected slots:
void handleReceivedPacket(const Protocol::PacketInfo& packet);
protected:

View file

@ -242,6 +242,12 @@ void LibreVNATCPDriver::ReceivedData()
case Protocol::PacketType::Nack:
emit receivedAnswer(TransmissionResult::Nack);
break;
case Protocol::PacketType::SetTrigger:
emit receivedTrigger(this, true);
break;
case Protocol::PacketType::ClearTrigger:
emit receivedTrigger(this, false);
break;
default:
// pass on to LibreVNADriver class
emit receivedPacket(packet);

View file

@ -179,6 +179,12 @@ void LibreVNAUSBDriver::ReceivedData()
}
}
dataBuffer->removeBytes(handled_len);
if(packet.type == Protocol::PacketType::SetTrigger) {
qDebug() << "Incoming set trigger from " << serial;
}
if(packet.type == Protocol::PacketType::ClearTrigger) {
qDebug() << "Incoming clear trigger from " << serial;
}
switch(packet.type) {
case Protocol::PacketType::Ack:
emit receivedAnswer(TransmissionResult::Ack);
@ -186,6 +192,12 @@ void LibreVNAUSBDriver::ReceivedData()
case Protocol::PacketType::Nack:
emit receivedAnswer(TransmissionResult::Nack);
break;
case Protocol::PacketType::SetTrigger:
emit receivedTrigger(this, true);
break;
case Protocol::PacketType::ClearTrigger:
emit receivedTrigger(this, false);
break;
case Protocol::PacketType::None:
break;
default:
@ -377,6 +389,6 @@ bool LibreVNAUSBDriver::startNextTransmission()
return false;
}
transmissionTimer.start(t.timeout);
qDebug() << "Transmission started (packet type" << (int) t.packet.type << "), queue at " << transmissionQueue.size();
// qDebug() << "Transmission started (packet type" << (int) t.packet.type << "), queue at " << transmissionQueue.size();
return true;
}

View file

@ -37,8 +37,10 @@ DeviceLog::~DeviceLog()
void DeviceLog::addLine(QString line)
{
// Set color depending on log level
QColor color = Qt::black;
// Set color depending on log level.
// Use theme dependent color for info messages
QColor color = QApplication::palette().text().color();
// Use fixed colors for other log levels
if(line.contains(",CRT]")) {
color = Qt::red;
} else if(line.contains(",ERR]")) {

View file

@ -412,11 +412,12 @@ RESOURCES += \
resources/librevna.qrc
win32:RC_ICONS = resources/librevna.ico
mac:ICON = resources/librevna.icns
QMAKE_CXXFLAGS += -Wno-deprecated -Wno-deprecated-declarations -Wno-deprecated-copy
CONFIG += c++17
REVISION = $$system(git rev-parse HEAD)
DEFINES += GITHASH=\\"\"$$REVISION\\"\"
DEFINES += FW_MAJOR=1 FW_MINOR=6 FW_PATCH=2 FW_SUFFIX=""
DEFINES += FW_MAJOR=1 FW_MINOR=6 FW_PATCH=3 FW_SUFFIX=""
DEFINES -= _UNICODE UNICODE

View file

@ -538,7 +538,7 @@ void SpectrumAnalyzer::NewDatapoint(DeviceDriver::SAMeasurement m)
}
}
window->addStreamingData(m_avg, AppWindow::SADataType::Raw);
window->addStreamingData(m_avg, AppWindow::SADataType::Raw, settings.freqStart == settings.freqStop);
if(normalize.measuring) {
if(average.currentSweep() == averages) {
@ -569,7 +569,7 @@ void SpectrumAnalyzer::NewDatapoint(DeviceDriver::SAMeasurement m)
m.second /= normalize.portCorrection[m.first][m_avg.pointNum];
m.second *= corr;
}
window->addStreamingData(m_avg, AppWindow::SADataType::Normalized);
window->addStreamingData(m_avg, AppWindow::SADataType::Normalized, settings.freqStart == settings.freqStop);
}

View file

@ -46,6 +46,8 @@ Marker::Marker(MarkerModel *model, int number, Marker *parent, QString descr)
connect(this, &Marker::traceChanged, this, &Marker::updateContextmenu);
connect(this, &Marker::typeChanged, this, &Marker::updateContextmenu);
updateContextmenu();
connect(&Preferences::getInstance(), &Preferences::updated, this, &Marker::updateSymbol);
}
Marker::~Marker()
@ -815,7 +817,7 @@ void Marker::traceDataChanged(unsigned int begin, unsigned int end)
void Marker::updateSymbol()
{
if(isDisplayedMarker()) {
if(isDisplayedMarker() && parentTrace) {
auto style = Preferences::getInstance().Marker.symbolStyle;
switch(style) {
case MarkerSymbolStyle::FilledNumberInside: {

View file

@ -91,6 +91,14 @@ void Math::Expression::inputSamplesChanged(unsigned int begin, unsigned int end)
}
dataMutex.lock();
data.resize(in.size());
// sanity check input values
if(end > 0 && end > in.size()) {
end = in.size() - 1;
}
if(end <= begin) {
dataMutex.unlock();
return;
}
try {
for(unsigned int i=begin;i<end;i++) {
t = in[i].x;

View file

@ -427,6 +427,9 @@ QString Trace::getMathFormulaError() const
break;
}
}
if(varName == "x") {
found = true;
}
if(!found) {
return "Unknown variable: "+varName;
}
@ -465,7 +468,21 @@ void Trace::updateMathTracePoints()
double startX = std::numeric_limits<double>::lowest();
double stopX = std::numeric_limits<double>::max();
double stepSize = std::numeric_limits<double>::max();
auto domain = DataType::Invalid;
for(auto t : mathSourceTraces) {
if(domain == DataType::Invalid) {
domain = t.first->outputType();
} else {
if(domain != t.first->outputType()) {
// not all traces have the same domain, clear output and do not calculate
data.resize(0);
mathUpdateBegin = 0;
mathUpdateEnd = 0;
dataType = DataType::Invalid;
emit outputTypeChanged(dataType);
return;
}
}
if(t.first->minX() > startX) {
startX = t.first->minX();
}
@ -480,7 +497,14 @@ void Trace::updateMathTracePoints()
stepSize = traceStepSize;
}
}
unsigned int samples = round((stopX - startX) / stepSize + 1);
if(domain != this->domain) {
this->domain = domain;
emit typeChanged(this);
}
unsigned int samples = 0;
if(stopX > startX) {
samples = round((stopX - startX) / stepSize + 1);
}
// qDebug() << "Updated trace points, now"<<samples<<"points from"<<startX<<"to"<<stopX;
if(samples != data.size()) {
auto oldSize = data.size();
@ -530,6 +554,10 @@ void Trace::scheduleMathCalculation(unsigned int begin, unsigned int end)
void Trace::calculateMath()
{
lastMathUpdate = QTime::currentTime();
if(mathUpdateEnd <= mathUpdateBegin) {
// nothing to do
return;
}
if(mathUpdateBegin >= data.size() || mathUpdateEnd >= data.size() + 1) {
qWarning() << "Not calculating math trace, out of limits. Requested from" << mathUpdateBegin << "to" << mathUpdateEnd <<" but data is of size" << data.size();
return;
@ -639,6 +667,11 @@ bool Trace::canAddAsMathSource(Trace *t)
bool Trace::addMathSource(Trace *t, QString variableName)
{
if(mathSourceTraces.count(t)) {
// this trace is already used as a math source
mathSourceTraces[t] = variableName;
return true;
}
// qDebug() << "Adding trace" << t << "as a math source to" << this << "as variable" << variableName;
if(!canAddAsMathSource(t)) {
return false;

View file

@ -21,6 +21,7 @@ class TraceModel;
class Trace : public TraceMath
{
Q_OBJECT
friend class TraceEditDialog;
public:
using Data = TraceMath::Data;

View file

@ -393,6 +393,26 @@ std::complex<double> YAxis::reconstructValueFromYAxisType(std::map<YAxis::Type,
return ret;
}
double YAxis::getDefaultLimitMax(Type type)
{
return Preferences::getInstance().Graphs.defaultAxisLimits.max[(int) type];
}
double YAxis::getDefaultLimitMin(Type type)
{
return Preferences::getInstance().Graphs.defaultAxisLimits.min[(int) type];
}
double YAxis::getDefaultLimitMax()
{
return getDefaultLimitMax(type);
}
double YAxis::getDefaultLimitMin()
{
return getDefaultLimitMin(type);
}
void YAxis::updateTicks()
{
Axis::updateTicks();

View file

@ -108,6 +108,10 @@ public:
bool isSupported(XAxis::Type type, TraceModel::DataSource source);
static std::set<YAxis::Type> getSupported(XAxis::Type type, TraceModel::DataSource source);
static std::complex<double> reconstructValueFromYAxisType(std::map<Type, double> yaxistypes);
static double getDefaultLimitMax(YAxis::Type type);
static double getDefaultLimitMin(YAxis::Type type);
double getDefaultLimitMax();
double getDefaultLimitMin();
protected:
virtual void updateTicks() override;

View file

@ -172,6 +172,7 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
if(t.mathDependsOn(ts, true)) {
traceItem->setCheckState(Qt::Checked);
variableItem->setFlags(variableItem->flags() | Qt::ItemIsEnabled | Qt::ItemIsEditable);
ui->mathTraceTable->blockSignals(false);
} else {
traceItem->setCheckState(Qt::Unchecked);
}
@ -181,7 +182,7 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
connect(ui->mathTraceTable, &QTableWidget::itemChanged, [&](QTableWidgetItem *item){
auto row = ui->mathTraceTable->row(item);
auto column = ui->mathTraceTable->column(item);
qDebug() << "Item changed at row"<<row<<"column"<<column;
// qDebug() << "Item changed at row"<<row<<"column"<<column;
ui->mathTraceTable->blockSignals(true);
auto trace = t.getModel()->trace(row);
if(column == 0) {
@ -198,16 +199,7 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
variableItem->setText("");
variableItem->setFlags(variableItem->flags() & ~(Qt::ItemIsEnabled | Qt::ItemIsEditable));
}
// available trace selections may have changed, disable/enable other rows
for(unsigned int i=0;i<t.getModel()->getTraces().size();i++) {
auto traceItem = ui->mathTraceTable->item(i, 0);
auto flags = traceItem->flags();
if(t.canAddAsMathSource(t.getModel()->trace(i))) {
traceItem->setFlags(flags | Qt::ItemIsEnabled);
} else {
traceItem->setFlags(flags & ~Qt::ItemIsEnabled);
}
}
updateMathFormulaSelectableRows();
} else {
// changed the variable name text
t.addMathSource(trace, item->text());
@ -216,6 +208,8 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(updateMathFormulaStatus());
});
}
updateMathFormulaSelectableRows();
updateMathFormulaStatus();
switch(t.getSource()) {
case Trace::Source::Live: ui->bLive->click(); break;
@ -384,6 +378,21 @@ void TraceEditDialog::okClicked()
bool TraceEditDialog::updateMathFormulaStatus()
{
// check output domains first (there could be a problem if a trace changed its output domain
// after the math trace was created)
auto domain = TraceMath::DataType::Invalid;
for(auto t : trace.mathSourceTraces) {
if(domain == TraceMath::DataType::Invalid) {
domain = t.first->outputType();
} else {
if(domain != t.first->outputType()) {
// not all traces have the same domain
ui->lMathFormulaStatus->setText("Different output domains of selected source traces");
ui->lMathFormulaStatus->setStyleSheet("QLabel { color : red; }");
return false;
}
}
}
auto error = trace.getMathFormulaError();
if(error.isEmpty()) {
// all good
@ -397,6 +406,25 @@ bool TraceEditDialog::updateMathFormulaStatus()
}
}
void TraceEditDialog::updateMathFormulaSelectableRows()
{
// available trace selections may have changed, disable/enable other rows
// block signals, otherwise the trace names will be reset
ui->mathTraceTable->blockSignals(true);
for(unsigned int i=0;i<trace.getModel()->getTraces().size();i++) {
auto traceItem = ui->mathTraceTable->item(i, 0);
auto flags = traceItem->flags();
if(trace.canAddAsMathSource(trace.getModel()->trace(i)) || traceItem->checkState()) {
// Item can always be deselected but only selected if it is compatible
traceItem->setFlags(flags | Qt::ItemIsEnabled);
} else {
traceItem->setFlags(flags & ~Qt::ItemIsEnabled);
}
}
ui->mathTraceTable->blockSignals(false);
}
MathModel::MathModel(Trace &t, QObject *parent)
: QAbstractTableModel(parent),
t(t)

View file

@ -50,6 +50,7 @@ private slots:
private:
bool updateMathFormulaStatus();
void updateMathFormulaSelectableRows();
Ui::TraceEditDialog *ui;
Trace &trace;
bool VNAtrace;

View file

@ -17,6 +17,7 @@
#include <QMimeData>
#include <QDebug>
#include <QApplication>
#include <QInputDialog>
std::set<TracePlot*> TracePlot::plots;
@ -233,13 +234,24 @@ void TracePlot::paintEvent(QPaintEvent *event)
p.setBackground(QBrush(pref.Graphs.Color.background));
p.fillRect(0, 0, width(), height(), QBrush(pref.Graphs.Color.background));
marginTop = 0;
// draw title
if(!title.isEmpty()) {
QFont font = p.font();
font.setPixelSize(pref.Graphs.fontSizeTitle);
p.setFont(font);
p.setPen(Util::getFontColorFromBackground(pref.Graphs.Color.background));
p.drawText(QRect(0, 0, width(), pref.Graphs.fontSizeTitle), Qt::AlignCenter, title);
marginTop += pref.Graphs.fontSizeTitle;
}
// show names of active traces and marker data (if enabled)
bool hasMarkerData = false;
auto marginMarkerData = pref.Graphs.fontSizeMarkerData * 12.5;
marginTop = pref.Graphs.fontSizeTraceNames + 8;
auto traceNameTop = marginTop + 5;
marginTop += pref.Graphs.fontSizeTraceNames + 8;
int x = 1; // xcoordinate for the next trace name
int y = marginTop; // ycoordinate for the next marker data
auto areaTextTop = 5;
auto labelMarginRight = 4;
auto borderRadius = 5;
for(auto t : traces) {
@ -248,7 +260,7 @@ void TracePlot::paintEvent(QPaintEvent *event)
}
// Trace name
auto textArea = QRect(x, areaTextTop, width() - x, marginTop);
auto textArea = QRect(x, traceNameTop, width() - x, pref.Graphs.fontSizeTraceNames + 8);
QFont font = p.font();
font.setPixelSize(pref.Graphs.fontSizeTraceNames);
p.setFont(font);
@ -384,6 +396,13 @@ void TracePlot::paintEvent(QPaintEvent *event)
void TracePlot::finishContextMenu()
{
contextmenu->addSeparator();
auto setTitle = new QAction("Set Title", contextmenu);
contextmenu->addAction(setTitle);
connect(setTitle, &QAction::triggered, [=](){
title = QInputDialog::getText(contextmenu, "Set new graph title", "Enter new title:", QLineEdit::Normal, title);
});
contextmenu->addSeparator();
if(parentTile) {
auto add = new QMenu("Add tile...", contextmenu);
auto left = new QAction("to the left", contextmenu);
@ -723,6 +742,18 @@ QRect TracePlot::getDropRect()
return QRect(QPoint(w*dropBorders, h*dropBorders), QSize(w*(1.0-2*dropBorders), h*(1.0-2*dropBorders)));
}
nlohmann::json TracePlot::getBaseJSON()
{
nlohmann::json j;
j["title"] = title.toStdString();
return j;
}
void TracePlot::parseBaseJSON(nlohmann::json j)
{
title = QString::fromStdString(j.value("title", ""));
}
std::set<TracePlot *> TracePlot::getPlots()
{
return plots;

View file

@ -103,6 +103,10 @@ protected:
virtual QString mouseText(QPoint pos) {Q_UNUSED(pos) return QString();}
QRect getDropRect();
// save/load base class members. Should be called by derived classes in the toJSON/fromJSON functions
nlohmann::json getBaseJSON();
void parseBaseJSON(nlohmann::json j);
protected slots:
void newTraceAvailable(Trace *t);
void traceDeleted(Trace *t);
@ -150,6 +154,9 @@ protected:
unsigned int marginTop;
bool limitPassing;
private:
QString title;
};
#endif // TRACEPLOT_H

View file

@ -23,7 +23,7 @@ TracePolar::TracePolar(TraceModel &model, QWidget *parent)
nlohmann::json TracePolar::toJSON()
{
nlohmann::json j;
nlohmann::json j = getBaseJSON();
j["limit_to_span"] = limitToSpan;
j["limit_to_edge"] = limitToEdge;
j["edge_reflection"] = edgeReflection;
@ -44,6 +44,7 @@ nlohmann::json TracePolar::toJSON()
void TracePolar::fromJSON(nlohmann::json j)
{
parseBaseJSON(j);
limitToSpan = j.value("limit_to_span", true);
limitToEdge = j.value("limit_to_edge", false);
edgeReflection = j.value("edge_reflection", 1.0);

View file

@ -17,7 +17,23 @@ TraceTouchstoneExport::TraceTouchstoneExport(TraceModel &model, QWidget *parent)
ui->selector->setModel(&model);
ui->selector->setPartialSelectionAllowed(true);
connect(ui->selector, qOverload<>(&TraceSetSelector::selectionChanged), this, &TraceTouchstoneExport::selectionChanged);
on_sbPorts_valueChanged(ui->sbPorts->value());
connect(ui->sbPorts, &QSpinBox::valueChanged, this, &TraceTouchstoneExport::setPortNum);
// figure out how many ports the user most likely needs
unsigned int p;
for(p=4;p>=1;p--) {
// do we have a trace name which could indicate such a number of ports?
for(unsigned int i=1;i<=p;i++) {
auto n1 = "S"+QString::number(p)+QString::number(i);
auto n2 = "S"+QString::number(i)+QString::number(p);
for(auto t : model.getTraces()) {
if(t->name().contains(n1) || t->name().contains(n2)) {
goto traceFound;
}
}
}
}
traceFound:
setPortNum(p);
}
TraceTouchstoneExport::~TraceTouchstoneExport()
@ -30,13 +46,32 @@ bool TraceTouchstoneExport::setTrace(int portFrom, int portTo, Trace *t)
return ui->selector->setTrace(portTo, portFrom, t);
}
bool TraceTouchstoneExport::setPortNum(int ports)
bool TraceTouchstoneExport::setPortNum(unsigned int ports)
{
if(ports < 1 || ports > 4) {
return false;
}
if((unsigned int) ui->sbPorts->value() == ports && ui->selector->getPorts() == ports) {
// already set correctly, nothing to do
return true;
}
ui->sbPorts->setValue(ports);
ui->selector->setPorts(ports);
// Attempt to set default traces (this will result in correctly populated
// 2 port export if the initial 4 traces have not been modified)
auto traces = ui->selector->getModel()->getTraces();
for(unsigned int i=1;i<=ports;i++) {
for(unsigned int j=1;j<=ports;j++) {
auto name = "S"+QString::number(i)+QString::number(j);
for(auto t : traces) {
if(t->name().contains(name)) {
// this could be the correct trace
setTrace(j, i, t);
break;
}
}
}
}
return true;
}
@ -86,11 +121,6 @@ void TraceTouchstoneExport::on_buttonBox_accepted()
}
}
void TraceTouchstoneExport::on_sbPorts_valueChanged(int ports)
{
ui->selector->setPorts(ports);
}
void TraceTouchstoneExport::selectionChanged()
{
auto valid = ui->selector->selectionValid();

View file

@ -19,11 +19,10 @@ public:
explicit TraceTouchstoneExport(TraceModel &model, QWidget *parent = nullptr);
~TraceTouchstoneExport();
bool setTrace(int portFrom, int portTo, Trace *t);
bool setPortNum(int ports);
bool setPortNum(unsigned int ports);
private slots:
void on_buttonBox_accepted();
void on_sbPorts_valueChanged(int ports);
void selectionChanged();
private:

View file

@ -27,7 +27,7 @@ TraceWaterfall::TraceWaterfall(TraceModel &model, QWidget *parent)
plotAreaBottom = 0;
xAxis.set(XAxis::Type::Frequency, false, true, 0, 6000000000, 10, false);
yAxis.set(YAxis::Type::Magnitude, false, true, -1, 1, 10, false);
yAxis.set(YAxis::Type::Magnitude, false, true, YAxis::getDefaultLimitMin(YAxis::Type::Magnitude), YAxis::getDefaultLimitMax(YAxis::Type::Magnitude), 10, false);
initializeTraceInfo();
}
@ -95,6 +95,7 @@ void TraceWaterfall::replot()
void TraceWaterfall::fromJSON(nlohmann::json j)
{
parseBaseJSON(j);
resetWaterfall();
pixelsPerLine = j.value("pixelsPerLine", pixelsPerLine);
maxDataSweeps = j.value("maxLines", maxDataSweeps);
@ -127,7 +128,7 @@ void TraceWaterfall::fromJSON(nlohmann::json j)
nlohmann::json TraceWaterfall::toJSON()
{
nlohmann::json j;
nlohmann::json j = getBaseJSON();
j["pixelsPerLine"] = pixelsPerLine;
j["direction"] = dir == Direction::TopToBottom ? "TopToBottom" : "BottomToTop";
j["keepDataBeyondPlot"] = keepDataBeyondPlotSize;

View file

@ -27,8 +27,8 @@ TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent)
yAxis[1].setTickMaster(yAxis[0]);
// Setup default axis
setYAxis(0, YAxis::Type::Magnitude, false, false, -120, 20, 14, true);
setYAxis(1, YAxis::Type::Phase, false, false, -180, 180, 12, true);
setYAxis(0, YAxis::Type::Magnitude, false, false, YAxis::getDefaultLimitMin(YAxis::Type::Magnitude), YAxis::getDefaultLimitMax(YAxis::Type::Magnitude), 14, true);
setYAxis(1, YAxis::Type::Phase, false, false, YAxis::getDefaultLimitMin(YAxis::Type::Phase), YAxis::getDefaultLimitMax(YAxis::Type::Phase), 12, true);
// enable autoscaling and set for full span (no information about actual span available yet)
updateSpan(0, 6000000000);
setXAxis(XAxis::Type::Frequency, XAxisMode::UseSpan, false, 0, 6000000000, 10, true);
@ -157,7 +157,7 @@ void TraceXYPlot::setAuto(bool horizontally, bool vertically)
nlohmann::json TraceXYPlot::toJSON()
{
nlohmann::json j;
nlohmann::json j = getBaseJSON();
nlohmann::json jX;
jX["type"] = xAxis.TypeToName().toStdString();
jX["mode"] = AxisModeToName(xAxisMode).toStdString();
@ -198,6 +198,7 @@ nlohmann::json TraceXYPlot::toJSON()
void TraceXYPlot::fromJSON(nlohmann::json j)
{
parseBaseJSON(j);
auto jX = j["XAxis"];
// old format used enum value for type and mode, new format uses string encoding (more robust when additional enum values are added).
// Check which format is used and parse accordingly
@ -329,10 +330,10 @@ bool TraceXYPlot::configureForTrace(Trace *t)
return false;
}
if(!yAxis[0].isSupported(xAxis.getType(), getModel().getSource())) {
setYAxis(0, yLeftDefault, false, true, 0, 1, 10, false);
setYAxis(0, yLeftDefault, false, false, YAxis::getDefaultLimitMin(yLeftDefault), YAxis::getDefaultLimitMax(yLeftDefault), 10, true);
}
if(!yAxis[1].isSupported(xAxis.getType(), getModel().getSource())) {
setYAxis(1, yRightDefault, false, true, 0, 1, 10, false);
setYAxis(1, yRightDefault, false, false, YAxis::getDefaultLimitMin(yRightDefault), YAxis::getDefaultLimitMax(yRightDefault), 10, true);
}
traceRemovalPending = true;
return true;
@ -638,13 +639,25 @@ void TraceXYPlot::draw(QPainter &p)
p.drawLine(p1, p2);
}
if(pref.Marker.clipToYAxis) {
// clip Y coordinate of markers to visible area (always show markers, even when out of range)
if(point.y() < plotRect.top()) {
point.ry() = plotRect.top();
} else if(point.y() > plotRect.bottom()) {
point.ry() = plotRect.bottom();
}
}
if(!plotRect.contains(point)) {
// out of screen
continue;
}
auto symbol = m->getSymbol();
point += QPoint(-symbol.width()/2, -symbol.height());
// ignore clipRect for markers
p.setClipping(false);
p.drawPixmap(point, symbol);
p.setClipping(true);
}
}
}
@ -1027,8 +1040,14 @@ void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled)
// unable to add trace to the requested axis
return;
}
if(axis == 0) {
TracePlot::enableTrace(t, enabled);
if(enabled) {
TracePlot::enableTrace(t, true);
} else {
// only disable trace on parent trace list if disabled for both axes
int otherAxis = axis ? 0 : 1;
if(tracesAxis[otherAxis].find(t) == tracesAxis[otherAxis].end()) {
TracePlot::enableTrace(t, false);
}
}
bool alreadyEnabled = tracesAxis[axis].find(t) != tracesAxis[axis].end();
if(alreadyEnabled != enabled) {
@ -1089,11 +1108,6 @@ bool TraceXYPlot::supported(Trace *t, YAxis::Type type)
return false;
}
break;
case YAxis::Type::GroupDelay:
if(t->isReflection()) {
return false;
}
break;
default:
break;
}

View file

@ -59,6 +59,8 @@ WaterfallAxisDialog::WaterfallAxisDialog(TraceWaterfall *plot) :
ui->Wmin->setPrefixes(prefixes);
ui->Wmax->setUnit(unit);
ui->Wmax->setPrefixes(prefixes);
ui->Wmin->setValue(YAxis::getDefaultLimitMin(type));
ui->Wmax->setValue(YAxis::getDefaultLimitMax(type));
});
connect(ui->Wauto, &QCheckBox::toggled, [this](bool checked) {
ui->Wmin->setEnabled(!checked);

View file

@ -56,6 +56,10 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
log->setEnabled(false);
linear->setEnabled(false);
CBauto->setEnabled(false);
min->setEnabled(false);
max->setEnabled(false);
divs->setEnabled(false);
autoDivs->setEnabled(false);
} else {
// axis enabled
log->setEnabled(true);
@ -88,31 +92,29 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
max->setPrefixes(prefixes);
};
connect(ui->Y1type, qOverload<int>(&QComboBox::currentIndexChanged), [this, updateYenableState](int) {
updateYenableState(ui->Y1type, ui->Y1linear, ui->Y1log, ui->Y1auto, ui->Y1min, ui->Y1max, ui->Y1Divs, ui->Y1autoDivs);
});
connect(ui->Y1auto, &QCheckBox::toggled, [this, updateYenableState](bool) {
updateYenableState(ui->Y1type, ui->Y1linear, ui->Y1log, ui->Y1auto, ui->Y1min, ui->Y1max, ui->Y1Divs, ui->Y1autoDivs);
});
connect(ui->Y1log, &QCheckBox::toggled, [this, updateYenableState](bool) {
updateYenableState(ui->Y1type, ui->Y1linear, ui->Y1log, ui->Y1auto, ui->Y1min, ui->Y1max, ui->Y1Divs, ui->Y1autoDivs);
});
connect(ui->Y1autoDivs, &QCheckBox::toggled, [this, updateYenableState](bool) {
updateYenableState(ui->Y1type, ui->Y1linear, ui->Y1log, ui->Y1auto, ui->Y1min, ui->Y1max, ui->Y1Divs, ui->Y1autoDivs);
});
auto setupYAxisConnetions = [this, updateYenableState](QComboBox *type, QRadioButton *linear, QRadioButton *log, QCheckBox *CBauto, SIUnitEdit *min, SIUnitEdit *max, QSpinBox *divs, QCheckBox *autoDivs) {
connect(type, qOverload<int>(&QComboBox::currentIndexChanged), this, [=](int) {
updateYenableState(type, linear, log, CBauto, min, max, divs, autoDivs);
// update min/max settings when axis has changed
if(type->currentIndex() != 0) {
auto axisType = (YAxis::Type) type->currentIndex();
min->setValue(YAxis::getDefaultLimitMin(axisType));
max->setValue(YAxis::getDefaultLimitMax(axisType));
}
});
connect(CBauto, &QCheckBox::toggled, this, [=](bool) {
updateYenableState(type, linear, log, CBauto, min, max, divs, autoDivs);
});
connect(log, &QCheckBox::toggled, this, [=](bool) {
updateYenableState(type, linear, log, CBauto, min, max, divs, autoDivs);
});
connect(autoDivs, &QCheckBox::toggled, this, [=](bool) {
updateYenableState(type, linear, log, CBauto, min, max, divs, autoDivs);
});
};
connect(ui->Y2type, qOverload<int>(&QComboBox::currentIndexChanged), [this, updateYenableState](int) {
updateYenableState(ui->Y2type, ui->Y2linear, ui->Y2log, ui->Y2auto, ui->Y2min, ui->Y2max, ui->Y2Divs, ui->Y2autoDivs);
});
connect(ui->Y2auto, &QCheckBox::toggled, [this, updateYenableState](bool) {
updateYenableState(ui->Y2type, ui->Y2linear, ui->Y2log, ui->Y2auto, ui->Y2min, ui->Y2max, ui->Y2Divs, ui->Y2autoDivs);
});
connect(ui->Y2log, &QCheckBox::toggled, [this, updateYenableState](bool) {
updateYenableState(ui->Y2type, ui->Y2linear, ui->Y2log, ui->Y2auto, ui->Y2min, ui->Y2max, ui->Y2Divs, ui->Y2autoDivs);
});
connect(ui->Y2autoDivs, &QCheckBox::toggled, [this, updateYenableState](bool) {
updateYenableState(ui->Y2type, ui->Y2linear, ui->Y2log, ui->Y2auto, ui->Y2min, ui->Y2max, ui->Y2Divs, ui->Y2autoDivs);
});
setupYAxisConnetions(ui->Y1type, ui->Y1linear, ui->Y1log, ui->Y1auto, ui->Y1min, ui->Y1max, ui->Y1Divs, ui->Y1autoDivs);
setupYAxisConnetions(ui->Y2type, ui->Y2linear, ui->Y2log, ui->Y2auto, ui->Y2min, ui->Y2max, ui->Y2Divs, ui->Y2autoDivs);
auto updateXenableState = [](QRadioButton *linear, QRadioButton *log, QCheckBox *CBauto, SIUnitEdit *min, SIUnitEdit *max, QSpinBox *divs, QCheckBox *autoDivs) {
log->setEnabled(true);

View file

@ -35,16 +35,6 @@ void TraceWidgetVNA::exportCSV()
void TraceWidgetVNA::exportTouchstone()
{
auto e = new TraceTouchstoneExport(model);
// Attempt to set default traces (this will result in correctly populated
// 2 port export if the initial 4 traces have not been modified)
e->setPortNum(2);
auto traces = model.getTraces();
for(unsigned int i=0;i<4;i++) {
if(i >= traces.size()) {
break;
}
e->setTrace(i%2+1, i/2+1, traces[i]);
}
if(AppWindow::showGUI()) {
e->show();
}

View file

@ -168,7 +168,11 @@ VNA::VNA(AppWindow *window, QString name)
// A modal QProgressDialog calls processEvents() in setValue(). Needs to use a queued connection to update the progress
// value from within the NewDatapoint slot to prevent possible re-entrancy.
connect(this, &VNA::calibrationMeasurementPercentage, calDialog, &QProgressDialog::setValue, Qt::QueuedConnection);
connect(this, &VNA::calibrationMeasurementPercentage, calDialog, [=](int percent) {
if(calMeasuring || percent == 100) {
calDialog->setValue(percent);
}
}, Qt::QueuedConnection);
connect(calDialog, &QProgressDialog::canceled, this, [=]() {
// the user aborted the calibration measurement
@ -184,8 +188,9 @@ VNA::VNA(AppWindow *window, QString name)
auto menuDeembed = new QMenu("De-embedding", window);
window->menuBar()->insertMenu(window->getUi()->menuWindow->menuAction(), menuDeembed);
actions.insert(menuDeembed->menuAction());
auto confDeembed = menuDeembed->addAction("Setup...");
auto confDeembed = new QAction("Setup...", menuDeembed);
confDeembed->setMenuRole(QAction::NoRole);
menuDeembed->addAction(confDeembed);
connect(confDeembed, &QAction::triggered, &deembedding, &Deembedding::configure);
enableDeembeddingAction = menuDeembed->addAction("De-embed VNA samples");
@ -976,7 +981,30 @@ void VNA::NewDatapoint(DeviceDriver::VNAMeasurement m)
m_avg = average.process(m_avg);
window->addStreamingData(m_avg, AppWindow::VNADataType::Raw);
TraceMath::DataType type = TraceMath::DataType::Frequency;
if(settings.zerospan) {
type = TraceMath::DataType::TimeZeroSpan;
// keep track of first point time
if(m_avg.pointNum == 0) {
settings.firstPointTime = m_avg.us;
m_avg.us = 0;
} else {
m_avg.us -= settings.firstPointTime;
}
} else {
switch(settings.sweepType) {
case SweepType::Last:
case SweepType::Frequency:
type = TraceMath::DataType::Frequency;
break;
case SweepType::Power:
type = TraceMath::DataType::Power;
break;
}
}
window->addStreamingData(m_avg, AppWindow::VNADataType::Raw, settings.zerospan);
if(average.settled()) {
setOperationPending(false);
@ -1001,36 +1029,13 @@ void VNA::NewDatapoint(DeviceDriver::VNAMeasurement m)
cal.correctMeasurement(m_avg);
if(cal.getCaltype().type != Calibration::Type::None) {
window->addStreamingData(m_avg, AppWindow::VNADataType::Calibrated);
}
TraceMath::DataType type = TraceMath::DataType::Frequency;
if(settings.zerospan) {
type = TraceMath::DataType::TimeZeroSpan;
// keep track of first point time
if(m_avg.pointNum == 0) {
settings.firstPointTime = m_avg.us;
m_avg.us = 0;
} else {
m_avg.us -= settings.firstPointTime;
}
} else {
switch(settings.sweepType) {
case SweepType::Last:
case SweepType::Frequency:
type = TraceMath::DataType::Frequency;
break;
case SweepType::Power:
type = TraceMath::DataType::Power;
break;
}
window->addStreamingData(m_avg, AppWindow::VNADataType::Calibrated, settings.zerospan);
}
traceModel.addVNAData(m_avg, type, false);
if(deembedding_active) {
deembedding.Deembed(m_avg);
window->addStreamingData(m_avg, AppWindow::VNADataType::Deembedded);
window->addStreamingData(m_avg, AppWindow::VNADataType::Deembedded, settings.zerospan);
traceModel.addVNAData(m_avg, type, true);
}
@ -1222,6 +1227,7 @@ bool VNA::SpanMatchCal()
SetStartFreq(cal.getMinFreq());
SetStopFreq(cal.getMaxFreq());
SetPoints(cal.getNumPoints());
UpdateCalWidget();
return true;
}

View file

@ -507,16 +507,26 @@ void AppWindow::CreateToolbars()
tb_reference->addWidget(new QLabel("Ref out:"));
toolbars.reference.outFreq = new QComboBox();
tb_reference->addWidget(toolbars.reference.outFreq);
connect(toolbars.reference.type, qOverload<int>(&QComboBox::currentIndexChanged), this, &AppWindow::UpdateReference);
connect(toolbars.reference.outFreq, qOverload<int>(&QComboBox::currentIndexChanged), this, &AppWindow::UpdateReference);
connect(toolbars.reference.type, qOverload<int>(&QComboBox::currentIndexChanged), this, &AppWindow::ReferenceChanged);
connect(toolbars.reference.outFreq, qOverload<int>(&QComboBox::currentIndexChanged), this, &AppWindow::ReferenceChanged);
addToolBar(tb_reference);
tb_reference->setObjectName("Reference Toolbar");
referenceTimer.setSingleShot(true);
connect(&referenceTimer, &QTimer::timeout, this, &AppWindow::UpdateReference);
}
void AppWindow::SetupSCPI()
{
scpi.add(new SCPICommand("*IDN", nullptr, [=](QStringList){
return "LibreVNA,LibreVNA-GUI,dummy_serial,"+appVersion;
QString ret = "LibreVNA,LibreVNA-GUI,";
if(device) {
ret += device->getSerial();
} else {
ret += "Not connected";
}
ret += ","+appVersion;
return ret;
}));
scpi.add(new SCPICommand("*RST", [=](QStringList){
SetResetState();
@ -859,7 +869,7 @@ SCPI* AppWindow::getSCPI()
return &scpi;
}
void AppWindow::addStreamingData(const DeviceDriver::VNAMeasurement &m, VNADataType type)
void AppWindow::addStreamingData(const DeviceDriver::VNAMeasurement &m, VNADataType type, bool is_zerospan)
{
StreamingServer *server = nullptr;
switch(type) {
@ -869,11 +879,11 @@ void AppWindow::addStreamingData(const DeviceDriver::VNAMeasurement &m, VNADataT
}
if(server) {
server->addData(m);
server->addData(m, is_zerospan);
}
}
void AppWindow::addStreamingData(const DeviceDriver::SAMeasurement &m, SADataType type)
void AppWindow::addStreamingData(const DeviceDriver::SAMeasurement &m, SADataType type, bool is_zerospan)
{
StreamingServer *server = nullptr;
switch(type) {
@ -882,7 +892,7 @@ void AppWindow::addStreamingData(const DeviceDriver::SAMeasurement &m, SADataTyp
}
if(server) {
server->addData(m);
server->addData(m, is_zerospan);
}
}
@ -945,7 +955,7 @@ void AppWindow::ResetReference()
toolbars.reference.outFreq->setCurrentIndex(0);
toolbars.reference.type->blockSignals(false);
toolbars.reference.outFreq->blockSignals(false);
UpdateReference();
ReferenceChanged();
}
//void AppWindow::StartManualControl()
@ -1001,7 +1011,16 @@ void AppWindow::UpdateReferenceToolbar()
}
toolbars.reference.type->blockSignals(false);
toolbars.reference.outFreq->blockSignals(false);
UpdateReference();
ReferenceChanged();
}
void AppWindow::ReferenceChanged()
{
if(!device) {
// can't update without a device connected
return;
}
referenceTimer.start(100);
}
void AppWindow::UpdateReference()

View file

@ -59,14 +59,14 @@ public:
Deembedded = 2,
};
void addStreamingData(const DeviceDriver::VNAMeasurement &m, VNADataType type);
void addStreamingData(const DeviceDriver::VNAMeasurement &m, VNADataType type, bool is_zerospan);
enum class SADataType {
Raw = 0,
Normalized = 1,
};
void addStreamingData(const DeviceDriver::SAMeasurement &m, SADataType type);
void addStreamingData(const DeviceDriver::SAMeasurement &m, SADataType type, bool is_zerospan);
public slots:
void setModeStatus(QString msg);
@ -82,6 +82,7 @@ private slots:
// void StartManualControl();
void ResetReference();
void UpdateReferenceToolbar();
void ReferenceChanged();
void UpdateReference();
void DeviceStatusUpdated();
void DeviceFlagsUpdated();
@ -144,6 +145,9 @@ private:
QString deviceSerial;
QActionGroup *deviceActionGroup;
// Reference change timer
QTimer referenceTimer;
// Status bar widgets
QLabel lConnectionStatus;
QLabel lDeviceInfo;

View file

@ -179,10 +179,14 @@ void ModeHandler::setStatusBarMessageChanged(const QString &msg)
}
}
bool ModeHandler::nameAllowed(const QString &name)
bool ModeHandler::nameAllowed(const QString &name, unsigned int ignoreIndex)
{
for(auto m : modes) {
if(m->getName() == name) {
for(unsigned int i=0;i<modes.size();i++) {
if(i == ignoreIndex) {
// ignore possible collision at this index
continue;
}
if(modes[i]->getName() == name) {
/* name already taken, no duplicates allowed
* when importing, name is used as value
*/

View file

@ -29,7 +29,7 @@ public:
Mode* getMode(int index);
std::vector<Mode*> getModes();
bool nameAllowed(const QString &name);
bool nameAllowed(const QString &name, unsigned int ignoreIndex=-1);
int findIndex(Mode *targetMode);
Mode* findFirstOfType(Mode::Type t);

View file

@ -45,6 +45,9 @@ void ModeWindow::SetupUi()
tabBar->setStyleSheet("QTabBar::tab { height: " + QString::number(aw->menuBar()->height()) + "px;}");
tabBar->setTabsClosable(true);
cornerWidget->layout()->addWidget(tabBar);
connect(tabBar, &QTabBar::tabBarDoubleClicked, this, [=](int index) {
renameMode(index);
});
auto bAdd = new QPushButton();
QIcon icon;
@ -94,6 +97,11 @@ void ModeWindow::SetupUi()
menu->addSeparator();
auto submenuAdd = new QMenu("Create new");
menu->addMenu(submenuAdd);
auto rename = new QAction("Rename active mode");
connect(rename, &QAction::triggered, this, [=](){
renameMode(handler->getCurrentIndex());
});
menu->addAction(rename);
auto mAdd = new QMenu();
for(unsigned int i=0;i<(int) Mode::Type::Last;i++) {
@ -180,3 +188,19 @@ void ModeWindow::CurrentModeChanged(int modeIndex)
}
menuActions[modeIndex]->setChecked(true);
}
void ModeWindow::renameMode(int modeIndex)
{
auto mode = handler->getMode(modeIndex);
auto newName = QInputDialog::getText(this, "Rename", "Enter new name for mode \""+mode->getName()+"\":");
if(newName.isEmpty()) {
return;
}
if(handler->nameAllowed(newName, modeIndex)) {
mode->setName(newName);
tabBar->setTabText(modeIndex, newName);
menu->actions()[modeIndex]->setText(newName);
} else {
InformationBox::ShowError("Error", "Unable to set name. Mode names must be unique.");
}
}

View file

@ -29,6 +29,7 @@ private slots:
void ModeCreated(int modeIndex);
void ModeClosed(int modeIndex);
void CurrentModeChanged(int modeIndex);
void renameMode(int modeIndex);
};
#endif // MODEWINDOW_H

View file

@ -110,6 +110,18 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
ui->GraphsSweepHidePercent->setPrecision(3);
ui->GraphsSweepHidePercent->setUnit("%");
auto layout = static_cast<QGridLayout*>(ui->GraphAxisLimitGroup->layout());
for(unsigned int i=(int) YAxis::Type::Disabled + 1;i<(int) YAxis::Type::Last;i++) {
auto type = (YAxis::Type) i;
layout->addWidget(new QLabel(YAxis::TypeToName(type)), i, 0);
auto minEntry = new SIUnitEdit(YAxis::Unit(type), YAxis::Prefixes(type), 5);
layout->addWidget(minEntry, i, 1);
graphAxisLimitsMinEntries[type] = minEntry;
auto maxEntry = new SIUnitEdit(YAxis::Unit(type), YAxis::Prefixes(type), 5);
layout->addWidget(maxEntry, i, 2);
graphAxisLimitsMaxEntries[type] = maxEntry;
}
// General page
if(p->TCPoverride) {
ui->SCPIServerPort->setEnabled(false);
@ -183,10 +195,12 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
// apply GUI state to settings
updateFromGUI();
accept();
emit p->updated();
});
connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, [=](){
// apply GUI state to settings
updateFromGUI();
emit p->updated();
});
connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getSaveFileName(this, "Save preferences", "", "LibreVNA preferences files (*.vnapref)", nullptr, Preferences::QFileDialogOptions());
@ -211,6 +225,7 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
file.close();
p->fromJSON(j);
setInitialGUIState();
emit p->updated();
}
});
connect(ui->AcquisitionLimitTDRCheckbox, &QCheckBox::toggled, [=](bool enabled){
@ -280,6 +295,7 @@ void PreferencesDialog::setInitialGUIState()
ui->GraphsLimitIndication->setCurrentIndex((int) p->Graphs.limitIndication);
ui->GraphsLimitNaNpasses->setCurrentIndex(p->Graphs.limitNaNpasses ? 1 : 0);
ui->GraphsLineWidth->setValue(p->Graphs.lineWidth);
ui->GraphsFontSizeTitle->setValue(p->Graphs.fontSizeTitle);
ui->GraphsFontSizeAxis->setValue(p->Graphs.fontSizeAxis);
ui->GraphsFontSizeCursorOverlay->setValue(p->Graphs.fontSizeCursorOverlay);
ui->GraphsFontSizeMarkerData->setValue(p->Graphs.fontSizeMarkerData);
@ -292,9 +308,13 @@ void PreferencesDialog::setInitialGUIState()
ui->GraphsSweepHide->setChecked(p->Graphs.SweepIndicator.hide);
ui->GraphsSweepHidePercent->setValue(p->Graphs.SweepIndicator.hidePercent);
ui->graphsEnableMasterTicksForYAxis->setChecked(p->Graphs.enableMasterTicksForYAxis);
for(unsigned int i=(int) YAxis::Type::Disabled + 1;i<(int) YAxis::Type::Last;i++) {
auto type = (YAxis::Type) i;
graphAxisLimitsMinEntries[type]->setValue(p->Graphs.defaultAxisLimits.min[i]);
graphAxisLimitsMaxEntries[type]->setValue(p->Graphs.defaultAxisLimits.max[i]);
}
ui->MarkerShowMarkerData->setChecked(p->Marker.defaultBehavior.showDataOnGraphs);
ui->MarkerShowdB->setChecked(p->Marker.defaultBehavior.showdB);
ui->MarkerShowdBm->setChecked(p->Marker.defaultBehavior.showdBm);
ui->MarkerShowdBUv->setChecked(p->Marker.defaultBehavior.showdBuV);
@ -322,6 +342,7 @@ void PreferencesDialog::setInitialGUIState()
ui->MarkerInterpolate->setCurrentIndex(p->Marker.interpolatePoints ? 1 : 0);
ui->MarkerSortOrder->setCurrentIndex((int) p->Marker.sortOrder);
ui->MarkerSymbolStyle->setCurrentIndex((int) p->Marker.symbolStyle);
ui->MarkerClipToYAxis->setChecked(p->Marker.clipToYAxis);
ui->SCPIServerEnabled->setChecked(p->SCPIServer.enabled);
ui->SCPIServerPort->setValue(p->SCPIServer.port);
@ -396,6 +417,7 @@ void PreferencesDialog::updateFromGUI()
p->Graphs.limitIndication = (GraphLimitIndication) ui->GraphsLimitIndication->currentIndex();
p->Graphs.limitNaNpasses = ui->GraphsLimitNaNpasses->currentIndex() == 1;
p->Graphs.lineWidth = ui->GraphsLineWidth->value();
p->Graphs.fontSizeTitle = ui->GraphsFontSizeTitle->value();
p->Graphs.fontSizeAxis = ui->GraphsFontSizeAxis->value();
p->Graphs.fontSizeCursorOverlay = ui->GraphsFontSizeCursorOverlay->value();
p->Graphs.fontSizeMarkerData = ui->GraphsFontSizeMarkerData->value();
@ -408,6 +430,11 @@ void PreferencesDialog::updateFromGUI()
p->Graphs.SweepIndicator.hide = ui->GraphsSweepHide->isChecked();
p->Graphs.SweepIndicator.hidePercent = ui->GraphsSweepHidePercent->value();
p->Graphs.enableMasterTicksForYAxis = ui->graphsEnableMasterTicksForYAxis->isChecked();
for(unsigned int i=(int) YAxis::Type::Disabled + 1;i<(int) YAxis::Type::Last;i++) {
auto type = (YAxis::Type) i;
p->Graphs.defaultAxisLimits.min[i] = graphAxisLimitsMinEntries[type]->value();
p->Graphs.defaultAxisLimits.max[i] = graphAxisLimitsMaxEntries[type]->value();
}
p->Marker.defaultBehavior.showDataOnGraphs = ui->MarkerShowMarkerData->isChecked();
p->Marker.defaultBehavior.showdB = ui->MarkerShowdB->isChecked();
@ -437,6 +464,7 @@ void PreferencesDialog::updateFromGUI()
p->Marker.interpolatePoints = ui->MarkerInterpolate->currentIndex() == 1;
p->Marker.sortOrder = (MarkerSortOrder) ui->MarkerSortOrder->currentIndex();
p->Marker.symbolStyle = (MarkerSymbolStyle) ui->MarkerSymbolStyle->currentIndex();
p->Marker.clipToYAxis = ui->MarkerClipToYAxis->isChecked();
p->SCPIServer.enabled = ui->SCPIServerEnabled->isChecked();
p->SCPIServer.port = ui->SCPIServerPort->value();

View file

@ -3,6 +3,8 @@
#include "Util/qpointervariant.h"
#include "savable.h"
#include "Traces/traceaxis.h"
#include "CustomWidgets/siunitedit.h"
#include "Device/LibreVNA/Compound/compounddevice.h"
@ -44,7 +46,8 @@ enum MarkerSymbolStyle {
Q_DECLARE_METATYPE(MarkerSymbolStyle);
class Preferences : public Savable {
class Preferences : public QObject, public Savable {
Q_OBJECT
friend class PreferencesDialog;
public:
static Preferences& getInstance() {
@ -133,6 +136,7 @@ public:
bool limitNaNpasses;
double lineWidth;
int fontSizeTitle;
int fontSizeAxis;
int fontSizeMarkerData;
int fontSizeTraceNames;
@ -155,6 +159,11 @@ public:
QString transmission;
QString reflection;
} defaultGraphs;
struct {
double min[(int) YAxis::Type::Last];
double max[(int) YAxis::Type::Last];
} defaultAxisLimits;
} Graphs;
struct {
struct {
@ -165,6 +174,7 @@ public:
bool interpolatePoints;
MarkerSortOrder sortOrder;
MarkerSymbolStyle symbolStyle;
bool clipToYAxis;
} Marker;
struct {
bool enabled;
@ -206,6 +216,9 @@ public:
bool set(QString name, QVariant value);
QVariant get(QString name);
signals:
void updated();
private:
Preferences() :
TCPoverride(false) {}
@ -262,6 +275,7 @@ private:
{&Graphs.limitIndication, "Graphs.limitIndication", GraphLimitIndication::PassFailText},
{&Graphs.limitNaNpasses, "Graphs.limitNaNpasses", false},
{&Graphs.lineWidth, "Graphs.lineWidth", 1.0},
{&Graphs.fontSizeTitle, "Graphs.fontSizeTitle", 18},
{&Graphs.fontSizeAxis, "Graphs.fontSizeAxis", 10},
{&Graphs.fontSizeCursorOverlay, "Graphs.fontSizeCursorOverlay", 12},
{&Graphs.fontSizeMarkerData, "Graphs.fontSizeMarkerData", 12},
@ -276,6 +290,46 @@ private:
{&Graphs.SweepIndicator.hidePercent, "Graphs.SweepIndicator.hidePercent", 3.0},
{&Graphs.defaultGraphs.transmission, "Graphs.defaultGraphs.transmission", "XY Plot"},
{&Graphs.defaultGraphs.reflection, "Graphs.defaultGraphs.reflection", "Smith Chart"},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::Magnitude], "Graphs.defaultAxisLimits.Magnitude.max", 20.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::Magnitude], "Graphs.defaultAxisLimits.Magnitude.min", -120.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::MagnitudedBuV], "Graphs.defaultAxisLimits.MagnitudedBuV.max", 128.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::MagnitudedBuV], "Graphs.defaultAxisLimits.MagnitudedBuV.min", -13.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::MagnitudeLinear], "Graphs.defaultAxisLimits.MagnitudeLinear.max", 1.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::MagnitudeLinear], "Graphs.defaultAxisLimits.MagnitudeLinear.min", 0.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::Phase], "Graphs.defaultAxisLimits.Phase.max",180.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::Phase], "Graphs.defaultAxisLimits.Phase.min", -180.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::UnwrappedPhase], "Graphs.defaultAxisLimits.UnwrappedPhase.max", 0.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::UnwrappedPhase], "Graphs.defaultAxisLimits.UnwrappedPhase.min", -360.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::VSWR], "Graphs.defaultAxisLimits.VSWR.max", 10.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::VSWR], "Graphs.defaultAxisLimits.VSWR.min", 1.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::Real], "Graphs.defaultAxisLimits.Real.max", 1.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::Real], "Graphs.defaultAxisLimits.Real.min", -1.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::Imaginary], "Graphs.defaultAxisLimits.Imaginary.max", 1.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::Imaginary], "Graphs.defaultAxisLimits.Imaginary.min", -1.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::AbsImpedance], "Graphs.defaultAxisLimits.AbsImpedance.max", 100.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::AbsImpedance], "Graphs.defaultAxisLimits.AbsImpedance.min", 0.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::SeriesR], "Graphs.defaultAxisLimits.SeriesR.max", 100.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::SeriesR], "Graphs.defaultAxisLimits.SeriesR.min", 0.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::Reactance], "Graphs.defaultAxisLimits.Reactance.max", 100.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::Reactance], "Graphs.defaultAxisLimits.Reactance.min", 0.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::Capacitance], "Graphs.defaultAxisLimits.Capacitance.max", 10e-6},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::Capacitance], "Graphs.defaultAxisLimits.Capacitance.min", 0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::Inductance], "Graphs.defaultAxisLimits.Inductance.max", 1e-3},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::Inductance], "Graphs.defaultAxisLimits.Inductance.min", 0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::QualityFactor], "Graphs.defaultAxisLimits.QualityFactor.max", 100.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::QualityFactor], "Graphs.defaultAxisLimits.QualityFactor.min", 0.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::GroupDelay], "Graphs.defaultAxisLimits.GroupDelay.max", 1e-6},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::GroupDelay], "Graphs.defaultAxisLimits.GroupDelay.min", 0.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::ImpulseReal], "Graphs.defaultAxisLimits.ImpulseReal.max", 1.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::ImpulseReal], "Graphs.defaultAxisLimits.ImpulseReal.min", -1.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::ImpulseMag], "Graphs.defaultAxisLimits.ImpulseMag.max", 0.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::ImpulseMag], "Graphs.defaultAxisLimits.ImpulseMag.min", -100.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::Step], "Graphs.defaultAxisLimits.Step.max", 1.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::Step], "Graphs.defaultAxisLimits.Step.min", -1.0},
{&Graphs.defaultAxisLimits.max[(int) YAxis::Type::Impedance], "Graphs.defaultAxisLimits.Impedance.max", 100.0},
{&Graphs.defaultAxisLimits.min[(int) YAxis::Type::Impedance], "Graphs.defaultAxisLimits.Impedance.min", 0.0},
{&Marker.defaultBehavior.showDataOnGraphs, "Marker.defaultBehavior.ShowDataOnGraphs", true},
{&Marker.defaultBehavior.showdB, "Marker.defaultBehavior.showdB", true},
{&Marker.defaultBehavior.showdBm, "Marker.defaultBehavior.showdBm", true},
@ -303,7 +357,8 @@ private:
{&Marker.defaultBehavior.showMaxDeltaPos, "Marker.defaultBehavior.showMaxDeltaPos", true},
{&Marker.interpolatePoints, "Marker.interpolatePoints", false},
{&Marker.sortOrder, "Marker.sortOrder", MarkerSortOrder::PrefMarkerSortXCoord},
{&Marker.symbolStyle, "Marker.symbolStyle", MarkerSymbolStyle::EmptyNumberAbove},
{&Marker.symbolStyle, "Marker.symbolStyle", MarkerSymbolStyle::FilledNumberAbove},
{&Marker.clipToYAxis, "Marker.clipToYAxis", true},
{&SCPIServer.enabled, "SCPIServer.enabled", true},
{&SCPIServer.port, "SCPIServer.port", 19542},
{&StreamingServers.VNARawData.enabled, "StreamingServers.VNARawData.enabled", false},
@ -339,6 +394,8 @@ private:
void updateFromGUI();
Ui::PreferencesDialog *ui;
Preferences *p;
std::map<YAxis::Type, SIUnitEdit*> graphAxisLimitsMaxEntries;
std::map<YAxis::Type, SIUnitEdit*> graphAxisLimitsMinEntries;
};
#endif // PREFERENCESDIALOG_H

View file

@ -98,7 +98,7 @@
</size>
</property>
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<widget class="QWidget" name="Startup">
<layout class="QHBoxLayout" name="horizontalLayout_4">
@ -112,8 +112,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>683</width>
<height>928</height>
<width>522</width>
<height>945</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
@ -955,7 +955,7 @@
<x>0</x>
<y>0</y>
<width>683</width>
<height>1118</height>
<height>1217</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_22">
@ -1066,62 +1066,76 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_39">
<property name="text">
<string>Font (axes):</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QSpinBox" name="GraphsFontSizeAxis">
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QSpinBox" name="GraphsFontSizeTraceNames">
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="1">
<widget class="QSpinBox" name="GraphsFontSizeMarkerData">
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<widget class="QSpinBox" name="GraphsFontSizeCursorOverlay">
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_40">
<property name="text">
<string>Font (trace names):</string>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_41">
<property name="text">
<string>Font (marker data):</string>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_42">
<property name="text">
<string>Font (cursor overlay):</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_68">
<property name="text">
<string>Font (title):</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="GraphsFontSizeTitle">
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -1443,6 +1457,36 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="GraphAxisLimitGroup">
<property name="title">
<string>Default Y Axis limits</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="2">
<widget class="QLabel" name="label_66">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Default Maximum Limit&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_65">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Axis Type&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_67">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Default Minimum Limit&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
@ -1476,8 +1520,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>683</width>
<height>605</height>
<width>602</width>
<height>628</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
@ -1787,6 +1831,20 @@
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_64">
<property name="text">
<string>Show out-of-range marker at top/bottom:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="MarkerClipToYAxis">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
@ -1822,8 +1880,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>697</width>
<height>563</height>
<width>168</width>
<height>127</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_12">
@ -2105,8 +2163,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>697</width>
<height>563</height>
<width>258</width>
<height>241</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_19">

View file

@ -20,12 +20,16 @@ StreamingServer::StreamingServer(int port)
});
}
void StreamingServer::addData(const DeviceDriver::VNAMeasurement &m)
void StreamingServer::addData(const DeviceDriver::VNAMeasurement &m, bool is_zerospan)
{
nlohmann::json j;
j["pointNum"] = m.pointNum;
j["frequency"] = m.frequency;
j["dBm"] = m.dBm;
if(is_zerospan) {
j["time"] = m.us * 0.000001;
} else {
j["frequency"] = m.frequency;
j["dBm"] = m.dBm;
}
j["Z0"] = m.Z0;
nlohmann::json jp;
for(auto const &p : m.measurements) {
@ -41,11 +45,15 @@ void StreamingServer::addData(const DeviceDriver::VNAMeasurement &m)
}
}
void StreamingServer::addData(const DeviceDriver::SAMeasurement &m)
void StreamingServer::addData(const DeviceDriver::SAMeasurement &m, bool is_zerospan)
{
nlohmann::json j;
j["pointNum"] = m.pointNum;
j["frequency"] = m.frequency;
if(is_zerospan) {
j["time"] = m.us * 0.000001;
} else {
j["frequency"] = m.frequency;
}
nlohmann::json jp;
for(auto const &p : m.measurements) {
jp[p.first.toStdString()] = p.second;

View file

@ -13,8 +13,8 @@ class StreamingServer : public QObject
public:
StreamingServer(int port);
void addData(const DeviceDriver::VNAMeasurement &m);
void addData(const DeviceDriver::SAMeasurement &m);
void addData(const DeviceDriver::VNAMeasurement &m, bool is_zerospan);
void addData(const DeviceDriver::SAMeasurement &m, bool is_zerospan);
int getPort() {return port;}