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.port2 /= 126500000.0;
d = average.process(d);
traceModel.addSAData(d);
traceModel.addSAData(d, settings);
emit dataChanged();
if(d.pointNum == settings.pointNum - 1) {
UpdateAverageCount();

View File

@ -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<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)
{
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 <set>
#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<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);
std::set<TraceMarker *> getMarkers() const;
void setCalibration(bool value);
@ -131,6 +136,13 @@ private:
QString touchstoneFilename;
unsigned int touchstoneParameter;
std::set<TraceMarker*> markers;
struct {
union {
Protocol::SweepSettings VNA;
Protocol::SpectrumAnalyzerSettings SA;
};
bool valid;
} settings;
};
#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);
}
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::Type> 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<helper_descr> 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;

View File

@ -70,11 +70,13 @@ private:
Maximum,
Minimum,
Delta,
Noise,
PeakTable,
Lowpass,
Highpass,
Bandpass,
TOI,
PhaseNoise,
};
std::set<Type> 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<TraceMarker*> helperMarkers;
TraceMarker *parent;
double cutoffAmplitude;
union {
double cutoffAmplitude;
double peakThreshold;
double offset;
};
};
#endif // TRACEMARKER_H

View File

@ -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);
}
}
}

View File

@ -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<Trace*> traces;

View File

@ -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();