Improve graph divisions and markers

- Added group delay option to markers
- Additional configuration for calculation of group delay in the preferences
- Specify divisions as amount of divisions instead of spacing
- Add option for auto divisions on graph with manual min/max limits
This commit is contained in:
Jan Käberich 2024-06-02 19:37:14 +02:00
parent fe08937bb7
commit b2d07d532d
24 changed files with 462 additions and 243 deletions

View file

@ -1249,8 +1249,8 @@ void SpectrumAnalyzer::createDefaultTracesAndGraphs(int ports)
{
tiles->clear();
auto traceXY = new TraceXYPlot(traceModel);
traceXY->setYAxis(0, YAxis::Type::Magnitude, false, false, -120,0,10);
traceXY->setYAxis(1, YAxis::Type::Disabled, false, true, 0,0,1);
traceXY->setYAxis(0, YAxis::Type::Magnitude, false, false, -120,0,12,false);
traceXY->setYAxis(1, YAxis::Type::Disabled, false, true, 0, 0, 10, false);
traceXY->updateSpan(settings.freqStart, settings.freqStop);
tiles->setPlot(traceXY);

View file

@ -127,6 +127,7 @@ QString Marker::formatToString(Marker::Format f)
case Format::Capacitance: return "Capacitance";
case Format::Inductance: return "Inductance";
case Format::QualityFactor: return "Quality Factor";
case Format::GroupDelay: return "Group Delay";
case Format::TOI: return "Third order intercept";
case Format::AvgTone: return "Average Tone Level";
case Format::AvgModulationProduct: return "Average Modulation Product Level";
@ -234,6 +235,7 @@ std::vector<Marker::Format> Marker::applicableFormats()
ret.push_back(Format::dB);
ret.push_back(Format::dBAngle);
ret.push_back(Format::RealImag);
ret.push_back(Format::GroupDelay);
}
if(parentTrace) {
if(parentTrace->isReflection()) {
@ -346,6 +348,9 @@ std::vector<Marker::Format> Marker::defaultActiveFormats()
if(pref.Marker.defaultBehavior.showQualityFactor) {
ret.push_back(Format::QualityFactor);
}
if(pref.Marker.defaultBehavior.showGroupDelay) {
ret.push_back(Format::GroupDelay);
}
if(pref.Marker.defaultBehavior.showNoise) {
ret.push_back(Format::Noise);
}
@ -477,6 +482,7 @@ QString Marker::readableData(Format f)
case Format::Capacitance: return "Δ:"+Unit::ToString(Util::SparamToCapacitance(data, position, trace()->getReferenceImpedance()) - Util::SparamToCapacitance(delta->data, delta->position, trace()->getReferenceImpedance()), "F", "pnum ", 4);
case Format::Inductance: return "Δ:"+Unit::ToString(Util::SparamToInductance(data, position, trace()->getReferenceImpedance()) - Util::SparamToInductance(delta->data, delta->position, trace()->getReferenceImpedance()), "H", "pnum ", 4);
case Format::QualityFactor: return "ΔQ:" + Unit::ToString(Util::SparamToQualityFactor(data) - Util::SparamToQualityFactor(delta->data), "", " ", 3);
case Format::GroupDelay: return "Δτg:"+Unit::ToString(trace()->getGroupDelay(position) - delta->trace()->getGroupDelay(delta->position), "s", "pnum ", 4);
case Format::Noise: return "Δ:"+Unit::ToString(parentTrace->getNoise(position) - delta->parentTrace->getNoise(delta->position), "dbm/Hz", " ", 3);
default: return "Invalid";
}
@ -501,6 +507,7 @@ QString Marker::readableData(Format f)
case Format::Capacitance: return Unit::ToString(Util::SparamToCapacitance(data, position, trace()->getReferenceImpedance()), "F", "pnum ", 4);
case Format::Inductance: return Unit::ToString(Util::SparamToInductance(data, position, trace()->getReferenceImpedance()), "H", "pnum ", 4);
case Format::QualityFactor: return "Q:" + Unit::ToString(Util::SparamToQualityFactor(data), "", " ", 3);
case Format::GroupDelay: return "τg:"+Unit::ToString(trace()->getGroupDelay(position), "s", "pnum ", 4);
case Format::Noise: return Unit::ToString(parentTrace->getNoise(position), "dbm/Hz", " ", 3);
case Format::TOI: {
auto avgFundamental = (helperMarkers[0]->toDecibel() + helperMarkers[1]->toDecibel()) / 2;

View file

@ -34,6 +34,7 @@ public:
Capacitance,
Inductance,
QualityFactor,
GroupDelay,
// Noise marker parameters
Noise,
PhaseNoise,

View file

@ -32,7 +32,7 @@
<property name="title">
<string>X Axis</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
@ -75,7 +75,18 @@
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="Xdivs"/>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSpinBox" name="Xdivs"/>
</item>
<item>
<widget class="QCheckBox" name="XautoDivs">
<property name="text">
<string>Auto</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
@ -85,7 +96,7 @@
<property name="title">
<string>Y Axis</string>
</property>
<layout class="QFormLayout" name="formLayout_5">
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
@ -128,7 +139,18 @@
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="Ydivs"/>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSpinBox" name="Ydivs"/>
</item>
<item>
<widget class="QCheckBox" name="YautoDivs">
<property name="text">
<string>Auto</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>

View file

@ -45,8 +45,8 @@ EyeDiagramPlot::EyeDiagramPlot(TraceModel &model, QWidget *parent)
calcData = &data[0];
displayData = &data[1];
xAxis.set(XAxis::Type::Time, false, true, 0, 0.000001, 1);
yAxis.set(YAxis::Type::Real, false, true, -1, 1, 1);
xAxis.set(XAxis::Type::Time, false, true, 0, 0.000001, 10, true);
yAxis.set(YAxis::Type::Real, false, true, -1, 1, 10, true);
initializeTraceInfo();
destructing = false;
@ -101,10 +101,10 @@ void EyeDiagramPlot::enableTrace(Trace *t, bool enabled)
void EyeDiagramPlot::replot()
{
if(xAxis.getAutorange()) {
xAxis.set(xAxis.getType(), false, true, 0, calculatedTime(), 8);
xAxis.set(xAxis.getType(), false, true, 0, calculatedTime(), 10, false);
}
if(yAxis.getAutorange()) {
yAxis.set(yAxis.getType(), false, true, minDisplayVoltage(), maxDisplayVoltage(), 8);
yAxis.set(yAxis.getType(), false, true, minDisplayVoltage(), maxDisplayVoltage(), 10, false);
}
TracePlot::replot();
}
@ -115,13 +115,13 @@ void EyeDiagramPlot::move(const QPoint &vect)
// can only move axis in linear mode
// calculate amount of movement
double distance = xAxis.inverseTransform(vect.x(), 0, plotAreaWidth) - xAxis.getRangeMin();
xAxis.set(xAxis.getType(), false, false, xAxis.getRangeMin() - distance, xAxis.getRangeMax() - distance, xAxis.getRangeDiv());
xAxis.set(xAxis.getType(), false, false, xAxis.getRangeMin() - distance, xAxis.getRangeMax() - distance, xAxis.getDivs(), xAxis.getAutoDivs());
}
if(!yAxis.getLog()) {
// can only move axis in linear mode
// calculate amount of movement
double distance = yAxis.inverseTransform(vect.y(), 0, plotAreaTop - plotAreaBottom) - yAxis.getRangeMin();
yAxis.set(yAxis.getType(), false, false, yAxis.getRangeMin() - distance, yAxis.getRangeMax() - distance, yAxis.getRangeDiv());
yAxis.set(yAxis.getType(), false, false, yAxis.getRangeMin() - distance, yAxis.getRangeMax() - distance, yAxis.getDivs(), yAxis.getAutoDivs());
}
replot();
}
@ -134,7 +134,7 @@ void EyeDiagramPlot::zoom(const QPoint &center, double factor, bool horizontally
double cp = xAxis.inverseTransform(center.x(), plotAreaLeft, plotAreaLeft + plotAreaWidth);
double min = ((xAxis.getRangeMin() - cp) * factor) + cp;
double max = ((xAxis.getRangeMax() - cp) * factor) + cp;
xAxis.set(xAxis.getType(), false, false, min, max, xAxis.getRangeDiv() * factor);
xAxis.set(xAxis.getType(), false, false, min, max, xAxis.getDivs(), xAxis.getAutoDivs());
}
if(vertically) {
// can only move axis in linear mode
@ -142,7 +142,7 @@ void EyeDiagramPlot::zoom(const QPoint &center, double factor, bool horizontally
double cp = yAxis.inverseTransform(center.y(), plotAreaBottom, plotAreaTop);
double min = ((yAxis.getRangeMin() - cp) * factor) + cp;
double max = ((yAxis.getRangeMax() - cp) * factor) + cp;
yAxis.set(yAxis.getType(), false, false, min, max, yAxis.getRangeDiv() * factor);
yAxis.set(yAxis.getType(), false, false, min, max, yAxis.getDivs(), yAxis.getAutoDivs());
}
replot();
}
@ -150,10 +150,10 @@ void EyeDiagramPlot::zoom(const QPoint &center, double factor, bool horizontally
void EyeDiagramPlot::setAuto(bool horizontally, bool vertically)
{
if(horizontally) {
xAxis.set(xAxis.getType(), xAxis.getLog(), true, xAxis.getRangeMin(), xAxis.getRangeMax(), xAxis.getRangeDiv());
xAxis.set(xAxis.getType(), xAxis.getLog(), true, xAxis.getRangeMin(), xAxis.getRangeMax(), xAxis.getDivs(), xAxis.getAutoDivs());
}
if(vertically) {
yAxis.set(yAxis.getType(), yAxis.getLog(), true, yAxis.getRangeMin(), yAxis.getRangeMax(), yAxis.getRangeDiv());
yAxis.set(yAxis.getType(), yAxis.getLog(), true, yAxis.getRangeMin(), yAxis.getRangeMax(), yAxis.getDivs(), yAxis.getAutoDivs());
}
replot();
}
@ -164,15 +164,25 @@ void EyeDiagramPlot::fromJSON(nlohmann::json j)
bool xAuto = jX.value("autorange", xAxis.getAutorange());
double xMin = jX.value("min", xAxis.getRangeMin());
double xMax = jX.value("max", xAxis.getRangeMax());
double xDivs = jX.value("div", xAxis.getRangeDiv());
xAxis.set(xAxis.getType(), false, xAuto, xMin, xMax, xDivs);
double xDivs = jX.value("divs", xAxis.getDivs());
// older formats specified the spacing instead of the number of divisions
if(jX.contains("div")) {
xDivs = (xMax - xMin) / jX.value("div", (xMax - xMin) / xDivs);
}
auto xautodivs = jX.value("autoDivs", false);
xAxis.set(xAxis.getType(), false, xAuto, xMin, xMax, xDivs, xautodivs);
auto jY = j["YAxis"];
bool yAuto = jY.value("autorange", yAxis.getAutorange());
double yMin = jY.value("min", yAxis.getRangeMin());
double yMax = jY.value("max", yAxis.getRangeMax());
double yDivs = jY.value("div", yAxis.getRangeDiv());
yAxis.set(yAxis.getType(), false, yAuto, yMin, yMax, yDivs);
double yDivs = jY.value("divs", yAxis.getDivs());
// older formats specified the spacing instead of the number of divisions
if(jY.contains("div")) {
yDivs = (yMax - yMin) / jY.value("div", (yMax - yMin) / yDivs);
}
auto yautodivs = jY.value("autoDivs", false);
yAxis.set(yAxis.getType(), false, yAuto, yMin, yMax, yDivs, yautodivs);
datarate = j.value("datarate", datarate);
risetime = j.value("risetime", risetime);
@ -211,13 +221,15 @@ nlohmann::json EyeDiagramPlot::toJSON()
jX["autorange"] = yAxis.getAutorange();
jX["min"] = xAxis.getRangeMin();
jX["max"] = xAxis.getRangeMax();
jX["div"] = xAxis.getRangeDiv();
jX["divs"] = xAxis.getDivs();
jX["autoDivs"] = xAxis.getAutoDivs();
j["XAxis"] = jX;
nlohmann::json jY;
jY["autorange"] = yAxis.getAutorange();
jY["min"] = yAxis.getRangeMin();
jY["max"] = yAxis.getRangeMax();
jY["div"] = yAxis.getRangeDiv();
jY["divs"] = yAxis.getDivs();
jY["autoDivs"] = yAxis.getAutoDivs();
j["YAxis"] = jY;
nlohmann::json jtraces;
for(auto t : traces) {
@ -286,10 +298,6 @@ void EyeDiagramPlot::axisSetupDialog()
ui->Xmax->setPrefixes("pnum ");
ui->Xmax->setPrecision(5);
ui->Xdivs->setUnit("s");
ui->Xdivs->setPrefixes("pnum ");
ui->Xdivs->setPrecision(3);
ui->Ymin->setUnit("V");
ui->Ymin->setPrefixes("um ");
ui->Ymin->setPrecision(4);
@ -298,10 +306,6 @@ void EyeDiagramPlot::axisSetupDialog()
ui->Ymax->setPrefixes("um ");
ui->Ymax->setPrecision(4);
ui->Ydivs->setUnit("V");
ui->Ydivs->setPrefixes("um ");
ui->Ydivs->setPrecision(3);
// set initial values
ui->datarate->setValue(datarate);
ui->risetime->setValue(risetime);
@ -322,22 +326,30 @@ void EyeDiagramPlot::axisSetupDialog()
connect(ui->Xauto, &QCheckBox::toggled, [=](bool checked) {
ui->Xmin->setEnabled(!checked);
ui->Xmax->setEnabled(!checked);
ui->Xdivs->setEnabled(!checked && !ui->XautoDivs->isChecked());
ui->XautoDivs->setEnabled(!checked);
});
connect(ui->XautoDivs, &QCheckBox::toggled, [=](bool checked) {
ui->Xdivs->setEnabled(!checked);
});
ui->Xauto->setChecked(xAxis.getAutorange());
ui->Xmin->setValue(xAxis.getRangeMin());
ui->Xmax->setValue(xAxis.getRangeMax());
ui->Xdivs->setValue(xAxis.getRangeDiv());
ui->Xdivs->setValue(xAxis.getDivs());
connect(ui->Yauto, &QCheckBox::toggled, [=](bool checked) {
ui->Ymin->setEnabled(!checked);
ui->Ymax->setEnabled(!checked);
ui->Ydivs->setEnabled(!checked && !ui->YautoDivs->isChecked());
ui->YautoDivs->setEnabled(!checked);
});
connect(ui->YautoDivs, &QCheckBox::toggled, [=](bool checked) {
ui->Ydivs->setEnabled(!checked);
});
ui->Yauto->setChecked(yAxis.getAutorange());
ui->Ymin->setValue(yAxis.getRangeMin());
ui->Ymax->setValue(yAxis.getRangeMax());
ui->Ydivs->setValue(yAxis.getRangeDiv());
ui->Ydivs->setValue(yAxis.getDivs());
auto updateValues = [=](){
std::lock_guard<std::mutex> guard(calcMutex);
@ -357,8 +369,8 @@ void EyeDiagramPlot::axisSetupDialog()
xSamples = ui->pointsPerCycle->value();
traceBlurring = ui->traceBlurring->value();
xAxis.set(xAxis.getType(), false, ui->Xauto->isChecked(), ui->Xmin->value(), ui->Xmax->value(), ui->Xdivs->value());
yAxis.set(yAxis.getType(), false, ui->Yauto->isChecked(), ui->Ymin->value(), ui->Ymax->value(), ui->Ydivs->value());
xAxis.set(xAxis.getType(), false, ui->Xauto->isChecked(), ui->Xmin->value(), ui->Xmax->value(), ui->Xdivs->value(), ui->XautoDivs->isChecked());
yAxis.set(yAxis.getType(), false, ui->Yauto->isChecked(), ui->Ymin->value(), ui->Ymax->value(), ui->Ydivs->value(), ui->YautoDivs->isChecked());
};
connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, [=](){

View file

@ -1572,6 +1572,48 @@ double Trace::getNoise(double frequency)
return dbm;
}
double Trace::getGroupDelay(double frequency)
{
if(!isVNAParameter(liveParam) || lastMath->getDataType() != DataType::Frequency) {
// data not suitable for group delay calculation
return std::numeric_limits<double>::quiet_NaN();
}
// get index that matches frequency best
unsigned int sample = index(frequency);
auto &p = Preferences::getInstance();
const unsigned int requiredSamples = p.Acquisition.groupDelaySamples;
if(size() < requiredSamples) {
// unable to calculate
return std::numeric_limits<double>::quiet_NaN();
}
// needs at least some samples before/after current sample for calculating the derivative.
// For samples too far at either end of the trace, return group delay of "inner" trace sample instead
if(sample < requiredSamples / 2) {
sample = requiredSamples / 2;
} else if(sample >= size() - requiredSamples / 2) {
sample = size() - requiredSamples / 2 - 1;
}
// got enough samples at either end to calculate derivative.
// acquire phases of the required samples
std::vector<double> phases;
phases.reserve(requiredSamples);
for(unsigned int index = sample - requiredSamples / 2;index <= sample + requiredSamples / 2;index++) {
phases.push_back(arg(this->sample(index).y));
}
// make sure there are no phase jumps
Util::unwrapPhase(phases);
// calculate linearRegression to get derivative
double B_0, B_1;
Util::linearRegression(phases, B_0, B_1);
// B_1 now contains the derived phase vs. the sample. Scale by frequency to get group delay
double freq_step = this->sample(sample).x - this->sample(sample - 1).x;
return -B_1 / (2.0*M_PI * freq_step);
}
int Trace::index(double x)
{
auto lower = lower_bound(lastMath->rData().begin(), lastMath->rData().end(), x, [](const Data &lhs, const double x) -> bool {

View file

@ -104,6 +104,7 @@ public:
unsigned int getFileParameter() const;
/* Returns the noise in dbm/Hz for spectrum analyzer measurements. May return NaN if calculation not possible */
double getNoise(double frequency);
double getGroupDelay(double frequency);
int index(double x);
std::set<Marker *> getMarkers() const;
void setCalibration();

View file

@ -1,23 +1,24 @@
#include "traceaxis.h"
#include "Util/util.h"
#include "preferences.h"
#include <cmath>
using namespace std;
static void createEvenlySpacedTicks(vector<double>& ticks, double start, double stop, double step) {
static void createEvenlySpacedTicks(vector<double>& ticks, double start, double stop, unsigned int divisions) {
ticks.clear();
if(start > stop) {
swap(start, stop);
}
step = abs(step);
auto step = (stop - start) / divisions;
constexpr unsigned int maxTicks = 100;
for(double tick = start; tick - stop < numeric_limits<double>::epsilon() && ticks.size() <= maxTicks;tick+= step) {
ticks.push_back(tick);
}
}
static double createAutomaticTicks(vector<double>& ticks, double start, double stop, int minDivisions) {
static unsigned int createAutomaticTicks(vector<double>& ticks, double start, double stop, int minDivisions) {
Q_ASSERT(stop > start);
ticks.clear();
double max_div_step = (stop - start) / minDivisions;
@ -37,7 +38,7 @@ static double createAutomaticTicks(vector<double>& ticks, double start, double s
for(double tick = start_div;tick <= stop;tick += div_step) {
ticks.push_back(tick);
}
return div_step;
return (stop - start) / div_step;
}
static void createLogarithmicTicks(vector<double>& ticks, double start, double stop, int minDivisions) {
@ -132,37 +133,11 @@ double YAxis::sampleToCoordinate(Trace::Data data, Trace *t, unsigned int sample
return Util::SparamToInductance(data.y, data.x);
case YAxis::Type::QualityFactor:
return Util::SparamToQualityFactor(data.y);
case YAxis::Type::GroupDelay: {
constexpr int requiredSamples = 5;
if(!t || t->size() < requiredSamples) {
// unable to calculate
case YAxis::Type::GroupDelay:
if(!t) {
return 0.0;
}
// needs at least some samples before/after current sample for calculating the derivative.
// For samples too far at either end of the trace, return group delay of "inner" trace sample instead
if(sample < requiredSamples / 2) {
return sampleToCoordinate(data, t, requiredSamples / 2);
} else if(sample >= t->size() - requiredSamples / 2) {
return sampleToCoordinate(data, t, t->size() - requiredSamples / 2 - 1);
} else {
// got enough samples at either end to calculate derivative.
// acquire phases of the required samples
std::vector<double> phases;
phases.reserve(requiredSamples);
for(unsigned int index = sample - requiredSamples / 2;index <= sample + requiredSamples / 2;index++) {
phases.push_back(arg(t->sample(index).y));
}
// make sure there are no phase jumps
Util::unwrapPhase(phases);
// calculate linearRegression to get derivative
double B_0, B_1;
Util::linearRegression(phases, B_0, B_1);
// B_1 now contains the derived phase vs. the sample. Scale by frequency to get group delay
double freq_step = t->sample(sample).x - t->sample(sample - 1).x;
return -B_1 / (2.0*M_PI * freq_step);
}
}
return t->getGroupDelay(data.x);
case YAxis::Type::ImpulseReal:
return real(data.y);
case YAxis::Type::ImpulseMag:
@ -190,14 +165,15 @@ double YAxis::sampleToCoordinate(Trace::Data data, Trace *t, unsigned int sample
return 0.0;
}
void YAxis::set(Type type, bool log, bool autorange, double min, double max, double div)
void YAxis::set(Type type, bool log, bool autorange, double min, double max, unsigned int divs, bool autoDivs)
{
this->type = type;
this->log = log;
this->autorange = autorange;
this->rangeMin = min;
this->rangeMax = max;
this->rangeDiv = div;
this->divs = divs;
this->autoDivs = autoDivs;
if(type != Type::Disabled) {
updateTicks();
}
@ -440,9 +416,13 @@ void Axis::updateTicks()
rangeMin -= 1.0;
rangeMax += 1.0;
}
rangeDiv = createAutomaticTicks(ticks, rangeMin, rangeMax, 8);
divs = createAutomaticTicks(ticks, rangeMin, rangeMax, 8);
} else {
createEvenlySpacedTicks(ticks, rangeMin, rangeMax, rangeDiv);
if(autoDivs) {
divs = createAutomaticTicks(ticks, rangeMin, rangeMax, 8);
} else {
createEvenlySpacedTicks(ticks, rangeMin, rangeMax, divs);
}
}
}
@ -451,9 +431,14 @@ const std::vector<double> &Axis::getTicks() const
return ticks;
}
double Axis::getRangeDiv() const
unsigned int Axis::getDivs() const
{
return rangeDiv;
return divs;
}
bool Axis::getAutoDivs() const
{
return autoDivs;
}
double Axis::getRangeMax() const
@ -495,7 +480,7 @@ double XAxis::sampleToCoordinate(Trace::Data data, Trace *t, unsigned int sample
}
}
void XAxis::set(Type type, bool log, bool autorange, double min, double max, double div)
void XAxis::set(Type type, bool log, bool autorange, double min, double max, unsigned int divs, bool autoDivs)
{
if(max <= min) {
auto info = DeviceDriver::getInfo(DeviceDriver::getActiveDriver());
@ -529,7 +514,8 @@ void XAxis::set(Type type, bool log, bool autorange, double min, double max, dou
this->autorange = autorange;
this->rangeMin = min;
this->rangeMax = max;
this->rangeDiv = div;
this->divs = divs;
this->autoDivs = autoDivs;
updateTicks();
}
@ -589,7 +575,8 @@ Axis::Axis()
autorange = true;
rangeMin = -1.0;
rangeMax = 1.0;
rangeDiv = 1.0;
divs = 10;
autoDivs = false;
ticks.clear();
}

View file

@ -17,7 +17,8 @@ public:
bool getAutorange() const;
double getRangeMin() const;
double getRangeMax() const;
double getRangeDiv() const;
unsigned int getDivs() const;
bool getAutoDivs() const;
const std::vector<double> &getTicks() const;
protected:
@ -26,7 +27,8 @@ protected:
bool autorange;
double rangeMin;
double rangeMax;
double rangeDiv;
unsigned int divs;
bool autoDivs;
std::vector<double> ticks;
};
@ -42,7 +44,7 @@ public:
};
XAxis();
double sampleToCoordinate(Trace::Data data, Trace *t = nullptr, unsigned int sample = 0) override;
void set(Type type, bool log, bool autorange, double min, double max, double div);
void set(Type type, bool log, bool autorange, double min, double max, unsigned int divs, bool autoDivs);
static QString TypeToName(Type type);
static Type TypeFromName(QString name);
static QString Unit(Type type);
@ -88,7 +90,7 @@ public:
YAxis();
double sampleToCoordinate(Trace::Data data, Trace *t = nullptr, unsigned int sample = 0) override;
void set(Type type, bool log, bool autorange, double min, double max, double div);
void set(Type type, bool log, bool autorange, double min, double max, unsigned int divs, bool autoDivs);
static QString TypeToName(Type type);
static Type TypeFromName(QString name);
static QString Unit(Type type, TraceModel::DataSource source = TraceModel::DataSource::VNA);

View file

@ -94,7 +94,7 @@ void TraceCSVExport::on_buttonBox_accepted()
for(auto trace : traces) {
for(auto ytype : getSelectedYAxisTypes()) {
auto axis = YAxis();
axis.set(ytype, false, false, 0, 1, 1);
axis.set(ytype, false, false, 0, 1, 10, false);
auto samples = trace->numSamples();
vector<double> values;
for(unsigned int i=0;i<samples;i++) {

View file

@ -26,8 +26,8 @@ TraceWaterfall::TraceWaterfall(TraceModel &model, QWidget *parent)
plotAreaWidth = 0;
plotAreaBottom = 0;
xAxis.set(XAxis::Type::Frequency, false, true, 0, 6000000000, 500000000);
yAxis.set(YAxis::Type::Magnitude, false, true, -1, 1, 1);
xAxis.set(XAxis::Type::Frequency, false, true, 0, 6000000000, 10, false);
yAxis.set(YAxis::Type::Magnitude, false, true, -1, 1, 10, false);
initializeTraceInfo();
}
@ -163,11 +163,11 @@ bool TraceWaterfall::configureForTrace(Trace *t)
switch(t->outputType()) {
case Trace::DataType::Frequency:
xAxis.set(XAxis::Type::Frequency, false, true, 0, 1, 0.1);
xAxis.set(XAxis::Type::Frequency, false, true, 0, 1, 10, false);
yDefault = YAxis::Type::Magnitude;
break;
case Trace::DataType::Power:
xAxis.set(XAxis::Type::Power, false, true, 0, 1, 0.1);
xAxis.set(XAxis::Type::Power, false, true, 0, 1, 10, false);
yDefault = YAxis::Type::Magnitude;
break;
case Trace::DataType::Time:
@ -177,7 +177,7 @@ bool TraceWaterfall::configureForTrace(Trace *t)
return false;
}
if(!yAxis.isSupported(xAxis.getType(), getModel().getSource())) {
yAxis.set(yDefault, false, true, 0, 1, 1.0);
yAxis.set(yDefault, false, true, 0, 1, 10, false);
}
traceRemovalPending = true;
return true;
@ -556,7 +556,7 @@ void TraceWaterfall::traceDataChanged(unsigned int begin, unsigned int end)
if(min_x != xAxis.getRangeMin() || max_x != xAxis.getRangeMax()) {
resetWaterfall();
// adjust axis
xAxis.set(xAxis.getType(), xAxis.getLog(), true, min_x, max_x, 0);
xAxis.set(xAxis.getType(), xAxis.getLog(), true, min_x, max_x, xAxis.getDivs(), xAxis.getAutoDivs());
}
}
bool YAxisUpdateRequired = false;
@ -593,7 +593,7 @@ void TraceWaterfall::traceDataChanged(unsigned int begin, unsigned int end)
}
if(yAxis.getAutorange() && !YAxisUpdateRequired && (min != yAxis.getRangeMin() || max != yAxis.getRangeMax())) {
// axis scaling needs update due to new trace data
yAxis.set(yAxis.getType(), yAxis.getLog(), true, min, max, 0);
yAxis.set(yAxis.getType(), yAxis.getLog(), true, min, max, yAxis.getDivs(), yAxis.getAutoDivs());
} else if(YAxisUpdateRequired) {
updateYAxis();
}
@ -619,7 +619,7 @@ void TraceWaterfall::updateYAxis()
}
}
if(max > min) {
yAxis.set(yAxis.getType(), yAxis.getLog(), true, min, max, 0);
yAxis.set(yAxis.getType(), yAxis.getLog(), true, min, max, yAxis.getDivs(), yAxis.getAutoDivs());
}
}
}

View file

@ -25,11 +25,11 @@ TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent)
xAxisMode = XAxisMode::UseSpan;
// Setup default axis
setYAxis(0, YAxis::Type::Magnitude, false, false, -120, 20, 10);
setYAxis(1, YAxis::Type::Phase, false, false, -180, 180, 30);
setYAxis(0, YAxis::Type::Magnitude, false, false, -120, 20, 14, true);
setYAxis(1, YAxis::Type::Phase, false, false, -180, 180, 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, 600000000);
setXAxis(XAxis::Type::Frequency, XAxisMode::UseSpan, false, 0, 6000000000, 10, true);
initializeTraceInfo();
}
TraceXYPlot::~TraceXYPlot()
@ -39,7 +39,7 @@ TraceXYPlot::~TraceXYPlot()
}
}
void TraceXYPlot::setYAxis(int axis, YAxis::Type type, bool log, bool autorange, double min, double max, double div)
void TraceXYPlot::setYAxis(int axis, YAxis::Type type, bool log, bool autorange, double min, double max, unsigned int divs, bool autoDivs)
{
if(yAxis[axis].getType() != type) {
// remove traces that are active but not supported with the new axis type
@ -55,19 +55,19 @@ void TraceXYPlot::setYAxis(int axis, YAxis::Type type, bool log, bool autorange,
}
} while(erased);
}
yAxis[axis].set(type, log, autorange, min, max, div);
yAxis[axis].set(type, log, autorange, min, max, divs, autoDivs);
traceRemovalPending = true;
updateContextMenu();
replot();
}
void TraceXYPlot::setXAxis(XAxis::Type type, XAxisMode mode, bool log, double min, double max, double div)
void TraceXYPlot::setXAxis(XAxis::Type type, XAxisMode mode, bool log, double min, double max, unsigned int divs, bool autoDivs)
{
bool autorange = false;
if(mode == XAxisMode::FitTraces || mode == XAxisMode::UseSpan) {
autorange = true;
}
xAxis.set(type, log, autorange, min, max, div);
xAxis.set(type, log, autorange, min, max, divs, autoDivs);
xAxisMode = mode;
traceRemovalPending = true;
updateContextMenu();
@ -101,7 +101,7 @@ void TraceXYPlot::move(const QPoint &vect)
// can only move axis in linear mode
// calculate amount of movement
double distance = xAxis.inverseTransform(vect.x(), 0, plotAreaWidth) - xAxis.getRangeMin();
xAxis.set(xAxis.getType(), false, false, xAxis.getRangeMin() - distance, xAxis.getRangeMax() - distance, xAxis.getRangeDiv());
xAxis.set(xAxis.getType(), false, false, xAxis.getRangeMin() - distance, xAxis.getRangeMax() - distance, xAxis.getDivs(), xAxis.getAutoDivs());
xAxisMode = XAxisMode::Manual;
}
for(int i=0;i<2;i++) {
@ -109,7 +109,7 @@ void TraceXYPlot::move(const QPoint &vect)
// can only move axis in linear mode
// calculate amount of movement
double distance = yAxis[i].inverseTransform(vect.y(), 0, plotAreaTop - plotAreaBottom) - yAxis[i].getRangeMin();
yAxis[i].set(yAxis[i].getType(), false, false, yAxis[i].getRangeMin() - distance, yAxis[i].getRangeMax() - distance, yAxis[i].getRangeDiv());
yAxis[i].set(yAxis[i].getType(), false, false, yAxis[i].getRangeMin() - distance, yAxis[i].getRangeMax() - distance, yAxis[i].getDivs(), yAxis[i].getAutoDivs());
}
}
replot();
@ -123,7 +123,7 @@ void TraceXYPlot::zoom(const QPoint &center, double factor, bool horizontally, b
double cp = xAxis.inverseTransform(center.x(), plotAreaLeft, plotAreaLeft + plotAreaWidth);
double min = ((xAxis.getRangeMin() - cp) * factor) + cp;
double max = ((xAxis.getRangeMax() - cp) * factor) + cp;
xAxis.set(xAxis.getType(), false, false, min, max, xAxis.getRangeDiv() * factor);
xAxis.set(xAxis.getType(), false, false, min, max, xAxis.getDivs(), xAxis.getAutoDivs());
xAxisMode = XAxisMode::Manual;
}
for(int i=0;i<2;i++) {
@ -133,7 +133,7 @@ void TraceXYPlot::zoom(const QPoint &center, double factor, bool horizontally, b
double cp = yAxis[i].inverseTransform(center.y(), plotAreaBottom, plotAreaTop);
double min = ((yAxis[i].getRangeMin() - cp) * factor) + cp;
double max = ((yAxis[i].getRangeMax() - cp) * factor) + cp;
yAxis[i].set(yAxis[i].getType(), false, false, min, max, yAxis[i].getRangeDiv() * factor);
yAxis[i].set(yAxis[i].getType(), false, false, min, max, yAxis[i].getDivs(), yAxis[i].getAutoDivs());
}
}
replot();
@ -143,11 +143,11 @@ void TraceXYPlot::setAuto(bool horizontally, bool vertically)
{
if(horizontally) {
xAxisMode = XAxisMode::FitTraces;
xAxis.set(xAxis.getType(), xAxis.getLog(), true, xAxis.getRangeMin(), xAxis.getRangeMax(), xAxis.getRangeDiv());
xAxis.set(xAxis.getType(), xAxis.getLog(), true, xAxis.getRangeMin(), xAxis.getRangeMax(), xAxis.getDivs(), xAxis.getAutoDivs());
}
for(int i=0;i<2;i++) {
if(vertically && yAxis[i].getType() != YAxis::Type::Disabled) {
yAxis[i].set(yAxis[i].getType(), yAxis[i].getLog(), true, yAxis[i].getRangeMin(), yAxis[i].getRangeMax(), yAxis[i].getRangeDiv());
yAxis[i].set(yAxis[i].getType(), yAxis[i].getLog(), true, yAxis[i].getRangeMin(), yAxis[i].getRangeMax(), yAxis[i].getDivs(), yAxis[i].getAutoDivs());
}
}
replot();
@ -162,7 +162,8 @@ nlohmann::json TraceXYPlot::toJSON()
jX["log"] = xAxis.getLog();
jX["min"] = xAxis.getRangeMin();
jX["max"] = xAxis.getRangeMax();
jX["div"] = xAxis.getRangeDiv();
jX["divs"] = xAxis.getDivs();
jX["autoDivs"] = xAxis.getAutoDivs();
j["XAxis"] = jX;
for(unsigned int i=0;i<2;i++) {
nlohmann::json jY;
@ -171,7 +172,8 @@ nlohmann::json TraceXYPlot::toJSON()
jY["autorange"] = yAxis[i].getAutorange();
jY["min"] = yAxis[i].getRangeMin();
jY["max"] = yAxis[i].getRangeMax();
jY["div"] = yAxis[i].getRangeDiv();
jY["divs"] = yAxis[i].getDivs();
jY["autoDivs"] = yAxis[i].getAutoDivs();
nlohmann::json jtraces;
for(auto t : tracesAxis[i]) {
jtraces.push_back(t->toHash());
@ -212,9 +214,14 @@ void TraceXYPlot::fromJSON(nlohmann::json j)
// auto xlog = jX.value("log", false);
auto xmin = jX.value("min", 0.0);
auto xmax = jX.value("max", 6000000000.0);
auto xdiv = jX.value("div", 600000000.0);
auto xdivs = jX.value("divs", 10);
// older formats specified the spacing instead of the number of divisions
if(jX.contains("div")) {
xdivs = (xmax - xmin) / jX.value("div", 600000000.0);
}
auto xautodivs = jX.value("autoDivs", false);
auto xlog = jX.value("log", false);
setXAxis(xtype, xmode, xlog, xmin, xmax, xdiv);
setXAxis(xtype, xmode, xlog, xmin, xmax, xdivs, xautodivs);
nlohmann::json jY[2] = {j["YPrimary"], j["YSecondary"]};
for(unsigned int i=0;i<2;i++) {
YAxis::Type ytype;
@ -227,8 +234,13 @@ void TraceXYPlot::fromJSON(nlohmann::json j)
auto ylog = jY[i].value("log", false);
auto ymin = jY[i].value("min", -120.0);
auto ymax = jY[i].value("max", 20.0);
auto ydiv = jY[i].value("div", 10.0);
setYAxis(i, ytype, ylog, yauto, ymin, ymax, ydiv);
auto ydivs = jY[i].value("divs", 10);
// older formats specified the spacing instead of the number of divisions
if(jY[i].contains("div")) {
ydivs = (ymax - ymin) / jY[i].value("div", 10);
}
auto yautodivs = jY[i].value("autoDivs", false);
setYAxis(i, ytype, ylog, yauto, ymin, ymax, ydivs, yautodivs);
for(unsigned int hash : jY[i]["traces"]) {
// attempt to find the traces with this hash
bool found = false;
@ -292,21 +304,21 @@ bool TraceXYPlot::configureForTrace(Trace *t)
switch(t->outputType()) {
case Trace::DataType::Frequency:
setXAxis(XAxis::Type::Frequency, XAxisMode::FitTraces, false, 0, 1, 0.1);
setXAxis(XAxis::Type::Frequency, XAxisMode::FitTraces, false, 0, 1, 10, false);
yLeftDefault = YAxis::Type::Magnitude;
yRightDefault = YAxis::Type::Phase;
break;
case Trace::DataType::Time:
setXAxis(XAxis::Type::Time, XAxisMode::FitTraces, false, 0, 1, 0.1);
setXAxis(XAxis::Type::Time, XAxisMode::FitTraces, false, 0, 1, 10, false);
yLeftDefault = YAxis::Type::ImpulseMag;
break;
case Trace::DataType::Power:
setXAxis(XAxis::Type::Power, XAxisMode::FitTraces, false, 0, 1, 0.1);
setXAxis(XAxis::Type::Power, XAxisMode::FitTraces, false, 0, 1, 10, false);
yLeftDefault = YAxis::Type::Magnitude;
yRightDefault = YAxis::Type::Phase;
break;
case Trace::DataType::TimeZeroSpan:
setXAxis(XAxis::Type::TimeZeroSpan, XAxisMode::FitTraces, false, 0, 1, 0.1);
setXAxis(XAxis::Type::TimeZeroSpan, XAxisMode::FitTraces, false, 0, 1, 10, false);
yLeftDefault = YAxis::Type::Magnitude;
yRightDefault = YAxis::Type::Phase;
break;
@ -315,10 +327,10 @@ bool TraceXYPlot::configureForTrace(Trace *t)
return false;
}
if(!yAxis[0].isSupported(xAxis.getType(), getModel().getSource())) {
setYAxis(0, yLeftDefault, false, true, 0, 1, 1.0);
setYAxis(0, yLeftDefault, false, true, 0, 1, 10, false);
}
if(!yAxis[1].isSupported(xAxis.getType(), getModel().getSource())) {
setYAxis(1, yRightDefault, false, true, 0, 1, 1.0);
setYAxis(1, yRightDefault, false, true, 0, 1, 10, false);
}
traceRemovalPending = true;
return true;
@ -899,7 +911,7 @@ void TraceXYPlot::updateAxisTicks()
}
}
}
xAxis.set(xAxis.getType(), xAxis.getLog(), true, min, max, 0);
xAxis.set(xAxis.getType(), xAxis.getLog(), true, min, max, 0, xAxis.getAutoDivs());
}
for(int i=0;i<2;i++) {
@ -981,7 +993,7 @@ void TraceXYPlot::updateAxisTicks()
min = 0.1;
}
}
yAxis[i].set(yAxis[i].getType(), yAxis[i].getLog(), true, min, max, 0);
yAxis[i].set(yAxis[i].getType(), yAxis[i].getLog(), true, min, max, 0, xAxis.getAutoDivs());
}
}
}

View file

@ -68,8 +68,8 @@ public:
Last,
};
void setYAxis(int axis, YAxis::Type type, bool log, bool autorange, double min, double max, double div);
void setXAxis(XAxis::Type type, XAxisMode mode, bool log, double min, double max, double div);
void setYAxis(int axis, YAxis::Type type, bool log, bool autorange, double min, double max, unsigned int divs, bool autoDivs);
void setXAxis(XAxis::Type type, XAxisMode mode, bool log, double min, double max, unsigned int divs, bool autoDivs);
void enableTrace(Trace *t, bool enabled) override;
void updateSpan(double min, double max) override;
void replot() override;

View file

@ -115,8 +115,8 @@ WaterfallAxisDialog::~WaterfallAxisDialog()
void WaterfallAxisDialog::on_buttonBox_accepted()
{
// set plot values to the ones selected in the dialog
plot->xAxis.set(plot->xAxis.getType(), ui->Xlog->isChecked(), true, plot->xAxis.getRangeMin(), plot->xAxis.getRangeMax(), 0);
plot->yAxis.set((YAxis::Type) ui->Wtype->currentIndex(), ui->Wlog->isChecked(), ui->Wauto->isChecked(), ui->Wmin->value(), ui->Wmax->value(), 2);
plot->xAxis.set(plot->xAxis.getType(), ui->Xlog->isChecked(), true, plot->xAxis.getRangeMin(), plot->xAxis.getRangeMax(), 10, false);
plot->yAxis.set((YAxis::Type) ui->Wtype->currentIndex(), ui->Wlog->isChecked(), ui->Wauto->isChecked(), ui->Wmin->value(), ui->Wmax->value(), 2, false);
if(ui->Wdir->currentIndex() == 0) {
plot->dir = TraceWaterfall::Direction::TopToBottom;
} else {

View file

@ -49,85 +49,114 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
}
// Setup GUI connections
connect(ui->Y1type, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index) {
ui->Y1log->setEnabled(index != 0);
ui->Y1linear->setEnabled(index != 0);
ui->Y1auto->setEnabled(index != 0);
bool autoRange = ui->Y1auto->isChecked();
ui->Y1min->setEnabled(index != 0 && !autoRange);
ui->Y1max->setEnabled(index != 0 && !autoRange);
ui->Y1divs->setEnabled(index != 0 && !autoRange);
auto type = (YAxis::Type) index;
QString unit = YAxis::Unit(type);
QString prefixes = YAxis::Prefixes(type);
ui->Y1min->setUnit(unit);
ui->Y1min->setPrefixes(prefixes);
ui->Y1max->setUnit(unit);
ui->Y1max->setPrefixes(prefixes);
ui->Y1divs->setUnit(unit);
ui->Y1divs->setPrefixes(prefixes);
auto updateYenableState = [](QComboBox *type, QRadioButton *linear, QRadioButton *log, QCheckBox *CBauto, SIUnitEdit *min, SIUnitEdit *max, QSpinBox *divs, QCheckBox *autoDivs) {
if(type->currentIndex() == 0) {
// axis disabled
log->setEnabled(false);
linear->setEnabled(false);
CBauto->setEnabled(false);
} else {
// axis enabled
log->setEnabled(true);
linear->setEnabled(true);
CBauto->setEnabled(true);
if(CBauto->isChecked()) {
// autorange active, other settings disabled
min->setEnabled(false);
max->setEnabled(false);
divs->setEnabled(false);
autoDivs->setEnabled(false);
} else {
min->setEnabled(true);
max->setEnabled(true);
if(log->isChecked()) {
divs->setEnabled(false);
autoDivs->setEnabled(false);
} else {
autoDivs->setEnabled(true);
divs->setEnabled(!autoDivs->isChecked());
}
}
}
auto t = (YAxis::Type) type->currentIndex();
QString unit = YAxis::Unit(t);
QString prefixes = YAxis::Prefixes(t);
min->setUnit(unit);
min->setPrefixes(prefixes);
max->setUnit(unit);
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](bool checked) {
ui->Y1min->setEnabled(!checked);
ui->Y1max->setEnabled(!checked);
ui->Y1divs->setEnabled(!checked && !ui->Y1log->isChecked());
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](bool checked) {
ui->Y1divs->setEnabled(!checked && !ui->Y1auto->isChecked());
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);
});
connect(ui->Y2type, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index) {
ui->Y2log->setEnabled(index != 0);
ui->Y2linear->setEnabled(index != 0);
ui->Y2auto->setEnabled(index != 0);
bool autoRange = ui->Y2auto->isChecked();
ui->Y2min->setEnabled(index != 0 && !autoRange);
ui->Y2max->setEnabled(index != 0 && !autoRange);
ui->Y2divs->setEnabled(index != 0 && !autoRange);
auto type = (YAxis::Type) index;
QString unit = YAxis::Unit(type);
QString prefixes = YAxis::Prefixes(type);
ui->Y2min->setUnit(unit);
ui->Y2min->setPrefixes(prefixes);
ui->Y2max->setUnit(unit);
ui->Y2max->setPrefixes(prefixes);
ui->Y2divs->setUnit(unit);
ui->Y2divs->setPrefixes(prefixes);
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);
});
connect(ui->Y2auto, &QCheckBox::toggled, [this](bool checked) {
ui->Y2min->setEnabled(!checked);
ui->Y2max->setEnabled(!checked);
ui->Y2divs->setEnabled(!checked && !ui->Y1log->isChecked());
});
connect(ui->Y2log, &QCheckBox::toggled, [this](bool checked) {
ui->Y2divs->setEnabled(!checked && !ui->Y2auto->isChecked());
});
auto updateXenableState = [](QRadioButton *linear, QRadioButton *log, QCheckBox *CBauto, SIUnitEdit *min, SIUnitEdit *max, QSpinBox *divs, QCheckBox *autoDivs) {
log->setEnabled(true);
linear->setEnabled(true);
CBauto->setEnabled(true);
if(CBauto->isChecked()) {
// autorange active, other settings disabled
min->setEnabled(false);
max->setEnabled(false);
divs->setEnabled(false);
autoDivs->setEnabled(false);
} else {
min->setEnabled(true);
max->setEnabled(true);
if(log->isChecked()) {
divs->setEnabled(false);
autoDivs->setEnabled(false);
} else {
autoDivs->setEnabled(true);
divs->setEnabled(!autoDivs->isChecked());
}
}
};
connect(ui->Xauto, &QCheckBox::toggled, [this](bool checked) {
ui->Xmin->setEnabled(!checked);
ui->Xmax->setEnabled(!checked);
ui->Xdivs->setEnabled(!checked && ui->Xlinear->isChecked());
ui->Xautomode->setEnabled(checked);
connect(ui->Xauto, &QCheckBox::toggled, [this, updateXenableState](bool checked) {
updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs);
});
connect(ui->XautoDivs, &QCheckBox::toggled, [this, updateXenableState](bool checked) {
updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs);
});
ui->XType->setCurrentIndex((int) plot->xAxis.getType());
ui->Xmin->setPrefixes("pnum kMG");
ui->Xmax->setPrefixes("pnum kMG");
ui->Xdivs->setPrefixes("pnum kMG");
ui->Y1min->setPrefixes("pnum kMG");
ui->Y1max->setPrefixes("pnum kMG");
ui->Y1divs->setPrefixes("pnum kMG");
ui->Y2min->setPrefixes("pnum kMG");
ui->Y2max->setPrefixes("pnum kMG");
ui->Y2divs->setPrefixes("pnum kMG");
XAxisTypeChanged((int) plot->xAxis.getType());
connect(ui->XType, qOverload<int>(&QComboBox::currentIndexChanged), this, &XYplotAxisDialog::XAxisTypeChanged);
connect(ui->Xlog, &QCheckBox::toggled, [=](bool checked){
ui->Xdivs->setEnabled(!checked && !ui->Xauto->isChecked());
updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs);
});
// Fill initial values
@ -140,7 +169,8 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
ui->Y1auto->setChecked(plot->yAxis[0].getAutorange());
ui->Y1min->setValueQuiet(plot->yAxis[0].getRangeMin());
ui->Y1max->setValueQuiet(plot->yAxis[0].getRangeMax());
ui->Y1divs->setValueQuiet(plot->yAxis[0].getRangeDiv());
ui->Y1Divs->setValue(plot->yAxis[0].getDivs());
ui->Y1autoDivs->setChecked(plot->yAxis[0].getAutoDivs());
ui->Y2type->setCurrentIndex((int) plot->yAxis[1].getType());
if(plot->yAxis[1].getLog()) {
@ -151,7 +181,8 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
ui->Y2auto->setChecked(plot->yAxis[1].getAutorange());
ui->Y2min->setValueQuiet(plot->yAxis[1].getRangeMin());
ui->Y2max->setValueQuiet(plot->yAxis[1].getRangeMax());
ui->Y2divs->setValueQuiet(plot->yAxis[1].getRangeDiv());
ui->Y2Divs->setValue(plot->yAxis[1].getDivs());
ui->Y2autoDivs->setChecked(plot->yAxis[1].getAutoDivs());
if(plot->xAxis.getLog()) {
ui->Xlog->setChecked(true);
@ -166,7 +197,8 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
}
ui->Xmin->setValueQuiet(plot->xAxis.getRangeMin());
ui->Xmax->setValueQuiet(plot->xAxis.getRangeMax());
ui->Xdivs->setValueQuiet(plot->xAxis.getRangeDiv());
ui->XDivs->setValue(plot->xAxis.getDivs());
ui->XautoDivs->setChecked(plot->xAxis.getAutoDivs());
// Constant line list handling
auto editLine = [&](XYPlotConstantLine *line) {
@ -277,8 +309,8 @@ XYplotAxisDialog::~XYplotAxisDialog()
void XYplotAxisDialog::on_buttonBox_accepted()
{
// set plot values to the ones selected in the dialog
plot->setYAxis(0, (YAxis::Type) ui->Y1type->currentIndex(), ui->Y1log->isChecked(), ui->Y1auto->isChecked(), ui->Y1min->value(), ui->Y1max->value(), ui->Y1divs->value());
plot->setYAxis(1, (YAxis::Type) ui->Y2type->currentIndex(), ui->Y2log->isChecked(), ui->Y2auto->isChecked(), ui->Y2min->value(), ui->Y2max->value(), ui->Y2divs->value());
plot->setYAxis(0, (YAxis::Type) ui->Y1type->currentIndex(), ui->Y1log->isChecked(), ui->Y1auto->isChecked(), ui->Y1min->value(), ui->Y1max->value(), ui->Y1Divs->value(), ui->Y1autoDivs->isChecked());
plot->setYAxis(1, (YAxis::Type) ui->Y2type->currentIndex(), ui->Y2log->isChecked(), ui->Y2auto->isChecked(), ui->Y2min->value(), ui->Y2max->value(), ui->Y2Divs->value(), ui->Y2autoDivs->isChecked());
TraceXYPlot::XAxisMode mode;
if(ui->Xauto->isChecked()) {
if(ui->Xautomode->currentIndex() == 0) {
@ -289,7 +321,7 @@ void XYplotAxisDialog::on_buttonBox_accepted()
} else {
mode = TraceXYPlot::XAxisMode::Manual;
}
plot->setXAxis((XAxis::Type) ui->XType->currentIndex(), mode, ui->Xlog->isChecked(), ui->Xmin->value(), ui->Xmax->value(), ui->Xdivs->value());
plot->setXAxis((XAxis::Type) ui->XType->currentIndex(), mode, ui->Xlog->isChecked(), ui->Xmin->value(), ui->Xmax->value(), ui->XDivs->value(), ui->XautoDivs->isChecked());
}
void XYplotAxisDialog::XAxisTypeChanged(int XAxisIndex)
@ -330,7 +362,6 @@ void XYplotAxisDialog::XAxisTypeChanged(int XAxisIndex)
QString unit = XAxis::Unit(type);
ui->Xmin->setUnit(unit);
ui->Xmax->setUnit(unit);
ui->Xdivs->setUnit(unit);
}
std::set<YAxis::Type> XYplotAxisDialog::supportedYAxis(XAxis::Type type)

View file

@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>814</width>
<height>458</height>
<width>810</width>
<height>461</height>
</rect>
</property>
<property name="windowTitle">
@ -21,9 +21,9 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="font">
@ -133,12 +133,27 @@
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Divisions:</string>
<string># Divisions:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="Y1divs"/>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QSpinBox" name="Y1Divs">
<property name="minimum">
<number>2</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="Y1autoDivs">
<property name="text">
<string>Auto</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
@ -262,12 +277,27 @@
<item row="3" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Divisions:</string>
<string># Divisions:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="Y2divs"/>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QSpinBox" name="Y2Divs">
<property name="minimum">
<number>2</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="Y2autoDivs">
<property name="text">
<string>Auto</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
@ -281,7 +311,7 @@
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_13">
<property name="sizePolicy">
@ -418,12 +448,27 @@
<item row="3" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Divisions:</string>
<string># Divisions:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="Xdivs"/>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QSpinBox" name="XDivs">
<property name="minimum">
<number>2</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="XautoDivs">
<property name="text">
<string>Auto</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
@ -592,8 +637,8 @@
</connection>
</connections>
<buttongroups>
<buttongroup name="Y1group"/>
<buttongroup name="Xgroup"/>
<buttongroup name="Y2group"/>
<buttongroup name="Y1group"/>
</buttongroups>
</ui>

View file

@ -262,6 +262,7 @@ void PreferencesDialog::setInitialGUIState()
ui->AcquisitionFullSpanCalibrated->setChecked(p->Acquisition.fullSpanCalibratedRange);
ui->AcquisitionLimitTDRCheckbox->setChecked(p->Acquisition.limitDFT);
ui->AcquisitionDFTLimitValue->setValue(p->Acquisition.maxDFTrate);
ui->AcquisitionGroupDelaySamples->setValue(p->Acquisition.groupDelaySamples);
ui->GraphsDefaultTransmission->setCurrentText(p->Graphs.defaultGraphs.transmission);
ui->GraphsDefaultReflection->setCurrentText(p->Graphs.defaultGraphs.reflection);
@ -299,6 +300,7 @@ void PreferencesDialog::setInitialGUIState()
ui->MarkerShowCapacitance->setChecked(p->Marker.defaultBehavior.showCapacitance);
ui->MarkerShowInductance->setChecked(p->Marker.defaultBehavior.showInductance);
ui->MarkerShowQualityFactor->setChecked(p->Marker.defaultBehavior.showQualityFactor);
ui->MarkerShowGroupDelay->setChecked(p->Marker.defaultBehavior.showGroupDelay);
ui->MarkerShowNoise->setChecked(p->Marker.defaultBehavior.showNoise);
ui->MarkerShowPhasenoise->setChecked(p->Marker.defaultBehavior.showPhasenoise);
ui->MarkerShowCenterBandwidth->setChecked(p->Marker.defaultBehavior.showCenterBandwidth);
@ -361,6 +363,7 @@ void PreferencesDialog::updateFromGUI()
p->Acquisition.fullSpanCalibratedRange = ui->AcquisitionFullSpanCalibrated->isChecked();
p->Acquisition.limitDFT = ui->AcquisitionLimitTDRCheckbox->isChecked();
p->Acquisition.maxDFTrate = ui->AcquisitionDFTLimitValue->value();
p->Acquisition.groupDelaySamples = ui->AcquisitionGroupDelaySamples->value();
p->Graphs.defaultGraphs.transmission = ui->GraphsDefaultTransmission->currentText();
p->Graphs.defaultGraphs.reflection = ui->GraphsDefaultReflection->currentText();
@ -397,6 +400,7 @@ void PreferencesDialog::updateFromGUI()
p->Marker.defaultBehavior.showCapacitance = ui->MarkerShowCapacitance->isChecked();
p->Marker.defaultBehavior.showInductance = ui->MarkerShowInductance->isChecked();
p->Marker.defaultBehavior.showQualityFactor = ui->MarkerShowQualityFactor->isChecked();
p->Marker.defaultBehavior.showGroupDelay = ui->MarkerShowGroupDelay->isChecked();
p->Marker.defaultBehavior.showNoise = ui->MarkerShowNoise->isChecked();
p->Marker.defaultBehavior.showPhasenoise = ui->MarkerShowPhasenoise->isChecked();
p->Marker.defaultBehavior.showCenterBandwidth = ui->MarkerShowCenterBandwidth->isChecked();

View file

@ -108,6 +108,7 @@ public:
// Math settings
bool limitDFT;
double maxDFTrate;
int groupDelaySamples;
} Acquisition;
struct {
bool showUnits;
@ -150,7 +151,7 @@ public:
struct {
struct {
bool showDataOnGraphs;
bool showdB, showdBm, showdBuV, showdBAngle, showRealImag, showImpedance, showVSWR, showResistance, showCapacitance, showInductance, showQualityFactor;
bool showdB, showdBm, showdBuV, showdBAngle, showRealImag, showImpedance, showVSWR, showResistance, showCapacitance, showInductance, showQualityFactor, showGroupDelay;
bool showNoise, showPhasenoise, showCenterBandwidth, showCutoff, showInsertionLoss, showTOI, showAvgTone, showAvgModulation, showP1dB, showFlatness, showMaxDeltaNeg, showMaxDeltaPos;
} defaultBehavior;
bool interpolatePoints;
@ -214,6 +215,7 @@ private:
{&Acquisition.fullSpanCalibratedRange, "Acquisition.fullSpanCalibratedRange", false},
{&Acquisition.limitDFT, "Acquisition.limitDFT", true},
{&Acquisition.maxDFTrate, "Acquisition.maxDFTrate", 1.0},
{&Acquisition.groupDelaySamples, "Acquisition.groupDelaySamples", 5},
{&Graphs.showUnits, "Graphs.showUnits", true},
{&Graphs.Color.background, "Graphs.Color.background", QColor(Qt::black)},
{&Graphs.Color.axis, "Graphs.Color.axis", QColor(Qt::white)},
@ -248,6 +250,7 @@ private:
{&Marker.defaultBehavior.showCapacitance, "Marker.defaultBehavior.showCapacitance", true},
{&Marker.defaultBehavior.showInductance, "Marker.defaultBehavior.showInductance", true},
{&Marker.defaultBehavior.showQualityFactor, "Marker.defaultBehavior.showQualityFactor", true},
{&Marker.defaultBehavior.showGroupDelay, "Marker.defaultBehavior.showGroupDelay", true},
{&Marker.defaultBehavior.showNoise, "Marker.defaultBehavior.showNoise", true},
{&Marker.defaultBehavior.showPhasenoise, "Marker.defaultBehavior.showPhasenoise", true},
{&Marker.defaultBehavior.showCenterBandwidth, "Marker.defaultBehavior.showCenterBandwidth", true},

View file

@ -93,7 +93,7 @@
</size>
</property>
<property name="currentIndex">
<number>1</number>
<number>3</number>
</property>
<widget class="QWidget" name="Startup">
<layout class="QHBoxLayout" name="horizontalLayout_4">
@ -107,8 +107,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>683</width>
<height>897</height>
<width>522</width>
<height>914</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
@ -829,46 +829,81 @@
<property name="title">
<string>Math operations</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<layout class="QVBoxLayout" name="verticalLayout_19">
<item>
<widget class="QCheckBox" name="AcquisitionLimitTDRCheckbox">
<property name="text">
<string>Limit TDR/DFT to </string>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_13">
<item>
<widget class="QCheckBox" name="AcquisitionLimitTDRCheckbox">
<property name="text">
<string>Limit TDR/DFT to </string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="AcquisitionDFTLimitValue">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_30">
<property name="text">
<string>updates per second</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QDoubleSpinBox" name="AcquisitionDFTLimitValue">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_30">
<property name="text">
<string>updates per second</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_31">
<property name="text">
<string>Number of samples used for group delay calculation: </string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="AcquisitionGroupDelaySamples">
<property name="minimum">
<number>2</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
@ -1503,6 +1538,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="MarkerShowGroupDelay">
<property name="text">
<string>Group Delay</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -1737,8 +1779,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">
@ -1827,8 +1869,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>697</width>
<height>563</height>
<width>215</width>
<height>168</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_19">