Waterfall display

This commit is contained in:
Jan Käberich 2022-03-16 14:45:59 +01:00
parent a7fcaf7d97
commit 5897705f32
18 changed files with 1706 additions and 902 deletions

View File

@ -39,6 +39,7 @@ void TileWidget::clear()
{
if(hasContent) {
delete content;
content = nullptr;
hasContent = false;
}
if(isSplit) {
@ -99,11 +100,13 @@ void TileWidget::fromJSON(nlohmann::json j)
content = new TraceSmithChart(model);
} else if (plotname == "XY-plot"){
content = new TraceXYPlot(model);
} else {
} else if (plotname == "Waterfall"){
content = new TraceWaterfall(model);
}
setContent(content);
content->fromJSON(j["plotsettings"]);
if(content) {
setContent(content);
content->fromJSON(j["plotsettings"]);
}
}
}

View File

@ -91,6 +91,7 @@ HEADERS += \
Traces/fftcomplex.h \
Traces/sparamtraceselector.h \
Traces/trace.h \
Traces/traceaxis.h \
Traces/tracecsvexport.h \
Traces/traceeditdialog.h \
Traces/traceimportdialog.h \
@ -101,6 +102,7 @@ HEADERS += \
Traces/tracewaterfall.h \
Traces/tracewidget.h \
Traces/tracexyplot.h \
Traces/waterfallaxisdialog.h \
Traces/xyplotaxisdialog.h \
Util/qpointervariant.h \
Util/util.h \
@ -209,6 +211,7 @@ SOURCES += \
Traces/fftcomplex.cpp \
Traces/sparamtraceselector.cpp \
Traces/trace.cpp \
Traces/traceaxis.cpp \
Traces/tracecsvexport.cpp \
Traces/traceeditdialog.cpp \
Traces/traceimportdialog.cpp \
@ -219,6 +222,7 @@ SOURCES += \
Traces/tracewaterfall.cpp \
Traces/tracewidget.cpp \
Traces/tracexyplot.cpp \
Traces/waterfallaxisdialog.cpp \
Traces/xyplotaxisdialog.cpp \
Util/util.cpp \
VNA/Deembedding/deembedding.cpp \
@ -286,6 +290,7 @@ FORMS += \
Traces/traceimportdialog.ui \
Traces/tracetouchstoneexport.ui \
Traces/tracewidget.ui \
Traces/waterfallaxisdialog.ui \
Traces/xyplotaxisdialog.ui \
VNA/Deembedding/deembeddingdialog.ui \
VNA/Deembedding/manualdeembeddingdialog.ui \

View File

@ -70,8 +70,8 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
auto traceXY = new TraceXYPlot(traceModel);
traceXY->enableTrace(tPort1, true);
traceXY->enableTrace(tPort2, true);
traceXY->setYAxis(0, TraceXYPlot::YAxisType::Magnitude, false, false, -120,0,10);
traceXY->setYAxis(1, TraceXYPlot::YAxisType::Disabled, false, true, 0,0,1);
traceXY->setYAxis(0, YAxis::Type::Magnitude, false, false, -120,0,10);
traceXY->setYAxis(1, YAxis::Type::Disabled, false, true, 0,0,1);
connect(this, &SpectrumAnalyzer::graphColorsChanged, [=](){
for (auto p : TracePlot::getPlots()) {

View File

@ -0,0 +1,441 @@
#include "traceaxis.h"
#include "Util/util.h"
#include <cmath>
using namespace std;
static void createEvenlySpacedTicks(vector<double>& ticks, double start, double stop, double step) {
ticks.clear();
if(start > stop) {
swap(start, stop);
}
step = abs(step);
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) {
Q_ASSERT(stop > start);
ticks.clear();
double max_div_step = (stop - start) / minDivisions;
int zeros = floor(log10(max_div_step));
double decimals_shift = pow(10, zeros);
max_div_step /= decimals_shift;
if(max_div_step >= 5) {
max_div_step = 5;
} else if(max_div_step >= 2) {
max_div_step = 2;
} else {
max_div_step = 1;
}
auto div_step = max_div_step * decimals_shift;
// round min up to next multiple of div_step
auto start_div = ceil(start / div_step) * div_step;
for(double tick = start_div;tick <= stop;tick += div_step) {
ticks.push_back(tick);
}
return div_step;
}
static void createLogarithmicTicks(vector<double>& ticks, double start, double stop, int minDivisions) {
// enforce usable log settings
if(start <= 0) {
start = 1.0;
}
if(stop <= start) {
stop = start + 1.0;
}
ticks.clear();
auto decades = log10(stop) - log10(start);
double max_div_decade = minDivisions / decades;
int zeros = floor(log10(max_div_decade));
double decimals_shift = pow(10, zeros);
max_div_decade /= decimals_shift;
if(max_div_decade < 2) {
max_div_decade = 2;
} else if(max_div_decade < 5) {
max_div_decade = 5;
} else {
max_div_decade = 10;
}
auto step = pow(10, floor(log10(start))+1) / (max_div_decade * decimals_shift);
// round min up to next multiple of div_step
auto div = ceil(start / step) * step;
if(floor(log10(div)) != floor(log10(start))) {
// first div is already at the next decade
step *= 10;
}
do {
ticks.push_back(div);
if(ticks.size() > 1 && div != step && floor(log10(div)) != floor(log10(div - step))) {
// reached a new decade with this switch
step *= 10;
div = step;
} else {
div += step;
}
} while(div <= stop);
}
double YAxis::sampleToCoordinate(Trace::Data data, Trace *t, unsigned int sample)
{
switch(type) {
case YAxis::Type::Magnitude:
return Util::SparamTodB(data.y);
case YAxis::Type::MagnitudedBuV:
return Util::dBmTodBuV(Util::SparamTodB(data.y));
case YAxis::Type::MagnitudeLinear:
return abs(data.y);
case YAxis::Type::Phase:
return Util::SparamToDegree(data.y);
case YAxis::Type::UnwrappedPhase:
if(!t) {
return 0.0;
}
return t->getUnwrappedPhase(sample) * 180.0 / M_PI;
case YAxis::Type::VSWR:
return Util::SparamToVSWR(data.y);
case YAxis::Type::Real:
return data.y.real();
case YAxis::Type::Imaginary:
return data.y.imag();
case YAxis::Type::SeriesR:
return Util::SparamToResistance(data.y);
case YAxis::Type::Reactance:
return Util::SparamToImpedance(data.y).imag();
case YAxis::Type::Capacitance:
return Util::SparamToCapacitance(data.y, data.x);
case YAxis::Type::Inductance:
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
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);
}
}
case YAxis::Type::ImpulseReal:
return real(data.y);
case YAxis::Type::ImpulseMag:
return Util::SparamTodB(data.y);
case YAxis::Type::Step:
if(!t) {
return 0.0;
}
return t->sample(sample, true).y.real();
case YAxis::Type::Impedance: {
if(!t) {
return 0.0;
}
double step = t->sample(sample, true).y.real();
if(abs(step) < 1.0) {
return Util::SparamToImpedance(step).real();
}
}
break;
case YAxis::Type::Disabled:
case YAxis::Type::Last:
// no valid axis
break;
}
return 0.0;
}
void YAxis::set(Type type, bool log, bool autorange, double min, double max, double div)
{
this->type = type;
this->log = log;
this->autorange = autorange;
this->rangeMin = min;
this->rangeMax = max;
this->rangeDiv = div;
if(type != Type::Disabled) {
updateTicks();
}
}
QString YAxis::TypeToName(Type type)
{
switch(type) {
case Type::Disabled: return "Disabled";
case Type::Magnitude: return "Magnitude (dB/dBm)";
case Type::MagnitudedBuV: return "Magnitude (dBuV)";
case Type::MagnitudeLinear: return "Magnitude (linear)";
case Type::Phase: return "Phase";
case Type::UnwrappedPhase: return "Unwrapped Phase";
case Type::VSWR: return "VSWR";
case Type::Real: return "Real";
case Type::Imaginary: return "Imaginary";
case Type::SeriesR: return "Resistance";
case Type::Reactance: return "Reactance";
case Type::Capacitance: return "Capacitance";
case Type::Inductance: return "Inductance";
case Type::QualityFactor: return "Quality Factor";
case Type::GroupDelay: return "Group delay";
case Type::ImpulseReal: return "Impulse Response (Real)";
case Type::ImpulseMag: return "Impulse Response (Magnitude)";
case Type::Step: return "Step Response";
case Type::Impedance: return "Impedance";
case Type::Last: return "Unknown";
}
return "Missing case";
}
YAxis::Type YAxis::TypeFromName(QString name)
{
for(unsigned int i=0;i<(int) Type::Last;i++) {
if(TypeToName((Type) i) == name) {
return (Type) i;
}
}
// not found, use default
return Type::Magnitude;
}
QString YAxis::Unit(Type type, TraceModel::DataSource source)
{
if(source == TraceModel::DataSource::VNA) {
switch(type) {
case Type::Magnitude: return "dB";
case Type::MagnitudeLinear: return "";
case Type::Phase: return "°";
case Type::UnwrappedPhase: return "°";
case Type::VSWR: return "";
case Type::ImpulseReal: return "";
case Type::ImpulseMag: return "dB";
case Type::Step: return "";
case Type::Impedance: return "Ω";
case Type::GroupDelay: return "s";
case Type::Disabled:
case Type::Real:
case Type::Imaginary:
case Type::QualityFactor:
return "";
case Type::SeriesR: return "Ω";
case Type::Reactance: return "Ω";
case Type::Capacitance: return "F";
case Type::Inductance: return "H";
case Type::Last: return "";
}
} else if(source == TraceModel::DataSource::SA) {
switch(type) {
case Type::Magnitude: return "dBm";
case Type::MagnitudedBuV: return "dBuV";
default: return "";
}
}
return "";
}
QString YAxis::Prefixes(Type type, TraceModel::DataSource source)
{
if(source == TraceModel::DataSource::VNA) {
switch(type) {
case Type::Magnitude: return " ";
case Type::MagnitudeLinear: return "num ";
case Type::Phase: return " ";
case Type::UnwrappedPhase: return " ";
case Type::VSWR: return " ";
case Type::ImpulseReal: return "pnum kMG";
case Type::ImpulseMag: return " ";
case Type::Step: return "pnum kMG";
case Type::Impedance: return "m kM";
case Type::GroupDelay: return "pnum ";
case Type::Disabled: return " ";
case Type::Real: return "pnum ";
case Type::Imaginary: return "pnum ";
case Type::QualityFactor: return " ";
case Type::SeriesR: return "m kM";
case Type::Reactance: return "m kM";
case Type::Capacitance: return "pnum ";
case Type::Inductance: return "pnum ";
case Type::Last: return " ";
}
} else if(source == TraceModel::DataSource::SA) {
switch(type) {
case Type::Magnitude: return " ";
case Type::MagnitudedBuV: return " ";
default: return " ";
}
}
return " ";
}
QString YAxis::TypeToName()
{
return TypeToName(type);
}
QString YAxis::Unit(TraceModel::DataSource source)
{
return Unit(type, source);
}
QString YAxis::Prefixes(TraceModel::DataSource source)
{
return Prefixes(type, source);
}
YAxis::Type YAxis::getType() const
{
return type;
}
void Axis::updateTicks()
{
if(log) {
createLogarithmicTicks(ticks, rangeMin, rangeMax, 20);
} else if(autorange) {
if(rangeMin >= rangeMax) {
// problem, min must be less than max
rangeMin -= 1.0;
rangeMax += 1.0;
}
rangeDiv = createAutomaticTicks(ticks, rangeMin, rangeMax, 8);
} else {
createEvenlySpacedTicks(ticks, rangeMin, rangeMax, rangeDiv);
}
}
const std::vector<double> &Axis::getTicks() const
{
return ticks;
}
double Axis::getRangeDiv() const
{
return rangeDiv;
}
double Axis::getRangeMax() const
{
return rangeMax;
}
double Axis::getRangeMin() const
{
return rangeMin;
}
bool Axis::getAutorange() const
{
return autorange;
}
bool Axis::getLog() const
{
return log;
}
double XAxis::sampleToCoordinate(Trace::Data data, Trace *t, unsigned int sample)
{
switch(type) {
case Type::Distance:
if(!t) {
return 0.0;
}
return t->timeToDistance(data.x);
default:
return data.x;
}
}
void XAxis::set(Type type, bool log, bool autorange, double min, double max, double div)
{
this->type = type;
this->log = log;
this->autorange = autorange;
this->rangeMin = min;
this->rangeMax = max;
this->rangeDiv = div;
updateTicks();
}
QString XAxis::TypeToName(Type type)
{
switch(type) {
case Type::Frequency: return "Frequency";
case Type::Time: return "Time";
case Type::Distance: return "Distance";
case Type::Power: return "Power";
default: return "Unknown";
}
}
XAxis::Type XAxis::TypeFromName(QString name)
{
for(unsigned int i=0;i<(int) Type::Last;i++) {
if(TypeToName((Type) i) == name) {
return (Type) i;
}
}
// not found, use default
return Type::Frequency;
}
QString XAxis::Unit(Type type)
{
switch(type) {
case Type::Frequency: return "Hz";
case Type::Time: return "s";
case Type::Distance: return "m";
case Type::Power: return "dBm";
default: return "";
}
}
QString XAxis::TypeToName()
{
return TypeToName(type);
}
QString XAxis::Unit()
{
return Unit(type);
}
XAxis::Type XAxis::getType() const
{
return type;
}
double Axis::transform(double value, double to_low, double to_high)
{
return Util::Scale(value, rangeMin, rangeMax, to_low, to_high, log);
}
double Axis::inverseTransform(double value, double to_low, double to_high)
{
return Util::Scale(value, to_low, to_high, rangeMin, rangeMax, false, log);
}

View File

@ -0,0 +1,97 @@
#ifndef TRACEAXIS_H
#define TRACEAXIS_H
#include "tracemodel.h"
#include <vector>
#include <QString>
class Axis {
public:
virtual double sampleToCoordinate(Trace::Data data, Trace *t = nullptr, unsigned int sample = 0) = 0;
double transform(double value, double to_low, double to_high);
double inverseTransform(double value, double to_low, double to_high);
bool getLog() const;
bool getAutorange() const;
double getRangeMin() const;
double getRangeMax() const;
double getRangeDiv() const;
const std::vector<double> &getTicks() const;
protected:
void updateTicks();
bool log;
bool autorange;
double rangeMin;
double rangeMax;
double rangeDiv;
std::vector<double> ticks;
};
class YAxis : public Axis {
public:
enum class Type {
Disabled,
// S parameter options
Magnitude,
MagnitudedBuV,
MagnitudeLinear,
Phase,
UnwrappedPhase,
VSWR,
Real,
Imaginary,
// derived parameter options
SeriesR,
Reactance,
Capacitance,
Inductance,
QualityFactor,
GroupDelay,
// TDR options
ImpulseReal,
ImpulseMag,
Step,
Impedance,
Last,
};
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);
static QString TypeToName(Type type);
static Type TypeFromName(QString name);
static QString Unit(Type type, TraceModel::DataSource source = TraceModel::DataSource::VNA);
static QString Prefixes(Type type, TraceModel::DataSource source = TraceModel::DataSource::VNA);
QString TypeToName();
QString Unit(TraceModel::DataSource source = TraceModel::DataSource::VNA);
QString Prefixes(TraceModel::DataSource source = TraceModel::DataSource::VNA);
Type getType() const;
private:
Type type;
};
class XAxis : public Axis {
public:
enum class Type {
Frequency,
Time,
Distance,
Power,
Last,
};
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);
static QString TypeToName(Type type);
static Type TypeFromName(QString name);
static QString Unit(Type type);
QString TypeToName();
QString Unit();
Type getType() const;
private:
Type type;
};
#endif // TRACEAXIS_H

View File

@ -185,7 +185,7 @@ void TracePlot::paintEvent(QPaintEvent *event)
auto tmarkers = t.first->getMarkers();
for(auto m : tmarkers) {
if(!xCoordinateVisible(m->getPosition())) {
if(!markerVisible(m->getPosition())) {
// marker not visible with current plot settings
continue;
}

View File

@ -86,7 +86,7 @@ protected slots:
void checkIfStillSupported(Trace *t);
virtual void markerAdded(Marker *m);
virtual void markerRemoved(Marker *m);
virtual bool xCoordinateVisible(double x) = 0;
virtual bool markerVisible(double x) = 0;
protected:
static constexpr unsigned int marginTop = 20;
static constexpr unsigned int marginBottom = 0;

View File

@ -250,7 +250,7 @@ double TraceSmithChart::nearestTracePoint(Trace *t, QPoint pixel, double *distan
return closestXpos;
}
bool TraceSmithChart::xCoordinateVisible(double x)
bool TraceSmithChart::markerVisible(double x)
{
if(limitToSpan) {
if(x >= sweep_fmin && x <= sweep_fmax) {

View File

@ -123,7 +123,7 @@ protected:
std::complex<double> pixelToData(QPoint p);
QPoint markerToPixel(Marker *m) override;
double nearestTracePoint(Trace *t, QPoint pixel, double *distance = nullptr) override;
virtual bool xCoordinateVisible(double x);
virtual bool markerVisible(double x);
//void paintEvent(QPaintEvent *event) override;
virtual void updateContextMenu() override;

View File

@ -3,6 +3,8 @@
#include "preferences.h"
#include "unit.h"
#include "Util/util.h"
#include "waterfallaxisdialog.h"
#include "appwindow.h"
#include <QFileDialog>
#include <QPainter>
@ -11,8 +13,14 @@ using namespace std;
TraceWaterfall::TraceWaterfall(TraceModel &model, QWidget *parent)
: TracePlot(model, parent),
pixelsPerLine(1)
dir(Direction::TopToBottom),
trace(nullptr),
pixelsPerLine(1),
keepDataBeyondPlotSize(false),
maxDataSweeps(500)
{
XAxis.set(XAxis::Type::Frequency, false, true, 0, 6000000000, 500000000);
YAxis.set(YAxis::Type::Magnitude, false, true, -1, 1, 1);
initializeTraceInfo();
}
@ -23,32 +31,39 @@ void TraceWaterfall::enableTrace(Trace *t, bool enabled)
for(auto t : traces) {
if(t.second) {
TracePlot::enableTrace(t.first, false);
disconnect(t.first, &Trace::dataChanged, this, &TraceWaterfall::traceDataChanged);
break;
}
}
}
TracePlot::enableTrace(t, enabled);
resetWaterfall();
}
if(enabled) {
trace = t;
connect(t, &Trace::dataChanged, this, &TraceWaterfall::traceDataChanged);
} else {
trace = nullptr;
}
void TraceWaterfall::updateSpan(double min, double max)
{
TracePlot::updateSpan(min, max);
updateAxisTicks();
resetWaterfall();
}
void TraceWaterfall::replot()
{
if(XAxis.mode != XAxisMode::Manual) {
updateAxisTicks();
}
TracePlot::replot();
}
void TraceWaterfall::fromJSON(nlohmann::json j)
{
resetWaterfall();
pixelsPerLine = j.value("pixelsPerLine", 1);
pixelsPerLine = j.value("pixelsPerLine", pixelsPerLine);
maxDataSweeps = j.value("maxLines", maxDataSweeps);
keepDataBeyondPlotSize = j.value("keepDataBeyondPlot", keepDataBeyondPlotSize);
if(QString::fromStdString(j.value("direction", "TopToBottom")) == "TopToBottom") {
dir = Direction::TopToBottom;
} else {
dir = Direction::BottomToTop;
}
for(unsigned int hash : j["traces"]) {
// attempt to find the traces with this hash
bool found = false;
@ -69,6 +84,9 @@ nlohmann::json TraceWaterfall::toJSON()
{
nlohmann::json j;
j["pixelsPerLine"] = pixelsPerLine;
j["direction"] = dir == Direction::TopToBottom ? "TopToBottom" : "BottomToTop";
j["keepDataBeyondPlot"] = keepDataBeyondPlotSize;
j["maxLines"] = maxDataSweeps;
nlohmann::json jtraces;
for(auto t : traces) {
if(t.second) {
@ -79,37 +97,59 @@ nlohmann::json TraceWaterfall::toJSON()
return j;
}
void TraceWaterfall::setXAxis(XAxisType type, XAxisMode mode, bool log, double min, double max, double div)
{
XAxis.type = type;
XAxis.mode = mode;
XAxis.log = log;
XAxis.rangeMin = min;
XAxis.rangeMax = max;
XAxis.rangeDiv = div;
traceRemovalPending = true;
updateAxisTicks();
updateContextMenu();
replot();
}
void TraceWaterfall::axisSetupDialog()
{
// TODO
auto setup = new WaterfallAxisDialog(this);
if(AppWindow::showGUI()) {
setup->show();
}
}
void TraceWaterfall::resetWaterfall()
{
data.clear();
updateYAxis();
}
bool TraceWaterfall::configureForTrace(Trace *t)
{
// TODO
switch(t->outputType()) {
case Trace::DataType::Frequency:
XAxis.set(XAxis::Type::Frequency, false, true, 0, 1, 0.1);
YAxis.set(YAxis::Type::Magnitude, false, true, 0, 1, 1.0);
break;
case Trace::DataType::Time:
XAxis.set(XAxis::Type::Time, false, true, 0, 1, 0.1);
YAxis.set(YAxis::Type::ImpulseMag, false, true, 0, 1, 1.0);
break;
case Trace::DataType::Power:
XAxis.set(XAxis::Type::Power, false, true, 0, 1, 0.1);
YAxis.set(YAxis::Type::Magnitude, false, true, 0, 1, 1.0);
break;
case Trace::DataType::Invalid:
// unable to add
return false;
}
traceRemovalPending = true;
return true;
}
bool TraceWaterfall::domainMatch(Trace *t)
{
switch(XAxis.getType()) {
case XAxis::Type::Frequency:
return t->outputType() == Trace::DataType::Frequency;
case XAxis::Type::Distance:
case XAxis::Type::Time:
return t->outputType() == Trace::DataType::Time;
case XAxis::Type::Power:
return t->outputType() == Trace::DataType::Power;
case XAxis::Type::Last:
return false;
}
return false;
}
void TraceWaterfall::updateContextMenu()
{
contextmenu->clear();
@ -162,7 +202,7 @@ void TraceWaterfall::draw(QPainter &p)
{
auto pref = Preferences::getInstance();
constexpr int yAxisSpace = 55;
constexpr int yAxisLegendSpace = 25;
constexpr int yAxisDisabledSpace = 10;
constexpr int xAxisSpace = 30;
auto w = p.window();
@ -170,32 +210,59 @@ void TraceWaterfall::draw(QPainter &p)
pen.setCosmetic(true);
p.setPen(pen);
plotAreaLeft = yAxisDisabledSpace;
plotAreaWidth = w.width() - 2 * yAxisDisabledSpace;
plotAreaWidth = w.width() - 3 * yAxisDisabledSpace - yAxisLegendSpace;
plotAreaTop = 10;
plotAreaBottom = w.height() - xAxisSpace;
auto plotRect = QRect(plotAreaLeft, plotAreaTop, plotAreaWidth + 1, plotAreaBottom-plotAreaTop);
// draw Y legend
auto plotRect = QRect(w.width() - yAxisDisabledSpace - yAxisLegendSpace, plotAreaTop, yAxisLegendSpace, plotAreaBottom-plotAreaTop);
p.drawRect(plotRect);
for(int i=plotAreaTop + 1;i<plotAreaBottom;i++) {
auto color = getColor(Util::Scale<double>(i, plotAreaTop, plotAreaBottom, 1.0, 0.0));
p.setPen(QColor(color));
pen.setCosmetic(true);
p.drawLine(w.width() - yAxisDisabledSpace - yAxisLegendSpace + 1, i, w.width() - yAxisDisabledSpace - 1, i);
}
QString unit = "";
if(pref.Graphs.showUnits) {
unit = YAxis.Unit();
}
QString labelMin = Unit::ToString(YAxis.getRangeMin(), unit, YAxis.Prefixes(), 4);
QString labelMax = Unit::ToString(YAxis.getRangeMax(), unit, YAxis.Prefixes(), 4);
p.setPen(QPen(pref.Graphs.Color.axis, 1));
p.save();
p.translate(w.width() - yAxisDisabledSpace - yAxisLegendSpace, w.height());
p.rotate(-90);
p.drawText(QRect(xAxisSpace + 10, 0, plotAreaBottom - plotAreaTop - 20, yAxisLegendSpace), Qt::AlignRight | Qt::AlignVCenter, labelMax);
p.drawText(QRect(xAxisSpace + 10, 0, plotAreaBottom - plotAreaTop - 20, yAxisLegendSpace), Qt::AlignLeft | Qt::AlignVCenter, labelMin);
p.restore();
pen = QPen(pref.Graphs.Color.axis, 0);
pen.setCosmetic(true);
p.setPen(pen);
plotRect = QRect(plotAreaLeft, plotAreaTop, plotAreaWidth + 1, plotAreaBottom-plotAreaTop);
p.drawRect(plotRect);
// draw axis types
auto font = p.font();
font.setPixelSize(AxisLabelSize);
p.setFont(font);
p.drawText(QRect(0, w.height()-AxisLabelSize*1.5, w.width(), AxisLabelSize*1.5), Qt::AlignHCenter, AxisTypeToName(XAxis.type));
p.drawText(QRect(0, w.height()-AxisLabelSize*1.5, w.width(), AxisLabelSize*1.5), Qt::AlignHCenter, XAxis.TypeToName());
if(XAxis.ticks.size() >= 1) {
if(XAxis.getTicks().size() >= 1) {
// draw X ticks
int significantDigits;
bool displayFullFreq;
if(XAxis.log) {
if(XAxis.getLog()) {
significantDigits = 5;
displayFullFreq = true;
} else {
// this only works for evenly distributed ticks:
auto max = qMax(abs(XAxis.ticks.front()), abs(XAxis.ticks.back()));
auto max = qMax(abs(XAxis.getTicks().front()), abs(XAxis.getTicks().back()));
double step;
if(XAxis.ticks.size() >= 2) {
step = abs(XAxis.ticks[0] - XAxis.ticks[1]);
if(XAxis.getTicks().size() >= 2) {
step = abs(XAxis.getTicks()[0] - XAxis.getTicks()[1]);
} else {
// only one tick, set arbitrary number of digits
step = max / 1000;
@ -207,11 +274,11 @@ void TraceWaterfall::draw(QPainter &p)
QString prefixes = "fpnum kMG";
QString unit = "";
if(pref.Graphs.showUnits) {
unit = AxisUnit(XAxis.type);
unit = XAxis.Unit();
}
QString commonPrefix = QString();
if(!displayFullFreq) {
auto fullFreq = Unit::ToString(XAxis.ticks.front(), unit, prefixes, significantDigits);
auto fullFreq = Unit::ToString(XAxis.getTicks().front(), unit, prefixes, significantDigits);
commonPrefix = fullFreq.at(fullFreq.size() - 1);
auto front = fullFreq;
front.truncate(fullFreq.size() - displayLastDigits - unit.length());
@ -226,8 +293,8 @@ void TraceWaterfall::draw(QPainter &p)
}
int lastTickLabelEnd = 0;
for(auto t : XAxis.ticks) {
auto xCoord = Util::Scale<double>(t, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth, XAxis.log);
for(auto t : XAxis.getTicks()) {
auto xCoord = XAxis.transform(t, plotAreaLeft, plotAreaLeft + plotAreaWidth);
p.setPen(QPen(pref.Graphs.Color.axis, 1));
p.drawLine(xCoord, plotAreaBottom, xCoord, plotAreaBottom + 2);
if(xCoord != plotAreaLeft && xCoord != plotAreaLeft + plotAreaWidth) {
@ -261,6 +328,76 @@ void TraceWaterfall::draw(QPainter &p)
}
}
p.setClipRect(QRect(plotRect.x()+1, plotRect.y()+1, plotRect.width()-1, plotRect.height()-1));
if(data.size()) {
// plot waterfall data
int ytop, ybottom;
bool lastLine = false;
if(dir == Direction::TopToBottom) {
ytop = plotAreaTop;
ybottom = ytop + pixelsPerLine - 1;
} else {
ybottom = plotAreaBottom - 1;
ytop = ybottom - pixelsPerLine + 1;
}
int i;
for(i=data.size() - 1;i>=0;i--) {
auto sweep = data[i];
for(unsigned int s=0;s<sweep.size();s++) {
auto x = XAxis.sampleToCoordinate(sweep[s], trace);
double x_start;
double x_stop;
if(x < XAxis.getRangeMin() || x > XAxis.getRangeMax()) {
// out of range, skip
continue;
}
if(s == 0) {
x_start = x;
} else {
auto prev_x = XAxis.sampleToCoordinate(sweep[s-1], trace);
x_start = (prev_x + x) / 2.0;
}
x_start = XAxis.transform(x_start, plotAreaLeft, plotAreaLeft + plotAreaWidth);
if(s == sweep.size() - 1) {
x_stop = x;
} else {
auto next_x = XAxis.sampleToCoordinate(sweep[s+1], trace);
x_stop = (next_x + x) / 2.0;
}
x_stop = XAxis.transform(x_stop, plotAreaLeft, plotAreaLeft + plotAreaWidth);
auto y = YAxis.sampleToCoordinate(sweep[s]);
auto color = getColor(YAxis.transform(y, 0.0, 1.0));
auto rect = QRect(round(x_start), ytop, round(x_stop - x_start) + 1, ybottom - ytop + 1);
p.fillRect(rect, QBrush(color));
}
if(lastLine) {
break;
}
// update ycoords for next line
if(dir == Direction::TopToBottom) {
ytop = ybottom + 1;
ybottom = ytop + pixelsPerLine - 1;
if(ybottom >= plotAreaBottom) {
ybottom = plotAreaBottom;
lastLine = true;
}
} else {
ybottom = ytop - 1;
ytop = ybottom - pixelsPerLine + 1;
if(ytop <= plotAreaTop) {
ytop = plotAreaTop;
lastLine = true;
}
}
}
if(!keepDataBeyondPlotSize && i >= 0) {
// not all data could be plotted, drop
data.erase(data.begin(), data.begin() + i);
updateYAxis();
}
}
p.setClipping(false);
if(dropPending) {
p.setOpacity(0.5);
p.setBrush(Qt::white);
@ -279,208 +416,144 @@ void TraceWaterfall::draw(QPainter &p)
bool TraceWaterfall::supported(Trace *t)
{
// TODO
if(!domainMatch(t)) {
return false;
}
switch(YAxis.getType()) {
case YAxis::Type::Disabled:
return false;
case YAxis::Type::VSWR:
case YAxis::Type::SeriesR:
case YAxis::Type::Reactance:
case YAxis::Type::Capacitance:
case YAxis::Type::Inductance:
case YAxis::Type::QualityFactor:
if(!t->isReflection()) {
return false;
}
break;
case YAxis::Type::GroupDelay:
if(t->isReflection()) {
return false;
}
break;
default:
break;
}
return true;
}
double TraceWaterfall::nearestTracePoint(Trace *t, QPoint pixel, double *distance)
{
// TODO
// this function is used for the movement of markers.
// No markers on waterfall plot, nothing to do
Q_UNUSED(t)
Q_UNUSED(pixel)
Q_UNUSED(distance)
return 0;
}
QString TraceWaterfall::mouseText(QPoint pos)
{
// TODO
return "Test";
QString ret;
if(QRect(plotAreaLeft, 0, plotAreaWidth + 1, plotAreaBottom).contains(pos)) {
double x = XAxis.inverseTransform(pos.x(), plotAreaLeft, plotAreaLeft + plotAreaWidth);
int significantDigits = floor(log10(abs(XAxis.getRangeMax()))) - floor(log10((abs(XAxis.getRangeMax() - XAxis.getRangeMin())) / 1000.0)) + 1;
ret += Unit::ToString(x, XAxis.Unit(), "fpnum kMG", significantDigits) + "\n";
}
return ret;
}
bool TraceWaterfall::xCoordinateVisible(double x)
bool TraceWaterfall::markerVisible(double x)
{
// TODO
return true;
// no markers on waterfall
Q_UNUSED(x)
return false;
}
void TraceWaterfall::updateAxisTicks()
void TraceWaterfall::traceDataChanged(unsigned int begin, unsigned int end)
{
auto createEvenlySpacedTicks = [](vector<double>& ticks, double start, double stop, double step) {
ticks.clear();
if(start > stop) {
swap(start, stop);
if(XAxis.getAutorange()) {
double min_x = trace->sample(0).x;
double max_x = trace->sample(trace->size() - 1).x;
if(min_x != XAxis.getRangeMin() || max_x != XAxis.getRangeMax()) {
resetWaterfall();
// adjust axis
XAxis.set(XAxis.getType(), XAxis.getLog(), true, min_x, max_x, 0);
}
step = abs(step);
constexpr unsigned int maxTicks = 100;
for(double tick = start; tick - stop < numeric_limits<double>::epsilon() && ticks.size() <= maxTicks;tick+= step) {
ticks.push_back(tick);
}
bool YAxisUpdateRequired = false;
if (begin == 0 || data.size() == 0) {
if(data.size() == 1) {
YAxisUpdateRequired = true;
}
};
auto createAutomaticTicks = [](vector<double>& ticks, double start, double stop, int minDivisions) -> double {
Q_ASSERT(stop > start);
ticks.clear();
double max_div_step = (stop - start) / minDivisions;
int zeros = floor(log10(max_div_step));
double decimals_shift = pow(10, zeros);
max_div_step /= decimals_shift;
if(max_div_step >= 5) {
max_div_step = 5;
} else if(max_div_step >= 2) {
max_div_step = 2;
} else {
max_div_step = 1;
// start new row
data.push_back(std::vector<Trace::Data>());
while (data.size() > maxDataSweeps) {
data.pop_front();
// min/max might have changed due to removed data
YAxisUpdateRequired = true;
}
auto div_step = max_div_step * decimals_shift;
// round min up to next multiple of div_step
auto start_div = ceil(start / div_step) * div_step;
for(double tick = start_div;tick <= stop;tick += div_step) {
ticks.push_back(tick);
}
return div_step;
};
auto createLogarithmicTicks = [](vector<double>& ticks, double start, double stop, int minDivisions) {
// enforce usable log settings
if(start <= 0) {
start = 1.0;
}
if(stop <= start) {
stop = start + 1.0;
}
ticks.clear();
auto decades = log10(stop) - log10(start);
double max_div_decade = minDivisions / decades;
int zeros = floor(log10(max_div_decade));
double decimals_shift = pow(10, zeros);
max_div_decade /= decimals_shift;
if(max_div_decade < 2) {
max_div_decade = 2;
} else if(max_div_decade < 5) {
max_div_decade = 5;
} else {
max_div_decade = 10;
}
auto step = pow(10, floor(log10(start))+1) / (max_div_decade * decimals_shift);
// round min up to next multiple of div_step
auto div = ceil(start / step) * step;
if(floor(log10(div)) != floor(log10(start))) {
// first div is already at the next decade
step *= 10;
}
do {
ticks.push_back(div);
if(ticks.size() > 1 && div != step && floor(log10(div)) != floor(log10(div - step))) {
// reached a new decade with this switch
step *= 10;
div = step;
} else {
div += step;
}
// grab trace data
data.back().resize(trace->size());
double min = YAxis.getRangeMin();
double max = YAxis.getRangeMax();
for(unsigned int i=begin;i<end;i++) {
data.back()[i] = trace->sample(i);
if(YAxis.getAutorange() && !YAxisUpdateRequired) {
double val = YAxis.sampleToCoordinate(trace->sample(i));
if(val < min) {
min = val;
}
if(val > max) {
max = val;
}
} while(div <= stop);
};
if(XAxis.mode == XAxisMode::Manual) {
if(XAxis.log) {
createLogarithmicTicks(XAxis.ticks, XAxis.rangeMin, XAxis.rangeMax, 20);
} else {
createEvenlySpacedTicks(XAxis.ticks, XAxis.rangeMin, XAxis.rangeMax, XAxis.rangeDiv);
}
} else {
XAxis.ticks.clear();
// automatic mode, figure out limits
double max = std::numeric_limits<double>::lowest();
}
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);
} else if(YAxisUpdateRequired) {
updateYAxis();
}
}
void TraceWaterfall::updateYAxis()
{
if(YAxis.getAutorange()) {
double min = std::numeric_limits<double>::max();
if(XAxis.mode == XAxisMode::UseSpan) {
min = sweep_fmin;
max = sweep_fmax;
} else if(XAxis.mode == XAxisMode::FitTraces) {
for(auto t : traces) {
bool enabled = t.second;
auto trace = t.first;
if(enabled && trace->isVisible()) {
if(!trace->size()) {
// empty trace, do not use for automatic axis calculation
continue;
}
// this trace is currently displayed
double trace_min = trace->minX();
double trace_max = trace->maxX();
if(XAxis.type == XAxisType::Distance) {
trace_min = trace->timeToDistance(trace_min);
trace_max = trace->timeToDistance(trace_max);
}
if(trace_min < min) {
min = trace_min;
}
if(trace_max > max) {
max = trace_max;
}
double max = std::numeric_limits<double>::lowest();
for(auto sweep : data) {
for(unsigned int i=0;i<sweep.size();i++) {
double val = YAxis.sampleToCoordinate(sweep[i]);
if(isnan(val) || isinf(val)) {
continue;
}
if(val < min) {
min = val;
}
if(val > max) {
max = val;
}
}
}
if(min < max) {
// found min/max values
XAxis.rangeMin = min;
XAxis.rangeMax = max;
if(XAxis.log) {
createLogarithmicTicks(XAxis.ticks, XAxis.rangeMin, XAxis.rangeMax, 20);
} else {
XAxis.rangeDiv = createAutomaticTicks(XAxis.ticks, min, max, 8);
}
if(max > min) {
YAxis.set(YAxis.getType(), YAxis.getLog(), true, min, max, 0);
}
}
}
QString TraceWaterfall::AxisTypeToName(TraceWaterfall::XAxisType type)
QColor TraceWaterfall::getColor(double scale)
{
switch(type) {
case XAxisType::Frequency: return "Frequency";
case XAxisType::Time: return "Time";
case XAxisType::Distance: return "Distance";
case XAxisType::Power: return "Power";
default: return "Unknown";
}
}
QString TraceWaterfall::AxisModeToName(TraceWaterfall::XAxisMode mode)
{
switch(mode) {
case XAxisMode::Manual: return "Manual"; break;
case XAxisMode::FitTraces: return "Fit Traces"; break;
case XAxisMode::UseSpan: return "Use Span"; break;
default: return "Unknown";
}
}
TraceWaterfall::XAxisType TraceWaterfall::XAxisTypeFromName(QString name)
{
for(unsigned int i=0;i<(int) XAxisType::Last;i++) {
if(AxisTypeToName((XAxisType) i) == name) {
return (XAxisType) i;
}
}
// not found, use default
return XAxisType::Frequency;
}
TraceWaterfall::XAxisMode TraceWaterfall::AxisModeFromName(QString name)
{
for(unsigned int i=0;i<(int) XAxisMode::Last;i++) {
if(AxisModeToName((XAxisMode) i) == name) {
return (XAxisMode) i;
}
}
// not found, use default
return XAxisMode::UseSpan;
}
QString TraceWaterfall::AxisUnit(XAxisType type)
{
switch(type) {
case XAxisType::Frequency: return "Hz";
case XAxisType::Time: return "s";
case XAxisType::Distance: return "m";
case XAxisType::Power: return "dBm";
default: return "";
if(scale < 0.0) {
return Qt::black;
} else if(scale > 1.0) {
return Qt::white;
} else if(scale >= 0.0 && scale <= 1.0) {
return QColor::fromHsv(Util::Scale<double>(scale, 0.0, 1.0, 240, 0), 255, 255);
} else {
return Qt::black;
}
}

View File

@ -3,38 +3,25 @@
#include "traceplot.h"
#include "traceaxis.h"
#include <deque>
class TraceWaterfall : public TracePlot
{
friend class WaterfallAxisDialog;
Q_OBJECT
public:
TraceWaterfall(TraceModel &model, QWidget *parent = 0);;
virtual void enableTrace(Trace *t, bool enabled) override;
void updateSpan(double min, double max) override;
void replot() override;
virtual Type getType() override { return Type::Waterfall;};
void fromJSON(nlohmann::json j) override;
nlohmann::json toJSON() override;
enum class XAxisType {
Frequency,
Time,
Distance,
Power,
Last,
};
enum class XAxisMode {
UseSpan,
FitTraces,
Manual,
Last,
};
void setXAxis(XAxisType type, XAxisMode mode, bool log, double min, double max, double div);
public slots:
void axisSetupDialog();
void resetWaterfall();
@ -43,6 +30,7 @@ protected:
virtual bool configureForTrace(Trace *t) override;
virtual void updateContextMenu() override;
virtual void draw(QPainter& p) override;
bool domainMatch(Trace *t);
virtual bool supported(Trace *t) override;
virtual QPoint markerToPixel(Marker *m) override { Q_UNUSED(m) return QPoint(0,0);};
@ -51,34 +39,33 @@ protected:
virtual QString mouseText(QPoint pos) override;
protected slots:
virtual bool xCoordinateVisible(double x) override;
virtual bool markerVisible(double x) override;
void traceDataChanged(unsigned int begin, unsigned int end);
private slots:
void updateAxisTicks();
void updateYAxis();
private:
static constexpr int AxisLabelSize = 10;
static QString AxisTypeToName(XAxisType type);
static QString AxisModeToName(XAxisMode mode);
static XAxisType XAxisTypeFromName(QString name);
static XAxisMode AxisModeFromName(QString name);
static QString AxisUnit(XAxisType type);
// color scale, input value from 0.0 to 1.0
QColor getColor(double scale);
class XAxis {
public:
XAxisType type;
XAxisMode mode;
bool log;
double rangeMin;
double rangeMax;
double rangeDiv;
std::vector<double> ticks;
enum class Direction {
TopToBottom,
BottomToTop,
};
XAxis XAxis;
Direction dir;
std::vector<std::vector<Trace::Data>> data;
Trace *trace;
XAxis XAxis;
YAxis YAxis;
std::deque<std::vector<Trace::Data>> data;
unsigned int pixelsPerLine;
int plotAreaLeft, plotAreaWidth, plotAreaBottom, plotAreaTop;
bool keepDataBeyondPlotSize;
unsigned int maxDataSweeps;
};
#endif // TRACEWATERFALL_H

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
#define TRACEXYPLOT_H
#include "traceplot.h"
#include "traceaxis.h"
#include <set>
@ -12,39 +13,6 @@ class TraceXYPlot : public TracePlot
public:
TraceXYPlot(TraceModel &model, QWidget *parent = nullptr);
enum class YAxisType {
Disabled,
// S parameter options
Magnitude,
MagnitudedBuV,
MagnitudeLinear,
Phase,
UnwrappedPhase,
VSWR,
Real,
Imaginary,
// derived parameter options
SeriesR,
Reactance,
Capacitance,
Inductance,
QualityFactor,
GroupDelay,
// TDR options
ImpulseReal,
ImpulseMag,
Step,
Impedance,
Last,
};
enum class XAxisType {
Frequency,
Time,
Distance,
Power,
Last,
};
enum class XAxisMode {
UseSpan,
FitTraces,
@ -52,8 +20,8 @@ public:
Last,
};
void setYAxis(int axis, YAxisType type, bool log, bool autorange, double min, double max, double div);
void setXAxis(XAxisType 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, double div);
void setXAxis(XAxis::Type type, XAxisMode mode, bool log, double min, double max, double div);
void enableTrace(Trace *t, bool enabled) override;
void updateSpan(double min, double max) override;
void replot() override;
@ -62,7 +30,7 @@ public:
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
bool isTDRtype(YAxisType type);
bool isTDRtype(YAxis::Type type);
public slots:
void axisSetupDialog();
@ -77,54 +45,26 @@ private slots:
void updateAxisTicks();
private:
static constexpr int AxisLabelSize = 10;
static QString AxisTypeToName(YAxisType type);
static QString AxisTypeToName(XAxisType type);
static QString AxisModeToName(XAxisMode mode);
static XAxisType XAxisTypeFromName(QString name);
static YAxisType YAxisTypeFromName(QString name);
static XAxisMode AxisModeFromName(QString name);
void enableTraceAxis(Trace *t, int axis, bool enabled);
bool domainMatch(Trace *t);
bool supported(Trace *t) override;
bool supported(Trace *t, YAxisType type);
QPointF traceToCoordinate(Trace *t, unsigned int sample, YAxisType type);
bool supported(Trace *t, YAxis::Type type);
QPointF traceToCoordinate(Trace *t, unsigned int sample, YAxis &yaxis);
QPoint plotValueToPixel(QPointF plotValue, int Yaxis);
QPointF pixelToPlotValue(QPoint pixel, int YAxis);
QPoint markerToPixel(Marker *m) override;
double nearestTracePoint(Trace *t, QPoint pixel, double *distance = nullptr) override;
virtual bool xCoordinateVisible(double x) override;
virtual bool markerVisible(double x) override;
void traceDropped(Trace *t, QPoint position) override;
QString mouseText(QPoint pos) override;
QString AxisUnit(YAxisType type);
QString AxisPrefixes(YAxisType type);
static QString AxisUnit(XAxisType type);
std::set<Trace*> tracesAxis[2];
class YAxis {
public:
YAxisType type;
bool log; // not used yet
bool autorange;
double rangeMin;
double rangeMax;
double rangeDiv;
std::vector<double> ticks;
};
class XAxis {
public:
XAxisType type;
XAxisMode mode;
bool log; // not used yet
double rangeMin;
double rangeMax;
double rangeDiv;
std::vector<double> ticks;
};
YAxis YAxis[2];
XAxis XAxis;
XAxisMode xAxisMode;
int plotAreaLeft, plotAreaWidth, plotAreaBottom, plotAreaTop;
};

View File

@ -0,0 +1,209 @@
#include "waterfallaxisdialog.h"
#include "ui_waterfallaxisdialog.h"
#include <QStandardItemModel>
using namespace std;
static void enableComboBoxItem(QComboBox *cb, int itemNum, bool enable) {
auto *model = qobject_cast<QStandardItemModel *>(cb->model());
auto item = model->item(itemNum);
item->setFlags(enable ? item->flags() | Qt::ItemIsEnabled
: item->flags() & ~Qt::ItemIsEnabled);
}
WaterfallAxisDialog::WaterfallAxisDialog(TraceWaterfall *plot) :
QDialog(nullptr),
ui(new Ui::WaterfallAxisDialog),
plot(plot)
{
ui->setupUi(this);
ui->Wtype->clear();
ui->Wtype->setMaxVisibleItems(20);
for(int i=0;i<(int) YAxis::Type::Last;i++) {
ui->Wtype->addItem(YAxis::TypeToName((YAxis::Type) i));
}
for(int i=0;i<(int) XAxis::Type::Last;i++) {
ui->XType->addItem(XAxis::TypeToName((XAxis::Type) i));
}
if(plot->getModel().getSource() == TraceModel::DataSource::SA) {
for(int i=0;i<ui->XType->count();i++) {
auto xtype = XAxis::TypeFromName(ui->XType->itemText(i));
if(!isSupported(xtype)) {
enableComboBoxItem(ui->XType, i, false);
}
}
}
// Setup GUI connections
connect(ui->Wtype, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index) {
//ui->Y1log->setEnabled(index != 0);
ui->Wlinear->setEnabled(index != 0);
ui->Wauto->setEnabled(index != 0);
bool autoRange = ui->Wauto->isChecked();
ui->Wmin->setEnabled(index != 0 && !autoRange);
ui->Wmax->setEnabled(index != 0 && !autoRange);
auto type = (YAxis::Type) index;
QString unit = YAxis::Unit(type);
QString prefixes = YAxis::Prefixes(type);
ui->Wmin->setUnit(unit);
ui->Wmin->setPrefixes(prefixes);
ui->Wmax->setUnit(unit);
ui->Wmax->setPrefixes(prefixes);
});
connect(ui->Wauto, &QCheckBox::toggled, [this](bool checked) {
ui->Wmin->setEnabled(!checked);
ui->Wmax->setEnabled(!checked);
});
connect(ui->Wmode, qOverload<int>(&QComboBox::currentIndexChanged), [this](int index) {
ui->WmaxSweeps->setEnabled(index == 1);
});
ui->XType->setCurrentIndex((int) plot->XAxis.getType());
ui->Wmin->setPrefixes("pnum kMG");
ui->Wmax->setPrefixes("pnum kMG");
XAxisTypeChanged((int) plot->XAxis.getType());
connect(ui->XType, qOverload<int>(&QComboBox::currentIndexChanged), this, &WaterfallAxisDialog::XAxisTypeChanged);
// Fill initial values
ui->Wtype->setCurrentIndex((int) plot->YAxis.getType());
if(plot->YAxis.getLog()) {
ui->Wlog->setChecked(true);
} else {
ui->Wlinear->setChecked(true);
}
ui->Wauto->setChecked(plot->YAxis.getAutorange());
ui->Wmin->setValueQuiet(plot->YAxis.getRangeMin());
ui->Wmax->setValueQuiet(plot->YAxis.getRangeMax());
if(plot->dir == TraceWaterfall::Direction::TopToBottom) {
ui->Wdir->setCurrentIndex(0);
} else {
ui->Wdir->setCurrentIndex(1);
}
ui->Wpixels->setValue(plot->pixelsPerLine);
ui->Wmode->setCurrentIndex(plot->keepDataBeyondPlotSize ? 1 : 0);
ui->WmaxSweeps->setValue(plot->maxDataSweeps);
if(plot->XAxis.getLog()) {
ui->Xlog->setChecked(true);
} else {
ui->Xlinear->setChecked(true);
}
}
WaterfallAxisDialog::~WaterfallAxisDialog()
{
delete ui;
}
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);
if(ui->Wdir->currentIndex() == 0) {
plot->dir = TraceWaterfall::Direction::TopToBottom;
} else {
plot->dir = TraceWaterfall::Direction::BottomToTop;
}
plot->pixelsPerLine = ui->Wpixels->value();
plot->maxDataSweeps = ui->WmaxSweeps->value();
if(ui->Wmode->currentIndex() == 0) {
plot->keepDataBeyondPlotSize = false;
} else {
plot->keepDataBeyondPlotSize = true;
}
}
void WaterfallAxisDialog::XAxisTypeChanged(int XAxisIndex)
{
auto type = (XAxis::Type) XAxisIndex;
auto supported = supportedYAxis(type);
for(unsigned int i=0;i<(int) YAxis::Type::Last;i++) {
auto t = (YAxis::Type) i;
auto enable = supported.count(t) > 0;
auto index = (int) t;
enableComboBoxItem(ui->Wtype, index, enable);
}
// Disable Yaxis if previously selected type is not supported
if(!supported.count((YAxis::Type)ui->Wtype->currentIndex())) {
ui->Wtype->setCurrentIndex(0);
}
if(type == XAxis::Type::Frequency) {
ui->Xlog->setEnabled(true);
} else {
// log option only available for frequency axis
if(ui->Xlog->isChecked()) {
ui->Xlinear->setChecked(true);
}
ui->Xlog->setEnabled(false);
}
}
std::set<YAxis::Type> WaterfallAxisDialog::supportedYAxis(XAxis::Type type)
{
set<YAxis::Type> ret = {YAxis::Type::Disabled};
auto source = plot->getModel().getSource();
if(source == TraceModel::DataSource::VNA) {
switch(type) {
case XAxis::Type::Frequency:
case XAxis::Type::Power:
ret.insert(YAxis::Type::Magnitude);
ret.insert(YAxis::Type::MagnitudeLinear);
ret.insert(YAxis::Type::Phase);
ret.insert(YAxis::Type::UnwrappedPhase);
ret.insert(YAxis::Type::VSWR);
ret.insert(YAxis::Type::Real);
ret.insert(YAxis::Type::Imaginary);
ret.insert(YAxis::Type::SeriesR);
ret.insert(YAxis::Type::Reactance);
ret.insert(YAxis::Type::Capacitance);
ret.insert(YAxis::Type::Inductance);
ret.insert(YAxis::Type::QualityFactor);
ret.insert(YAxis::Type::GroupDelay);
break;
case XAxis::Type::Time:
case XAxis::Type::Distance:
ret.insert(YAxis::Type::ImpulseReal);
ret.insert(YAxis::Type::ImpulseMag);
ret.insert(YAxis::Type::Step);
ret.insert(YAxis::Type::Impedance);
break;
default:
break;
}
} else if(source == TraceModel::DataSource::SA) {
switch(type) {
case XAxis::Type::Frequency:
ret.insert(YAxis::Type::Magnitude);
ret.insert(YAxis::Type::MagnitudedBuV);
break;
default:
break;
}
}
return ret;
}
bool WaterfallAxisDialog::isSupported(XAxis::Type type)
{
auto source = plot->getModel().getSource();
if(source == TraceModel::DataSource::VNA) {
// all X axis types are supported
return true;
} else if(source == TraceModel::DataSource::SA) {
if (type == XAxis::Type::Frequency) {
return true;
} else {
return false;
}
}
return false;
}

View File

@ -0,0 +1,31 @@
#ifndef WATERFALLAXISDIALOG_H
#define WATERFALLAXISDIALOG_H
#include "tracewaterfall.h"
#include <QDialog>
namespace Ui {
class WaterfallAxisDialog;
}
class WaterfallAxisDialog : public QDialog
{
Q_OBJECT
public:
explicit WaterfallAxisDialog(TraceWaterfall *plot);
~WaterfallAxisDialog();
private slots:
void on_buttonBox_accepted();
void XAxisTypeChanged(int XAxisIndex);
private:
std::set<YAxis::Type> supportedYAxis(XAxis::Type type);
bool isSupported(XAxis::Type type);
Ui::WaterfallAxisDialog *ui;
TraceWaterfall *plot;
};
#endif // WATERFALLAXISDIALOG_H

View File

@ -0,0 +1,378 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WaterfallAxisDialog</class>
<widget class="QDialog" name="WaterfallAxisDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>539</width>
<height>351</height>
</rect>
</property>
<property name="windowTitle">
<string>Axis Setup</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0,1">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>Waterfall</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="Wtype"/>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="Wlinear">
<property name="text">
<string>Linear</string>
</property>
<attribute name="buttonGroup">
<string notr="true">Y1group</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="Wlog">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Log</string>
</property>
<attribute name="buttonGroup">
<string notr="true">Y1group</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Range:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Maximum:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="Wmax"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Minimum:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="Wmin"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Direction:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="Wauto">
<property name="text">
<string>Auto</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="Wdir">
<item>
<property name="text">
<string>Top to bottom</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom to top</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Pixels per line:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="Wpixels">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>20</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Mode:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="Wmode">
<item>
<property name="text">
<string>Only keep visible data</string>
</property>
</item>
<item>
<property name="text">
<string>Keep offscreen data</string>
</property>
</item>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Number of sweeps:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="WmaxSweeps">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_13">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>X axis</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="XType">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QRadioButton" name="Xlinear">
<property name="text">
<string>Linear</string>
</property>
<attribute name="buttonGroup">
<string notr="true">Xgroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="Xlog">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Log</string>
</property>
<attribute name="buttonGroup">
<string notr="true">Xgroup</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SIUnitEdit</class>
<extends>QLineEdit</extends>
<header>CustomWidgets/siunitedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>WaterfallAxisDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>WaterfallAxisDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="Y1group"/>
<buttongroup name="Y2group"/>
<buttongroup name="Xgroup"/>
</buttongroups>
</ui>

View File

@ -24,18 +24,18 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
ui->Y1type->setMaxVisibleItems(20);
ui->Y2type->setMaxVisibleItems(20);
for(int i=0;i<(int) TraceXYPlot::YAxisType::Last;i++) {
ui->Y1type->addItem(TraceXYPlot::AxisTypeToName((TraceXYPlot::YAxisType) i));
ui->Y2type->addItem(TraceXYPlot::AxisTypeToName((TraceXYPlot::YAxisType) i));
for(int i=0;i<(int) YAxis::Type::Last;i++) {
ui->Y1type->addItem(YAxis::TypeToName((YAxis::Type) i));
ui->Y2type->addItem(YAxis::TypeToName((YAxis::Type) i));
}
for(int i=0;i<(int) TraceXYPlot::XAxisType::Last;i++) {
ui->XType->addItem(TraceXYPlot::AxisTypeToName((TraceXYPlot::XAxisType) i));
for(int i=0;i<(int) XAxis::Type::Last;i++) {
ui->XType->addItem(XAxis::TypeToName((XAxis::Type) i));
}
if(plot->getModel().getSource() == TraceModel::DataSource::SA) {
for(int i=0;i<ui->XType->count();i++) {
auto xtype = TraceXYPlot::XAxisTypeFromName(ui->XType->itemText(i));
auto xtype = XAxis::TypeFromName(ui->XType->itemText(i));
if(!isSupported(xtype)) {
enableComboBoxItem(ui->XType, i, false);
}
@ -51,9 +51,9 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
ui->Y1min->setEnabled(index != 0 && !autoRange);
ui->Y1max->setEnabled(index != 0 && !autoRange);
ui->Y1divs->setEnabled(index != 0 && !autoRange);
auto type = (TraceXYPlot::YAxisType) index;
QString unit = plot->AxisUnit(type);
QString prefixes = plot->AxisPrefixes(type);
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);
@ -75,9 +75,9 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
ui->Y2min->setEnabled(index != 0 && !autoRange);
ui->Y2max->setEnabled(index != 0 && !autoRange);
ui->Y2divs->setEnabled(index != 0 && !autoRange);
auto type = (TraceXYPlot::YAxisType) index;
QString unit = plot->AxisUnit(type);
QString prefixes = plot->AxisPrefixes(type);
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);
@ -99,7 +99,7 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
ui->Xautomode->setEnabled(checked);
});
ui->XType->setCurrentIndex((int) plot->XAxis.type);
ui->XType->setCurrentIndex((int) plot->XAxis.getType());
ui->Xmin->setPrefixes("pnum kMG");
ui->Xmax->setPrefixes("pnum kMG");
ui->Xdivs->setPrefixes("pnum kMG");
@ -112,49 +112,49 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
ui->Y2max->setPrefixes("pnum kMG");
ui->Y2divs->setPrefixes("pnum kMG");
XAxisTypeChanged((int) plot->XAxis.type);
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());
});
// Fill initial values
ui->Y1type->setCurrentIndex((int) plot->YAxis[0].type);
if(plot->YAxis[0].log) {
ui->Y1type->setCurrentIndex((int) plot->YAxis[0].getType());
if(plot->YAxis[0].getLog()) {
ui->Y1log->setChecked(true);
} else {
ui->Y1linear->setChecked(true);
}
ui->Y1auto->setChecked(plot->YAxis[0].autorange);
ui->Y1min->setValueQuiet(plot->YAxis[0].rangeMin);
ui->Y1max->setValueQuiet(plot->YAxis[0].rangeMax);
ui->Y1divs->setValueQuiet(plot->YAxis[0].rangeDiv);
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->Y2type->setCurrentIndex((int) plot->YAxis[1].type);
if(plot->YAxis[1].log) {
ui->Y2type->setCurrentIndex((int) plot->YAxis[1].getType());
if(plot->YAxis[1].getLog()) {
ui->Y2log->setChecked(true);
} else {
ui->Y2linear->setChecked(true);
}
ui->Y2auto->setChecked(plot->YAxis[1].autorange);
ui->Y2min->setValueQuiet(plot->YAxis[1].rangeMin);
ui->Y2max->setValueQuiet(plot->YAxis[1].rangeMax);
ui->Y2divs->setValueQuiet(plot->YAxis[1].rangeDiv);
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());
if(plot->XAxis.log) {
if(plot->XAxis.getLog()) {
ui->Xlog->setChecked(true);
} else {
ui->Xlinear->setChecked(true);
}
ui->Xauto->setChecked(plot->XAxis.mode != TraceXYPlot::XAxisMode::Manual);
if(plot->XAxis.mode == TraceXYPlot::XAxisMode::UseSpan) {
ui->Xauto->setChecked(plot->xAxisMode != TraceXYPlot::XAxisMode::Manual);
if(plot->xAxisMode == TraceXYPlot::XAxisMode::UseSpan) {
ui->Xautomode->setCurrentIndex(0);
} else {
ui->Xautomode->setCurrentIndex(1);
}
ui->Xmin->setValueQuiet(plot->XAxis.rangeMin);
ui->Xmax->setValueQuiet(plot->XAxis.rangeMax);
ui->Xdivs->setValueQuiet(plot->XAxis.rangeDiv);
ui->Xmin->setValueQuiet(plot->XAxis.getRangeMin());
ui->Xmax->setValueQuiet(plot->XAxis.getRangeMax());
ui->Xdivs->setValueQuiet(plot->XAxis.getRangeDiv());
}
XYplotAxisDialog::~XYplotAxisDialog()
@ -165,8 +165,8 @@ XYplotAxisDialog::~XYplotAxisDialog()
void XYplotAxisDialog::on_buttonBox_accepted()
{
// set plot values to the ones selected in the dialog
plot->setYAxis(0, (TraceXYPlot::YAxisType) ui->Y1type->currentIndex(), ui->Y1log->isChecked(), ui->Y1auto->isChecked(), ui->Y1min->value(), ui->Y1max->value(), ui->Y1divs->value());
plot->setYAxis(1, (TraceXYPlot::YAxisType) 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());
plot->setYAxis(1, (YAxis::Type) ui->Y2type->currentIndex(), ui->Y2log->isChecked(), ui->Y2auto->isChecked(), ui->Y2min->value(), ui->Y2max->value(), ui->Y2divs->value());
TraceXYPlot::XAxisMode mode;
if(ui->Xauto->isChecked()) {
if(ui->Xautomode->currentIndex() == 0) {
@ -177,29 +177,29 @@ void XYplotAxisDialog::on_buttonBox_accepted()
} else {
mode = TraceXYPlot::XAxisMode::Manual;
}
plot->setXAxis((TraceXYPlot::XAxisType) 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());
}
void XYplotAxisDialog::XAxisTypeChanged(int XAxisIndex)
{
auto type = (TraceXYPlot::XAxisType) XAxisIndex;
auto type = (XAxis::Type) XAxisIndex;
auto supported = supportedYAxis(type);
for(unsigned int i=0;i<(int) TraceXYPlot::YAxisType::Last;i++) {
auto t = (TraceXYPlot::YAxisType) i;
for(unsigned int i=0;i<(int) YAxis::Type::Last;i++) {
auto t = (YAxis::Type) i;
auto enable = supported.count(t) > 0;
auto index = (int) t;
enableComboBoxItem(ui->Y1type, index, enable);
enableComboBoxItem(ui->Y2type, index, enable);
}
// Disable Yaxis if previously selected type is not supported
if(!supported.count((TraceXYPlot::YAxisType)ui->Y1type->currentIndex())) {
if(!supported.count((YAxis::Type)ui->Y1type->currentIndex())) {
ui->Y1type->setCurrentIndex(0);
}
if(!supported.count((TraceXYPlot::YAxisType)ui->Y2type->currentIndex())) {
if(!supported.count((YAxis::Type)ui->Y2type->currentIndex())) {
ui->Y2type->setCurrentIndex(0);
}
if(type == TraceXYPlot::XAxisType::Frequency) {
if(type == XAxis::Type::Frequency) {
enableComboBoxItem(ui->Xautomode, 0, true);
ui->Xlog->setEnabled(true);
} else {
@ -215,49 +215,49 @@ void XYplotAxisDialog::XAxisTypeChanged(int XAxisIndex)
ui->Xlog->setEnabled(false);
}
QString unit = TraceXYPlot::AxisUnit(type);
QString unit = XAxis::Unit(type);
ui->Xmin->setUnit(unit);
ui->Xmax->setUnit(unit);
ui->Xdivs->setUnit(unit);
}
std::set<TraceXYPlot::YAxisType> XYplotAxisDialog::supportedYAxis(TraceXYPlot::XAxisType type)
std::set<YAxis::Type> XYplotAxisDialog::supportedYAxis(XAxis::Type type)
{
set<TraceXYPlot::YAxisType> ret = {TraceXYPlot::YAxisType::Disabled};
set<YAxis::Type> ret = {YAxis::Type::Disabled};
auto source = plot->getModel().getSource();
if(source == TraceModel::DataSource::VNA) {
switch(type) {
case TraceXYPlot::XAxisType::Frequency:
case TraceXYPlot::XAxisType::Power:
ret.insert(TraceXYPlot::YAxisType::Magnitude);
ret.insert(TraceXYPlot::YAxisType::MagnitudeLinear);
ret.insert(TraceXYPlot::YAxisType::Phase);
ret.insert(TraceXYPlot::YAxisType::UnwrappedPhase);
ret.insert(TraceXYPlot::YAxisType::VSWR);
ret.insert(TraceXYPlot::YAxisType::Real);
ret.insert(TraceXYPlot::YAxisType::Imaginary);
ret.insert(TraceXYPlot::YAxisType::SeriesR);
ret.insert(TraceXYPlot::YAxisType::Reactance);
ret.insert(TraceXYPlot::YAxisType::Capacitance);
ret.insert(TraceXYPlot::YAxisType::Inductance);
ret.insert(TraceXYPlot::YAxisType::QualityFactor);
ret.insert(TraceXYPlot::YAxisType::GroupDelay);
case XAxis::Type::Frequency:
case XAxis::Type::Power:
ret.insert(YAxis::Type::Magnitude);
ret.insert(YAxis::Type::MagnitudeLinear);
ret.insert(YAxis::Type::Phase);
ret.insert(YAxis::Type::UnwrappedPhase);
ret.insert(YAxis::Type::VSWR);
ret.insert(YAxis::Type::Real);
ret.insert(YAxis::Type::Imaginary);
ret.insert(YAxis::Type::SeriesR);
ret.insert(YAxis::Type::Reactance);
ret.insert(YAxis::Type::Capacitance);
ret.insert(YAxis::Type::Inductance);
ret.insert(YAxis::Type::QualityFactor);
ret.insert(YAxis::Type::GroupDelay);
break;
case TraceXYPlot::XAxisType::Time:
case TraceXYPlot::XAxisType::Distance:
ret.insert(TraceXYPlot::YAxisType::ImpulseReal);
ret.insert(TraceXYPlot::YAxisType::ImpulseMag);
ret.insert(TraceXYPlot::YAxisType::Step);
ret.insert(TraceXYPlot::YAxisType::Impedance);
case XAxis::Type::Time:
case XAxis::Type::Distance:
ret.insert(YAxis::Type::ImpulseReal);
ret.insert(YAxis::Type::ImpulseMag);
ret.insert(YAxis::Type::Step);
ret.insert(YAxis::Type::Impedance);
break;
default:
break;
}
} else if(source == TraceModel::DataSource::SA) {
switch(type) {
case TraceXYPlot::XAxisType::Frequency:
ret.insert(TraceXYPlot::YAxisType::Magnitude);
ret.insert(TraceXYPlot::YAxisType::MagnitudedBuV);
case XAxis::Type::Frequency:
ret.insert(YAxis::Type::Magnitude);
ret.insert(YAxis::Type::MagnitudedBuV);
break;
default:
break;
@ -266,14 +266,14 @@ std::set<TraceXYPlot::YAxisType> XYplotAxisDialog::supportedYAxis(TraceXYPlot::X
return ret;
}
bool XYplotAxisDialog::isSupported(TraceXYPlot::XAxisType type)
bool XYplotAxisDialog::isSupported(XAxis::Type type)
{
auto source = plot->getModel().getSource();
if(source == TraceModel::DataSource::VNA) {
// all X axis types are supported
return true;
} else if(source == TraceModel::DataSource::SA) {
if (type == TraceXYPlot::XAxisType::Frequency) {
if (type == XAxis::Type::Frequency) {
return true;
} else {
return false;

View File

@ -22,8 +22,8 @@ private slots:
void XAxisTypeChanged(int XAxisIndex);
private:
std::set<TraceXYPlot::YAxisType> supportedYAxis(TraceXYPlot::XAxisType type);
bool isSupported(TraceXYPlot::XAxisType type);
std::set<YAxis::Type> supportedYAxis(XAxis::Type type);
bool isSupported(XAxis::Type type);
Ui::XYplotAxisDialog *ui;
TraceXYPlot *plot;
};