diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Marker/marker.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/Marker/marker.cpp index c0207d4..60d854d 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Marker/marker.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Marker/marker.cpp @@ -136,6 +136,9 @@ QString Marker::formatToString(Marker::Format f) case Format::CenterBandwidth: return "Center + Bandwidth"; case Format::InsertionLoss: return "Insertion loss"; case Format::P1dB: return "P1dB"; + case Format::NonUniformity: return "Non-uniformity"; + case Format::maxDeltaNeg: return "Max. Delta Negative"; + case Format::maxDeltaPos: return "Max. Delta Positive"; case Format::Last: return ""; } return ""; @@ -262,6 +265,11 @@ std::vector Marker::applicableFormats() ret.push_back(Format::AvgTone); ret.push_back(Format::AvgModulationProduct); break; + case Type::NonUniformity: + ret.push_back(Format::NonUniformity); + ret.push_back(Format::maxDeltaNeg); + ret.push_back(Format::maxDeltaPos); + break; default: break; } @@ -363,6 +371,15 @@ std::vector Marker::defaultActiveFormats() if(pref.Marker.defaultBehavior.showP1dB) { ret.push_back(Format::P1dB); } + if(pref.Marker.defaultBehavior.showNonUniformity) { + ret.push_back(Format::NonUniformity); + } + if(pref.Marker.defaultBehavior.showMaxDeltaNeg) { + ret.push_back(Format::maxDeltaNeg); + } + if(pref.Marker.defaultBehavior.showMaxDeltaPos) { + ret.push_back(Format::maxDeltaPos); + } return ret; } @@ -549,6 +566,16 @@ QString Marker::readableData(Format f) } else { return "Input P1dB:"+Unit::ToString(position, "dBm", " ", 4); } + break; + case Format::NonUniformity: + return "Non-uniformity:"+Unit::ToString(maxDeltaNeg+maxDeltaPos, "dB", " ", 4); + break; + case Format::maxDeltaNeg: + return "max. Δ-:"+Unit::ToString(maxDeltaNeg, "dB", " ", 4); + break; + case Format::maxDeltaPos: + return "max. Δ+:"+Unit::ToString(maxDeltaPos, "dB", " ", 4); + break; case Format::Last: return "Invalid"; } @@ -625,6 +652,8 @@ QString Marker::readableSettings() return "none"; case Type::PhaseNoise: return Unit::ToString(offset, "Hz", " kM", 4); + case Type::NonUniformity: + return "None"; default: break; } @@ -987,6 +1016,7 @@ std::set Marker::getSupportedTypes() supported.insert(Type::Minimum); supported.insert(Type::Delta); supported.insert(Type::PeakTable); + supported.insert(Type::NonUniformity); if(!parentTrace->isReflection()) { supported.insert(Type::Lowpass); supported.insert(Type::Highpass); @@ -1172,6 +1202,9 @@ void Marker::setType(Marker::Type t) case Type::PhaseNoise: required_helpers = {{"o", "Offset", Type::Manual}}; break; + case Type::NonUniformity: + required_helpers = {{"l", "Lower Limit", Type::Manual}, {"u", "Upper Limit", Type::Manual}}; + break; default: break; } @@ -1183,6 +1216,14 @@ void Marker::setType(Marker::Type t) helper->setType(h.type); helperMarkers.push_back(helper); } + if(type == Type::NonUniformity) { + // need to update when any of the helper markers is moved + for(auto h : helperMarkers) { + connect(h, &Marker::positionChanged, [=](){ + setPosition((helperMarkers[0]->position + helperMarkers[1]->position)/2); + }); + } + } constrainFormat(); updateSymbol(); @@ -1881,6 +1922,32 @@ void Marker::update() setPosition(p1db); } break; + case Type::NonUniformity: { + auto dbLower = Util::SparamTodB(helperMarkers[0]->getData()); + auto dbUpper = Util::SparamTodB(helperMarkers[1]->getData()); + auto posLower = helperMarkers[0]->getPosition(); + auto posUpper = helperMarkers[1]->getPosition(); + if(posLower > posUpper) { + swap(posLower, posUpper); + swap(dbLower, dbUpper); + } + auto startIndex = parentTrace->index(posLower); + auto stopIndex = parentTrace->index(posUpper); + maxDeltaNeg = std::numeric_limits::lowest(); + maxDeltaPos = std::numeric_limits::lowest(); + for(int i=startIndex;i<=stopIndex;i++) { + auto dbTrace = Util::SparamTodB(parentTrace->sample(i).y); + auto dbStraightLine = Util::Scale((double) i, (double) startIndex, (double) stopIndex, dbLower, dbUpper); + auto delta = dbTrace - dbStraightLine; + if(delta > maxDeltaPos) { + maxDeltaPos = delta; + } + if(-delta > maxDeltaNeg) { + maxDeltaNeg = -delta; + } + } + } + break; case Type::Last: break; } @@ -1905,6 +1972,10 @@ std::complex Marker::getData() const bool Marker::isMovable() { if(parent) { + if(parent->type == Type::NonUniformity) { + // non-uniformity is the exception, it has movable helper markers + return true; + } // helper traces are never movable by the user return false; } @@ -1920,6 +1991,19 @@ bool Marker::isMovable() } } +bool Marker::isEditable() +{ + if(parent) { + if(parent->type == Type::NonUniformity) { + // non-uniformity is the exception, it has movable helper markers + return true; + } + // helper traces are never movable by the user + return false; + } + return true; +} + QPixmap &Marker::getSymbol() { return symbol; diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Marker/marker.h b/Software/PC_Application/LibreVNA-GUI/Traces/Marker/marker.h index 3212698..203c319 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Marker/marker.h +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Marker/marker.h @@ -47,6 +47,9 @@ public: AvgModulationProduct, // average level of modulation products // compression parameters P1dB, // power level at 1dB compression + NonUniformity, + maxDeltaPos, + maxDeltaNeg, // keep last at end Last, }; @@ -68,6 +71,7 @@ public: double getPosition() const; std::complex getData() const; bool isMovable(); + bool isEditable(); Trace::DataType getDomain(); QPixmap& getSymbol(); @@ -92,6 +96,7 @@ public: TOI, PhaseNoise, P1dB, + NonUniformity, // keep last at end Last, }; @@ -171,6 +176,7 @@ private: case Type::TOI: return "TOI/IP3"; case Type::PhaseNoise: return "Phase noise"; case Type::P1dB: return "1dB compression"; + case Type::NonUniformity: return "Non-uniformity"; default: return QString(); } } @@ -211,6 +217,10 @@ private: double peakThreshold; double offset; + // non-uniformity + double maxDeltaNeg; + double maxDeltaPos; + Format formatTable; std::set formatGraph; diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/Marker/markermodel.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/Marker/markermodel.cpp index 5ccc6c1..c089506 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/Marker/markermodel.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/Marker/markermodel.cpp @@ -303,7 +303,7 @@ QVariant MarkerModel::headerData(int section, Qt::Orientation orientation, int r bool MarkerModel::setData(const QModelIndex &index, const QVariant &value, int) { - if((unsigned int) index.row() >= markers.size()) { + if(!index.isValid()) { return false; } auto m = markerFromIndex(index); @@ -340,7 +340,7 @@ Qt::ItemFlags MarkerModel::flags(const QModelIndex &index) const case ColIndexData: flags |= Qt::ItemIsEnabled; break; } auto marker = markerFromIndex(index); - if(marker->getParent()) { + if(!marker->isEditable()) { // this is a helper marker -> nothing is editable flags &= ~Qt::ItemIsEditable; } diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp index 6baf18e..ac89e3f 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp @@ -433,7 +433,7 @@ Marker *TracePlot::markerAtPosition(QPoint p, bool onlyMovable) continue; } auto markers = t.first->getMarkers(); - for(auto m : markers) { + for(Marker* m : markers) { if(!m->isMovable() && onlyMovable) { continue; } @@ -448,6 +448,9 @@ Marker *TracePlot::markerAtPosition(QPoint p, bool onlyMovable) closestDistance = distance; if(m->getParent()) { closestMarker = m->getParent(); + if(closestMarker->getType() == Marker::Type::NonUniformity) { + closestMarker = m; + } } else { closestMarker = m; } diff --git a/Software/PC_Application/LibreVNA-GUI/preferences.cpp b/Software/PC_Application/LibreVNA-GUI/preferences.cpp index 1ce12b6..7b8ecf5 100644 --- a/Software/PC_Application/LibreVNA-GUI/preferences.cpp +++ b/Software/PC_Application/LibreVNA-GUI/preferences.cpp @@ -380,6 +380,9 @@ void PreferencesDialog::setInitialGUIState() ui->MarkerShowAvgTone->setChecked(p->Marker.defaultBehavior.showAvgTone); ui->MarkerShowAvgMod->setChecked(p->Marker.defaultBehavior.showAvgModulation); ui->MarkerShowP1dB->setChecked(p->Marker.defaultBehavior.showP1dB); + ui->MarkerShowNonUniformity->setChecked(p->Marker.defaultBehavior.showNonUniformity); + ui->MarkerShowMaxDeltaNeg->setChecked(p->Marker.defaultBehavior.showMaxDeltaNeg); + ui->MarkerShowMaxDeltaPos->setChecked(p->Marker.defaultBehavior.showMaxDeltaPos); ui->MarkerInterpolate->setCurrentIndex(p->Marker.interpolatePoints ? 1 : 0); ui->MarkerSortOrder->setCurrentIndex((int) p->Marker.sortOrder); ui->MarkerSymbolStyle->setCurrentIndex((int) p->Marker.symbolStyle); @@ -485,6 +488,9 @@ void PreferencesDialog::updateFromGUI() p->Marker.defaultBehavior.showAvgTone = ui->MarkerShowAvgTone->isChecked(); p->Marker.defaultBehavior.showAvgModulation = ui->MarkerShowAvgMod->isChecked(); p->Marker.defaultBehavior.showP1dB = ui->MarkerShowP1dB->isChecked(); + p->Marker.defaultBehavior.showNonUniformity = ui->MarkerShowNonUniformity->isChecked(); + p->Marker.defaultBehavior.showMaxDeltaNeg = ui->MarkerShowMaxDeltaNeg->isChecked(); + p->Marker.defaultBehavior.showMaxDeltaPos = ui->MarkerShowMaxDeltaPos->isChecked(); p->Marker.interpolatePoints = ui->MarkerInterpolate->currentIndex() == 1; p->Marker.sortOrder = (MarkerSortOrder) ui->MarkerSortOrder->currentIndex(); p->Marker.symbolStyle = (MarkerSymbolStyle) ui->MarkerSymbolStyle->currentIndex(); diff --git a/Software/PC_Application/LibreVNA-GUI/preferences.h b/Software/PC_Application/LibreVNA-GUI/preferences.h index 331ccf6..411b490 100644 --- a/Software/PC_Application/LibreVNA-GUI/preferences.h +++ b/Software/PC_Application/LibreVNA-GUI/preferences.h @@ -150,7 +150,7 @@ public: struct { bool showDataOnGraphs; bool showdB, showdBm, showdBuV, showdBAngle, showRealImag, showImpedance, showVSWR, showResistance, showCapacitance, showInductance, showQualityFactor; - bool showNoise, showPhasenoise, showCenterBandwidth, showCutoff, showInsertionLoss, showTOI, showAvgTone, showAvgModulation, showP1dB; + bool showNoise, showPhasenoise, showCenterBandwidth, showCutoff, showInsertionLoss, showTOI, showAvgTone, showAvgModulation, showP1dB, showNonUniformity, showMaxDeltaNeg, showMaxDeltaPos; } defaultBehavior; bool interpolatePoints; MarkerSortOrder sortOrder; @@ -264,6 +264,9 @@ private: {&Marker.defaultBehavior.showAvgTone, "Marker.defaultBehavior.showAvgTone", true}, {&Marker.defaultBehavior.showAvgModulation, "Marker.defaultBehavior.showAvgModulation", true}, {&Marker.defaultBehavior.showP1dB, "Marker.defaultBehavior.showP1dB", true}, + {&Marker.defaultBehavior.showNonUniformity, "Marker.defaultBehavior.showNonUniformity", true}, + {&Marker.defaultBehavior.showMaxDeltaNeg, "Marker.defaultBehavior.showMaxDeltaNeg", true}, + {&Marker.defaultBehavior.showMaxDeltaPos, "Marker.defaultBehavior.showMaxDeltaPos", true}, {&Marker.interpolatePoints, "Marker.interpolatePoints", false}, {&Marker.sortOrder, "Marker.sortOrder", MarkerSortOrder::PrefMarkerSortXCoord}, {&Marker.symbolStyle, "Marker.symbolStyle", MarkerSymbolStyle::EmptyNumberAbove}, diff --git a/Software/PC_Application/LibreVNA-GUI/preferencesdialog.ui b/Software/PC_Application/LibreVNA-GUI/preferencesdialog.ui index 5cfd6c7..6ea3bc0 100644 --- a/Software/PC_Application/LibreVNA-GUI/preferencesdialog.ui +++ b/Software/PC_Application/LibreVNA-GUI/preferencesdialog.ui @@ -93,7 +93,7 @@ - 6 + 3 @@ -1457,7 +1457,7 @@ 0 0 749 - 576 + 605 @@ -1640,6 +1640,27 @@ + + + + Non-uniformity + + + + + + + Max. Delta Negative + + + + + + + Max. Delta Positive + + + @@ -1774,8 +1795,8 @@ 0 0 - 763 - 561 + 168 + 127