Selectable marker data format, preparation for showing marker data on graphs
This commit is contained in:
parent
2d13fdfe5e
commit
036837a6ae
@ -416,7 +416,7 @@ void AmplitudeCalDialog::ReceivedMeasurement(Protocol::SpectrumAnalyzerResult re
|
||||
if(measured.size() >= averages) {
|
||||
measured.pop_front();
|
||||
}
|
||||
MeasurementResult m = {.port1 = 20*log10(res.port1), .port2 = 20*log10(res.port2)};
|
||||
MeasurementResult m = {.port1 = Unit::dB(res.port1), .port2 = Unit::dB(res.port2)};
|
||||
measured.push_back(m);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <QDebug>
|
||||
#include <QButtonGroup>
|
||||
#include <complex>
|
||||
#include "unit.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -206,8 +207,8 @@ void ManualControlDialog::NewStatus(Protocol::ManualStatus status)
|
||||
|
||||
auto port1referenced = port1 / ref;
|
||||
auto port2referenced = port2 / ref;
|
||||
auto port1db = 20*log10(abs(port1referenced));
|
||||
auto port2db = 20*log10(abs(port2referenced));
|
||||
auto port1db = Unit::dB(port1referenced);
|
||||
auto port2db = Unit::dB(port2referenced);
|
||||
|
||||
ui->port1referenced->setText(QString::number(port1db, 'f', 1) + "db@" + QString::number(arg(port1referenced)*180/M_PI, 'f', 0) + "°");
|
||||
ui->port2referenced->setText(QString::number(port2db, 'f', 1) + "db@" + QString::number(arg(port2referenced)*180/M_PI, 'f', 0) + "°");
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "impedancematchdialog.h"
|
||||
#include "ui_impedancematchdialog.h"
|
||||
#include "Tools/eseries.h"
|
||||
#include "unit.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -239,7 +240,7 @@ void ImpedanceMatchDialog::calculateMatch()
|
||||
ui->mReal->setValue(Zmatched.real());
|
||||
ui->mImag->setValue(Zmatched.imag());
|
||||
double reflection = abs((Zmatched-Z0)/(Zmatched+Z0));
|
||||
auto loss = 20.0*log10(reflection);
|
||||
auto loss = Unit::dB(reflection);
|
||||
ui->mLoss->setValue(loss);
|
||||
|
||||
// set correct image
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <QLabel>
|
||||
#include <QDebug>
|
||||
#include "ui_tdrexplanationwidget.h"
|
||||
#include "unit.h"
|
||||
|
||||
using namespace Math;
|
||||
using namespace std;
|
||||
|
||||
@ -93,7 +95,7 @@ void TDR::edit()
|
||||
|
||||
ui->manualMag->setUnit("dBm");
|
||||
ui->manualMag->setPrecision(3);
|
||||
ui->manualMag->setValue(20*log10(abs(manualDC)));
|
||||
ui->manualMag->setValue(Unit::dB(manualDC));
|
||||
ui->manualPhase->setUnit("°");
|
||||
ui->manualPhase->setPrecision(4);
|
||||
ui->manualPhase->setValue(180.0/M_PI * arg(manualDC));
|
||||
|
@ -345,8 +345,8 @@ void Math::TimeGateGraph::paintEvent(QPaintEvent *event)
|
||||
auto last = input[i-1];
|
||||
auto now = input[i];
|
||||
|
||||
auto y_last = 20*log10(abs(last.y));
|
||||
auto y_now = 20*log10(abs(now.y));
|
||||
auto y_last = Unit::dB(last.y);
|
||||
auto y_now = Unit::dB(now.y);
|
||||
|
||||
if(std::isnan(y_last) || std::isnan(y_now) || std::isinf(y_last) || std::isinf(y_now)) {
|
||||
continue;
|
||||
@ -366,8 +366,8 @@ void Math::TimeGateGraph::paintEvent(QPaintEvent *event)
|
||||
auto x_last = input[i-1].x;
|
||||
auto x_now = input[i].x;
|
||||
|
||||
auto f_last = 20*log10(filter[i-1]);
|
||||
auto f_now = 20*log10(filter[i]);
|
||||
auto f_last = Unit::dB(filter[i-1]);
|
||||
auto f_now = Unit::dB(filter[i]);
|
||||
|
||||
if(std::isnan(f_last) || std::isnan(f_now) || std::isinf(f_last) || std::isinf(f_now)) {
|
||||
continue;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QScrollBar>
|
||||
#include <QSettings>
|
||||
#include <functional>
|
||||
#include "unit.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -867,7 +868,7 @@ std::vector<double> Trace::findPeakFrequencies(unsigned int maxPeaks, double min
|
||||
double max_dbm = -200.0;
|
||||
double min_dbm = 200.0;
|
||||
for(auto d : lastMath->rData()) {
|
||||
double dbm = 20*log10(abs(d.y));
|
||||
double dbm = Unit::dB(d.y);
|
||||
if((dbm >= max_dbm) && (min_dbm <= dbm - minValley)) {
|
||||
// potential peak frequency
|
||||
frequency = d.x;
|
||||
@ -941,7 +942,7 @@ double Trace::getNoise(double frequency)
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
// convert to dbm
|
||||
auto dbm = 20*log10(abs(lastMath->getInterpolatedSample(frequency).y));
|
||||
auto dbm = Unit::dB(lastMath->getInterpolatedSample(frequency).y);
|
||||
// convert to 1Hz bandwidth
|
||||
dbm -= 10*log10(settings.SA.RBW);
|
||||
return dbm;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "tracemarker.h"
|
||||
#include "tracemarker.h"
|
||||
#include <QPainter>
|
||||
#include "CustomWidgets/siunitedit.h"
|
||||
#include <QHBoxLayout>
|
||||
@ -9,6 +9,7 @@
|
||||
#include "unit.h"
|
||||
#include <QMenu>
|
||||
#include <QActionGroup>
|
||||
#include <QApplication>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -26,7 +27,8 @@ TraceMarker::TraceMarker(TraceMarkerModel *model, int number, TraceMarker *paren
|
||||
parent(parent),
|
||||
cutoffAmplitude(-3.0),
|
||||
peakThreshold(-40.0),
|
||||
offset(10000)
|
||||
offset(10000),
|
||||
formatTable(Format::dBAngle)
|
||||
{
|
||||
connect(this, &TraceMarker::traceChanged, this, &TraceMarker::updateContextmenu);
|
||||
connect(this, &TraceMarker::typeChanged, this, &TraceMarker::updateContextmenu);
|
||||
@ -71,6 +73,7 @@ void TraceMarker::assignTrace(Trace *t)
|
||||
for(auto m : helperMarkers) {
|
||||
m->assignTrace(t);
|
||||
}
|
||||
constrainFormat();
|
||||
update();
|
||||
emit traceChanged(this);
|
||||
}
|
||||
@ -80,7 +83,110 @@ Trace *TraceMarker::trace()
|
||||
return parentTrace;
|
||||
}
|
||||
|
||||
QString TraceMarker::readableData()
|
||||
QString TraceMarker::formatToString(TraceMarker::Format f)
|
||||
{
|
||||
switch(f) {
|
||||
case Format::dB: return "dB";
|
||||
case Format::dBAngle: return "dB + angle";
|
||||
case Format::RealImag: return "real + imag";
|
||||
case Format::Impedance: return "Impedance";
|
||||
case Format::TOI: return "Third order intercept";
|
||||
case Format::AvgTone: return "Average Tone Level";
|
||||
case Format::AvgModulationProduct: return "Average Modulation Product Level";
|
||||
case Format::Noise: return "Noise level";
|
||||
case Format::PhaseNoise: return "Phase noise";
|
||||
case Format::Cutoff: return "Cutoff frequency";
|
||||
case Format::CenterBandwidth: return "Center + Bandwidth";
|
||||
case Format::InsertionLoss: return "Insertion loss";
|
||||
case Format::Last: return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
TraceMarker::Format TraceMarker::formatFromString(QString s)
|
||||
{
|
||||
for(int i=0;i<(int) Format::Last;i++) {
|
||||
if(s.compare(formatToString((Format) i)) == 0) {
|
||||
return (Format) i;
|
||||
}
|
||||
}
|
||||
return Format::Last;
|
||||
}
|
||||
|
||||
std::vector<TraceMarker::Format> TraceMarker::formats()
|
||||
{
|
||||
std::vector<Format> ret;
|
||||
for(int i=0;i<(int) Format::Last;i++) {
|
||||
ret.push_back((Format) i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::set<TraceMarker::Format> TraceMarker::applicableFormats()
|
||||
{
|
||||
std::set<Format> ret;
|
||||
if(isTimeDomain()) {
|
||||
switch(type) {
|
||||
case Type::Manual:
|
||||
case Type::Delta:
|
||||
ret.insert(Format::dB);
|
||||
ret.insert(Format::RealImag);
|
||||
if(parentTrace) {
|
||||
auto step = parentTrace->sample(parentTrace->index(position), Trace::SampleType::TimeStep).y.real();
|
||||
if(!isnan(step)) {
|
||||
ret.insert(Format::Impedance);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
switch(type) {
|
||||
case Type::Manual:
|
||||
case Type::Delta:
|
||||
case Type::Maximum:
|
||||
case Type::Minimum:
|
||||
case Type::PeakTable:
|
||||
ret.insert(Format::dB);
|
||||
ret.insert(Format::dBAngle);
|
||||
ret.insert(Format::RealImag);
|
||||
if(parentTrace) {
|
||||
if(parentTrace->isReflection()) {
|
||||
ret.insert(Format::Impedance);
|
||||
}
|
||||
if(!isnan(parentTrace->getNoise(parentTrace->minX()))) {
|
||||
ret.insert(Format::Noise);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case Type::Bandpass:
|
||||
ret.insert(Format::CenterBandwidth);
|
||||
ret.insert(Format::InsertionLoss);
|
||||
break;
|
||||
case Type::Lowpass:
|
||||
case Type::Highpass:
|
||||
ret.insert(Format::Cutoff);
|
||||
ret.insert(Format::InsertionLoss);
|
||||
break;
|
||||
case Type::PhaseNoise:
|
||||
ret.insert(Format::PhaseNoise);
|
||||
ret.insert(Format::dB);
|
||||
break;
|
||||
case Type::TOI:
|
||||
ret.insert(Format::TOI);
|
||||
ret.insert(Format::AvgTone);
|
||||
ret.insert(Format::AvgModulationProduct);
|
||||
break;
|
||||
case Type::Last:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString TraceMarker::readableData(Format f)
|
||||
{
|
||||
if(!parentTrace) {
|
||||
return "";
|
||||
@ -88,128 +194,128 @@ QString TraceMarker::readableData()
|
||||
if(position < parentTrace->minX() || position > parentTrace->maxX()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if(f == Format::Last) {
|
||||
// format not explicitly specified, use setting for table
|
||||
f = formatTable;
|
||||
}
|
||||
|
||||
if(isTimeDomain()) {
|
||||
switch(type) {
|
||||
case Type::Manual: {
|
||||
QString ret;
|
||||
auto impulse = data.real();
|
||||
auto step = parentTrace->sample(parentTrace->index(position), Trace::SampleType::TimeStep).y.real();
|
||||
ret += "Impulse:"+Unit::ToString(impulse, "", "m ", 3);
|
||||
if(!isnan(step)) {
|
||||
ret += " Step:"+Unit::ToString(step, "", "m ", 3);
|
||||
if(abs(step) < 1.0) {
|
||||
auto impedance = 50.0 * (1.0 + step) / (1.0 - step);
|
||||
ret += " Impedance:"+Unit::ToString(impedance, "Ω", "m kM", 3);
|
||||
}
|
||||
if(type != Type::Delta) {
|
||||
switch(f) {
|
||||
case Format::dB:
|
||||
return Unit::ToString(Unit::dB(data), "dB", " ", 3);
|
||||
case Format::RealImag:
|
||||
return Unit::ToString(data.real(), "", " ", 5) + "+"+Unit::ToString(data.imag(), "", " ", 5)+"j";
|
||||
case Format::Impedance: {
|
||||
auto step = parentTrace->sample(parentTrace->index(position), Trace::SampleType::TimeStep).y.real();
|
||||
auto impedance = 50.0 * (1.0 + step) / (1.0 - step);
|
||||
return Unit::ToString(impedance, "Ω", "m kM", 3);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case Type::Delta: {
|
||||
break;
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
} else {
|
||||
if(!delta || !delta->isTimeDomain()) {
|
||||
return "Invalid delta marker";
|
||||
return "Invalid";
|
||||
}
|
||||
// calculate difference between markers
|
||||
auto impulse = data.real() - delta->data.real();
|
||||
QString ret;
|
||||
auto timeDiff = position - delta->position;
|
||||
auto distanceDiff = parentTrace->timeToDistance(position) - delta->parentTrace->timeToDistance(delta->position);
|
||||
ret += "Δ:"+Unit::ToString(timeDiff, "s", "fpnum ", 4) + "/" + Unit::ToString(distanceDiff, "m", "m k", 4);
|
||||
ret += " ΔImpulse:"+Unit::ToString(impulse, "", "m ", 3);
|
||||
auto step = parentTrace->sample(parentTrace->index(position), Trace::SampleType::TimeStep).y.real();
|
||||
auto stepDelta = delta->parentTrace->sample(delta->parentTrace->index(delta->position), Trace::SampleType::TimeStep).y.real();
|
||||
if(!isnan(step) && !isnan(stepDelta)) {
|
||||
auto stepDiff = step - stepDelta;
|
||||
ret += " ΔStep:"+Unit::ToString(stepDiff, "", "m ", 3);
|
||||
if(abs(step) < 1.0 && abs(stepDelta) < 1.0) {
|
||||
auto impedance = 50.0 * (1.0 + step) / (1.0 - step);
|
||||
auto impedanceDelta = 50.0 * (1.0 + stepDelta) / (1.0 - stepDelta);
|
||||
auto impedanceDiff = impedance - impedanceDelta;
|
||||
ret += " ΔImpedance:"+Unit::ToString(impedanceDiff, "Ω", "m kM", 3);
|
||||
}
|
||||
switch(f) {
|
||||
case Format::dB:
|
||||
return "Δ:"+Unit::ToString(Unit::dB(data) - Unit::dB(delta->data), "dB", " ", 3);
|
||||
case Format::RealImag:
|
||||
return "Δ:"+Unit::ToString(data.real() - delta->data.real(), "", " ", 5) + "+"+Unit::ToString(data.imag() - delta->data.real(), "", " ", 5)+"j";
|
||||
case Format::Impedance: {
|
||||
auto step = parentTrace->sample(parentTrace->index(position), Trace::SampleType::TimeStep).y.real();
|
||||
auto stepDelta = delta->parentTrace->sample(delta->parentTrace->index(delta->position), Trace::SampleType::TimeStep).y.real();
|
||||
auto impedance = 50.0 * (1.0 + step) / (1.0 - step);
|
||||
auto impedanceDelta = 50.0 * (1.0 + stepDelta) / (1.0 - stepDelta);
|
||||
return "Δ:"+Unit::ToString(impedance - impedanceDelta, "Ω", "m kM", 3);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
return "Invalid type";
|
||||
}
|
||||
} else {
|
||||
switch(type) {
|
||||
case Type::Manual:
|
||||
case Type::Maximum:
|
||||
case Type::Minimum: {
|
||||
auto phase = arg(data);
|
||||
return QString::number(toDecibel(), 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
|
||||
}
|
||||
case Type::Delta:
|
||||
if(!delta || delta->isTimeDomain()) {
|
||||
return "Invalid delta marker";
|
||||
} else {
|
||||
// calculate difference between markers
|
||||
auto freqDiff = position - delta->position;
|
||||
auto valueDiff = data / delta->data;
|
||||
auto phase = arg(valueDiff);
|
||||
auto db = 20*log10(abs(valueDiff));
|
||||
return Unit::ToString(freqDiff, "Hz", " kMG") + " / " + QString::number(db, 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
|
||||
}
|
||||
break;
|
||||
case Type::Noise:
|
||||
return Unit::ToString(parentTrace->getNoise(position), "dbm/Hz", " ", 3);
|
||||
case Type::PeakTable:
|
||||
return "Found " + QString::number(helperMarkers.size()) + " peaks";
|
||||
case Type::Lowpass:
|
||||
case Type::Highpass:
|
||||
if(parentTrace->isReflection()) {
|
||||
return "Calculation not possible with reflection measurement";
|
||||
} else {
|
||||
auto insertionLoss = toDecibel();
|
||||
auto cutoff = helperMarkers[0]->toDecibel();
|
||||
QString ret = "fc: ";
|
||||
if(cutoff > insertionLoss + cutoffAmplitude) {
|
||||
// the trace never dipped below the specified cutoffAmplitude, exact cutoff frequency unknown
|
||||
ret += type == Type::Lowpass ? ">" : "<";
|
||||
}
|
||||
ret += Unit::ToString(helperMarkers[0]->position, "Hz", " kMG", 4);
|
||||
ret += ", Ins.Loss: >=" + QString::number(-insertionLoss, 'g', 4) + "db";
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case Type::Bandpass:
|
||||
if(parentTrace->isReflection()) {
|
||||
return "Calculation not possible with reflection measurement";
|
||||
} else {
|
||||
auto insertionLoss = toDecibel();
|
||||
auto cutoffL = helperMarkers[0]->toDecibel();
|
||||
auto cutoffH = helperMarkers[1]->toDecibel();
|
||||
auto bandwidth = helperMarkers[1]->position - helperMarkers[0]->position;
|
||||
auto center = helperMarkers[2]->position;
|
||||
QString ret = "fc: ";
|
||||
if(cutoffL > insertionLoss + cutoffAmplitude || cutoffH > insertionLoss + cutoffAmplitude) {
|
||||
// the trace never dipped below the specified cutoffAmplitude, center and exact bandwidth unknown
|
||||
ret += "?, BW: >";
|
||||
} else {
|
||||
ret += Unit::ToString(center, "Hz", " kMG", 5)+ ", BW: ";
|
||||
}
|
||||
ret += Unit::ToString(bandwidth, "Hz", " kMG", 4);
|
||||
ret += ", Ins.Loss: >=" + QString::number(-insertionLoss, 'g', 4) + "db";
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case Type::TOI: {
|
||||
auto avgFundamental = (helperMarkers[0]->toDecibel() + helperMarkers[1]->toDecibel()) / 2;
|
||||
auto avgDistortion = (helperMarkers[2]->toDecibel() + helperMarkers[3]->toDecibel()) / 2;
|
||||
auto TOI = (3 * avgFundamental - avgDistortion) / 2;
|
||||
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]->position) - carrier;
|
||||
return Unit::ToString(phasenoise, "dbc/Hz", " ", 3) +"@" + Unit::ToString(offset, "Hz", " kM", 4) + " offset (" + Unit::ToString(position, "Hz", " kMG", 6) + " carrier)";
|
||||
}
|
||||
case Type::Delta:
|
||||
// TODO
|
||||
return "TODO";
|
||||
default:
|
||||
return "Unknown marker type";
|
||||
switch(f) {
|
||||
case Format::dB: return Unit::ToString(Unit::dB(data), "dB", " ", 3);
|
||||
case Format::dBAngle: return Unit::ToString(Unit::dB(data), "dB", " ", 3) + "/"+Unit::ToString(arg(data)*180/M_PI, "°", " ", 3);
|
||||
case Format::RealImag: return Unit::ToString(data.real(), "", " ", 5) + "+"+Unit::ToString(data.imag(), "", " ", 5)+"j";
|
||||
case Format::Noise: return Unit::ToString(parentTrace->getNoise(position), "dbm/Hz", " ", 3);
|
||||
case Format::TOI: {
|
||||
auto avgFundamental = (helperMarkers[0]->toDecibel() + helperMarkers[1]->toDecibel()) / 2;
|
||||
auto avgDistortion = (helperMarkers[2]->toDecibel() + helperMarkers[3]->toDecibel()) / 2;
|
||||
auto TOI = (3 * avgFundamental - avgDistortion) / 2;
|
||||
return "TOI: "+Unit::ToString(TOI, "dbm", " ", 3);
|
||||
}
|
||||
break;
|
||||
case Format::AvgTone: {
|
||||
auto avgFundamental = (helperMarkers[0]->toDecibel() + helperMarkers[1]->toDecibel()) / 2;
|
||||
return "Avg. Tone: " + Unit::ToString(avgFundamental, "dbm", " ", 3);
|
||||
}
|
||||
break;
|
||||
case Format::AvgModulationProduct: {
|
||||
auto avgDistortion = (helperMarkers[2]->toDecibel() + helperMarkers[3]->toDecibel()) / 2;
|
||||
return "Distortion: " + Unit::ToString(avgDistortion, "dbm", " ", 3);
|
||||
}
|
||||
break;
|
||||
case Format::Cutoff:
|
||||
if(parentTrace->isReflection()) {
|
||||
return "Calculation not possible with reflection measurement";
|
||||
} else {
|
||||
return "Cutoff:" + Unit::ToString(helperMarkers[0]->position, "Hz", " kMG", 4);
|
||||
}
|
||||
case Format::InsertionLoss:
|
||||
if(parentTrace->isReflection()) {
|
||||
return "Calculation not possible with reflection measurement";
|
||||
} else {
|
||||
return "Ins. Loss:"+Unit::ToString(Unit::dB(data), "dB", " ", 3);
|
||||
}
|
||||
case Format::PhaseNoise: {
|
||||
auto carrier = toDecibel();
|
||||
auto phasenoise = parentTrace->getNoise(helperMarkers[0]->position) - carrier;
|
||||
return Unit::ToString(phasenoise, "dbc/Hz", " ", 3) +"@" + Unit::ToString(offset, "Hz", " kM", 4) + " offset";
|
||||
}
|
||||
break;
|
||||
case Format::Impedance: {
|
||||
auto impedance = 50.0 * (1.0 + data) / (1.0 - data);
|
||||
return Unit::ToString(impedance.real(), "Ω", "m k", 5) + "+"+Unit::ToString(impedance.imag(), "Ω", "m k", 5)+"j";
|
||||
}
|
||||
case Format::CenterBandwidth:
|
||||
if(parentTrace->isReflection()) {
|
||||
return "Calculation not possible with reflection measurement";
|
||||
} else {
|
||||
auto insertionLoss = toDecibel();
|
||||
auto cutoffL = helperMarkers[0]->toDecibel();
|
||||
auto cutoffH = helperMarkers[1]->toDecibel();
|
||||
auto bandwidth = helperMarkers[1]->position - helperMarkers[0]->position;
|
||||
auto center = helperMarkers[2]->position;
|
||||
QString ret = "fc: ";
|
||||
if(cutoffL > insertionLoss + cutoffAmplitude || cutoffH > insertionLoss + cutoffAmplitude) {
|
||||
// the trace never dipped below the specified cutoffAmplitude, center and exact bandwidth unknown
|
||||
ret += "?, BW: >";
|
||||
} else {
|
||||
ret += Unit::ToString(center, "Hz", " kMG", 5)+ ", BW: ";
|
||||
}
|
||||
ret += Unit::ToString(bandwidth, "Hz", " kMG", 4);
|
||||
ret += ", Ins.Loss: >=" + QString::number(-insertionLoss, 'g', 4) + "db";
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case Format::Last:
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
QString TraceMarker::readableSettings()
|
||||
@ -228,7 +334,6 @@ QString TraceMarker::readableSettings()
|
||||
case Type::Maximum:
|
||||
case Type::Minimum:
|
||||
case Type::Delta:
|
||||
case Type::Noise:
|
||||
return Unit::ToString(position, "Hz", " kMG", 6);
|
||||
case Type::Lowpass:
|
||||
case Type::Highpass:
|
||||
@ -262,7 +367,6 @@ QString TraceMarker::tooltipSettings()
|
||||
case Type::Maximum:
|
||||
case Type::Minimum:
|
||||
case Type::Delta:
|
||||
case Type::Noise:
|
||||
return "Marker frequency";
|
||||
case Type::Lowpass:
|
||||
case Type::Highpass:
|
||||
@ -370,25 +474,70 @@ void TraceMarker::deltaDeleted()
|
||||
|
||||
void TraceMarker::updateContextmenu()
|
||||
{
|
||||
if(contextmenu) {
|
||||
delete contextmenu;
|
||||
}
|
||||
contextmenu = new QMenu();
|
||||
auto typemenu = new QMenu("Type");
|
||||
auto typegroup = new QActionGroup(contextmenu);
|
||||
for(auto t : getSupportedTypes()) {
|
||||
auto setTypeAction = new QAction(typeToString(t));
|
||||
setTypeAction->setCheckable(true);
|
||||
if(t == type) {
|
||||
setTypeAction->setChecked(true);
|
||||
if(parent) {
|
||||
parent->updateContextmenu();
|
||||
contextmenu = parent->contextmenu;
|
||||
} else {
|
||||
if(contextmenu) {
|
||||
// check if the contextmenu or one of its submenus is currently open
|
||||
auto *activeWidget = QApplication::activePopupWidget();
|
||||
while (activeWidget) {
|
||||
if(activeWidget == contextmenu) {
|
||||
// contextmenu currently open, do not update
|
||||
return;
|
||||
}
|
||||
activeWidget = activeWidget->parentWidget();
|
||||
}
|
||||
delete contextmenu;
|
||||
}
|
||||
contextmenu = new QMenu();
|
||||
auto typemenu = contextmenu->addMenu("Type");
|
||||
auto typegroup = new QActionGroup(contextmenu);
|
||||
for(auto t : getSupportedTypes()) {
|
||||
auto setTypeAction = new QAction(typeToString(t));
|
||||
setTypeAction->setCheckable(true);
|
||||
if(t == type) {
|
||||
setTypeAction->setChecked(true);
|
||||
}
|
||||
connect(setTypeAction, &QAction::triggered, [=](){
|
||||
setType(t);
|
||||
});
|
||||
typegroup->addAction(setTypeAction);
|
||||
typemenu->addAction(setTypeAction);
|
||||
}
|
||||
|
||||
auto table = contextmenu->addMenu("Data Format in Table");
|
||||
auto tablegroup = new QActionGroup(contextmenu);
|
||||
for(auto f : applicableFormats()) {
|
||||
auto setFormatAction = new QAction(formatToString(f));
|
||||
setFormatAction->setCheckable(true);
|
||||
if(f == formatTable) {
|
||||
setFormatAction->setChecked(true);
|
||||
}
|
||||
connect(setFormatAction, &QAction::triggered, [=](){
|
||||
setTableFormat(f);
|
||||
});
|
||||
tablegroup->addAction(setFormatAction);
|
||||
table->addAction(setFormatAction);
|
||||
}
|
||||
|
||||
auto graph = contextmenu->addMenu("Show on Graph");
|
||||
for(auto f : applicableFormats()) {
|
||||
auto setFormatAction = new QAction(formatToString(f));
|
||||
setFormatAction->setCheckable(true);
|
||||
if(formatGraph.count(f)) {
|
||||
setFormatAction->setChecked(true);
|
||||
}
|
||||
connect(setFormatAction, &QAction::triggered, [=](bool checked){
|
||||
if(checked) {
|
||||
formatGraph.insert(f);
|
||||
} else {
|
||||
formatGraph.erase(f);
|
||||
}
|
||||
});
|
||||
graph->addAction(setFormatAction);
|
||||
}
|
||||
connect(setTypeAction, &QAction::triggered, [=](){
|
||||
setType(t);
|
||||
});
|
||||
typegroup->addAction(setTypeAction);
|
||||
typemenu->addAction(setTypeAction);
|
||||
}
|
||||
contextmenu->addMenu(typemenu);
|
||||
}
|
||||
|
||||
std::set<TraceMarker::Type> TraceMarker::getSupportedTypes()
|
||||
@ -420,7 +569,6 @@ 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;
|
||||
@ -446,6 +594,19 @@ void TraceMarker::constrainPosition()
|
||||
}
|
||||
}
|
||||
|
||||
void TraceMarker::constrainFormat()
|
||||
{
|
||||
// check format
|
||||
if(!applicableFormats().count(formatTable)) {
|
||||
setTableFormat(*applicableFormats().begin());
|
||||
}
|
||||
for(auto f : formatGraph) {
|
||||
if(!applicableFormats().count(f)) {
|
||||
formatGraph.erase(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TraceMarker *TraceMarker::bestDeltaCandidate()
|
||||
{
|
||||
TraceMarker *match = nullptr;
|
||||
@ -535,7 +696,7 @@ void TraceMarker::setType(TraceMarker::Type t)
|
||||
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}};
|
||||
required_helpers = {{"o", "Offset", Type::Manual}};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -548,6 +709,8 @@ void TraceMarker::setType(TraceMarker::Type t)
|
||||
helper->setType(h.type);
|
||||
helperMarkers.push_back(helper);
|
||||
}
|
||||
constrainFormat();
|
||||
|
||||
updateSymbol();
|
||||
emit typeChanged(this);
|
||||
update();
|
||||
@ -555,7 +718,7 @@ void TraceMarker::setType(TraceMarker::Type t)
|
||||
|
||||
double TraceMarker::toDecibel()
|
||||
{
|
||||
return 20*log10(abs(data));
|
||||
return Unit::dB(data);
|
||||
}
|
||||
|
||||
bool TraceMarker::isVisible()
|
||||
@ -565,7 +728,6 @@ bool TraceMarker::isVisible()
|
||||
case Type::Delta:
|
||||
case Type::Maximum:
|
||||
case Type::Minimum:
|
||||
case Type::Noise:
|
||||
case Type::PhaseNoise:
|
||||
return true;
|
||||
default:
|
||||
@ -573,6 +735,24 @@ bool TraceMarker::isVisible()
|
||||
}
|
||||
}
|
||||
|
||||
void TraceMarker::setTableFormat(TraceMarker::Format f)
|
||||
{
|
||||
if(formatTable == f) {
|
||||
// already correct format, nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
if(!applicableFormats().count(f)) {
|
||||
// can't use requested format for this type of marker
|
||||
qWarning() << "Requested marker format" << formatToString(f) << "(not applicable for type" << typeToString(type) <<")";
|
||||
return;
|
||||
}
|
||||
|
||||
formatTable = f;
|
||||
updateContextmenu();
|
||||
emit dataChanged(this);
|
||||
}
|
||||
|
||||
TraceMarker::Type TraceMarker::getType() const
|
||||
{
|
||||
return type;
|
||||
@ -609,6 +789,12 @@ nlohmann::json TraceMarker::toJSON()
|
||||
// other types have no settings
|
||||
break;
|
||||
}
|
||||
j["formatTable"] = formatToString(formatTable).toStdString();
|
||||
nlohmann::json jformatGraph;
|
||||
for(auto f : formatGraph) {
|
||||
jformatGraph.push_back(formatToString(f).toStdString());
|
||||
}
|
||||
j["formatGraph"] = jformatGraph;
|
||||
return j;
|
||||
}
|
||||
|
||||
@ -660,6 +846,19 @@ void TraceMarker::fromJSON(nlohmann::json j)
|
||||
// other types have no settings
|
||||
break;
|
||||
}
|
||||
formatTable = formatFromString(QString::fromStdString(j.value("formatTable", formatToString(Format::dBAngle).toStdString())));
|
||||
if(formatTable == Format::Last) {
|
||||
// invalid string, use default
|
||||
formatTable = Format::dBAngle;
|
||||
}
|
||||
formatGraph.clear();
|
||||
for(std::string s : j["formatGraph"]) {
|
||||
auto f = formatFromString(QString::fromStdString(s));
|
||||
if(f != Format::Last) {
|
||||
formatGraph.insert(f);
|
||||
}
|
||||
}
|
||||
updateContextmenu();
|
||||
update();
|
||||
}
|
||||
|
||||
@ -790,7 +989,6 @@ SIUnitEdit *TraceMarker::getSettingsEditor()
|
||||
case Type::Maximum:
|
||||
case Type::Minimum:
|
||||
case Type::Delta:
|
||||
case Type::Noise:
|
||||
case Type::PhaseNoise:
|
||||
default:
|
||||
return new SIUnitEdit("Hz", " kMG", 6);
|
||||
@ -829,7 +1027,6 @@ void TraceMarker::adjustSettings(double value)
|
||||
case Type::Maximum:
|
||||
case Type::Minimum:
|
||||
case Type::Delta:
|
||||
case Type::Noise:
|
||||
setPosition(value);
|
||||
break;
|
||||
case Type::Lowpass:
|
||||
@ -862,7 +1059,6 @@ void TraceMarker::update()
|
||||
switch(type) {
|
||||
case Type::Manual:
|
||||
case Type::Delta:
|
||||
case Type::Noise:
|
||||
// nothing to do
|
||||
break;
|
||||
case Type::Maximum:
|
||||
@ -880,6 +1076,9 @@ void TraceMarker::update()
|
||||
helper->suffix = suffix;
|
||||
helper->assignTrace(parentTrace);
|
||||
helper->setPosition(p);
|
||||
helper->formatTable = formatTable;
|
||||
helper->formatGraph = formatGraph;
|
||||
helper->updateContextmenu();
|
||||
suffix++;
|
||||
helperMarkers.push_back(helper);
|
||||
}
|
||||
@ -897,11 +1096,11 @@ void TraceMarker::update()
|
||||
setPosition(peakFreq);
|
||||
// find the cutoff frequency
|
||||
auto index = parentTrace->index(peakFreq);
|
||||
auto peakAmplitude = 20*log10(abs(parentTrace->sample(index).y));
|
||||
auto peakAmplitude = Unit::dB(parentTrace->sample(index).y);
|
||||
auto cutoff = peakAmplitude + cutoffAmplitude;
|
||||
int inc = type == Type::Lowpass ? 1 : -1;
|
||||
while(index >= 0 && index < (int) parentTrace->size()) {
|
||||
auto amplitude = 20*log10(abs(parentTrace->sample(index).y));
|
||||
auto amplitude = Unit::dB(parentTrace->sample(index).y);
|
||||
if(amplitude <= cutoff) {
|
||||
break;
|
||||
}
|
||||
@ -927,12 +1126,12 @@ void TraceMarker::update()
|
||||
setPosition(peakFreq);
|
||||
// find the cutoff frequencies
|
||||
auto index = parentTrace->index(peakFreq);
|
||||
auto peakAmplitude = 20*log10(abs(parentTrace->sample(index).y));
|
||||
auto peakAmplitude = Unit::dB(parentTrace->sample(index).y);
|
||||
auto cutoff = peakAmplitude + cutoffAmplitude;
|
||||
|
||||
auto low_index = index;
|
||||
while(low_index >= 0) {
|
||||
auto amplitude = 20*log10(abs(parentTrace->sample(low_index).y));
|
||||
auto amplitude = Unit::dB(parentTrace->sample(low_index).y);
|
||||
if(amplitude <= cutoff) {
|
||||
break;
|
||||
}
|
||||
@ -946,7 +1145,7 @@ void TraceMarker::update()
|
||||
|
||||
auto high_index = index;
|
||||
while(high_index < (int) parentTrace->size()) {
|
||||
auto amplitude = 20*log10(abs(parentTrace->sample(high_index).y));
|
||||
auto amplitude = Unit::dB(parentTrace->sample(high_index).y);
|
||||
if(amplitude <= cutoff) {
|
||||
break;
|
||||
}
|
||||
@ -1011,7 +1210,6 @@ bool TraceMarker::isMovable()
|
||||
switch(type) {
|
||||
case Type::Manual:
|
||||
case Type::Delta:
|
||||
case Type::Noise:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -18,7 +18,33 @@ public:
|
||||
~TraceMarker();
|
||||
void assignTrace(Trace *t);
|
||||
Trace* trace();
|
||||
QString readableData();
|
||||
|
||||
enum class Format {
|
||||
dB,
|
||||
dBAngle,
|
||||
RealImag,
|
||||
Impedance,
|
||||
// Noise marker parameters
|
||||
Noise,
|
||||
PhaseNoise,
|
||||
// Filter parameters
|
||||
CenterBandwidth,
|
||||
Cutoff,
|
||||
InsertionLoss,
|
||||
// TOI parameters
|
||||
TOI, // third order intercept point
|
||||
AvgTone, // average level of tone
|
||||
AvgModulationProduct, // average level of modulation products
|
||||
// keep last at end
|
||||
Last,
|
||||
};
|
||||
|
||||
static QString formatToString(Format f);
|
||||
static Format formatFromString(QString s);
|
||||
static std::vector<Format> formats();
|
||||
std::set<Format> applicableFormats();
|
||||
|
||||
QString readableData(Format format = Format::Last);
|
||||
QString readableSettings();
|
||||
QString tooltipSettings();
|
||||
QString readableType();
|
||||
@ -41,7 +67,6 @@ public:
|
||||
Maximum,
|
||||
Minimum,
|
||||
Delta,
|
||||
Noise,
|
||||
PeakTable,
|
||||
Lowpass,
|
||||
Highpass,
|
||||
@ -107,7 +132,6 @@ 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";
|
||||
@ -118,12 +142,15 @@ private:
|
||||
}
|
||||
}
|
||||
void constrainPosition();
|
||||
void constrainFormat();
|
||||
TraceMarker *bestDeltaCandidate();
|
||||
void deleteHelperMarkers();
|
||||
void setType(Type t);
|
||||
double toDecibel();
|
||||
bool isVisible();
|
||||
|
||||
void setTableFormat(Format f);
|
||||
|
||||
TraceMarkerModel *model;
|
||||
Trace *parentTrace;
|
||||
double position;
|
||||
@ -145,6 +172,9 @@ private:
|
||||
double cutoffAmplitude;
|
||||
double peakThreshold;
|
||||
double offset;
|
||||
|
||||
Format formatTable;
|
||||
std::set<Format> formatGraph;
|
||||
};
|
||||
|
||||
#endif // TRACEMARKER_H
|
||||
|
@ -85,12 +85,12 @@ void TraceTouchstoneExport::on_buttonBox_accepted()
|
||||
}
|
||||
t.AddDatapoint(tData);
|
||||
}
|
||||
Touchstone::Unit unit = Touchstone::Unit::GHz;
|
||||
Touchstone::Scale unit = Touchstone::Scale::GHz;
|
||||
switch(ui->cUnit->currentIndex()) {
|
||||
case 0: unit = Touchstone::Unit::Hz; break;
|
||||
case 1: unit = Touchstone::Unit::kHz; break;
|
||||
case 2: unit = Touchstone::Unit::MHz; break;
|
||||
case 3: unit = Touchstone::Unit::GHz; break;
|
||||
case 0: unit = Touchstone::Scale::Hz; break;
|
||||
case 1: unit = Touchstone::Scale::kHz; break;
|
||||
case 2: unit = Touchstone::Scale::MHz; break;
|
||||
case 3: unit = Touchstone::Scale::GHz; break;
|
||||
}
|
||||
Touchstone::Format format = Touchstone::Format::RealImaginary;
|
||||
switch(ui->cFormat->currentIndex()) {
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <QMimeData>
|
||||
#include <QDebug>
|
||||
#include <QMenu>
|
||||
#include "unit.h"
|
||||
|
||||
TraceWidget::TraceWidget(TraceModel &model, QWidget *parent) :
|
||||
QWidget(parent),
|
||||
@ -171,7 +172,7 @@ void TraceWidget::SetupSCPI()
|
||||
if(std::isnan(d.x)) {
|
||||
return "NaN";
|
||||
}
|
||||
return QString::number(20*log10(d.y.real()));
|
||||
return QString::number(Unit::dB(d.y.real()));
|
||||
} else {
|
||||
if(std::isnan(d.x)) {
|
||||
return "NaN,NaN";
|
||||
|
@ -845,7 +845,7 @@ QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlo
|
||||
}
|
||||
switch(type) {
|
||||
case YAxisType::Magnitude:
|
||||
ret.setY(20*log10(abs(data.y)));
|
||||
ret.setY(Unit::dB(data.y));
|
||||
break;
|
||||
case YAxisType::Phase:
|
||||
ret.setY(arg(data.y) * 180.0 / M_PI);
|
||||
@ -859,7 +859,7 @@ QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlo
|
||||
ret.setY(real(data.y));
|
||||
break;
|
||||
case YAxisType::ImpulseMag:
|
||||
ret.setY(20*log10(abs(data.y)));
|
||||
ret.setY(Unit::dB(data.y));
|
||||
break;
|
||||
case YAxisType::Step:
|
||||
ret.setY(t->sample(sample, Trace::SampleType::TimeStep).y.real());
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <QCheckBox>
|
||||
#include <cmath>
|
||||
#include <QDebug>
|
||||
#include "unit.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -243,7 +244,7 @@ void PortExtension::measurementCompleted(std::vector<Protocol::Datapoint> m)
|
||||
}
|
||||
|
||||
double x = sqrt(p.frequency / m.back().frequency);
|
||||
double y = 20*log10(abs(reflection));
|
||||
double y = Unit::dB(reflection);
|
||||
att_x.push_back(x);
|
||||
att_y.push_back(y);
|
||||
avg_x += x;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <cmath>
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include "unit.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -33,7 +34,7 @@ void Touchstone::AddDatapoint(Touchstone::Datapoint p)
|
||||
}
|
||||
}
|
||||
|
||||
void Touchstone::toFile(string filename, Unit unit, Format format)
|
||||
void Touchstone::toFile(string filename, Scale unit, Format format)
|
||||
{
|
||||
// strip any potential file name extension and apply snp convention
|
||||
if(filename.find_last_of('.') != string::npos) {
|
||||
@ -49,10 +50,10 @@ void Touchstone::toFile(string filename, Unit unit, Format format)
|
||||
// write option line
|
||||
file << "# ";
|
||||
switch(unit) {
|
||||
case Unit::Hz: file << "HZ "; break;
|
||||
case Unit::kHz: file << "KHZ "; break;
|
||||
case Unit::MHz: file << "MHZ "; break;
|
||||
case Unit::GHz: file << "GHZ "; break;
|
||||
case Scale::Hz: file << "HZ "; break;
|
||||
case Scale::kHz: file << "KHZ "; break;
|
||||
case Scale::MHz: file << "MHZ "; break;
|
||||
case Scale::GHz: file << "GHZ "; break;
|
||||
}
|
||||
// only S parameters supported so far
|
||||
file << "S ";
|
||||
@ -73,17 +74,17 @@ void Touchstone::toFile(string filename, Unit unit, Format format)
|
||||
out << abs(c) << " " << arg(c) / M_PI * 180.0;
|
||||
break;
|
||||
case Format::DBAngle:
|
||||
out << 20*log10(abs(c)) << " " << arg(c) / M_PI * 180.0;
|
||||
out << Unit::dB(c) << " " << arg(c) / M_PI * 180.0;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
for(auto p : m_datapoints) {
|
||||
switch(unit) {
|
||||
case Unit::Hz: file << p.frequency; break;
|
||||
case Unit::kHz: file << p.frequency / 1e3; break;
|
||||
case Unit::MHz: file << p.frequency / 1e6; break;
|
||||
case Unit::GHz: file << p.frequency / 1e9; break;
|
||||
case Scale::Hz: file << p.frequency; break;
|
||||
case Scale::kHz: file << p.frequency / 1e3; break;
|
||||
case Scale::MHz: file << p.frequency / 1e6; break;
|
||||
case Scale::GHz: file << p.frequency / 1e9; break;
|
||||
}
|
||||
file << " ";
|
||||
// special cases for 1 and 2 port
|
||||
@ -141,7 +142,7 @@ Touchstone Touchstone::fromFile(string filename)
|
||||
unsigned int ports = filename[index_extension + 2] - '0';
|
||||
auto ret = Touchstone(ports);
|
||||
|
||||
Unit unit = Unit::GHz;
|
||||
Scale unit = Scale::GHz;
|
||||
Format format = Format::RealImaginary;
|
||||
|
||||
bool option_line_found = false;
|
||||
@ -187,13 +188,13 @@ Touchstone Touchstone::fromFile(string filename)
|
||||
break;
|
||||
}
|
||||
if (!s.compare("HZ")) {
|
||||
unit = Unit::Hz;
|
||||
unit = Scale::Hz;
|
||||
} else if (!s.compare("KHZ")) {
|
||||
unit = Unit::kHz;
|
||||
unit = Scale::kHz;
|
||||
} else if (!s.compare("MHZ")) {
|
||||
unit = Unit::MHz;
|
||||
unit = Scale::MHz;
|
||||
} else if (!s.compare("GHZ")) {
|
||||
unit = Unit::GHz;
|
||||
unit = Scale::GHz;
|
||||
} else if (!s.compare("S")) {
|
||||
// S parameter, nothing to do
|
||||
} else if (!s.compare("Y")) {
|
||||
@ -245,10 +246,10 @@ Touchstone Touchstone::fromFile(string filename)
|
||||
iss >> point.frequency;
|
||||
point.S.clear();
|
||||
switch(unit) {
|
||||
case Unit::Hz: break;
|
||||
case Unit::kHz: point.frequency *= 1e3; break;
|
||||
case Unit::MHz: point.frequency *= 1e6; break;
|
||||
case Unit::GHz: point.frequency *= 1e9; break;
|
||||
case Scale::Hz: break;
|
||||
case Scale::kHz: point.frequency *= 1e3; break;
|
||||
case Scale::MHz: point.frequency *= 1e6; break;
|
||||
case Scale::GHz: point.frequency *= 1e9; break;
|
||||
}
|
||||
}
|
||||
unsigned int parameters_per_line;
|
||||
|
@ -9,7 +9,7 @@
|
||||
class Touchstone
|
||||
{
|
||||
public:
|
||||
enum class Unit {
|
||||
enum class Scale {
|
||||
Hz,
|
||||
kHz,
|
||||
MHz,
|
||||
@ -30,7 +30,7 @@ public:
|
||||
|
||||
Touchstone(unsigned int m_ports);
|
||||
void AddDatapoint(Datapoint p);
|
||||
void toFile(std::string filename, Unit unit = Unit::GHz, Format format = Format::RealImaginary);
|
||||
void toFile(std::string filename, Scale unit = Scale::GHz, Format format = Format::RealImaginary);
|
||||
static Touchstone fromFile(std::string filename);
|
||||
double minFreq();
|
||||
double maxFreq();
|
||||
|
@ -87,3 +87,13 @@ double Unit::SIPrefixToFactor(char prefix)
|
||||
default: return 1e0; break;
|
||||
}
|
||||
}
|
||||
|
||||
double Unit::dB(double d)
|
||||
{
|
||||
return 20*log10(d);
|
||||
}
|
||||
|
||||
double Unit::dB(std::complex<double> d)
|
||||
{
|
||||
return dB(abs(d));
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define UNIT_H
|
||||
|
||||
#include <QString>
|
||||
#include <complex>
|
||||
|
||||
namespace Unit
|
||||
{
|
||||
@ -9,6 +10,8 @@ namespace Unit
|
||||
// prefixed need to be in ascending order (e.g. "m kMG" is okay, whjle "MkG" does not work)
|
||||
QString ToString(double value, QString unit = QString(), QString prefixes = " ", int precision = 6);
|
||||
double SIPrefixToFactor(char prefix);
|
||||
double dB(std::complex<double> d);
|
||||
double dB(double d);
|
||||
};
|
||||
|
||||
#endif // UNIT_H
|
||||
|
Loading…
Reference in New Issue
Block a user