diff --git a/Software/PC_Application/CustomWidgets/tilewidget.cpp b/Software/PC_Application/CustomWidgets/tilewidget.cpp index 2348a8a..33ed091 100644 --- a/Software/PC_Application/CustomWidgets/tilewidget.cpp +++ b/Software/PC_Application/CustomWidgets/tilewidget.cpp @@ -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"]); + } } } diff --git a/Software/PC_Application/LibreVNA-GUI.pro b/Software/PC_Application/LibreVNA-GUI.pro index 799b252..4052967 100644 --- a/Software/PC_Application/LibreVNA-GUI.pro +++ b/Software/PC_Application/LibreVNA-GUI.pro @@ -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 \ diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp index 7bd038a..a649dcc 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -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()) { diff --git a/Software/PC_Application/Traces/traceaxis.cpp b/Software/PC_Application/Traces/traceaxis.cpp new file mode 100644 index 0000000..bc6c9a8 --- /dev/null +++ b/Software/PC_Application/Traces/traceaxis.cpp @@ -0,0 +1,441 @@ +#include "traceaxis.h" +#include "Util/util.h" + +#include + +using namespace std; + +static void createEvenlySpacedTicks(vector& 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::epsilon() && ticks.size() <= maxTicks;tick+= step) { + ticks.push_back(tick); + } +} + +static double createAutomaticTicks(vector& 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& 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 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 &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); +} diff --git a/Software/PC_Application/Traces/traceaxis.h b/Software/PC_Application/Traces/traceaxis.h new file mode 100644 index 0000000..cc9ed8f --- /dev/null +++ b/Software/PC_Application/Traces/traceaxis.h @@ -0,0 +1,97 @@ +#ifndef TRACEAXIS_H +#define TRACEAXIS_H + +#include "tracemodel.h" + +#include +#include + +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 &getTicks() const; + +protected: + void updateTicks(); + bool log; + bool autorange; + double rangeMin; + double rangeMax; + double rangeDiv; + std::vector 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 diff --git a/Software/PC_Application/Traces/traceplot.cpp b/Software/PC_Application/Traces/traceplot.cpp index c7c9942..227bdbc 100644 --- a/Software/PC_Application/Traces/traceplot.cpp +++ b/Software/PC_Application/Traces/traceplot.cpp @@ -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; } diff --git a/Software/PC_Application/Traces/traceplot.h b/Software/PC_Application/Traces/traceplot.h index d134bcc..3ddc3f9 100644 --- a/Software/PC_Application/Traces/traceplot.h +++ b/Software/PC_Application/Traces/traceplot.h @@ -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; diff --git a/Software/PC_Application/Traces/tracesmithchart.cpp b/Software/PC_Application/Traces/tracesmithchart.cpp index 6ad0359..516aaa3 100644 --- a/Software/PC_Application/Traces/tracesmithchart.cpp +++ b/Software/PC_Application/Traces/tracesmithchart.cpp @@ -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) { diff --git a/Software/PC_Application/Traces/tracesmithchart.h b/Software/PC_Application/Traces/tracesmithchart.h index 08b0b85..c591763 100644 --- a/Software/PC_Application/Traces/tracesmithchart.h +++ b/Software/PC_Application/Traces/tracesmithchart.h @@ -123,7 +123,7 @@ protected: std::complex 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; diff --git a/Software/PC_Application/Traces/tracewaterfall.cpp b/Software/PC_Application/Traces/tracewaterfall.cpp index 8d95f20..c1724bd 100644 --- a/Software/PC_Application/Traces/tracewaterfall.cpp +++ b/Software/PC_Application/Traces/tracewaterfall.cpp @@ -3,6 +3,8 @@ #include "preferences.h" #include "unit.h" #include "Util/util.h" +#include "waterfallaxisdialog.h" +#include "appwindow.h" #include #include @@ -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(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(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 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& 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::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& 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()); + 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& 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;isample(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::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::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::lowest(); + for(auto sweep : data) { + for(unsigned int i=0;i 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(scale, 0.0, 1.0, 240, 0), 255, 255); + } else { + return Qt::black; } } diff --git a/Software/PC_Application/Traces/tracewaterfall.h b/Software/PC_Application/Traces/tracewaterfall.h index c058447..cb3c520 100644 --- a/Software/PC_Application/Traces/tracewaterfall.h +++ b/Software/PC_Application/Traces/tracewaterfall.h @@ -3,38 +3,25 @@ #include "traceplot.h" +#include "traceaxis.h" + +#include + 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 ticks; + enum class Direction { + TopToBottom, + BottomToTop, }; - XAxis XAxis; + Direction dir; - std::vector> data; + Trace *trace; + + XAxis XAxis; + YAxis YAxis; + + std::deque> data; unsigned int pixelsPerLine; int plotAreaLeft, plotAreaWidth, plotAreaBottom, plotAreaTop; + bool keepDataBeyondPlotSize; + unsigned int maxDataSweeps; }; #endif // TRACEWATERFALL_H diff --git a/Software/PC_Application/Traces/tracexyplot.cpp b/Software/PC_Application/Traces/tracexyplot.cpp index 572f1aa..13d2f24 100644 --- a/Software/PC_Application/Traces/tracexyplot.cpp +++ b/Software/PC_Application/Traces/tracexyplot.cpp @@ -21,37 +21,20 @@ using namespace std; TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent) : TracePlot(model, parent) { - YAxis[0].log = false; - YAxis[0].type = YAxisType::Disabled; - YAxis[0].rangeDiv = 1; - YAxis[0].rangeMax = 10; - YAxis[0].rangeMin = 0; - YAxis[0].autorange = false; - YAxis[1].log = false; - YAxis[1].type = YAxisType::Disabled; - YAxis[1].rangeDiv = 1; - YAxis[1].rangeMax = 10; - YAxis[1].rangeMin = 0; - YAxis[1].autorange = false; - XAxis.type = XAxisType::Frequency; - XAxis.log = false; - XAxis.rangeDiv = 1; - XAxis.rangeMax = 10; - XAxis.rangeMin = 0; - XAxis.mode = XAxisMode::UseSpan; + xAxisMode = XAxisMode::UseSpan; // Setup default axis - setYAxis(0, YAxisType::Magnitude, false, false, -120, 20, 10); - setYAxis(1, YAxisType::Phase, false, false, -180, 180, 30); + setYAxis(0, YAxis::Type::Magnitude, false, false, -120, 20, 10); + setYAxis(1, YAxis::Type::Phase, false, false, -180, 180, 30); // enable autoscaling and set for full span (no information about actual span available yet) updateSpan(0, 6000000000); - setXAxis(XAxisType::Frequency, XAxisMode::UseSpan, false, 0, 6000000000, 600000000); + setXAxis(XAxis::Type::Frequency, XAxisMode::UseSpan, false, 0, 6000000000, 600000000); initializeTraceInfo(); } -void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType 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, double div) { - if(YAxis[axis].type != type) { + if(YAxis[axis].getType() != type) { // remove traces that are active but not supported with the new axis type bool erased = false; do { @@ -64,29 +47,22 @@ void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool } } } while(erased); - YAxis[axis].type = type; } - YAxis[axis].log = log; - YAxis[axis].autorange = autorange; - YAxis[axis].rangeMin = min; - YAxis[axis].rangeMax = max; - YAxis[axis].rangeDiv = div; + YAxis[axis].set(type, log, autorange, min, max, div); traceRemovalPending = true; - updateAxisTicks(); updateContextMenu(); replot(); } -void TraceXYPlot::setXAxis(XAxisType 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, double div) { - XAxis.type = type; - XAxis.mode = mode; - XAxis.log = log; - XAxis.rangeMin = min; - XAxis.rangeMax = max; - XAxis.rangeDiv = div; + bool autorange = false; + if(mode == XAxisMode::FitTraces || mode == XAxisMode::UseSpan) { + autorange = true; + } + XAxis.set(type, log, autorange, min, max, div); + xAxisMode = mode; traceRemovalPending = true; - updateAxisTicks(); updateContextMenu(); replot(); } @@ -94,7 +70,7 @@ void TraceXYPlot::setXAxis(XAxisType type, XAxisMode mode, bool log, double min, void TraceXYPlot::enableTrace(Trace *t, bool enabled) { for(int axis = 0;axis < 2;axis++) { - enableTraceAxis(t, axis, enabled && supported(t, YAxis[axis].type)); + enableTraceAxis(t, axis, enabled && supported(t, YAxis[axis].getType())); } } @@ -106,7 +82,7 @@ void TraceXYPlot::updateSpan(double min, double max) void TraceXYPlot::replot() { - if(XAxis.mode != XAxisMode::Manual || YAxis[0].autorange || YAxis[1].autorange) { + if(xAxisMode != XAxisMode::Manual || YAxis[0].getAutorange() || YAxis[1].getAutorange()) { updateAxisTicks(); } TracePlot::replot(); @@ -116,21 +92,21 @@ nlohmann::json TraceXYPlot::toJSON() { nlohmann::json j; nlohmann::json jX; - jX["type"] = AxisTypeToName(XAxis.type).toStdString(); - jX["mode"] = AxisModeToName(XAxis.mode).toStdString(); - jX["log"] = XAxis.log; - jX["min"] = XAxis.rangeMin; - jX["max"] = XAxis.rangeMax; - jX["div"] = XAxis.rangeDiv; + jX["type"] = XAxis.TypeToName().toStdString(); + jX["mode"] = AxisModeToName(xAxisMode).toStdString(); + jX["log"] = XAxis.getLog(); + jX["min"] = XAxis.getRangeMin(); + jX["max"] = XAxis.getRangeMax(); + jX["div"] = XAxis.getRangeDiv(); j["XAxis"] = jX; for(unsigned int i=0;i<2;i++) { nlohmann::json jY; - jY["type"] = AxisTypeToName(YAxis[i].type).toStdString(); - jY["log"] = YAxis[i].log; - jY["autorange"] = YAxis[i].autorange; - jY["min"] = YAxis[i].rangeMin; - jY["max"] = YAxis[i].rangeMax; - jY["div"] = YAxis[i].rangeDiv; + jY["type"] = YAxis[i].TypeToName().toStdString(); + jY["log"] = YAxis[i].getLog(); + jY["autorange"] = YAxis[i].getAutorange(); + jY["min"] = YAxis[i].getRangeMin(); + jY["max"] = YAxis[i].getRangeMax(); + jY["div"] = YAxis[i].getRangeDiv(); nlohmann::json jtraces; for(auto t : tracesAxis[i]) { jtraces.push_back(t->toHash()); @@ -151,11 +127,11 @@ void TraceXYPlot::fromJSON(nlohmann::json j) auto jX = j["XAxis"]; // old format used enum value for type and mode, new format uses string encoding (more robust when additional enum values are added). // Check which format is used and parse accordingly - XAxisType xtype; + XAxis::Type xtype; if(jX["type"].type() == nlohmann::json::value_t::string) { - xtype = XAxisTypeFromName(QString::fromStdString(jX["type"])); + xtype = XAxis::TypeFromName(QString::fromStdString(jX["type"])); } else { - xtype = jX.value("type", XAxisType::Frequency); + xtype = jX.value("type", XAxis::Type::Frequency); } XAxisMode xmode; if(jX["mode"].type() == nlohmann::json::value_t::string) { @@ -171,11 +147,11 @@ void TraceXYPlot::fromJSON(nlohmann::json j) setXAxis(xtype, xmode, xlog, xmin, xmax, xdiv); nlohmann::json jY[2] = {j["YPrimary"], j["YSecondary"]}; for(unsigned int i=0;i<2;i++) { - YAxisType ytype; + YAxis::Type ytype; if(jY[i]["type"].type() == nlohmann::json::value_t::string) { - ytype = YAxisTypeFromName(QString::fromStdString(jY[i]["type"])); + ytype = YAxis::TypeFromName(QString::fromStdString(jY[i]["type"])); } else { - ytype = jY[i].value("type", YAxisType::Disabled); + ytype = jY[i].value("type", YAxis::Type::Disabled); } auto yauto = jY[i].value("autorange", true); auto ylog = jY[i].value("log", false); @@ -200,13 +176,13 @@ void TraceXYPlot::fromJSON(nlohmann::json j) } } -bool TraceXYPlot::isTDRtype(TraceXYPlot::YAxisType type) +bool TraceXYPlot::isTDRtype(YAxis::Type type) { switch(type) { - case YAxisType::ImpulseReal: - case YAxisType::ImpulseMag: - case YAxisType::Step: - case YAxisType::Impedance: + case YAxis::Type::ImpulseReal: + case YAxis::Type::ImpulseMag: + case YAxis::Type::Step: + case YAxis::Type::Impedance: return true; default: return false; @@ -225,19 +201,19 @@ bool TraceXYPlot::configureForTrace(Trace *t) { switch(t->outputType()) { case Trace::DataType::Frequency: - setXAxis(XAxisType::Frequency, XAxisMode::FitTraces, false, 0, 1, 0.1); - setYAxis(0, YAxisType::Magnitude, false, true, 0, 1, 1.0); - setYAxis(1, YAxisType::Phase, false, true, 0, 1, 1.0); + setXAxis(XAxis::Type::Frequency, XAxisMode::FitTraces, false, 0, 1, 0.1); + setYAxis(0, YAxis::Type::Magnitude, false, true, 0, 1, 1.0); + setYAxis(1, YAxis::Type::Phase, false, true, 0, 1, 1.0); break; case Trace::DataType::Time: - setXAxis(XAxisType::Time, XAxisMode::FitTraces, false, 0, 1, 0.1); - setYAxis(0, YAxisType::ImpulseMag, false, true, 0, 1, 1.0); - setYAxis(1, YAxisType::Disabled, false, true, 0, 1, 1.0); + setXAxis(XAxis::Type::Time, XAxisMode::FitTraces, false, 0, 1, 0.1); + setYAxis(0, YAxis::Type::ImpulseMag, false, true, 0, 1, 1.0); + setYAxis(1, YAxis::Type::Disabled, false, true, 0, 1, 1.0); break; case Trace::DataType::Power: - setXAxis(XAxisType::Power, XAxisMode::FitTraces, false, 0, 1, 0.1); - setYAxis(0, YAxisType::Magnitude, false, true, 0, 1, 1.0); - setYAxis(1, YAxisType::Phase, false, true, 0, 1, 1.0); + setXAxis(XAxis::Type::Power, XAxisMode::FitTraces, false, 0, 1, 0.1); + setYAxis(0, YAxis::Type::Magnitude, false, true, 0, 1, 1.0); + setYAxis(1, YAxis::Type::Phase, false, true, 0, 1, 1.0); break; case Trace::DataType::Invalid: // unable to add @@ -286,7 +262,7 @@ void TraceXYPlot::updateContextMenu() }); for(int axis = 0;axis < 2;axis++) { - if(YAxis[axis].type == YAxisType::Disabled) { + if(YAxis[axis].getType() == YAxis::Type::Disabled) { continue; } if(axis == 0) { @@ -296,7 +272,7 @@ void TraceXYPlot::updateContextMenu() } for(auto t : traces) { // Skip traces that are not applicable for the selected axis type - if(!supported(t.first, YAxis[axis].type)) { + if(!supported(t.first, YAxis[axis].getType())) { continue; } @@ -333,7 +309,7 @@ bool TraceXYPlot::dropSupported(Trace *t) bool TraceXYPlot::supported(Trace *t) { // potentially possible to add every kind of trace (depends on axis) - if(supported(t, YAxis[0].type) || supported(t, YAxis[1].type)) { + if(supported(t, YAxis[0].getType()) || supported(t, YAxis[1].getType())) { return true; } else { // no axis @@ -352,16 +328,16 @@ void TraceXYPlot::draw(QPainter &p) auto pen = QPen(pref.Graphs.Color.axis, 0); pen.setCosmetic(true); p.setPen(pen); - plotAreaLeft = YAxis[0].type == YAxisType::Disabled ? yAxisDisabledSpace : yAxisSpace; + plotAreaLeft = YAxis[0].getType() == YAxis::Type::Disabled ? yAxisDisabledSpace : yAxisSpace; plotAreaWidth = w.width(); plotAreaTop = 10; plotAreaBottom = w.height() - xAxisSpace; - if(YAxis[0].type != YAxisType::Disabled) { + if(YAxis[0].getType() != YAxis::Type::Disabled) { plotAreaWidth -= yAxisSpace; } else { plotAreaWidth -= yAxisDisabledSpace; } - if(YAxis[1].type != YAxisType::Disabled) { + if(YAxis[1].getType() != YAxis::Type::Disabled) { plotAreaWidth -= yAxisSpace; } else { plotAreaWidth -= yAxisDisabledSpace; @@ -374,12 +350,12 @@ void TraceXYPlot::draw(QPainter &p) 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()); for(int i=0;i<2;i++) { - if(YAxis[i].type == YAxisType::Disabled) { + if(YAxis[i].getType() == YAxis::Type::Disabled) { continue; } - QString labelY = AxisTypeToName(YAxis[i].type); + QString labelY = YAxis[i].TypeToName(); p.setPen(QPen(pref.Graphs.Color.axis, 1)); auto xStart = i == 0 ? 0 : w.width() - AxisLabelSize * 1.5; p.save(); @@ -388,20 +364,20 @@ void TraceXYPlot::draw(QPainter &p) p.drawText(QRect(0, 0, w.height()-xAxisSpace, AxisLabelSize*1.5), Qt::AlignHCenter, labelY); p.restore(); // draw ticks - if(YAxis[i].type != YAxisType::Disabled && YAxis[i].ticks.size() > 0) { + if(YAxis[i].getType() != YAxis::Type::Disabled && YAxis[i].getTicks().size() > 0) { // this only works for evenly distributed ticks: - auto max = qMax(abs(YAxis[i].ticks.front()), abs(YAxis[i].ticks.back())); + auto max = qMax(abs(YAxis[i].getTicks().front()), abs(YAxis[i].getTicks().back())); double step; - if(YAxis[i].ticks.size() >= 2) { - step = abs(YAxis[i].ticks[0] - YAxis[i].ticks[1]); + if(YAxis[i].getTicks().size() >= 2) { + step = abs(YAxis[i].getTicks()[0] - YAxis[i].getTicks()[1]); } else { // only one tick, set arbitrary number of digits step = max / 1000; } int significantDigits = floor(log10(max)) - floor(log10(step)) + 1; - for(unsigned int j = 0; j < YAxis[i].ticks.size(); j++) { - auto yCoord = Util::Scale(YAxis[i].ticks[j], YAxis[i].rangeMax, YAxis[i].rangeMin, plotAreaTop, w.height() - xAxisSpace); + for(unsigned int j = 0; j < YAxis[i].getTicks().size(); j++) { + auto yCoord = YAxis[i].transform(YAxis[i].getTicks()[j], w.height() - xAxisSpace, plotAreaTop); p.setPen(QPen(pref.Graphs.Color.axis, 1)); // draw tickmark on axis auto tickStart = i == 0 ? plotAreaLeft : plotAreaLeft + plotAreaWidth; @@ -410,10 +386,10 @@ void TraceXYPlot::draw(QPainter &p) QString unit = ""; QString prefix = " "; if(pref.Graphs.showUnits) { - unit = AxisUnit(YAxis[i].type); - prefix = AxisPrefixes(YAxis[i].type); + unit = YAxis[i].Unit(); + prefix = YAxis[i].Prefixes(); } - auto tickValue = Unit::ToString(YAxis[i].ticks[j], unit, prefix, significantDigits); + auto tickValue = Unit::ToString(YAxis[i].getTicks()[j], unit, prefix, significantDigits); if(i == 0) { p.drawText(QRectF(0, yCoord - AxisLabelSize/2 - 2, tickStart + 2 * tickLen, AxisLabelSize), Qt::AlignRight, tickValue); } else { @@ -430,8 +406,8 @@ void TraceXYPlot::draw(QPainter &p) if (pref.Graphs.Color.Ticks.Background.enabled) { if (j%2) { - int yCoordTop = Util::Scale(YAxis[i].ticks[j], YAxis[i].rangeMax, YAxis[i].rangeMin, plotAreaTop, w.height() - xAxisSpace); - int yCoordBot = Util::Scale(YAxis[i].ticks[j-1], YAxis[i].rangeMax, YAxis[i].rangeMin, plotAreaTop, w.height() - xAxisSpace); + int yCoordTop = YAxis[i].transform(YAxis[i].getTicks()[j], plotAreaTop, w.height() - xAxisSpace); + int yCoordBot = YAxis[i].transform(YAxis[i].getTicks()[j-1], plotAreaTop, w.height() - xAxisSpace); if(yCoordTop > yCoordBot) { auto buf = yCoordBot; yCoordBot = yCoordTop; @@ -465,8 +441,8 @@ void TraceXYPlot::draw(QPainter &p) p.setPen(pen); auto nPoints = t->size(); for(unsigned int j=1;jgetMarkers(); for(auto m : markers) { double xPosition = m->getPosition(); - if (xPosition < XAxis.rangeMin || xPosition > XAxis.rangeMax) { + if (xPosition < XAxis.getRangeMin() || xPosition > XAxis.getRangeMax()) { // marker not in graph range continue; } @@ -500,12 +476,12 @@ void TraceXYPlot::draw(QPainter &p) QPointF markerPoint; if(xPosition < t->sample(index).x && index > 0) { // marker is not located exactly at this point, interpolate display location - QPointF l0 = traceToCoordinate(t, index - 1, YAxis[i].type); - QPointF l1 = traceToCoordinate(t, index, YAxis[i].type); + QPointF l0 = traceToCoordinate(t, index - 1, YAxis[i]); + QPointF l1 = traceToCoordinate(t, index, YAxis[i]); auto t0 = (xPosition - t->sample(index - 1).x) / (t->sample(index).x - t->sample(index - 1).x); markerPoint = l0 + (l1 - l0) * t0; } else { - markerPoint = traceToCoordinate(t, t->index(xPosition), YAxis[i].type); + markerPoint = traceToCoordinate(t, t->index(xPosition), YAxis[i]); } auto point = plotValueToPixel(markerPoint, i); if(!plotRect.contains(point)) { @@ -521,19 +497,19 @@ void TraceXYPlot::draw(QPainter &p) p.setClipping(false); } - 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; @@ -545,11 +521,11 @@ void TraceXYPlot::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()); @@ -564,8 +540,8 @@ void TraceXYPlot::draw(QPainter &p) } int lastTickLabelEnd = 0; - for(auto t : XAxis.ticks) { - auto xCoord = Util::Scale(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) { @@ -603,8 +579,8 @@ void TraceXYPlot::draw(QPainter &p) p.setOpacity(0.5); p.setBrush(Qt::white); p.setPen(Qt::white); - if((YAxis[0].type == YAxisType::Disabled || !supported(dropTrace, YAxis[0].type)) - || (YAxis[1].type == YAxisType::Disabled || !supported(dropTrace, YAxis[1].type))) { + if((YAxis[0].getType() == YAxis::Type::Disabled || !supported(dropTrace, YAxis[0].getType())) + || (YAxis[1].getType() == YAxis::Type::Disabled || !supported(dropTrace, YAxis[1].getType()))) { // only one axis enabled, show drop area over whole plot p.drawRect(plotRect); auto font = p.font(); @@ -641,97 +617,14 @@ void TraceXYPlot::draw(QPainter &p) void TraceXYPlot::updateAxisTicks() { - auto createEvenlySpacedTicks = [](vector& 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::epsilon() && ticks.size() <= maxTicks;tick+= step) { - ticks.push_back(tick); - } - }; - - auto createAutomaticTicks = [](vector& 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; - } - 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& 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); - }; - - 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(); + if(xAxisMode != XAxisMode::Manual) { // automatic mode, figure out limits double max = std::numeric_limits::lowest(); double min = std::numeric_limits::max(); - if(XAxis.mode == XAxisMode::UseSpan) { + if(xAxisMode == XAxisMode::UseSpan) { min = sweep_fmin; max = sweep_fmax; - } else if(XAxis.mode == XAxisMode::FitTraces) { + } else if(xAxisMode == XAxisMode::FitTraces) { for(auto t : traces) { bool enabled = (tracesAxis[0].find(t.first) != tracesAxis[0].end() || tracesAxis[1].find(t.first) != tracesAxis[1].end()); @@ -744,7 +637,7 @@ void TraceXYPlot::updateAxisTicks() // this trace is currently displayed double trace_min = trace->minX(); double trace_max = trace->maxX(); - if(XAxis.type == XAxisType::Distance) { + if(XAxis.getType() == XAxis::Type::Distance) { trace_min = trace->timeToDistance(trace_min); trace_max = trace->timeToDistance(trace_max); } @@ -758,21 +651,12 @@ void TraceXYPlot::updateAxisTicks() } } 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); - } + XAxis.set(XAxis.getType(), XAxis.getLog(), true, min, max, 0); } } for(int i=0;i<2;i++) { - if(!YAxis[i].autorange) { - createEvenlySpacedTicks(YAxis[i].ticks, YAxis[i].rangeMin, YAxis[i].rangeMax, YAxis[i].rangeDiv); - } else { + if(YAxis[i].getAutorange()) { // automatic mode, figure out limits double max = std::numeric_limits::lowest(); double min = std::numeric_limits::max(); @@ -782,9 +666,9 @@ void TraceXYPlot::updateAxisTicks() } unsigned int samples = t->size(); for(unsigned int j=0;j XAxis.rangeMax) { + if(point.x() < XAxis.getRangeMin() || point.x() > XAxis.getRangeMax()) { // this point is not in the displayed X range, skip for auto Y range calculation continue; } @@ -820,22 +704,20 @@ void TraceXYPlot::updateAxisTicks() max = 1.0; min = -1.0; } - YAxis[i].rangeMin = min; - YAxis[i].rangeMax = max; - YAxis[i].rangeDiv = createAutomaticTicks(YAxis[i].ticks, min, max, 8); + YAxis[i].set(YAxis[i].getType(), YAxis[i].getLog(), true, min, max, 0); } } } -QString TraceXYPlot::AxisTypeToName(TraceXYPlot::XAxisType type) +TraceXYPlot::XAxisMode TraceXYPlot::AxisModeFromName(QString name) { - 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"; - } + 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 TraceXYPlot::AxisModeToName(TraceXYPlot::XAxisMode mode) @@ -848,69 +730,9 @@ QString TraceXYPlot::AxisModeToName(TraceXYPlot::XAxisMode mode) } } -TraceXYPlot::XAxisType TraceXYPlot::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; -} - -TraceXYPlot::YAxisType TraceXYPlot::YAxisTypeFromName(QString name) -{ - for(unsigned int i=0;i<(int) YAxisType::Last;i++) { - if(AxisTypeToName((YAxisType) i) == name) { - return (YAxisType) i; - } - } - // not found, use default - return YAxisType::Magnitude; -} - -TraceXYPlot::XAxisMode TraceXYPlot::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 TraceXYPlot::AxisTypeToName(TraceXYPlot::YAxisType type) -{ - switch(type) { - case YAxisType::Disabled: return "Disabled"; - case YAxisType::Magnitude: return "Magnitude (dB/dBm)"; - case YAxisType::MagnitudedBuV: return "Magnitude (dBuV)"; - case YAxisType::MagnitudeLinear: return "Magnitude (linear)"; - case YAxisType::Phase: return "Phase"; - case YAxisType::UnwrappedPhase: return "Unwrapped Phase"; - case YAxisType::VSWR: return "VSWR"; - case YAxisType::Real: return "Real"; - case YAxisType::Imaginary: return "Imaginary"; - case YAxisType::SeriesR: return "Resistance"; - case YAxisType::Reactance: return "Reactance"; - case YAxisType::Capacitance: return "Capacitance"; - case YAxisType::Inductance: return "Inductance"; - case YAxisType::QualityFactor: return "Quality Factor"; - case YAxisType::GroupDelay: return "Group delay"; - case YAxisType::ImpulseReal: return "Impulse Response (Real)"; - case YAxisType::ImpulseMag: return "Impulse Response (Magnitude)"; - case YAxisType::Step: return "Step Response"; - case YAxisType::Impedance: return "Impedance"; - case YAxisType::Last: return "Unknown"; - } - return "Missing case"; -} - void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled) { - if(enabled && !supported(t, YAxis[axis].type)) { + if(enabled && !supported(t, YAxis[axis].getType())) { // unable to add trace to the requested axis return; } @@ -940,40 +762,40 @@ void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled) bool TraceXYPlot::domainMatch(Trace *t) { - switch(XAxis.type) { - case XAxisType::Frequency: + switch(XAxis.getType()) { + case XAxis::Type::Frequency: return t->outputType() == Trace::DataType::Frequency; - case XAxisType::Distance: - case XAxisType::Time: + case XAxis::Type::Distance: + case XAxis::Type::Time: return t->outputType() == Trace::DataType::Time; - case XAxisType::Power: + case XAxis::Type::Power: return t->outputType() == Trace::DataType::Power; - case XAxisType::Last: + case XAxis::Type::Last: return false; } return false; } -bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type) +bool TraceXYPlot::supported(Trace *t, YAxis::Type type) { if(!domainMatch(t)) { return false; } switch(type) { - case YAxisType::Disabled: + case YAxis::Type::Disabled: return false; - case YAxisType::VSWR: - case YAxisType::SeriesR: - case YAxisType::Reactance: - case YAxisType::Capacitance: - case YAxisType::Inductance: - case YAxisType::QualityFactor: + 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 YAxisType::GroupDelay: + case YAxis::Type::GroupDelay: if(t->isReflection()) { return false; } @@ -984,134 +806,34 @@ bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type) return true; } -QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlot::YAxisType type) +QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, class YAxis &yaxis) { QPointF ret = QPointF(numeric_limits::quiet_NaN(), numeric_limits::quiet_NaN()); - auto data = t->sample(sample); - switch(XAxis.type) { - case XAxisType::Distance: - ret.setX(t->timeToDistance(data.x)); - break; - default: - ret.setX(data.x); - break; - } - switch(type) { - case YAxisType::Magnitude: - ret.setY(Util::SparamTodB(data.y)); - break; - case YAxisType::MagnitudedBuV: - ret.setY(Util::dBmTodBuV(Util::SparamTodB(data.y))); - break; - case YAxisType::MagnitudeLinear: - ret.setY(abs(data.y)); - break; - case YAxisType::Phase: - ret.setY(Util::SparamToDegree(data.y)); - break; - case YAxisType::UnwrappedPhase: - ret.setY(t->getUnwrappedPhase(sample) * 180.0 / M_PI); - break; - case YAxisType::VSWR: - ret.setY(Util::SparamToVSWR(data.y)); - break; - case YAxisType::Real: - ret.setY(data.y.real()); - break; - case YAxisType::Imaginary: - ret.setY(data.y.imag()); - break; - case YAxisType::SeriesR: - ret.setY(Util::SparamToResistance(data.y)); - break; - case YAxisType::Reactance: - ret.setY(Util::SparamToImpedance(data.y).imag()); - break; - case YAxisType::Capacitance: - ret.setY(Util::SparamToCapacitance(data.y, data.x)); - break; - case YAxisType::Inductance: - ret.setY(Util::SparamToInductance(data.y, data.x)); - break; - case YAxisType::QualityFactor: - ret.setY(Util::SparamToQualityFactor(data.y)); - 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 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: - ret.setY(real(data.y)); - break; - case YAxisType::ImpulseMag: - ret.setY(Util::SparamTodB(data.y)); - break; - case YAxisType::Step: - ret.setY(t->sample(sample, true).y.real()); - break; - case YAxisType::Impedance: { - double step = t->sample(sample, true).y.real(); - if(abs(step) < 1.0) { - ret.setY(Util::SparamToImpedance(step).real()); - } - } - break; - case YAxisType::Disabled: - case YAxisType::Last: - // no valid axis - break; - } + ret.setX(XAxis.sampleToCoordinate(t->sample(sample), t, sample)); + ret.setY(yaxis.sampleToCoordinate(t->sample(sample), t, sample)); return ret; } QPoint TraceXYPlot::plotValueToPixel(QPointF plotValue, int Yaxis) { QPoint p; - p.setX(Util::Scale(plotValue.x(), XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth, XAxis.log)); - p.setY(Util::Scale(plotValue.y(), YAxis[Yaxis].rangeMin, YAxis[Yaxis].rangeMax, plotAreaBottom, plotAreaTop)); + p.setX(XAxis.transform(plotValue.x(), plotAreaLeft, plotAreaLeft + plotAreaWidth)); + p.setY(YAxis[Yaxis].transform(plotValue.y(), plotAreaBottom, plotAreaTop)); return p; } QPointF TraceXYPlot::pixelToPlotValue(QPoint pixel, int Yaxis) { QPointF p; - p.setX(Util::Scale(pixel.x(), plotAreaLeft, plotAreaLeft + plotAreaWidth, XAxis.rangeMin, XAxis.rangeMax, false, XAxis.log)); - p.setY(Util::Scale(pixel.y(), plotAreaBottom, plotAreaTop, YAxis[Yaxis].rangeMin, YAxis[Yaxis].rangeMax)); + p.setX(XAxis.inverseTransform(pixel.x(), plotAreaLeft, plotAreaLeft + plotAreaWidth)); + p.setY(YAxis[Yaxis].inverseTransform(pixel.y(), plotAreaBottom, plotAreaTop)); return p; } QPoint TraceXYPlot::markerToPixel(Marker *m) { auto t = m->getTrace(); - QPointF plotPoint = traceToCoordinate(t, t->index(m->getPosition()), YAxis[0].type); + QPointF plotPoint = traceToCoordinate(t, t->index(m->getPosition()), YAxis[0]); return plotValueToPixel(plotPoint, 0); } @@ -1126,7 +848,7 @@ double TraceXYPlot::nearestTracePoint(Trace *t, QPoint pixel, double *distance) unsigned int closestIndex = 0; auto samples = t->size(); for(unsigned int i=0;i 0) { - auto l1 = plotValueToPixel(traceToCoordinate(t, closestIndex - 1, YAxis[0].type), 0); - auto l2 = plotValueToPixel(traceToCoordinate(t, closestIndex, YAxis[0].type), 0); + auto l1 = plotValueToPixel(traceToCoordinate(t, closestIndex - 1, YAxis[0]), 0); + auto l2 = plotValueToPixel(traceToCoordinate(t, closestIndex, YAxis[0]), 0); double ratio; auto distance = Util::distanceToLine(pixel, l1, l2, nullptr, &ratio); if(distance < closestDistance) { @@ -1151,8 +873,8 @@ double TraceXYPlot::nearestTracePoint(Trace *t, QPoint pixel, double *distance) } } if(closestIndex < t->size() - 1) { - auto l1 = plotValueToPixel(traceToCoordinate(t, closestIndex, YAxis[0].type), 0); - auto l2 = plotValueToPixel(traceToCoordinate(t, closestIndex + 1, YAxis[0].type), 0); + auto l1 = plotValueToPixel(traceToCoordinate(t, closestIndex, YAxis[0]), 0); + auto l2 = plotValueToPixel(traceToCoordinate(t, closestIndex + 1, YAxis[0]), 0); double ratio; auto distance = Util::distanceToLine(pixel, l1, l2, nullptr, &ratio); if(distance < closestDistance) { @@ -1160,7 +882,7 @@ double TraceXYPlot::nearestTracePoint(Trace *t, QPoint pixel, double *distance) closestXpos = t->sample(closestIndex).x + (t->sample(closestIndex+1).x - t->sample(closestIndex).x) * ratio; } } - if(XAxis.type == XAxisType::Distance) { + if(XAxis.getType() == XAxis::Type::Distance) { closestXpos = t->distanceToTime(closestXpos); } if(distance) { @@ -1169,9 +891,9 @@ double TraceXYPlot::nearestTracePoint(Trace *t, QPoint pixel, double *distance) return closestXpos; } -bool TraceXYPlot::xCoordinateVisible(double x) +bool TraceXYPlot::markerVisible(double x) { - if(x >= min(XAxis.rangeMin, XAxis.rangeMax) && x <= max(XAxis.rangeMax, XAxis.rangeMin)) { + if(x >= min(XAxis.getRangeMin(), XAxis.getRangeMax()) && x <= max(XAxis.getRangeMax(), XAxis.getRangeMin())) { return true; } else { return false; @@ -1192,16 +914,16 @@ void TraceXYPlot::traceDropped(Trace *t, QPoint position) return; } } - if(YAxis[0].type == YAxisType::Disabled && YAxis[1].type == YAxisType::Disabled) { + if(YAxis[0].getType() == YAxis::Type::Disabled && YAxis[1].getType() == YAxis::Type::Disabled) { // no Y axis enabled, unable to drop return; } - if(YAxis[0].type == YAxisType::Disabled) { + if(YAxis[0].getType() == YAxis::Type::Disabled) { // only axis 1 enabled enableTraceAxis(t, 1, true); return; } - if(YAxis[1].type == YAxisType::Disabled) { + if(YAxis[1].getType() == YAxis::Type::Disabled) { // only axis 0 enabled enableTraceAxis(t, 0, true); return; @@ -1224,98 +946,16 @@ QString TraceXYPlot::mouseText(QPoint pos) QPointF coords[2]; coords[0] = pixelToPlotValue(pos, 0); coords[1] = pixelToPlotValue(pos, 1); - int significantDigits = floor(log10(abs(XAxis.rangeMax))) - floor(log10((abs(XAxis.rangeMax - XAxis.rangeMin)) / 1000.0)) + 1; - ret += Unit::ToString(coords[0].x(), AxisUnit(XAxis.type), "fpnum kMG", significantDigits) + "\n"; + int significantDigits = floor(log10(abs(XAxis.getRangeMax()))) - floor(log10((abs(XAxis.getRangeMax() - XAxis.getRangeMin())) / 1000.0)) + 1; + ret += Unit::ToString(coords[0].x(), XAxis.Unit(), "fpnum kMG", significantDigits) + "\n"; for(int i=0;i<2;i++) { - if(YAxis[i].type != YAxisType::Disabled) { - auto max = qMax(abs(YAxis[i].rangeMax), abs(YAxis[i].rangeMin)); - auto step = abs(YAxis[i].rangeMax - YAxis[i].rangeMin) / 1000.0; + if(YAxis[i].getType() != YAxis::Type::Disabled) { + auto max = qMax(abs(YAxis[i].getRangeMax()), abs(YAxis[i].getRangeMin())); + auto step = abs(YAxis[i].getRangeMax() - YAxis[i].getRangeMin()) / 1000.0; significantDigits = floor(log10(max)) - floor(log10(step)) + 1; - ret += Unit::ToString(coords[i].y(), AxisUnit(YAxis[i].type), AxisPrefixes(YAxis[i].type), significantDigits) + "\n"; + ret += Unit::ToString(coords[i].y(), YAxis[i].Unit(), YAxis[i].Prefixes(), significantDigits) + "\n"; } } } return ret; } - -QString TraceXYPlot::AxisUnit(TraceXYPlot::YAxisType type) -{ - auto source = model.getSource(); - if(source == TraceModel::DataSource::VNA) { - switch(type) { - case TraceXYPlot::YAxisType::Magnitude: return "dB"; - case TraceXYPlot::YAxisType::MagnitudeLinear: return ""; - case TraceXYPlot::YAxisType::Phase: return "°"; - case TraceXYPlot::YAxisType::UnwrappedPhase: return "°"; - case TraceXYPlot::YAxisType::VSWR: return ""; - case TraceXYPlot::YAxisType::ImpulseReal: return ""; - case TraceXYPlot::YAxisType::ImpulseMag: return "dB"; - case TraceXYPlot::YAxisType::Step: return ""; - case TraceXYPlot::YAxisType::Impedance: return "Ω"; - case TraceXYPlot::YAxisType::GroupDelay: return "s"; - case TraceXYPlot::YAxisType::Disabled: - case TraceXYPlot::YAxisType::Real: - case TraceXYPlot::YAxisType::Imaginary: - case TraceXYPlot::YAxisType::QualityFactor: - return ""; - case TraceXYPlot::YAxisType::SeriesR: return "Ω"; - case TraceXYPlot::YAxisType::Reactance: return "Ω"; - case TraceXYPlot::YAxisType::Capacitance: return "F"; - case TraceXYPlot::YAxisType::Inductance: return "H"; - case TraceXYPlot::YAxisType::Last: return ""; - } - } else if(source == TraceModel::DataSource::SA) { - switch(type) { - case TraceXYPlot::YAxisType::Magnitude: return "dBm"; - case TraceXYPlot::YAxisType::MagnitudedBuV: return "dBuV"; - default: return ""; - } - } - return ""; -} - -QString TraceXYPlot::AxisPrefixes(TraceXYPlot::YAxisType type) -{ - auto source = model.getSource(); - if(source == TraceModel::DataSource::VNA) { - switch(type) { - case TraceXYPlot::YAxisType::Magnitude: return " "; - case TraceXYPlot::YAxisType::MagnitudeLinear: return "num "; - case TraceXYPlot::YAxisType::Phase: return " "; - case TraceXYPlot::YAxisType::UnwrappedPhase: return " "; - case TraceXYPlot::YAxisType::VSWR: return " "; - case TraceXYPlot::YAxisType::ImpulseReal: return "pnum kMG"; - case TraceXYPlot::YAxisType::ImpulseMag: return " "; - case TraceXYPlot::YAxisType::Step: return "pnum kMG"; - case TraceXYPlot::YAxisType::Impedance: return "m kM"; - case TraceXYPlot::YAxisType::GroupDelay: return "pnum "; - case TraceXYPlot::YAxisType::Disabled: return " "; - case TraceXYPlot::YAxisType::Real: return "pnum "; - case TraceXYPlot::YAxisType::Imaginary: return "pnum "; - case TraceXYPlot::YAxisType::QualityFactor: return " "; - case TraceXYPlot::YAxisType::SeriesR: return "m kM"; - case TraceXYPlot::YAxisType::Reactance: return "m kM"; - case TraceXYPlot::YAxisType::Capacitance: return "pnum "; - case TraceXYPlot::YAxisType::Inductance: return "pnum "; - case TraceXYPlot::YAxisType::Last: return " "; - } - } else if(source == TraceModel::DataSource::SA) { - switch(type) { - case TraceXYPlot::YAxisType::Magnitude: return " "; - case TraceXYPlot::YAxisType::MagnitudedBuV: return " "; - default: return " "; - } - } - return " "; -} - -QString TraceXYPlot::AxisUnit(TraceXYPlot::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 ""; - } -} diff --git a/Software/PC_Application/Traces/tracexyplot.h b/Software/PC_Application/Traces/tracexyplot.h index 82840b3..69a19db 100644 --- a/Software/PC_Application/Traces/tracexyplot.h +++ b/Software/PC_Application/Traces/tracexyplot.h @@ -2,6 +2,7 @@ #define TRACEXYPLOT_H #include "traceplot.h" +#include "traceaxis.h" #include @@ -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 tracesAxis[2]; - class YAxis { - public: - YAxisType type; - bool log; // not used yet - bool autorange; - double rangeMin; - double rangeMax; - double rangeDiv; - std::vector ticks; - }; - class XAxis { - public: - XAxisType type; - XAxisMode mode; - bool log; // not used yet - double rangeMin; - double rangeMax; - double rangeDiv; - std::vector ticks; - }; - YAxis YAxis[2]; XAxis XAxis; + XAxisMode xAxisMode; int plotAreaLeft, plotAreaWidth, plotAreaBottom, plotAreaTop; }; diff --git a/Software/PC_Application/Traces/waterfallaxisdialog.cpp b/Software/PC_Application/Traces/waterfallaxisdialog.cpp new file mode 100644 index 0000000..6f1772b --- /dev/null +++ b/Software/PC_Application/Traces/waterfallaxisdialog.cpp @@ -0,0 +1,209 @@ +#include "waterfallaxisdialog.h" + +#include "ui_waterfallaxisdialog.h" + +#include + +using namespace std; + +static void enableComboBoxItem(QComboBox *cb, int itemNum, bool enable) { + auto *model = qobject_cast(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;iXType->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(&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(&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(&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 WaterfallAxisDialog::supportedYAxis(XAxis::Type type) +{ + set 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; +} diff --git a/Software/PC_Application/Traces/waterfallaxisdialog.h b/Software/PC_Application/Traces/waterfallaxisdialog.h new file mode 100644 index 0000000..a6af905 --- /dev/null +++ b/Software/PC_Application/Traces/waterfallaxisdialog.h @@ -0,0 +1,31 @@ +#ifndef WATERFALLAXISDIALOG_H +#define WATERFALLAXISDIALOG_H + +#include "tracewaterfall.h" + +#include + +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 supportedYAxis(XAxis::Type type); + bool isSupported(XAxis::Type type); + Ui::WaterfallAxisDialog *ui; + TraceWaterfall *plot; +}; + +#endif // WATERFALLAXISDIALOG_H diff --git a/Software/PC_Application/Traces/waterfallaxisdialog.ui b/Software/PC_Application/Traces/waterfallaxisdialog.ui new file mode 100644 index 0000000..39c4b01 --- /dev/null +++ b/Software/PC_Application/Traces/waterfallaxisdialog.ui @@ -0,0 +1,378 @@ + + + WaterfallAxisDialog + + + Qt::NonModal + + + + 0 + 0 + 539 + 351 + + + + Axis Setup + + + true + + + + + + + + + + + 15 + + + + Waterfall + + + Qt::AlignCenter + + + + + + + + + Type: + + + + + + + + + + + + Qt::Horizontal + + + + + + + + + Linear + + + Y1group + + + + + + + false + + + Log + + + Y1group + + + + + + + + + Qt::Horizontal + + + + + + + + + Range: + + + + + + + Maximum: + + + + + + + + + + Minimum: + + + + + + + + + + Direction: + + + + + + + Auto + + + + + + + + Top to bottom + + + + + Bottom to top + + + + + + + + Pixels per line: + + + + + + + 1 + + + 20 + + + + + + + Mode: + + + + + + + + Only keep visible data + + + + + Keep offscreen data + + + + + + + + Number of sweeps: + + + + + + + 1 + + + 999 + + + + + + + + + + + Qt::Vertical + + + + + + + + + + 0 + 0 + + + + + 15 + + + + X axis + + + Qt::AlignCenter + + + + + + + + + Type: + + + + + + + false + + + + + + + + + Qt::Horizontal + + + + + + + + + Linear + + + Xgroup + + + + + + + true + + + Log + + + Xgroup + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + SIUnitEdit + QLineEdit +
CustomWidgets/siunitedit.h
+
+
+ + + + buttonBox + accepted() + WaterfallAxisDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + WaterfallAxisDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + + + + + +
diff --git a/Software/PC_Application/Traces/xyplotaxisdialog.cpp b/Software/PC_Application/Traces/xyplotaxisdialog.cpp index a6c8b71..e23b339 100644 --- a/Software/PC_Application/Traces/xyplotaxisdialog.cpp +++ b/Software/PC_Application/Traces/xyplotaxisdialog.cpp @@ -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;iXType->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(&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 XYplotAxisDialog::supportedYAxis(TraceXYPlot::XAxisType type) +std::set XYplotAxisDialog::supportedYAxis(XAxis::Type type) { - set ret = {TraceXYPlot::YAxisType::Disabled}; + set 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 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; diff --git a/Software/PC_Application/Traces/xyplotaxisdialog.h b/Software/PC_Application/Traces/xyplotaxisdialog.h index db2c944..e14b434 100644 --- a/Software/PC_Application/Traces/xyplotaxisdialog.h +++ b/Software/PC_Application/Traces/xyplotaxisdialog.h @@ -22,8 +22,8 @@ private slots: void XAxisTypeChanged(int XAxisIndex); private: - std::set supportedYAxis(TraceXYPlot::XAxisType type); - bool isSupported(TraceXYPlot::XAxisType type); + std::set supportedYAxis(XAxis::Type type); + bool isSupported(XAxis::Type type); Ui::XYplotAxisDialog *ui; TraceXYPlot *plot; };