Noise + phase noise markers

This commit is contained in:
Jan Käberich 2020-11-08 21:28:47 +01:00
parent 5ed3547d3d
commit 7921c8db2f
8 changed files with 102 additions and 19 deletions

View File

@ -235,7 +235,7 @@ void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
d.port1 /= 126500000.0; d.port1 /= 126500000.0;
d.port2 /= 126500000.0; d.port2 /= 126500000.0;
d = average.process(d); d = average.process(d);
traceModel.addSAData(d); traceModel.addSAData(d, settings);
emit dataChanged(); emit dataChanged();
if(d.pointNum == settings.pointNum - 1) { if(d.pointNum == settings.pointNum - 1) {
UpdateAverageCount(); UpdateAverageCount();

View File

@ -29,11 +29,12 @@ void Trace::clear() {
return; return;
} }
_data.clear(); _data.clear();
settings.valid = false;
emit cleared(this); emit cleared(this);
emit dataChanged(); 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 // 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 { auto lower = lower_bound(_data.begin(), _data.end(), d, [](const Data &lhs, const Data &rhs) -> bool {
return lhs.frequency < rhs.frequency; 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) { void Trace::setName(QString name) {
_name = name; _name = name;
emit nameChanged(); emit nameChanged();
@ -388,6 +403,19 @@ std::complex<double> 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<double>::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) int Trace::index(double frequency)
{ {
auto lower = lower_bound(_data.begin(), _data.end(), frequency, [](const Data &lhs, const double freq) -> bool { auto lower = lower_bound(_data.begin(), _data.end(), frequency, [](const Data &lhs, const double freq) -> bool {

View File

@ -7,6 +7,7 @@
#include <QColor> #include <QColor>
#include <set> #include <set>
#include "touchstone.h" #include "touchstone.h"
#include "Device/device.h"
class TraceMarker; class TraceMarker;
@ -49,7 +50,9 @@ public:
void clear(); 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 setName(QString name);
void fillFromTouchstone(Touchstone &t, unsigned int parameter, QString filename = QString()); void fillFromTouchstone(Touchstone &t, unsigned int parameter, QString filename = QString());
void fromLivedata(LivedataType type, LiveParameter param); void fromLivedata(LivedataType type, LiveParameter param);
@ -79,6 +82,8 @@ public:
QString getTouchstoneFilename() const; QString getTouchstoneFilename() const;
unsigned int getTouchstoneParameter() const; unsigned int getTouchstoneParameter() const;
std::complex<double> getData(double frequency); std::complex<double> 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); int index(double frequency);
std::set<TraceMarker *> getMarkers() const; std::set<TraceMarker *> getMarkers() const;
void setCalibration(bool value); void setCalibration(bool value);
@ -131,6 +136,13 @@ private:
QString touchstoneFilename; QString touchstoneFilename;
unsigned int touchstoneParameter; unsigned int touchstoneParameter;
std::set<TraceMarker*> markers; std::set<TraceMarker*> markers;
struct {
union {
Protocol::SweepSettings VNA;
Protocol::SpectrumAnalyzerSettings SA;
};
bool valid;
} settings;
}; };
#endif // TRACE_H #endif // TRACE_H

View File

@ -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); return Unit::ToString(freqDiff, "Hz", " kMG") + " / " + QString::number(toDecibel(), 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
} }
break; break;
case Type::Noise:
return Unit::ToString(parentTrace->getNoise(frequency), "dbm/Hz", " ", 3);
case Type::PeakTable: case Type::PeakTable:
return "Found " + QString::number(helperMarkers.size()) + " peaks"; return "Found " + QString::number(helperMarkers.size()) + " peaks";
case Type::Lowpass: 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); return "Fundamental: " + Unit::ToString(avgFundamental, "dbm", " ", 3) + ", distortion: " + Unit::ToString(avgDistortion, "dbm", " ", 3) + ", TOI: "+Unit::ToString(TOI, "dbm", " ", 3);
} }
break; 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: default:
return "Unknown marker type"; return "Unknown marker type";
} }
@ -147,14 +154,18 @@ QString TraceMarker::readableSettings()
case Type::Maximum: case Type::Maximum:
case Type::Minimum: case Type::Minimum:
case Type::Delta: case Type::Delta:
case Type::Noise:
return Unit::ToString(frequency, "Hz", " kMG", 6); return Unit::ToString(frequency, "Hz", " kMG", 6);
case Type::Lowpass: case Type::Lowpass:
case Type::Highpass: case Type::Highpass:
case Type::Bandpass: case Type::Bandpass:
case Type::PeakTable:
return Unit::ToString(cutoffAmplitude, "db", " ", 3); return Unit::ToString(cutoffAmplitude, "db", " ", 3);
case Type::PeakTable:
return Unit::ToString(peakThreshold, "db", " ", 3);
case Type::TOI: case Type::TOI:
return "none"; return "none";
case Type::PhaseNoise:
return Unit::ToString(offset, "Hz", " kM", 4);
default: default:
return "Unhandled case"; return "Unhandled case";
} }
@ -239,7 +250,9 @@ std::set<TraceMarker::Type> TraceMarker::getSupportedTypes()
case Trace::LiveParameter::Port1: case Trace::LiveParameter::Port1:
case Trace::LiveParameter::Port2: case Trace::LiveParameter::Port2:
// special SA marker types // special SA marker types
supported.insert(Type::Noise);
supported.insert(Type::TOI); supported.insert(Type::TOI);
supported.insert(Type::PhaseNoise);
break; break;
} }
} }
@ -293,6 +306,7 @@ void TraceMarker::setType(TraceMarker::Type t)
using helper_descr = struct { using helper_descr = struct {
QString suffix; QString suffix;
QString description; QString description;
Type type;
}; };
vector<helper_descr> required_helpers; vector<helper_descr> required_helpers;
switch(type) { switch(type) {
@ -320,13 +334,17 @@ void TraceMarker::setType(TraceMarker::Type t)
break; break;
case Type::Lowpass: case Type::Lowpass:
case Type::Highpass: case Type::Highpass:
required_helpers = {{"c", "cutoff"}}; required_helpers = {{"c", "cutoff", Type::Manual}};
break; break;
case Type::Bandpass: 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; break;
case Type::TOI: 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: default:
break; break;
} }
@ -335,6 +353,7 @@ void TraceMarker::setType(TraceMarker::Type t)
auto helper = new TraceMarker(model, number, this, h.description); auto helper = new TraceMarker(model, number, this, h.description);
helper->suffix = h.suffix; helper->suffix = h.suffix;
helper->assignTrace(parentTrace); helper->assignTrace(parentTrace);
helper->setType(h.type);
helperMarkers.push_back(helper); helperMarkers.push_back(helper);
} }
updateSymbol(); updateSymbol();
@ -354,6 +373,8 @@ bool TraceMarker::isVisible()
case Type::Delta: case Type::Delta:
case Type::Maximum: case Type::Maximum:
case Type::Minimum: case Type::Minimum:
case Type::Noise:
case Type::PhaseNoise:
return true; return true;
default: default:
return false; return false;
@ -475,6 +496,8 @@ SIUnitEdit *TraceMarker::getSettingsEditor()
case Type::Maximum: case Type::Maximum:
case Type::Minimum: case Type::Minimum:
case Type::Delta: case Type::Delta:
case Type::Noise:
case Type::PhaseNoise:
default: default:
return new SIUnitEdit("Hz", " kMG"); return new SIUnitEdit("Hz", " kMG");
case Type::Lowpass: case Type::Lowpass:
@ -493,6 +516,7 @@ void TraceMarker::adjustSettings(double value)
case Type::Maximum: case Type::Maximum:
case Type::Minimum: case Type::Minimum:
case Type::Delta: case Type::Delta:
case Type::Noise:
default: default:
setFrequency(value); setFrequency(value);
/* no break */ /* no break */
@ -502,9 +526,14 @@ void TraceMarker::adjustSettings(double value)
if(value > 0.0) { if(value > 0.0) {
value = -value; value = -value;
} }
/* no break */
case Type::PeakTable:
cutoffAmplitude = value; cutoffAmplitude = value;
break;
case Type::PeakTable:
peakThreshold = value;
break;
case Type::PhaseNoise:
offset = value;
break;
} }
update(); update();
} }
@ -518,6 +547,7 @@ void TraceMarker::update()
switch(type) { switch(type) {
case Type::Manual: case Type::Manual:
case Type::Delta: case Type::Delta:
case Type::Noise:
// nothing to do // nothing to do
break; break;
case Type::Maximum: case Type::Maximum:
@ -528,7 +558,7 @@ void TraceMarker::update()
break; break;
case Type::PeakTable: { case Type::PeakTable: {
deleteHelperMarkers(); deleteHelperMarkers();
auto peaks = parentTrace->findPeakFrequencies(100, cutoffAmplitude); auto peaks = parentTrace->findPeakFrequencies(100, peakThreshold);
char suffix = 'a'; char suffix = 'a';
for(auto p : peaks) { for(auto p : peaks) {
auto helper = new TraceMarker(model, number, this); auto helper = new TraceMarker(model, number, this);
@ -632,6 +662,10 @@ void TraceMarker::update()
helperMarkers[3]->setFrequency(peaks[1] + freqDiff); helperMarkers[3]->setFrequency(peaks[1] + freqDiff);
} }
break; break;
case Type::PhaseNoise:
setFrequency(parentTrace->findExtremumFreq(true));
helperMarkers[0]->setFrequency(frequency + offset);
break;
} }
emit dataChanged(this); emit dataChanged(this);
} }
@ -660,6 +694,7 @@ bool TraceMarker::isMovable()
switch(type) { switch(type) {
case Type::Manual: case Type::Manual:
case Type::Delta: case Type::Delta:
case Type::Noise:
return true; return true;
default: default:
return false; return false;

View File

@ -70,11 +70,13 @@ private:
Maximum, Maximum,
Minimum, Minimum,
Delta, Delta,
Noise,
PeakTable, PeakTable,
Lowpass, Lowpass,
Highpass, Highpass,
Bandpass, Bandpass,
TOI, TOI,
PhaseNoise,
}; };
std::set<Type> getSupportedTypes(); std::set<Type> getSupportedTypes();
static QString typeToString(Type t) { static QString typeToString(Type t) {
@ -83,11 +85,13 @@ private:
case Type::Maximum: return "Maximum"; case Type::Maximum: return "Maximum";
case Type::Minimum: return "Minimum"; case Type::Minimum: return "Minimum";
case Type::Delta: return "Delta"; case Type::Delta: return "Delta";
case Type::Noise: return "Noise";
case Type::PeakTable: return "Peak Table"; case Type::PeakTable: return "Peak Table";
case Type::Lowpass: return "Lowpass"; case Type::Lowpass: return "Lowpass";
case Type::Highpass: return "Highpass"; case Type::Highpass: return "Highpass";
case Type::Bandpass: return "Bandpass"; case Type::Bandpass: return "Bandpass";
case Type::TOI: return "TOI/IP3"; case Type::TOI: return "TOI/IP3";
case Type::PhaseNoise: return "Phase noise";
default: return QString(); default: return QString();
} }
} }
@ -111,7 +115,11 @@ private:
TraceMarker *delta; TraceMarker *delta;
std::vector<TraceMarker*> helperMarkers; std::vector<TraceMarker*> helperMarkers;
TraceMarker *parent; TraceMarker *parent;
double cutoffAmplitude; union {
double cutoffAmplitude;
double peakThreshold;
double offset;
};
}; };
#endif // TRACEMARKER_H #endif // TRACEMARKER_H

View File

@ -141,14 +141,14 @@ bool TraceModel::PortExcitationRequired(int port)
void TraceModel::clearVNAData() void TraceModel::clearVNAData()
{ {
for(auto t : traces) { for(auto t : traces) {
if (!t->isTouchstone()) { if (t->isLive()) {
// this trace is fed from live data // this trace is fed from live data
t->clear(); t->clear();
} }
} }
} }
void TraceModel::addVNAData(Protocol::Datapoint d) void TraceModel::addVNAData(const Protocol::Datapoint &d, const Protocol::SweepSettings& settings)
{ {
for(auto t : traces) { for(auto t : traces) {
if (t->isLive() && !t->isPaused()) { if (t->isLive() && !t->isPaused()) {
@ -163,12 +163,12 @@ void TraceModel::addVNAData(Protocol::Datapoint d)
// not a VNA trace, skip // not a VNA trace, skip
continue; 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) { for(auto t : traces) {
if (t->isLive() && !t->isPaused()) { if (t->isLive() && !t->isPaused()) {
@ -181,7 +181,7 @@ void TraceModel::addSAData(Protocol::SpectrumAnalyzerResult d)
// not a SA trace, skip // not a SA trace, skip
continue; continue;
} }
t->addData(td); t->addData(td, settings);
} }
} }
} }

View File

@ -36,8 +36,8 @@ signals:
public slots: public slots:
void clearVNAData(); void clearVNAData();
void addVNAData(Protocol::Datapoint d); void addVNAData(const Protocol::Datapoint& d, const Protocol::SweepSettings& settings);
void addSAData(Protocol::SpectrumAnalyzerResult d); void addSAData(const Protocol::SpectrumAnalyzerResult& d, const Protocol::SpectrumAnalyzerSettings& settings);
private: private:
std::vector<Trace*> traces; std::vector<Trace*> traces;

View File

@ -427,7 +427,7 @@ void VNA::NewDatapoint(Protocol::Datapoint d)
} }
portExtension.applyToMeasurement(d); portExtension.applyToMeasurement(d);
traceModel.addVNAData(d); traceModel.addVNAData(d, settings);
emit dataChanged(); emit dataChanged();
if(d.pointNum == settings.points - 1) { if(d.pointNum == settings.points - 1) {
UpdateAverageCount(); UpdateAverageCount();