From 7921c8db2f5c5fd08387a1b0b4c007c52510dc64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Sun, 8 Nov 2020 21:28:47 +0100 Subject: [PATCH] Noise + phase noise markers --- .../SpectrumAnalyzer/spectrumanalyzer.cpp | 2 +- Software/PC_Application/Traces/trace.cpp | 30 +++++++++++- Software/PC_Application/Traces/trace.h | 14 +++++- .../PC_Application/Traces/tracemarker.cpp | 49 ++++++++++++++++--- Software/PC_Application/Traces/tracemarker.h | 10 +++- Software/PC_Application/Traces/tracemodel.cpp | 10 ++-- Software/PC_Application/Traces/tracemodel.h | 4 +- Software/PC_Application/VNA/vna.cpp | 2 +- 8 files changed, 102 insertions(+), 19 deletions(-) diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp index a1c612e..d658a88 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -235,7 +235,7 @@ void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d) d.port1 /= 126500000.0; d.port2 /= 126500000.0; d = average.process(d); - traceModel.addSAData(d); + traceModel.addSAData(d, settings); emit dataChanged(); if(d.pointNum == settings.pointNum - 1) { UpdateAverageCount(); diff --git a/Software/PC_Application/Traces/trace.cpp b/Software/PC_Application/Traces/trace.cpp index 1f8bd44..4a900fb 100644 --- a/Software/PC_Application/Traces/trace.cpp +++ b/Software/PC_Application/Traces/trace.cpp @@ -29,11 +29,12 @@ void Trace::clear() { return; } _data.clear(); + settings.valid = false; emit cleared(this); emit dataChanged(); } -void Trace::addData(Trace::Data d) { +void Trace::addData(const Trace::Data& d) { // add or replace data in vector while keeping it sorted with increasing frequency auto lower = lower_bound(_data.begin(), _data.end(), d, [](const Data &lhs, const Data &rhs) -> bool { return lhs.frequency < rhs.frequency; @@ -75,6 +76,20 @@ void Trace::addData(Trace::Data d) { } } +void Trace::addData(const Trace::Data &d, const Protocol::SweepSettings &s) +{ + settings.VNA = s; + settings.valid = true; + addData(d); +} + +void Trace::addData(const Trace::Data &d, const Protocol::SpectrumAnalyzerSettings &s) +{ + settings.SA = s; + settings.valid = true; + addData(d); +} + void Trace::setName(QString name) { _name = name; emit nameChanged(); @@ -388,6 +403,19 @@ std::complex Trace::getData(double frequency) } } +double Trace::getNoise(double frequency) +{ + if(!isLive() || !settings.valid || (_liveParam != LiveParameter::Port1 && _liveParam != LiveParameter::Port2)) { + // data not suitable for noise calculation + return std::numeric_limits::quiet_NaN(); + } + // convert to dbm + auto dbm = 20*log10(abs(getData(frequency))); + // convert to 1Hz bandwidth + dbm -= 10*log10(settings.SA.RBW); + return dbm; +} + int Trace::index(double frequency) { auto lower = lower_bound(_data.begin(), _data.end(), frequency, [](const Data &lhs, const double freq) -> bool { diff --git a/Software/PC_Application/Traces/trace.h b/Software/PC_Application/Traces/trace.h index baea5f0..d79773b 100644 --- a/Software/PC_Application/Traces/trace.h +++ b/Software/PC_Application/Traces/trace.h @@ -7,6 +7,7 @@ #include #include #include "touchstone.h" +#include "Device/device.h" class TraceMarker; @@ -49,7 +50,9 @@ public: void clear(); - void addData(Data d); + void addData(const Data& d); + void addData(const Data& d, const Protocol::SweepSettings& s); + void addData(const Data& d, const Protocol::SpectrumAnalyzerSettings& s); void setName(QString name); void fillFromTouchstone(Touchstone &t, unsigned int parameter, QString filename = QString()); void fromLivedata(LivedataType type, LiveParameter param); @@ -79,6 +82,8 @@ public: QString getTouchstoneFilename() const; unsigned int getTouchstoneParameter() const; std::complex getData(double frequency); + /* Returns the noise in dbm/Hz for spectrum analyzer measurements. May return NaN if calculation not possible */ + double getNoise(double frequency); int index(double frequency); std::set getMarkers() const; void setCalibration(bool value); @@ -131,6 +136,13 @@ private: QString touchstoneFilename; unsigned int touchstoneParameter; std::set markers; + struct { + union { + Protocol::SweepSettings VNA; + Protocol::SpectrumAnalyzerSettings SA; + }; + bool valid; + } settings; }; #endif // TRACE_H diff --git a/Software/PC_Application/Traces/tracemarker.cpp b/Software/PC_Application/Traces/tracemarker.cpp index 1a35704..8417367 100644 --- a/Software/PC_Application/Traces/tracemarker.cpp +++ b/Software/PC_Application/Traces/tracemarker.cpp @@ -88,6 +88,8 @@ QString TraceMarker::readableData() return Unit::ToString(freqDiff, "Hz", " kMG") + " / " + QString::number(toDecibel(), 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4); } break; + case Type::Noise: + return Unit::ToString(parentTrace->getNoise(frequency), "dbm/Hz", " ", 3); case Type::PeakTable: return "Found " + QString::number(helperMarkers.size()) + " peaks"; case Type::Lowpass: @@ -135,6 +137,11 @@ QString TraceMarker::readableData() return "Fundamental: " + Unit::ToString(avgFundamental, "dbm", " ", 3) + ", distortion: " + Unit::ToString(avgDistortion, "dbm", " ", 3) + ", TOI: "+Unit::ToString(TOI, "dbm", " ", 3); } break; + case Type::PhaseNoise: { + auto carrier = toDecibel(); + auto phasenoise = parentTrace->getNoise(helperMarkers[0]->frequency) - carrier; + return Unit::ToString(phasenoise, "dbc/Hz", " ", 3) +"@" + Unit::ToString(offset, "Hz", " kM", 4) + " offset (" + Unit::ToString(frequency, "Hz", " kMG", 6) + " carrier)"; + } default: return "Unknown marker type"; } @@ -147,14 +154,18 @@ QString TraceMarker::readableSettings() case Type::Maximum: case Type::Minimum: case Type::Delta: + case Type::Noise: return Unit::ToString(frequency, "Hz", " kMG", 6); case Type::Lowpass: case Type::Highpass: case Type::Bandpass: - case Type::PeakTable: return Unit::ToString(cutoffAmplitude, "db", " ", 3); + case Type::PeakTable: + return Unit::ToString(peakThreshold, "db", " ", 3); case Type::TOI: return "none"; + case Type::PhaseNoise: + return Unit::ToString(offset, "Hz", " kM", 4); default: return "Unhandled case"; } @@ -239,7 +250,9 @@ std::set TraceMarker::getSupportedTypes() case Trace::LiveParameter::Port1: case Trace::LiveParameter::Port2: // special SA marker types + supported.insert(Type::Noise); supported.insert(Type::TOI); + supported.insert(Type::PhaseNoise); break; } } @@ -293,6 +306,7 @@ void TraceMarker::setType(TraceMarker::Type t) using helper_descr = struct { QString suffix; QString description; + Type type; }; vector required_helpers; switch(type) { @@ -320,13 +334,17 @@ void TraceMarker::setType(TraceMarker::Type t) break; case Type::Lowpass: case Type::Highpass: - required_helpers = {{"c", "cutoff"}}; + required_helpers = {{"c", "cutoff", Type::Manual}}; break; case Type::Bandpass: - required_helpers = {{"l", "lower cutoff"}, {"h", "higher cutoff"} ,{"c", "center"}}; + required_helpers = {{"l", "lower cutoff", Type::Manual}, {"h", "higher cutoff", Type::Manual} ,{"c", "center", Type::Manual}}; break; case Type::TOI: - required_helpers = {{"p", "first peak"}, {"p", "second peak"}, {"l", "left intermodulation"}, {"r", "right intermodulation"}}; + required_helpers = {{"p", "first peak", Type::Manual}, {"p", "second peak", Type::Manual}, {"l", "left intermodulation", Type::Manual}, {"r", "right intermodulation", Type::Manual}}; + break; + case Type::PhaseNoise: + required_helpers = {{"o", "Offset", Type::Noise}}; + break; default: break; } @@ -335,6 +353,7 @@ void TraceMarker::setType(TraceMarker::Type t) auto helper = new TraceMarker(model, number, this, h.description); helper->suffix = h.suffix; helper->assignTrace(parentTrace); + helper->setType(h.type); helperMarkers.push_back(helper); } updateSymbol(); @@ -354,6 +373,8 @@ bool TraceMarker::isVisible() case Type::Delta: case Type::Maximum: case Type::Minimum: + case Type::Noise: + case Type::PhaseNoise: return true; default: return false; @@ -475,6 +496,8 @@ SIUnitEdit *TraceMarker::getSettingsEditor() case Type::Maximum: case Type::Minimum: case Type::Delta: + case Type::Noise: + case Type::PhaseNoise: default: return new SIUnitEdit("Hz", " kMG"); case Type::Lowpass: @@ -493,6 +516,7 @@ void TraceMarker::adjustSettings(double value) case Type::Maximum: case Type::Minimum: case Type::Delta: + case Type::Noise: default: setFrequency(value); /* no break */ @@ -502,9 +526,14 @@ void TraceMarker::adjustSettings(double value) if(value > 0.0) { value = -value; } - /* no break */ - case Type::PeakTable: cutoffAmplitude = value; + break; + case Type::PeakTable: + peakThreshold = value; + break; + case Type::PhaseNoise: + offset = value; + break; } update(); } @@ -518,6 +547,7 @@ void TraceMarker::update() switch(type) { case Type::Manual: case Type::Delta: + case Type::Noise: // nothing to do break; case Type::Maximum: @@ -528,7 +558,7 @@ void TraceMarker::update() break; case Type::PeakTable: { deleteHelperMarkers(); - auto peaks = parentTrace->findPeakFrequencies(100, cutoffAmplitude); + auto peaks = parentTrace->findPeakFrequencies(100, peakThreshold); char suffix = 'a'; for(auto p : peaks) { auto helper = new TraceMarker(model, number, this); @@ -632,6 +662,10 @@ void TraceMarker::update() helperMarkers[3]->setFrequency(peaks[1] + freqDiff); } break; + case Type::PhaseNoise: + setFrequency(parentTrace->findExtremumFreq(true)); + helperMarkers[0]->setFrequency(frequency + offset); + break; } emit dataChanged(this); } @@ -660,6 +694,7 @@ bool TraceMarker::isMovable() switch(type) { case Type::Manual: case Type::Delta: + case Type::Noise: return true; default: return false; diff --git a/Software/PC_Application/Traces/tracemarker.h b/Software/PC_Application/Traces/tracemarker.h index 9a7ce3f..4ad78cf 100644 --- a/Software/PC_Application/Traces/tracemarker.h +++ b/Software/PC_Application/Traces/tracemarker.h @@ -70,11 +70,13 @@ private: Maximum, Minimum, Delta, + Noise, PeakTable, Lowpass, Highpass, Bandpass, TOI, + PhaseNoise, }; std::set getSupportedTypes(); static QString typeToString(Type t) { @@ -83,11 +85,13 @@ private: case Type::Maximum: return "Maximum"; case Type::Minimum: return "Minimum"; case Type::Delta: return "Delta"; + case Type::Noise: return "Noise"; case Type::PeakTable: return "Peak Table"; case Type::Lowpass: return "Lowpass"; case Type::Highpass: return "Highpass"; case Type::Bandpass: return "Bandpass"; case Type::TOI: return "TOI/IP3"; + case Type::PhaseNoise: return "Phase noise"; default: return QString(); } } @@ -111,7 +115,11 @@ private: TraceMarker *delta; std::vector helperMarkers; TraceMarker *parent; - double cutoffAmplitude; + union { + double cutoffAmplitude; + double peakThreshold; + double offset; + }; }; #endif // TRACEMARKER_H diff --git a/Software/PC_Application/Traces/tracemodel.cpp b/Software/PC_Application/Traces/tracemodel.cpp index d1a4287..c198930 100644 --- a/Software/PC_Application/Traces/tracemodel.cpp +++ b/Software/PC_Application/Traces/tracemodel.cpp @@ -141,14 +141,14 @@ bool TraceModel::PortExcitationRequired(int port) void TraceModel::clearVNAData() { for(auto t : traces) { - if (!t->isTouchstone()) { + if (t->isLive()) { // this trace is fed from live data t->clear(); } } } -void TraceModel::addVNAData(Protocol::Datapoint d) +void TraceModel::addVNAData(const Protocol::Datapoint &d, const Protocol::SweepSettings& settings) { for(auto t : traces) { if (t->isLive() && !t->isPaused()) { @@ -163,12 +163,12 @@ void TraceModel::addVNAData(Protocol::Datapoint d) // not a VNA trace, skip continue; } - t->addData(td); + t->addData(td, settings); } } } -void TraceModel::addSAData(Protocol::SpectrumAnalyzerResult d) +void TraceModel::addSAData(const Protocol::SpectrumAnalyzerResult& d, const Protocol::SpectrumAnalyzerSettings& settings) { for(auto t : traces) { if (t->isLive() && !t->isPaused()) { @@ -181,7 +181,7 @@ void TraceModel::addSAData(Protocol::SpectrumAnalyzerResult d) // not a SA trace, skip continue; } - t->addData(td); + t->addData(td, settings); } } } diff --git a/Software/PC_Application/Traces/tracemodel.h b/Software/PC_Application/Traces/tracemodel.h index 6ad464a..1531c82 100644 --- a/Software/PC_Application/Traces/tracemodel.h +++ b/Software/PC_Application/Traces/tracemodel.h @@ -36,8 +36,8 @@ signals: public slots: void clearVNAData(); - void addVNAData(Protocol::Datapoint d); - void addSAData(Protocol::SpectrumAnalyzerResult d); + void addVNAData(const Protocol::Datapoint& d, const Protocol::SweepSettings& settings); + void addSAData(const Protocol::SpectrumAnalyzerResult& d, const Protocol::SpectrumAnalyzerSettings& settings); private: std::vector traces; diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index 74f91b3..3bd9b74 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -427,7 +427,7 @@ void VNA::NewDatapoint(Protocol::Datapoint d) } portExtension.applyToMeasurement(d); - traceModel.addVNAData(d); + traceModel.addVNAData(d, settings); emit dataChanged(); if(d.pointNum == settings.points - 1) { UpdateAverageCount();