From cdfe95af17f4042a926c257e7cf1c115edd8e71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Tue, 16 Apr 2024 18:56:26 +0200 Subject: [PATCH 1/3] Add YAxis: impedance and enable log option --- Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp | 6 ++++++ Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.h | 1 + .../PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.cpp | 4 ++-- .../PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.ui | 6 +++--- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp index 0d3541c..2af7051 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp @@ -108,6 +108,8 @@ double YAxis::sampleToCoordinate(Trace::Data data, Trace *t, unsigned int sample return data.y.real(); case YAxis::Type::Imaginary: return data.y.imag(); + case YAxis::Type::AbsImpedance: + return abs(Util::SparamToImpedance(data.y, t->getReferenceImpedance())); case YAxis::Type::SeriesR: return Util::SparamToResistance(data.y, t->getReferenceImpedance()); case YAxis::Type::Reactance: @@ -201,6 +203,7 @@ QString YAxis::TypeToName(Type type) case Type::VSWR: return "VSWR"; case Type::Real: return "Real"; case Type::Imaginary: return "Imaginary"; + case Type::AbsImpedance: return "Impedance (absolute)"; case Type::SeriesR: return "Resistance"; case Type::Reactance: return "Reactance"; case Type::Capacitance: return "Capacitance"; @@ -247,6 +250,7 @@ QString YAxis::Unit(Type type, TraceModel::DataSource source) case Type::Imaginary: case Type::QualityFactor: return ""; + case Type::AbsImpedance: return "Ω"; case Type::SeriesR: return "Ω"; case Type::Reactance: return "Ω"; case Type::Capacitance: return "F"; @@ -281,6 +285,7 @@ QString YAxis::Prefixes(Type type, TraceModel::DataSource source) case Type::Real: return "pnum "; case Type::Imaginary: return "pnum "; case Type::QualityFactor: return " "; + case Type::AbsImpedance: return " "; case Type::SeriesR: return "m kM"; case Type::Reactance: return "m kM"; case Type::Capacitance: return "pnum "; @@ -337,6 +342,7 @@ std::set YAxis::getSupported(XAxis::Type type, TraceModel::DataSour ret.insert(YAxis::Type::VSWR); ret.insert(YAxis::Type::Real); ret.insert(YAxis::Type::Imaginary); + ret.insert(YAxis::Type::AbsImpedance); ret.insert(YAxis::Type::SeriesR); ret.insert(YAxis::Type::Reactance); ret.insert(YAxis::Type::Capacitance); diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.h b/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.h index 9334664..dfde29a 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.h +++ b/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.h @@ -71,6 +71,7 @@ public: Real, Imaginary, // derived parameter options + AbsImpedance, SeriesR, Reactance, Capacitance, diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.cpp index 37dabf0..8438509 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.cpp @@ -50,7 +50,7 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) : // Setup GUI connections connect(ui->Y1type, qOverload(&QComboBox::currentIndexChanged), [=](int index) { - //ui->Y1log->setEnabled(index != 0); + ui->Y1log->setEnabled(index != 0); ui->Y1linear->setEnabled(index != 0); ui->Y1auto->setEnabled(index != 0); bool autoRange = ui->Y1auto->isChecked(); @@ -74,7 +74,7 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) : }); connect(ui->Y2type, qOverload(&QComboBox::currentIndexChanged), [=](int index) { - //ui->Y2log->setEnabled(index != 0); + ui->Y2log->setEnabled(index != 0); ui->Y2linear->setEnabled(index != 0); ui->Y2auto->setEnabled(index != 0); bool autoRange = ui->Y2auto->isChecked(); diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.ui b/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.ui index 15b7dc5..b8e2ba7 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.ui +++ b/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.ui @@ -75,7 +75,7 @@ - false + true Log @@ -204,7 +204,7 @@ - false + true Log @@ -592,8 +592,8 @@ - + From 994b536bc62afec66a19bae5c5c0af7ae41eea56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Tue, 16 Apr 2024 21:13:23 +0200 Subject: [PATCH 2/3] improve XY-plot rendering, especially for logarithmic Y-axes --- .../LibreVNA-GUI/Traces/traceaxis.cpp | 26 ++++-- .../LibreVNA-GUI/Traces/tracexyplot.cpp | 89 +++++++++++++------ .../LibreVNA-GUI/Traces/xyplotaxisdialog.cpp | 10 ++- 3 files changed, 89 insertions(+), 36 deletions(-) diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp index 2af7051..9af22b3 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp @@ -41,6 +41,14 @@ static double createAutomaticTicks(vector& ticks, double start, double s } static void createLogarithmicTicks(vector& ticks, double start, double stop, int minDivisions) { + double mult = 1.0; + if(start < 0.0 && stop < 0.0) { + mult = -1.0; + auto buf = -stop; + stop = -start; + start = buf; + } + // enforce usable log settings if(start <= 0) { start = 1.0; @@ -55,7 +63,9 @@ static void createLogarithmicTicks(vector& ticks, double start, double s int zeros = floor(log10(max_div_decade)); double decimals_shift = pow(10, zeros); max_div_decade /= decimals_shift; - if(max_div_decade < 2) { + if(max_div_decade <= 1) { + max_div_decade = 1; + } else if(max_div_decade < 2) { max_div_decade = 2; } else if(max_div_decade < 5) { max_div_decade = 5; @@ -70,15 +80,17 @@ static void createLogarithmicTicks(vector& ticks, double start, double s step *= 10; } do { - ticks.push_back(div); - if(ticks.size() > 1 && div != step && floor(log10(div)) != floor(log10(div - step))) { + ticks.push_back(div * mult); + if(ticks.size() > 1 && div != step && floor(log10(div)+std::numeric_limits::epsilon()) != floor(log10(div - step)+std::numeric_limits::epsilon())) { // reached a new decade with this switch step *= 10; - div = step; - } else { - div += step; } + div += step; } while(div <= stop); + + if(mult == -1.0) { + std::reverse(ticks.begin(), ticks.end()); + } } YAxis::YAxis() @@ -285,7 +297,7 @@ QString YAxis::Prefixes(Type type, TraceModel::DataSource source) case Type::Real: return "pnum "; case Type::Imaginary: return "pnum "; case Type::QualityFactor: return " "; - case Type::AbsImpedance: return " "; + case Type::AbsImpedance: return "m k"; case Type::SeriesR: return "m kM"; case Type::Reactance: return "m kM"; case Type::Capacitance: return "pnum "; diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp index 07b96c9..b3f014c 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp @@ -478,7 +478,11 @@ void TraceXYPlot::draw(QPainter &p) step = max / 1000; } int significantDigits = floor(log10(max)) - floor(log10(step)) + 1; + if(yAxis[i].getLog() && yAxis[i].getRangeMax()/yAxis[i].getRangeMin() >= 100) { + significantDigits = floor(log10(max)) + 1; + } + int lastTickLabelEnd = std::numeric_limits::max(); for(unsigned int j = 0; j < yAxis[i].getTicks().size(); j++) { auto yCoord = yAxis[i].transform(yAxis[i].getTicks()[j], w.height() - xAxisSpace, plotAreaTop); p.setPen(QPen(pref.Graphs.Color.axis, 1)); @@ -486,17 +490,23 @@ void TraceXYPlot::draw(QPainter &p) auto tickStart = i == 0 ? plotAreaLeft : plotAreaLeft + plotAreaWidth; auto tickLen = i == 0 ? -2 : 2; p.drawLine(tickStart, yCoord, tickStart + tickLen, yCoord); - QString unit = ""; - QString prefix = " "; - if(pref.Graphs.showUnits) { - unit = yAxis[i].Unit(); - prefix = yAxis[i].Prefixes(); - } - auto tickValue = Unit::ToString(yAxis[i].getTicks()[j], unit, prefix, significantDigits); - if(i == 0) { - p.drawText(QRectF(0, yCoord - pref.Graphs.fontSizeAxis/2 - 2, tickStart + 2 * tickLen, pref.Graphs.fontSizeAxis*1.5), Qt::AlignRight, tickValue); + if(yCoord + pref.Graphs.fontSizeAxis >= lastTickLabelEnd) { + // would overlap previous tick label, skip } else { - p.drawText(QRectF(tickStart + 2 * tickLen + 2, yCoord - pref.Graphs.fontSizeAxis/2 - 2, yAxisSpace, pref.Graphs.fontSizeAxis*1.5), Qt::AlignLeft, tickValue); + QString unit = ""; + QString prefix = " "; + if(pref.Graphs.showUnits) { + unit = yAxis[i].Unit(); + prefix = yAxis[i].Prefixes(); + } + auto tickValue = Unit::ToString(yAxis[i].getTicks()[j], unit, prefix, significantDigits); + QRect bounding; + if(i == 0) { + p.drawText(QRect(0, yCoord - pref.Graphs.fontSizeAxis/2 - 2, tickStart + 2 * tickLen, pref.Graphs.fontSizeAxis*1.5), Qt::AlignRight, tickValue, &bounding); + } else { + p.drawText(QRect(tickStart + 2 * tickLen + 2, yCoord - pref.Graphs.fontSizeAxis/2 - 2, yAxisSpace, pref.Graphs.fontSizeAxis*1.5), Qt::AlignLeft, tickValue, &bounding); + } + lastTickLabelEnd = bounding.y(); } // tick lines @@ -509,8 +519,8 @@ void TraceXYPlot::draw(QPainter &p) if (pref.Graphs.Color.Ticks.Background.enabled) { if (j%2) { - int yCoordTop = yAxis[i].transform(yAxis[i].getTicks()[j], plotAreaTop, w.height() - xAxisSpace); - int yCoordBot = yAxis[i].transform(yAxis[i].getTicks()[j-1], plotAreaTop, w.height() - xAxisSpace); + int yCoordTop = yCoord; + int yCoordBot = yAxis[i].transform(yAxis[i].getTicks()[j-1], w.height() - xAxisSpace, plotAreaTop); if(yCoordTop > yCoordBot) { auto buf = yCoordBot; yCoordBot = yCoordTop; @@ -925,26 +935,51 @@ void TraceXYPlot::updateAxisTicks() } if(max >= min) { auto range = max - min; - if(range == 0.0) { - // this could happen if all values in a trace are identical (e.g. imported ideal touchstone files) - if(max == 0.0) { - // simply use +/-1 range - max = 1.0; - min = -1.0; - } else { - // +/-5% around value - max += abs(max * 0.05); - min -= abs(max * 0.05); + if(yAxis[i].getLog()){ + // log axis + + double maxLog10 = log10(abs(max)); + // prevent zero-crossing + if(min <= 0.0 && max > 0) { + min = pow(10, maxLog10 - 3); // just show 3 decades by default + } else if(min >= 0.0 && max < 0) { + // same thing if negative + min = -pow(10, maxLog10 - 3); } + // add 5% visible range + double ratio = log10(max/min); + max *= pow(10, ratio * 0.05); + min /= pow(10, ratio * 0.05); } else { - // add 5% of range at both ends - min -= range * 0.05; - max += range * 0.05; + // linear axis + if(range == 0.0) { + // this could happen if all values in a trace are identical (e.g. imported ideal touchstone files) + if(max == 0.0) { + // simply use +/-1 range + max = 1.0; + min = -1.0; + } else { + // +/-5% around value + max += abs(max * 0.05); + min -= abs(max * 0.05); + } + } else { + // add 5% of range at both ends + min -= range * 0.05; + max += range * 0.05; + } } } else { // max/min still at default values, no valid samples are available for this axis, use default range - max = 1.0; - min = -1.0; + if(!yAxis[i].getLog()) { + // linear axis + max = 1.0; + min = -1.0; + } else { + // log axis + max = 100.0; + min = 0.1; + } } yAxis[i].set(yAxis[i].getType(), yAxis[i].getLog(), true, min, max, 0); } diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.cpp index 8438509..512b136 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.cpp @@ -70,7 +70,10 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) : connect(ui->Y1auto, &QCheckBox::toggled, [this](bool checked) { ui->Y1min->setEnabled(!checked); ui->Y1max->setEnabled(!checked); - ui->Y1divs->setEnabled(!checked); + ui->Y1divs->setEnabled(!checked && !ui->Y1log->isChecked()); + }); + connect(ui->Y1log, &QCheckBox::toggled, [this](bool checked) { + ui->Y1divs->setEnabled(!checked && !ui->Y1auto->isChecked()); }); connect(ui->Y2type, qOverload(&QComboBox::currentIndexChanged), [=](int index) { @@ -95,7 +98,10 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) : connect(ui->Y2auto, &QCheckBox::toggled, [this](bool checked) { ui->Y2min->setEnabled(!checked); ui->Y2max->setEnabled(!checked); - ui->Y2divs->setEnabled(!checked); + ui->Y2divs->setEnabled(!checked && !ui->Y1log->isChecked()); + }); + connect(ui->Y2log, &QCheckBox::toggled, [this](bool checked) { + ui->Y2divs->setEnabled(!checked && !ui->Y2auto->isChecked()); }); connect(ui->Xauto, &QCheckBox::toggled, [this](bool checked) { From 3f6dad5b9281785a244267dd1249bb6d6be8f75f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Tue, 16 Apr 2024 21:31:30 +0200 Subject: [PATCH 3/3] disable impedance for transmission traces, reword graph adjustment dialog --- .../PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp index b3f014c..0974d46 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp @@ -1070,6 +1070,7 @@ bool TraceXYPlot::supported(Trace *t, YAxis::Type type) case YAxis::Type::Capacitance: case YAxis::Type::Inductance: case YAxis::Type::QualityFactor: + case YAxis::Type::AbsImpedance: if(!t->isReflection()) { return false; } @@ -1206,9 +1207,9 @@ void TraceXYPlot::traceDropped(Trace *t, QPoint position) { Q_UNUSED(position) if(!supported(t)) { - // needs to switch to a different domain for the graph - if(!InformationBox::AskQuestion("X Axis Domain Change", "You dropped a trace that is not supported with the currently selected X axis domain." - " Do you want to remove all traces and change the graph to the correct domain?", true, "DomainChangeRequest")) { + // needs to switch to a different setting for the graph + if(!InformationBox::AskQuestion("Graph Configuration Change", "You dropped a trace that is not supported with the currently configured axes." + " Do you want to remove all traces and change the graph to the correct configuration?", true, "DomainChangeRequest")) { // user declined to change domain, to not add trace return; }