group delay option for Y axis

This commit is contained in:
Jan Käberich 2021-12-10 23:36:28 +01:00
parent 75f4ee245f
commit 0d6dac4969
6 changed files with 99 additions and 36 deletions

View File

@ -218,6 +218,7 @@ SOURCES += \
Traces/tracewidget.cpp \ Traces/tracewidget.cpp \
Traces/tracexyplot.cpp \ Traces/tracexyplot.cpp \
Traces/xyplotaxisdialog.cpp \ Traces/xyplotaxisdialog.cpp \
Util/util.cpp \
VNA/Deembedding/deembedding.cpp \ VNA/Deembedding/deembedding.cpp \
VNA/Deembedding/deembeddingdialog.cpp \ VNA/Deembedding/deembeddingdialog.cpp \
VNA/Deembedding/deembeddingoption.cpp \ VNA/Deembedding/deembeddingoption.cpp \

View File

@ -17,22 +17,6 @@
using namespace std; using namespace std;
const set<TraceXYPlot::YAxisType> TraceXYPlot::YAxisTypes = {TraceXYPlot::YAxisType::Disabled,
TraceXYPlot::YAxisType::Magnitude,
TraceXYPlot::YAxisType::Phase,
TraceXYPlot::YAxisType::VSWR,
TraceXYPlot::YAxisType::Real,
TraceXYPlot::YAxisType::Imaginary,
TraceXYPlot::YAxisType::SeriesR,
TraceXYPlot::YAxisType::Reactance,
TraceXYPlot::YAxisType::Capacitance,
TraceXYPlot::YAxisType::Inductance,
TraceXYPlot::YAxisType::QualityFactor,
TraceXYPlot::YAxisType::ImpulseReal,
TraceXYPlot::YAxisType::ImpulseMag,
TraceXYPlot::YAxisType::Step,
TraceXYPlot::YAxisType::Impedance};
TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent) TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent)
: TracePlot(model, parent) : TracePlot(model, parent)
{ {
@ -333,8 +317,11 @@ void TraceXYPlot::updateContextMenu()
bool TraceXYPlot::dropSupported(Trace *t) bool TraceXYPlot::dropSupported(Trace *t)
{ {
Q_UNUSED(t) if(domainMatch(t) && !supported(t)) {
// all kind of traces can be dropped, the graph will be reconfigured to support the dropped trace if required // correct domain configured but Y axis do not match, prevent drop
return false;
}
// either directly compatible or domain change required
return true; return true;
} }
@ -823,12 +810,14 @@ QString TraceXYPlot::AxisTypeToName(TraceXYPlot::YAxisType type)
case YAxisType::Capacitance: return "Capacitance"; case YAxisType::Capacitance: return "Capacitance";
case YAxisType::Inductance: return "Inductance"; case YAxisType::Inductance: return "Inductance";
case YAxisType::QualityFactor: return "Quality Factor"; case YAxisType::QualityFactor: return "Quality Factor";
case YAxisType::GroupDelay: return "Group delay";
case YAxisType::ImpulseReal: return "Impulse Response (Real)"; case YAxisType::ImpulseReal: return "Impulse Response (Real)";
case YAxisType::ImpulseMag: return "Impulse Response (Magnitude)"; case YAxisType::ImpulseMag: return "Impulse Response (Magnitude)";
case YAxisType::Step: return "Step Response"; case YAxisType::Step: return "Step Response";
case YAxisType::Impedance: return "Impedance"; case YAxisType::Impedance: return "Impedance";
default: return "Unknown"; case YAxisType::Last: return "Unknown";
} }
return "Missing case";
} }
void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled) void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled)
@ -861,27 +850,26 @@ void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled)
} }
} }
bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type) bool TraceXYPlot::domainMatch(Trace *t)
{ {
switch(XAxis.type) { switch(XAxis.type) {
case XAxisType::Frequency: case XAxisType::Frequency:
if(t->outputType() != Trace::DataType::Frequency) { return t->outputType() == Trace::DataType::Frequency;
return false;
}
break;
case XAxisType::Distance: case XAxisType::Distance:
case XAxisType::Time: case XAxisType::Time:
if(t->outputType() != Trace::DataType::Time) { return t->outputType() == Trace::DataType::Time;
return false;
}
break;
case XAxisType::Power: case XAxisType::Power:
if(t->outputType() != Trace::DataType::Power) { return t->outputType() == Trace::DataType::Power;
case XAxisType::Last:
return false; return false;
} }
break; return false;
default: }
break;
bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
{
if(!domainMatch(t)) {
return false;
} }
switch(type) { switch(type) {
@ -897,6 +885,11 @@ bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
return false; return false;
} }
break; break;
case YAxisType::GroupDelay:
if(t->isReflection()) {
return false;
}
break;
default: default:
break; break;
} }
@ -946,6 +939,38 @@ QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlo
case YAxisType::QualityFactor: case YAxisType::QualityFactor:
ret.setY(Util::SparamToQualityFactor(data.y)); ret.setY(Util::SparamToQualityFactor(data.y));
break; break;
case YAxisType::GroupDelay: {
constexpr int requiredSamples = 5;
if(t->size() < requiredSamples) {
// unable to calculate
ret.setY(0.0);
break;
}
// 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 traceToCoordinate(t, requiredSamples / 2, type);
} else if(sample >= t->size() - requiredSamples / 2) {
return traceToCoordinate(t, t->size() - requiredSamples / 2 - 1, type);
} 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;
ret.setY(-B_1 / (2.0*M_PI * freq_step));
}
}
break;
case YAxisType::ImpulseReal: case YAxisType::ImpulseReal:
ret.setY(real(data.y)); ret.setY(real(data.y));
break; break;
@ -1103,6 +1128,7 @@ QString TraceXYPlot::AxisUnit(TraceXYPlot::YAxisType type)
case TraceXYPlot::YAxisType::ImpulseMag: return "db"; case TraceXYPlot::YAxisType::ImpulseMag: return "db";
case TraceXYPlot::YAxisType::Step: return ""; case TraceXYPlot::YAxisType::Step: return "";
case TraceXYPlot::YAxisType::Impedance: return "Ohm"; case TraceXYPlot::YAxisType::Impedance: return "Ohm";
case TraceXYPlot::YAxisType::GroupDelay: return "s";
default: return ""; default: return "";
} }
} }

View File

@ -26,6 +26,7 @@ public:
Capacitance, Capacitance,
Inductance, Inductance,
QualityFactor, QualityFactor,
GroupDelay,
// TDR options // TDR options
ImpulseReal, ImpulseReal,
ImpulseMag, ImpulseMag,
@ -33,7 +34,7 @@ public:
Impedance, Impedance,
Last, Last,
}; };
static const std::set<YAxisType> YAxisTypes;
enum class XAxisType { enum class XAxisType {
Frequency, Frequency,
Time, Time,
@ -80,6 +81,7 @@ private:
YAxisType YAxisTypeFromName(QString name); YAxisType YAxisTypeFromName(QString name);
XAxisMode AxisModeFromName(QString name); XAxisMode AxisModeFromName(QString name);
void enableTraceAxis(Trace *t, int axis, bool enabled); void enableTraceAxis(Trace *t, int axis, bool enabled);
bool domainMatch(Trace *t);
bool supported(Trace *t) override; bool supported(Trace *t) override;
bool supported(Trace *t, YAxisType type); bool supported(Trace *t, YAxisType type);
QPointF traceToCoordinate(Trace *t, unsigned int sample, YAxisType type); QPointF traceToCoordinate(Trace *t, unsigned int sample, YAxisType type);

View File

@ -151,7 +151,8 @@ void XYplotAxisDialog::XAxisTypeChanged(int XAxisIndex)
{ {
auto type = (TraceXYPlot::XAxisType) XAxisIndex; auto type = (TraceXYPlot::XAxisType) XAxisIndex;
auto supported = supportedYAxis(type); auto supported = supportedYAxis(type);
for(auto t : TraceXYPlot::YAxisTypes) { for(unsigned int i=0;i<(int) TraceXYPlot::YAxisType::Last;i++) {
auto t = (TraceXYPlot::YAxisType) i;
auto enable = supported.count(t) > 0; auto enable = supported.count(t) > 0;
auto index = (int) t; auto index = (int) t;
enableComboBoxItem(ui->Y1type, index, enable); enableComboBoxItem(ui->Y1type, index, enable);
@ -197,6 +198,7 @@ std::set<TraceXYPlot::YAxisType> XYplotAxisDialog::supportedYAxis(TraceXYPlot::X
ret.insert(TraceXYPlot::YAxisType::Capacitance); ret.insert(TraceXYPlot::YAxisType::Capacitance);
ret.insert(TraceXYPlot::YAxisType::Inductance); ret.insert(TraceXYPlot::YAxisType::Inductance);
ret.insert(TraceXYPlot::YAxisType::QualityFactor); ret.insert(TraceXYPlot::YAxisType::QualityFactor);
ret.insert(TraceXYPlot::YAxisType::GroupDelay);
break; break;
case TraceXYPlot::XAxisType::Time: case TraceXYPlot::XAxisType::Time:
case TraceXYPlot::XAxisType::Distance: case TraceXYPlot::XAxisType::Distance:

View File

@ -0,0 +1,26 @@
#include "util.h"
void Util::unwrapPhase(std::vector<double> &phase)
{
for (unsigned int i = 1; i < phase.size(); i++) {
double d = phase[i] - phase[i-1];
d = d > M_PI ? d - 2 * M_PI : (d < -M_PI ? d + 2 * M_PI : d);
phase[i] = phase[i-1] + d;
}
}
void Util::linearRegression(const std::vector<double> &input, double &B_0, double &B_1)
{
double x_mean = (input.size() - 1.0) / 2.0;
double y_mean = std::accumulate(input.begin(), input.end(), 0.0) / input.size();
double ss_xy = 0.0;
for(unsigned int i=0;i<input.size();i++) {
ss_xy += input[i] * i;
}
ss_xy -= input.size() * x_mean * y_mean;
int n = input.size() - 1;
double ss_xx = (1.0/6.0) * n * (n + 1) * (2*n + 1) - input.size() * x_mean * x_mean;
B_1 = ss_xy / ss_xx;
B_0 = y_mean - B_1 * x_mean;
}

View File

@ -4,6 +4,7 @@
#include <complex> #include <complex>
#include <math.h> #include <math.h>
#include <limits> #include <limits>
#include <vector>
#include <QColor> #include <QColor>
@ -55,6 +56,11 @@ namespace Util {
auto brightness = q.redF() * 0.299 + q.greenF() * 0.587 + q.blueF() * 0.114; auto brightness = q.redF() * 0.299 + q.greenF() * 0.587 + q.blueF() * 0.114;
return brightness > 0.6 ? Qt::black : Qt::white; return brightness > 0.6 ? Qt::black : Qt::white;
} }
void unwrapPhase(std::vector<double> &phase);
// input values are Y coordinates, assumes evenly spaced linear X values from 0 to input.size() - 1
void linearRegression(const std::vector<double> &input, double &B_0, double &B_1);
} }
#endif // UTILH_H #endif // UTILH_H