Noise + phase noise markers
This commit is contained in:
parent
5ed3547d3d
commit
7921c8db2f
@ -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();
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user