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

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

View file

@ -1029,7 +1029,7 @@ Different types are also available for the Y-axes. The Y-axis type determines ho
\hline \hline
Inductance & Extracted inductance from a reflection measurement\\ Inductance & Extracted inductance from a reflection measurement\\
\hline \hline
Quality Facotr & Quality Factor of the impedance from a reflection measurement\\ Quality Factor & Quality Factor of the impedance from a reflection measurement\\
\hline \hline
Group Delay & Group Delay of a transmission measurement\\ Group Delay & Group Delay of a transmission measurement\\
\hline \hline
@ -1137,6 +1137,11 @@ Both plots are still completely independent of each other. For the alignment to
The Polar Chart looks similar to the smithchart but doesn't perform the transformation from S-paramter to impedance. Furthermore, through measurements can be displayed as well. The available settings are identical to the smithchart but the Polar Chart does not support adding custom constant lines: The Polar Chart looks similar to the smithchart but doesn't perform the transformation from S-paramter to impedance. Furthermore, through measurements can be displayed as well. The available settings are identical to the smithchart but the Polar Chart does not support adding custom constant lines:
\screenshot{0.6}{GraphPolarchartSetup.png} \screenshot{0.6}{GraphPolarchartSetup.png}
\subsection{Eye Diagram}
\screenshot{1.0}{GraphEyeDiagram.png}
The eye diagram graph shows how a simulated signal would look like after being passed through a transmission line. The transmission line is created from a through measurement (e.g. S21). The simulated signal is a PRBS sequence with additional noise, jitter and limited rise and fall times. All parameters can be edited in the setup dialog:
\screenshot{1.0}{GraphEyeDiagramSetup.png}
\section{Markers} \section{Markers}
Markers provide an easy read-out of trace data at specific points. Each marker is assigned to one trace and will show up on any graph that show the trace at the marker position. Markers provide an easy read-out of trace data at specific points. Each marker is assigned to one trace and will show up on any graph that show the trace at the marker position.
@ -1234,7 +1239,7 @@ This marker type is only available for through measurements on power sweeps. It
\subsection{Marker Data} \subsection{Marker Data}
The trace data at the marker position can be displayed in the marker dock and on the graphs in various formats. The available formats depend on the marker type as well as the domain of the trace data. Only one of the available formats can be displayed in the marker dock at a time. On graphs, any amount of formats can be displayed at once. The shown formats can be selected in the context menu of the marker. The trace data at the marker position can be displayed in the marker dock and on the graphs in various formats. The available formats depend on the marker type as well as the domain of the trace data. Only one of the available formats can be displayed in the marker dock at a time. On graphs, any amount of formats can be displayed at once. The shown formats can be selected in the context menu of the marker.
\begin{footnotesize}
\begin{center} \begin{center}
\begin{threeparttable} \begin{threeparttable}
\begin{tabularx}{\textwidth}{L{3cm}|X|L{7cm}} \begin{tabularx}{\textwidth}{L{3cm}|X|L{7cm}}
@ -1303,6 +1308,8 @@ $Quality factor$\\
& & dB + angle\\ & & dB + angle\\
\cline{3-3} \cline{3-3}
& & Real/Imaginary \\ & & Real/Imaginary \\
\cline{3-3}
& & Group Delay \\
\cline{3-3} \cline{3-3}
& & \multirow{6}{*}{$\left.\begin{array}{l} & & \multirow{6}{*}{$\left.\begin{array}{l}
$Impedance$\\ $Impedance$\\
@ -1355,6 +1362,7 @@ $Quality factor$\\
\end{tabularx} \end{tabularx}
\end{threeparttable} \end{threeparttable}
\end{center} \end{center}
\end{footnotesize}
\subsection{Linking markers} \subsection{Linking markers}
\label{marker:linking} \label{marker:linking}

View file

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

View file

@ -127,6 +127,7 @@ QString Marker::formatToString(Marker::Format f)
case Format::Capacitance: return "Capacitance"; case Format::Capacitance: return "Capacitance";
case Format::Inductance: return "Inductance"; case Format::Inductance: return "Inductance";
case Format::QualityFactor: return "Quality Factor"; case Format::QualityFactor: return "Quality Factor";
case Format::GroupDelay: return "Group Delay";
case Format::TOI: return "Third order intercept"; case Format::TOI: return "Third order intercept";
case Format::AvgTone: return "Average Tone Level"; case Format::AvgTone: return "Average Tone Level";
case Format::AvgModulationProduct: return "Average Modulation Product 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::dB);
ret.push_back(Format::dBAngle); ret.push_back(Format::dBAngle);
ret.push_back(Format::RealImag); ret.push_back(Format::RealImag);
ret.push_back(Format::GroupDelay);
} }
if(parentTrace) { if(parentTrace) {
if(parentTrace->isReflection()) { if(parentTrace->isReflection()) {
@ -346,6 +348,9 @@ std::vector<Marker::Format> Marker::defaultActiveFormats()
if(pref.Marker.defaultBehavior.showQualityFactor) { if(pref.Marker.defaultBehavior.showQualityFactor) {
ret.push_back(Format::QualityFactor); ret.push_back(Format::QualityFactor);
} }
if(pref.Marker.defaultBehavior.showGroupDelay) {
ret.push_back(Format::GroupDelay);
}
if(pref.Marker.defaultBehavior.showNoise) { if(pref.Marker.defaultBehavior.showNoise) {
ret.push_back(Format::Noise); 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::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::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::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); case Format::Noise: return "Δ:"+Unit::ToString(parentTrace->getNoise(position) - delta->parentTrace->getNoise(delta->position), "dbm/Hz", " ", 3);
default: return "Invalid"; 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::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::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::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::Noise: return Unit::ToString(parentTrace->getNoise(position), "dbm/Hz", " ", 3);
case Format::TOI: { case Format::TOI: {
auto avgFundamental = (helperMarkers[0]->toDecibel() + helperMarkers[1]->toDecibel()) / 2; auto avgFundamental = (helperMarkers[0]->toDecibel() + helperMarkers[1]->toDecibel()) / 2;

View file

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

View file

@ -32,7 +32,7 @@
<property name="title"> <property name="title">
<string>X Axis</string> <string>X Axis</string>
</property> </property>
<layout class="QFormLayout" name="formLayout_3"> <layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_19"> <widget class="QLabel" name="label_19">
<property name="text"> <property name="text">
@ -75,7 +75,18 @@
</widget> </widget>
</item> </item>
<item row="3" column="1"> <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> </item>
</layout> </layout>
</widget> </widget>
@ -85,7 +96,7 @@
<property name="title"> <property name="title">
<string>Y Axis</string> <string>Y Axis</string>
</property> </property>
<layout class="QFormLayout" name="formLayout_5"> <layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_15"> <widget class="QLabel" name="label_15">
<property name="text"> <property name="text">
@ -128,7 +139,18 @@
</widget> </widget>
</item> </item>
<item row="3" column="1"> <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> </item>
</layout> </layout>
</widget> </widget>

View file

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

View file

@ -1572,6 +1572,48 @@ double Trace::getNoise(double frequency)
return dbm; 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) int Trace::index(double x)
{ {
auto lower = lower_bound(lastMath->rData().begin(), lastMath->rData().end(), x, [](const Data &lhs, const double x) -> bool { 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; unsigned int getFileParameter() const;
/* Returns the noise in dbm/Hz for spectrum analyzer measurements. May return NaN if calculation not possible */ /* Returns the noise in dbm/Hz for spectrum analyzer measurements. May return NaN if calculation not possible */
double getNoise(double frequency); double getNoise(double frequency);
double getGroupDelay(double frequency);
int index(double x); int index(double x);
std::set<Marker *> getMarkers() const; std::set<Marker *> getMarkers() const;
void setCalibration(); void setCalibration();

View file

@ -1,23 +1,24 @@
#include "traceaxis.h" #include "traceaxis.h"
#include "Util/util.h" #include "Util/util.h"
#include "preferences.h"
#include <cmath> #include <cmath>
using namespace std; 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(); ticks.clear();
if(start > stop) { if(start > stop) {
swap(start, stop); swap(start, stop);
} }
step = abs(step); auto step = (stop - start) / divisions;
constexpr unsigned int maxTicks = 100; constexpr unsigned int maxTicks = 100;
for(double tick = start; tick - stop < numeric_limits<double>::epsilon() && ticks.size() <= maxTicks;tick+= step) { for(double tick = start; tick - stop < numeric_limits<double>::epsilon() && ticks.size() <= maxTicks;tick+= step) {
ticks.push_back(tick); 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); Q_ASSERT(stop > start);
ticks.clear(); ticks.clear();
double max_div_step = (stop - start) / minDivisions; 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) { for(double tick = start_div;tick <= stop;tick += div_step) {
ticks.push_back(tick); ticks.push_back(tick);
} }
return div_step; return (stop - start) / div_step;
} }
static void createLogarithmicTicks(vector<double>& ticks, double start, double stop, int minDivisions) { 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); return Util::SparamToInductance(data.y, data.x);
case YAxis::Type::QualityFactor: case YAxis::Type::QualityFactor:
return Util::SparamToQualityFactor(data.y); return Util::SparamToQualityFactor(data.y);
case YAxis::Type::GroupDelay: { case YAxis::Type::GroupDelay:
constexpr int requiredSamples = 5; if(!t) {
if(!t || t->size() < requiredSamples) {
// unable to calculate
return 0.0; return 0.0;
} }
// needs at least some samples before/after current sample for calculating the derivative. return t->getGroupDelay(data.x);
// 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);
}
}
case YAxis::Type::ImpulseReal: case YAxis::Type::ImpulseReal:
return real(data.y); return real(data.y);
case YAxis::Type::ImpulseMag: case YAxis::Type::ImpulseMag:
@ -190,14 +165,15 @@ double YAxis::sampleToCoordinate(Trace::Data data, Trace *t, unsigned int sample
return 0.0; 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->type = type;
this->log = log; this->log = log;
this->autorange = autorange; this->autorange = autorange;
this->rangeMin = min; this->rangeMin = min;
this->rangeMax = max; this->rangeMax = max;
this->rangeDiv = div; this->divs = divs;
this->autoDivs = autoDivs;
if(type != Type::Disabled) { if(type != Type::Disabled) {
updateTicks(); updateTicks();
} }
@ -440,9 +416,13 @@ void Axis::updateTicks()
rangeMin -= 1.0; rangeMin -= 1.0;
rangeMax += 1.0; rangeMax += 1.0;
} }
rangeDiv = createAutomaticTicks(ticks, rangeMin, rangeMax, 8); divs = createAutomaticTicks(ticks, rangeMin, rangeMax, 8);
} else { } 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; 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 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) { if(max <= min) {
auto info = DeviceDriver::getInfo(DeviceDriver::getActiveDriver()); 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->autorange = autorange;
this->rangeMin = min; this->rangeMin = min;
this->rangeMax = max; this->rangeMax = max;
this->rangeDiv = div; this->divs = divs;
this->autoDivs = autoDivs;
updateTicks(); updateTicks();
} }
@ -589,7 +575,8 @@ Axis::Axis()
autorange = true; autorange = true;
rangeMin = -1.0; rangeMin = -1.0;
rangeMax = 1.0; rangeMax = 1.0;
rangeDiv = 1.0; divs = 10;
autoDivs = false;
ticks.clear(); ticks.clear();
} }

View file

@ -17,7 +17,8 @@ public:
bool getAutorange() const; bool getAutorange() const;
double getRangeMin() const; double getRangeMin() const;
double getRangeMax() const; double getRangeMax() const;
double getRangeDiv() const; unsigned int getDivs() const;
bool getAutoDivs() const;
const std::vector<double> &getTicks() const; const std::vector<double> &getTicks() const;
protected: protected:
@ -26,7 +27,8 @@ protected:
bool autorange; bool autorange;
double rangeMin; double rangeMin;
double rangeMax; double rangeMax;
double rangeDiv; unsigned int divs;
bool autoDivs;
std::vector<double> ticks; std::vector<double> ticks;
}; };
@ -42,7 +44,7 @@ public:
}; };
XAxis(); XAxis();
double sampleToCoordinate(Trace::Data data, Trace *t = nullptr, unsigned int sample = 0) override; 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 QString TypeToName(Type type);
static Type TypeFromName(QString name); static Type TypeFromName(QString name);
static QString Unit(Type type); static QString Unit(Type type);
@ -88,7 +90,7 @@ public:
YAxis(); YAxis();
double sampleToCoordinate(Trace::Data data, Trace *t = nullptr, unsigned int sample = 0) override; 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 QString TypeToName(Type type);
static Type TypeFromName(QString name); static Type TypeFromName(QString name);
static QString Unit(Type type, TraceModel::DataSource source = TraceModel::DataSource::VNA); 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 trace : traces) {
for(auto ytype : getSelectedYAxisTypes()) { for(auto ytype : getSelectedYAxisTypes()) {
auto axis = YAxis(); auto axis = YAxis();
axis.set(ytype, false, false, 0, 1, 1); axis.set(ytype, false, false, 0, 1, 10, false);
auto samples = trace->numSamples(); auto samples = trace->numSamples();
vector<double> values; vector<double> values;
for(unsigned int i=0;i<samples;i++) { for(unsigned int i=0;i<samples;i++) {

View file

@ -26,8 +26,8 @@ TraceWaterfall::TraceWaterfall(TraceModel &model, QWidget *parent)
plotAreaWidth = 0; plotAreaWidth = 0;
plotAreaBottom = 0; plotAreaBottom = 0;
xAxis.set(XAxis::Type::Frequency, false, true, 0, 6000000000, 500000000); xAxis.set(XAxis::Type::Frequency, false, true, 0, 6000000000, 10, false);
yAxis.set(YAxis::Type::Magnitude, false, true, -1, 1, 1); yAxis.set(YAxis::Type::Magnitude, false, true, -1, 1, 10, false);
initializeTraceInfo(); initializeTraceInfo();
} }
@ -163,11 +163,11 @@ bool TraceWaterfall::configureForTrace(Trace *t)
switch(t->outputType()) { switch(t->outputType()) {
case Trace::DataType::Frequency: 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; yDefault = YAxis::Type::Magnitude;
break; break;
case Trace::DataType::Power: 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; yDefault = YAxis::Type::Magnitude;
break; break;
case Trace::DataType::Time: case Trace::DataType::Time:
@ -177,7 +177,7 @@ bool TraceWaterfall::configureForTrace(Trace *t)
return false; return false;
} }
if(!yAxis.isSupported(xAxis.getType(), getModel().getSource())) { 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; traceRemovalPending = true;
return 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()) { if(min_x != xAxis.getRangeMin() || max_x != xAxis.getRangeMax()) {
resetWaterfall(); resetWaterfall();
// adjust axis // 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; 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())) { if(yAxis.getAutorange() && !YAxisUpdateRequired && (min != yAxis.getRangeMin() || max != yAxis.getRangeMax())) {
// axis scaling needs update due to new trace data // 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) { } else if(YAxisUpdateRequired) {
updateYAxis(); updateYAxis();
} }
@ -619,7 +619,7 @@ void TraceWaterfall::updateYAxis()
} }
} }
if(max > min) { 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; xAxisMode = XAxisMode::UseSpan;
// Setup default axis // Setup default axis
setYAxis(0, YAxis::Type::Magnitude, false, false, -120, 20, 10); setYAxis(0, YAxis::Type::Magnitude, false, false, -120, 20, 14, true);
setYAxis(1, YAxis::Type::Phase, false, false, -180, 180, 30); 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) // enable autoscaling and set for full span (no information about actual span available yet)
updateSpan(0, 6000000000); 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(); initializeTraceInfo();
} }
TraceXYPlot::~TraceXYPlot() 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) { if(yAxis[axis].getType() != type) {
// remove traces that are active but not supported with the new axis 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); } while(erased);
} }
yAxis[axis].set(type, log, autorange, min, max, div); yAxis[axis].set(type, log, autorange, min, max, divs, autoDivs);
traceRemovalPending = true; traceRemovalPending = true;
updateContextMenu(); updateContextMenu();
replot(); 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; bool autorange = false;
if(mode == XAxisMode::FitTraces || mode == XAxisMode::UseSpan) { if(mode == XAxisMode::FitTraces || mode == XAxisMode::UseSpan) {
autorange = true; autorange = true;
} }
xAxis.set(type, log, autorange, min, max, div); xAxis.set(type, log, autorange, min, max, divs, autoDivs);
xAxisMode = mode; xAxisMode = mode;
traceRemovalPending = true; traceRemovalPending = true;
updateContextMenu(); updateContextMenu();
@ -101,7 +101,7 @@ void TraceXYPlot::move(const QPoint &vect)
// can only move axis in linear mode // can only move axis in linear mode
// calculate amount of movement // calculate amount of movement
double distance = xAxis.inverseTransform(vect.x(), 0, plotAreaWidth) - xAxis.getRangeMin(); 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; xAxisMode = XAxisMode::Manual;
} }
for(int i=0;i<2;i++) { for(int i=0;i<2;i++) {
@ -109,7 +109,7 @@ void TraceXYPlot::move(const QPoint &vect)
// can only move axis in linear mode // can only move axis in linear mode
// calculate amount of movement // calculate amount of movement
double distance = yAxis[i].inverseTransform(vect.y(), 0, plotAreaTop - plotAreaBottom) - yAxis[i].getRangeMin(); 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(); 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 cp = xAxis.inverseTransform(center.x(), plotAreaLeft, plotAreaLeft + plotAreaWidth);
double min = ((xAxis.getRangeMin() - cp) * factor) + cp; double min = ((xAxis.getRangeMin() - cp) * factor) + cp;
double max = ((xAxis.getRangeMax() - 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; xAxisMode = XAxisMode::Manual;
} }
for(int i=0;i<2;i++) { 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 cp = yAxis[i].inverseTransform(center.y(), plotAreaBottom, plotAreaTop);
double min = ((yAxis[i].getRangeMin() - cp) * factor) + cp; double min = ((yAxis[i].getRangeMin() - cp) * factor) + cp;
double max = ((yAxis[i].getRangeMax() - 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(); replot();
@ -143,11 +143,11 @@ void TraceXYPlot::setAuto(bool horizontally, bool vertically)
{ {
if(horizontally) { if(horizontally) {
xAxisMode = XAxisMode::FitTraces; 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++) { for(int i=0;i<2;i++) {
if(vertically && yAxis[i].getType() != YAxis::Type::Disabled) { 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(); replot();
@ -162,7 +162,8 @@ nlohmann::json TraceXYPlot::toJSON()
jX["log"] = xAxis.getLog(); jX["log"] = xAxis.getLog();
jX["min"] = xAxis.getRangeMin(); jX["min"] = xAxis.getRangeMin();
jX["max"] = xAxis.getRangeMax(); jX["max"] = xAxis.getRangeMax();
jX["div"] = xAxis.getRangeDiv(); jX["divs"] = xAxis.getDivs();
jX["autoDivs"] = xAxis.getAutoDivs();
j["XAxis"] = jX; j["XAxis"] = jX;
for(unsigned int i=0;i<2;i++) { for(unsigned int i=0;i<2;i++) {
nlohmann::json jY; nlohmann::json jY;
@ -171,7 +172,8 @@ nlohmann::json TraceXYPlot::toJSON()
jY["autorange"] = yAxis[i].getAutorange(); jY["autorange"] = yAxis[i].getAutorange();
jY["min"] = yAxis[i].getRangeMin(); jY["min"] = yAxis[i].getRangeMin();
jY["max"] = yAxis[i].getRangeMax(); jY["max"] = yAxis[i].getRangeMax();
jY["div"] = yAxis[i].getRangeDiv(); jY["divs"] = yAxis[i].getDivs();
jY["autoDivs"] = yAxis[i].getAutoDivs();
nlohmann::json jtraces; nlohmann::json jtraces;
for(auto t : tracesAxis[i]) { for(auto t : tracesAxis[i]) {
jtraces.push_back(t->toHash()); jtraces.push_back(t->toHash());
@ -212,9 +214,14 @@ void TraceXYPlot::fromJSON(nlohmann::json j)
// auto xlog = jX.value("log", false); // auto xlog = jX.value("log", false);
auto xmin = jX.value("min", 0.0); auto xmin = jX.value("min", 0.0);
auto xmax = jX.value("max", 6000000000.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); 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"]}; nlohmann::json jY[2] = {j["YPrimary"], j["YSecondary"]};
for(unsigned int i=0;i<2;i++) { for(unsigned int i=0;i<2;i++) {
YAxis::Type ytype; YAxis::Type ytype;
@ -227,8 +234,13 @@ void TraceXYPlot::fromJSON(nlohmann::json j)
auto ylog = jY[i].value("log", false); auto ylog = jY[i].value("log", false);
auto ymin = jY[i].value("min", -120.0); auto ymin = jY[i].value("min", -120.0);
auto ymax = jY[i].value("max", 20.0); auto ymax = jY[i].value("max", 20.0);
auto ydiv = jY[i].value("div", 10.0); auto ydivs = jY[i].value("divs", 10);
setYAxis(i, ytype, ylog, yauto, ymin, ymax, ydiv); // 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"]) { for(unsigned int hash : jY[i]["traces"]) {
// attempt to find the traces with this hash // attempt to find the traces with this hash
bool found = false; bool found = false;
@ -292,21 +304,21 @@ bool TraceXYPlot::configureForTrace(Trace *t)
switch(t->outputType()) { switch(t->outputType()) {
case Trace::DataType::Frequency: 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; yLeftDefault = YAxis::Type::Magnitude;
yRightDefault = YAxis::Type::Phase; yRightDefault = YAxis::Type::Phase;
break; break;
case Trace::DataType::Time: 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; yLeftDefault = YAxis::Type::ImpulseMag;
break; break;
case Trace::DataType::Power: 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; yLeftDefault = YAxis::Type::Magnitude;
yRightDefault = YAxis::Type::Phase; yRightDefault = YAxis::Type::Phase;
break; break;
case Trace::DataType::TimeZeroSpan: 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; yLeftDefault = YAxis::Type::Magnitude;
yRightDefault = YAxis::Type::Phase; yRightDefault = YAxis::Type::Phase;
break; break;
@ -315,10 +327,10 @@ bool TraceXYPlot::configureForTrace(Trace *t)
return false; return false;
} }
if(!yAxis[0].isSupported(xAxis.getType(), getModel().getSource())) { 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())) { 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; traceRemovalPending = true;
return 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++) { for(int i=0;i<2;i++) {
@ -981,7 +993,7 @@ void TraceXYPlot::updateAxisTicks()
min = 0.1; 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, Last,
}; };
void setYAxis(int axis, YAxis::Type type, bool log, bool autorange, 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, double div); 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 enableTrace(Trace *t, bool enabled) override;
void updateSpan(double min, double max) override; void updateSpan(double min, double max) override;
void replot() override; void replot() override;

View file

@ -115,8 +115,8 @@ WaterfallAxisDialog::~WaterfallAxisDialog()
void WaterfallAxisDialog::on_buttonBox_accepted() void WaterfallAxisDialog::on_buttonBox_accepted()
{ {
// set plot values to the ones selected in the dialog // 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->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); 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) { if(ui->Wdir->currentIndex() == 0) {
plot->dir = TraceWaterfall::Direction::TopToBottom; plot->dir = TraceWaterfall::Direction::TopToBottom;
} else { } else {

View file

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

View file

@ -9,8 +9,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>814</width> <width>810</width>
<height>458</height> <height>461</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -21,9 +21,9 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_5"> <layout class="QVBoxLayout" name="verticalLayout_5">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_5"> <layout class="QHBoxLayout" name="horizontalLayout_9">
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="font"> <property name="font">
@ -133,12 +133,27 @@
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_6"> <widget class="QLabel" name="label_6">
<property name="text"> <property name="text">
<string>Divisions:</string> <string># Divisions:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <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> </item>
</layout> </layout>
</item> </item>
@ -262,12 +277,27 @@
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_12"> <widget class="QLabel" name="label_12">
<property name="text"> <property name="text">
<string>Divisions:</string> <string># Divisions:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <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> </item>
</layout> </layout>
</item> </item>
@ -281,7 +311,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QLabel" name="label_13"> <widget class="QLabel" name="label_13">
<property name="sizePolicy"> <property name="sizePolicy">
@ -418,12 +448,27 @@
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_18"> <widget class="QLabel" name="label_18">
<property name="text"> <property name="text">
<string>Divisions:</string> <string># Divisions:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <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> </item>
</layout> </layout>
</item> </item>
@ -592,8 +637,8 @@
</connection> </connection>
</connections> </connections>
<buttongroups> <buttongroups>
<buttongroup name="Y1group"/>
<buttongroup name="Xgroup"/> <buttongroup name="Xgroup"/>
<buttongroup name="Y2group"/> <buttongroup name="Y2group"/>
<buttongroup name="Y1group"/>
</buttongroups> </buttongroups>
</ui> </ui>

View file

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

View file

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

View file

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