time domain markers

This commit is contained in:
Jan Käberich 2020-11-22 21:25:41 +01:00
parent c22d576984
commit 93bf7255c3
17 changed files with 505 additions and 185 deletions

View File

@ -617,7 +617,7 @@ void SpectrumAnalyzer::ConstrainAndUpdateFrequencies()
trackingOffset_limited = true; trackingOffset_limited = true;
settings.trackingGeneratorOffset = Device::Info().limits_maxFreq - settings.f_stop; settings.trackingGeneratorOffset = Device::Info().limits_maxFreq - settings.f_stop;
} }
if(settings.f_start + settings.trackingGeneratorOffset < Device::Info().limits_minFreq) { if((long) settings.f_start + settings.trackingGeneratorOffset < (long) Device::Info().limits_minFreq) {
trackingOffset_limited = true; trackingOffset_limited = true;
settings.trackingGeneratorOffset = Device::Info().limits_minFreq - settings.f_start; settings.trackingGeneratorOffset = Device::Info().limits_minFreq - settings.f_start;
} }

View File

@ -70,7 +70,7 @@ void ImpedanceMatchDialog::on_cSource_currentIndexChanged(int index)
auto reflection = Z0 * (1.0 + data) / (1.0 - data); auto reflection = Z0 * (1.0 + data) / (1.0 - data);
ui->zReal->setValue(reflection.real()); ui->zReal->setValue(reflection.real());
ui->zImag->setValue(reflection.imag()); ui->zImag->setValue(reflection.imag());
ui->zFreq->setValue(m->getFrequency()); ui->zFreq->setValue(m->getPosition());
} }
} }

View File

@ -19,6 +19,7 @@ MarkerWidget::MarkerWidget(TraceMarkerModel &model, QWidget *parent) :
connect(&model.getModel(), &TraceModel::traceAdded, this, &MarkerWidget::updatePersistentEditors); connect(&model.getModel(), &TraceModel::traceAdded, this, &MarkerWidget::updatePersistentEditors);
connect(&model.getModel(), &TraceModel::traceRemoved, this, &MarkerWidget::updatePersistentEditors); connect(&model.getModel(), &TraceModel::traceRemoved, this, &MarkerWidget::updatePersistentEditors);
connect(&model.getModel(), &TraceModel::traceNameChanged, this, &MarkerWidget::updatePersistentEditors); connect(&model.getModel(), &TraceModel::traceNameChanged, this, &MarkerWidget::updatePersistentEditors);
connect(&model.getModel(), &TraceModel::traceTDRstateChanged, this, &MarkerWidget::updatePersistentEditors);
} }
MarkerWidget::~MarkerWidget() MarkerWidget::~MarkerWidget()
@ -55,6 +56,7 @@ void MarkerWidget::on_bAdd_clicked()
{ {
auto marker = model.createDefaultMarker(); auto marker = model.createDefaultMarker();
connect(marker, &TraceMarker::typeChanged, this, &MarkerWidget::updatePersistentEditors); connect(marker, &TraceMarker::typeChanged, this, &MarkerWidget::updatePersistentEditors);
connect(marker, &TraceMarker::timeDomainChanged, this, &MarkerWidget::updatePersistentEditors);
model.addMarker(marker); model.addMarker(marker);
updatePersistentEditors(); updatePersistentEditors();
} }

View File

@ -215,6 +215,11 @@ void Trace::updateTimeDomainData()
} }
t.impulseResponse = real(frequencyDomain[i]) / fft_bins; t.impulseResponse = real(frequencyDomain[i]) / fft_bins;
t.stepResponse = last_step; t.stepResponse = last_step;
if(abs(t.stepResponse) < 1.0) {
t.impedance = 50.0 * (1+t.stepResponse) / (1-t.stepResponse);
} else {
t.impedance = numeric_limits<double>::quiet_NaN();
}
last_step += t.impulseResponse; last_step += t.impulseResponse;
timeDomain.push_back(t); timeDomain.push_back(t);
} }
@ -236,12 +241,67 @@ void Trace::addTDRinterest()
updateTimeDomainData(); updateTimeDomainData();
} }
tdr_users++; tdr_users++;
if(tdr_users == 1) {
emit changedTDRstate(true);
}
} }
void Trace::removeTDRinterest() void Trace::removeTDRinterest()
{ {
if(tdr_users > 0) { if(tdr_users > 0) {
tdr_users--; tdr_users--;
if(tdr_users == 0) {
emit changedTDRstate(false);
}
}
}
Trace::TimedomainData Trace::getTDR(double position)
{
TimedomainData ret = {};
if(!TDRactive() || position < 0) {
return ret;
}
int index = 0;
bool exact = false;
double alpha = 0.0;
if(position <= timeDomain.back().time) {
auto lower = lower_bound(timeDomain.begin(), timeDomain.end(), position, [](const TimedomainData &lhs, const double pos) -> bool {
return lhs.time < pos;
});
index = lower - timeDomain.begin();
if(timeDomain.at(index).time == position) {
exact = true;
} else {
alpha = (position - timeDomain.at(index-1).time) / (timeDomain.at(index).time - timeDomain.at(index-1).time);
}
} else {
if(position > timeDomain.back().distance) {
// too high, invalid position
return ret;
}
auto lower = lower_bound(timeDomain.begin(), timeDomain.end(), position, [](const TimedomainData &lhs, const double pos) -> bool {
return lhs.distance < pos;
});
index = lower - timeDomain.begin();
if(timeDomain.at(index).distance == position) {
exact = true;
} else {
alpha = (position - timeDomain.at(index-1).distance) / (timeDomain.at(index).distance - timeDomain.at(index-1).distance);
}
}
if(exact) {
return timeDomain.at(index);
} else {
// need to interpolate
auto low = timeDomain.at(index-1);
auto high = timeDomain.at(index);
ret.time = low.time * (1.0 - alpha) + high.time * alpha;
ret.distance = low.distance * (1.0 - alpha) + high.distance * alpha;
ret.stepResponse = low.stepResponse * (1.0 - alpha) + high.stepResponse * alpha;
ret.impulseResponse = low.impulseResponse * (1.0 - alpha) + high.impulseResponse * alpha;
ret.impedance = low.impedance * (1.0 - alpha) + high.impedance * alpha;
return ret;
} }
} }

View File

@ -28,6 +28,7 @@ public:
double distance; double distance;
double impulseResponse; double impulseResponse;
double stepResponse; double stepResponse;
double impedance;
}; };
enum class LiveParameter { enum class LiveParameter {
@ -95,7 +96,12 @@ public:
// The data is only updated at the end of a sweep and upon the first addTDRinterest() call. // The data is only updated at the end of a sweep and upon the first addTDRinterest() call.
void addTDRinterest(); void addTDRinterest();
void removeTDRinterest(); void removeTDRinterest();
bool TDRactive() { return tdr_users > 0;};
const std::vector<TimedomainData>& getTDR() { return timeDomain;} const std::vector<TimedomainData>& getTDR() { return timeDomain;}
// interpolates the TDR data
// position is assumed to be the delay if it is smaller than the maximum sampled delay, otherwise it is assumed to be the distance.
// Since the usual delay values are way smaller than the distance values this should work
TimedomainData getTDR(double position);
public slots: public slots:
void setTouchstoneParameter(int value); void setTouchstoneParameter(int value);
@ -117,6 +123,7 @@ signals:
void colorChanged(Trace *t); void colorChanged(Trace *t);
void markerAdded(TraceMarker *m); void markerAdded(TraceMarker *m);
void markerRemoved(TraceMarker *m); void markerRemoved(TraceMarker *m);
void changedTDRstate(bool enabled);
private: private:
void updateTimeDomainData(); void updateTimeDomainData();

View File

@ -14,7 +14,7 @@ TraceMarker::TraceMarker(TraceMarkerModel *model, int number, TraceMarker *paren
: editingFrequeny(false), : editingFrequeny(false),
model(model), model(model),
parentTrace(nullptr), parentTrace(nullptr),
frequency(1000000000), position(1000000000),
number(number), number(number),
data(0), data(0),
type(Type::Manual), type(Type::Manual),
@ -35,6 +35,35 @@ TraceMarker::~TraceMarker()
emit deleted(this); emit deleted(this);
} }
void TraceMarker::setTimeDomain(bool timeDomain)
{
if(timeDomain != this->timeDomain) {
// setting changed, check if actually available
if(timeDomain && !parentTrace->TDRactive()) {
qWarning() << "Attempted to enable TDR marker on trace without active TDR";
return;
}
this->timeDomain = timeDomain;
// TODO handle changed setting and everything
if(timeDomain) {
// need to delete this marker if the TDR data of the trace is no longer available
connect(parentTrace, &Trace::changedTDRstate, [=](bool tdr_available){
if(!tdr_available) {
delete this;
}
});
// check if current type still supported
if(!getSupportedTypes().count(type)) {
// unsupported type, change to manual
setType(Type::Manual);
}
} else {
disconnect(parentTrace, &Trace::changedTDRstate, nullptr, nullptr);
}
emit timeDomainChanged();
}
}
void TraceMarker::assignTrace(Trace *t) void TraceMarker::assignTrace(Trace *t)
{ {
if(parentTrace) { if(parentTrace) {
@ -44,6 +73,7 @@ void TraceMarker::assignTrace(Trace *t)
disconnect(parentTrace, &Trace::dataChanged, this, &TraceMarker::traceDataChanged); disconnect(parentTrace, &Trace::dataChanged, this, &TraceMarker::traceDataChanged);
disconnect(parentTrace, &Trace::colorChanged, this, &TraceMarker::updateSymbol); disconnect(parentTrace, &Trace::colorChanged, this, &TraceMarker::updateSymbol);
} }
setTimeDomain(false);
parentTrace = t; parentTrace = t;
if(!getSupportedTypes().count(type)) { if(!getSupportedTypes().count(type)) {
// new trace does not support the current type // new trace does not support the current type
@ -53,7 +83,7 @@ void TraceMarker::assignTrace(Trace *t)
connect(parentTrace, &Trace::deleted, this, &TraceMarker::parentTraceDeleted); connect(parentTrace, &Trace::deleted, this, &TraceMarker::parentTraceDeleted);
connect(parentTrace, &Trace::dataChanged, this, &TraceMarker::traceDataChanged); connect(parentTrace, &Trace::dataChanged, this, &TraceMarker::traceDataChanged);
connect(parentTrace, &Trace::colorChanged, this, &TraceMarker::updateSymbol); connect(parentTrace, &Trace::colorChanged, this, &TraceMarker::updateSymbol);
constrainFrequency(); constrainPosition();
updateSymbol(); updateSymbol();
parentTrace->addMarker(this); parentTrace->addMarker(this);
for(auto m : helperMarkers) { for(auto m : helperMarkers) {
@ -73,24 +103,49 @@ QString TraceMarker::readableData()
switch(type) { switch(type) {
case Type::Manual: case Type::Manual:
case Type::Maximum: case Type::Maximum:
case Type::Minimum: { case Type::Minimum:
auto phase = arg(data); if(isTimeDomain()) {
return QString::number(toDecibel(), 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4); QString ret;
} ret += "Impulse:"+Unit::ToString(timeData.impulseResponse, "", "m ", 3)+" Step:"+Unit::ToString(timeData.stepResponse, "", "m ", 3)+" Impedance:";
if(isnan(timeData.impedance)) {
ret += "Invalid";
} else {
ret += Unit::ToString(timeData.impedance, "Ω", "m k", 3);
}
return ret;
} else {
auto phase = arg(data);
return QString::number(toDecibel(), 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
}
case Type::Delta: case Type::Delta:
if(!delta) { if(!delta || delta->isTimeDomain() != isTimeDomain()) {
return "Invalid delta marker"; return "Invalid delta marker";
} else { } else {
// calculate difference between markers if(isTimeDomain()) {
auto freqDiff = frequency - delta->frequency; // calculate difference between markers
auto valueDiff = data / delta->data; auto impulse = timeData.impulseResponse - delta->timeData.impulseResponse;
auto phase = arg(valueDiff); auto step = timeData.stepResponse - delta->timeData.stepResponse;
auto db = 20*log10(abs(valueDiff)); auto impedance = timeData.impedance - delta->timeData.impedance;
return Unit::ToString(freqDiff, "Hz", " kMG") + " / " + QString::number(db, 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4); QString ret;
ret += "ΔImpulse:"+Unit::ToString(impulse, "", "m ", 3)+" ΔStep:"+Unit::ToString(step, "", "m ", 3)+" ΔImpedance:";
if(isnan(timeData.impedance)) {
ret += "Invalid";
} else {
ret += Unit::ToString(impedance, "Ω", "m k", 3);
}
return ret;
} 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; break;
case Type::Noise: case Type::Noise:
return Unit::ToString(parentTrace->getNoise(frequency), "dbm/Hz", " ", 3); return Unit::ToString(parentTrace->getNoise(position), "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:
@ -105,7 +160,7 @@ QString TraceMarker::readableData()
// the trace never dipped below the specified cutoffAmplitude, exact cutoff frequency unknown // the trace never dipped below the specified cutoffAmplitude, exact cutoff frequency unknown
ret += type == Type::Lowpass ? ">" : "<"; ret += type == Type::Lowpass ? ">" : "<";
} }
ret += Unit::ToString(helperMarkers[0]->frequency, "Hz", " kMG", 4); ret += Unit::ToString(helperMarkers[0]->position, "Hz", " kMG", 4);
ret += ", Ins.Loss: >=" + QString::number(-insertionLoss, 'g', 4) + "db"; ret += ", Ins.Loss: >=" + QString::number(-insertionLoss, 'g', 4) + "db";
return ret; return ret;
} }
@ -117,8 +172,8 @@ QString TraceMarker::readableData()
auto insertionLoss = toDecibel(); auto insertionLoss = toDecibel();
auto cutoffL = helperMarkers[0]->toDecibel(); auto cutoffL = helperMarkers[0]->toDecibel();
auto cutoffH = helperMarkers[1]->toDecibel(); auto cutoffH = helperMarkers[1]->toDecibel();
auto bandwidth = helperMarkers[1]->frequency - helperMarkers[0]->frequency; auto bandwidth = helperMarkers[1]->position - helperMarkers[0]->position;
auto center = helperMarkers[2]->frequency; auto center = helperMarkers[2]->position;
QString ret = "fc: "; QString ret = "fc: ";
if(cutoffL > insertionLoss + cutoffAmplitude || cutoffH > insertionLoss + cutoffAmplitude) { if(cutoffL > insertionLoss + cutoffAmplitude || cutoffH > insertionLoss + cutoffAmplitude) {
// the trace never dipped below the specified cutoffAmplitude, center and exact bandwidth unknown // the trace never dipped below the specified cutoffAmplitude, center and exact bandwidth unknown
@ -140,8 +195,8 @@ QString TraceMarker::readableData()
break; break;
case Type::PhaseNoise: { case Type::PhaseNoise: {
auto carrier = toDecibel(); auto carrier = toDecibel();
auto phasenoise = parentTrace->getNoise(helperMarkers[0]->frequency) - carrier; auto phasenoise = parentTrace->getNoise(helperMarkers[0]->position) - carrier;
return Unit::ToString(phasenoise, "dbc/Hz", " ", 3) +"@" + Unit::ToString(offset, "Hz", " kM", 4) + " offset (" + Unit::ToString(frequency, "Hz", " kMG", 6) + " carrier)"; return Unit::ToString(phasenoise, "dbc/Hz", " ", 3) +"@" + Unit::ToString(offset, "Hz", " kM", 4) + " offset (" + Unit::ToString(position, "Hz", " kMG", 6) + " carrier)";
} }
default: default:
return "Unknown marker type"; return "Unknown marker type";
@ -150,25 +205,42 @@ QString TraceMarker::readableData()
QString TraceMarker::readableSettings() QString TraceMarker::readableSettings()
{ {
switch(type) { if(timeDomain) {
case Type::Manual: switch(type) {
case Type::Maximum: case Type::Manual:
case Type::Minimum: case Type::Delta: {
case Type::Delta: QString unit;
case Type::Noise: if(position <= parentTrace->getTDR().back().time) {
return Unit::ToString(frequency, "Hz", " kMG", 6); unit = "s";
case Type::Lowpass: } else {
case Type::Highpass: unit = "m";
case Type::Bandpass: }
return Unit::ToString(cutoffAmplitude, "db", " ", 3); return Unit::ToString(position, unit, "fpnum k", 4);
case Type::PeakTable: }
return Unit::ToString(peakThreshold, "db", " ", 3); default:
case Type::TOI: return "Unhandled case";
return "none"; }
case Type::PhaseNoise: } else {
return Unit::ToString(offset, "Hz", " kM", 4); switch(type) {
default: case Type::Manual:
return "Unhandled case"; 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:
case Type::Bandpass:
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";
}
} }
} }
@ -181,10 +253,10 @@ QString TraceMarker::readableType()
} }
} }
void TraceMarker::setFrequency(double freq) void TraceMarker::setPosition(double pos)
{ {
frequency = freq; position = pos;
constrainFrequency(); constrainPosition();
} }
void TraceMarker::parentTraceDeleted(Trace *t) void TraceMarker::parentTraceDeleted(Trace *t)
@ -197,9 +269,15 @@ void TraceMarker::parentTraceDeleted(Trace *t)
void TraceMarker::traceDataChanged() void TraceMarker::traceDataChanged()
{ {
// some data of the parent trace changed, check if marker data also changed // some data of the parent trace changed, check if marker data also changed
auto tracedata = parentTrace->getData(frequency); complex<double> newdata;
if(tracedata != data) { if (timeDomain) {
data = tracedata; timeData = parentTrace->getTDR(position);
newdata = complex<double>(timeData.stepResponse, timeData.impulseResponse);
} else {
newdata = parentTrace->getData(position);
}
if (newdata != data) {
data = newdata;
update(); update();
emit rawDataChanged(); emit rawDataChanged();
} }
@ -231,43 +309,59 @@ std::set<TraceMarker::Type> TraceMarker::getSupportedTypes()
{ {
set<TraceMarker::Type> supported; set<TraceMarker::Type> supported;
if(parentTrace) { if(parentTrace) {
// all traces support some basic markers if(timeDomain) {
supported.insert(Type::Manual); // only basic markers in time domain
supported.insert(Type::Maximum); supported.insert(Type::Manual);
supported.insert(Type::Minimum); supported.insert(Type::Delta);
supported.insert(Type::Delta); } else {
supported.insert(Type::PeakTable); // all traces support some basic markers
if(parentTrace->isLive()) { supported.insert(Type::Manual);
switch(parentTrace->liveParameter()) { supported.insert(Type::Maximum);
case Trace::LiveParameter::S11: supported.insert(Type::Minimum);
case Trace::LiveParameter::S12: supported.insert(Type::Delta);
case Trace::LiveParameter::S21: supported.insert(Type::PeakTable);
case Trace::LiveParameter::S22: if(parentTrace->isLive()) {
// special VNA marker types switch(parentTrace->liveParameter()) {
supported.insert(Type::Lowpass); case Trace::LiveParameter::S11:
supported.insert(Type::Highpass); case Trace::LiveParameter::S12:
supported.insert(Type::Bandpass); case Trace::LiveParameter::S21:
break; case Trace::LiveParameter::S22:
case Trace::LiveParameter::Port1: // special VNA marker types
case Trace::LiveParameter::Port2: supported.insert(Type::Lowpass);
// special SA marker types supported.insert(Type::Highpass);
supported.insert(Type::Noise); supported.insert(Type::Bandpass);
supported.insert(Type::TOI); break;
supported.insert(Type::PhaseNoise); case Trace::LiveParameter::Port1:
break; case Trace::LiveParameter::Port2:
// special SA marker types
supported.insert(Type::Noise);
supported.insert(Type::TOI);
supported.insert(Type::PhaseNoise);
break;
}
} }
} }
} }
return supported; return supported;
} }
void TraceMarker::constrainFrequency() void TraceMarker::constrainPosition()
{ {
if(parentTrace && parentTrace->size() > 0) { if(parentTrace) {
if(frequency > parentTrace->maxFreq()) { if(timeDomain) {
frequency = parentTrace->maxFreq(); if(position < 0) {
} else if(frequency < parentTrace->minFreq()) { position = 0;
frequency = parentTrace->minFreq(); } else if(position > parentTrace->getTDR().back().distance) {
position = parentTrace->getTDR().back().distance;
}
} else {
if(parentTrace->size() > 0) {
if(position > parentTrace->maxFreq()) {
position = parentTrace->maxFreq();
} else if(position < parentTrace->minFreq()) {
position = parentTrace->minFreq();
}
}
} }
traceDataChanged(); traceDataChanged();
} }
@ -312,24 +406,27 @@ void TraceMarker::setType(TraceMarker::Type t)
vector<helper_descr> required_helpers; vector<helper_descr> required_helpers;
switch(type) { switch(type) {
case Type::Delta: case Type::Delta:
if(!delta) { delta = nullptr;
// invalid delta marker assigned, attempt to find a matching marker // invalid delta marker assigned, attempt to find a matching marker
for(int pass = 0;pass < 3;pass++) { for(int pass = 0;pass < 3;pass++) {
for(auto m : model->getMarkers()) { for(auto m : model->getMarkers()) {
if(pass == 0 && m->parentTrace != parentTrace) { if(m->isTimeDomain() != isTimeDomain()) {
// ignore markers on different traces in first pass // markers are not on the same domain
continue; continue;
}
if(pass <= 1 && m == this) {
// ignore itself on second pass
continue;
}
assignDeltaMarker(m);
break;
} }
if(delta) { if(pass == 0 && m->parentTrace != parentTrace) {
break; // ignore markers on different traces in first pass
continue;
} }
if(pass <= 1 && m == this) {
// ignore itself on second pass
continue;
}
assignDeltaMarker(m);
break;
}
if(delta) {
break;
} }
} }
break; break;
@ -387,6 +484,11 @@ QString TraceMarker::getSuffix() const
return suffix; return suffix;
} }
bool TraceMarker::isTimeDomain() const
{
return timeDomain;
}
const std::vector<TraceMarker *> &TraceMarker::getHelperMarkers() const const std::vector<TraceMarker *> &TraceMarker::getHelperMarkers() const
{ {
return helperMarkers; return helperMarkers;
@ -492,21 +594,31 @@ void TraceMarker::updateTypeFromEditor(QWidget *w)
SIUnitEdit *TraceMarker::getSettingsEditor() SIUnitEdit *TraceMarker::getSettingsEditor()
{ {
switch(type) { if(timeDomain) {
case Type::Manual: switch(type) {
case Type::Maximum: case Type::Manual:
case Type::Minimum: case Type::Delta:
case Type::Delta: return new SIUnitEdit("", "fpnum k", 6);
case Type::Noise: default:
case Type::PhaseNoise: return nullptr;
default: }
return new SIUnitEdit("Hz", " kMG", 6); } else {
case Type::Lowpass: switch(type) {
case Type::Highpass: case Type::Manual:
case Type::PeakTable: case Type::Maximum:
return new SIUnitEdit("db", " ", 3); case Type::Minimum:
case Type::TOI: case Type::Delta:
return nullptr; case Type::Noise:
case Type::PhaseNoise:
default:
return new SIUnitEdit("Hz", " kMG", 6);
case Type::Lowpass:
case Type::Highpass:
case Type::PeakTable:
return new SIUnitEdit("db", " ", 3);
case Type::TOI:
return nullptr;
}
} }
} }
@ -519,7 +631,7 @@ void TraceMarker::adjustSettings(double value)
case Type::Delta: case Type::Delta:
case Type::Noise: case Type::Noise:
default: default:
setFrequency(value); setPosition(value);
/* no break */ /* no break */
case Type::Lowpass: case Type::Lowpass:
case Type::Highpass: case Type::Highpass:
@ -552,10 +664,10 @@ void TraceMarker::update()
// nothing to do // nothing to do
break; break;
case Type::Maximum: case Type::Maximum:
setFrequency(parentTrace->findExtremumFreq(true)); setPosition(parentTrace->findExtremumFreq(true));
break; break;
case Type::Minimum: case Type::Minimum:
setFrequency(parentTrace->findExtremumFreq(false)); setPosition(parentTrace->findExtremumFreq(false));
break; break;
case Type::PeakTable: { case Type::PeakTable: {
deleteHelperMarkers(); deleteHelperMarkers();
@ -565,7 +677,7 @@ void TraceMarker::update()
auto helper = new TraceMarker(model, number, this); auto helper = new TraceMarker(model, number, this);
helper->suffix = suffix; helper->suffix = suffix;
helper->assignTrace(parentTrace); helper->assignTrace(parentTrace);
helper->setFrequency(p); helper->setPosition(p);
suffix++; suffix++;
helperMarkers.push_back(helper); helperMarkers.push_back(helper);
} }
@ -580,7 +692,7 @@ void TraceMarker::update()
// find the maximum // find the maximum
auto peakFreq = parentTrace->findExtremumFreq(true); auto peakFreq = parentTrace->findExtremumFreq(true);
// this marker shows the insertion loss // this marker shows the insertion loss
setFrequency(peakFreq); setPosition(peakFreq);
// find the cutoff frequency // find the cutoff frequency
auto index = parentTrace->index(peakFreq); auto index = parentTrace->index(peakFreq);
auto peakAmplitude = 20*log10(abs(parentTrace->sample(index).S)); auto peakAmplitude = 20*log10(abs(parentTrace->sample(index).S));
@ -599,7 +711,7 @@ void TraceMarker::update()
index = parentTrace->size() - 1; index = parentTrace->size() - 1;
} }
// set position of cutoff marker // set position of cutoff marker
helperMarkers[0]->setFrequency(parentTrace->sample(index).frequency); helperMarkers[0]->setPosition(parentTrace->sample(index).frequency);
} }
break; break;
case Type::Bandpass: case Type::Bandpass:
@ -610,7 +722,7 @@ void TraceMarker::update()
// find the maximum // find the maximum
auto peakFreq = parentTrace->findExtremumFreq(true); auto peakFreq = parentTrace->findExtremumFreq(true);
// this marker shows the insertion loss // this marker shows the insertion loss
setFrequency(peakFreq); setPosition(peakFreq);
// find the cutoff frequencies // find the cutoff frequencies
auto index = parentTrace->index(peakFreq); auto index = parentTrace->index(peakFreq);
auto peakAmplitude = 20*log10(abs(parentTrace->sample(index).S)); auto peakAmplitude = 20*log10(abs(parentTrace->sample(index).S));
@ -628,7 +740,7 @@ void TraceMarker::update()
low_index = 0; low_index = 0;
} }
// set position of cutoff marker // set position of cutoff marker
helperMarkers[0]->setFrequency(parentTrace->sample(low_index).frequency); helperMarkers[0]->setPosition(parentTrace->sample(low_index).frequency);
auto high_index = index; auto high_index = index;
while(high_index < (int) parentTrace->size()) { while(high_index < (int) parentTrace->size()) {
@ -642,9 +754,9 @@ void TraceMarker::update()
high_index = parentTrace->size() - 1; high_index = parentTrace->size() - 1;
} }
// set position of cutoff marker // set position of cutoff marker
helperMarkers[1]->setFrequency(parentTrace->sample(high_index).frequency); helperMarkers[1]->setPosition(parentTrace->sample(high_index).frequency);
// set center marker inbetween cutoff markers // set center marker inbetween cutoff markers
helperMarkers[2]->setFrequency((helperMarkers[0]->frequency + helperMarkers[1]->frequency) / 2); helperMarkers[2]->setPosition((helperMarkers[0]->position + helperMarkers[1]->position) / 2);
} }
break; break;
case Type::TOI: { case Type::TOI: {
@ -656,16 +768,16 @@ void TraceMarker::update()
// assign marker frequenies: // assign marker frequenies:
// this marker is the left peak, first helper the right peak. // this marker is the left peak, first helper the right peak.
// 2nd and 3rd helpers are left and right TOI peaks // 2nd and 3rd helpers are left and right TOI peaks
helperMarkers[0]->setFrequency(peaks[0]); helperMarkers[0]->setPosition(peaks[0]);
helperMarkers[1]->setFrequency(peaks[1]); helperMarkers[1]->setPosition(peaks[1]);
auto freqDiff = peaks[1] - peaks[0]; auto freqDiff = peaks[1] - peaks[0];
helperMarkers[2]->setFrequency(peaks[0] - freqDiff); helperMarkers[2]->setPosition(peaks[0] - freqDiff);
helperMarkers[3]->setFrequency(peaks[1] + freqDiff); helperMarkers[3]->setPosition(peaks[1] + freqDiff);
} }
break; break;
case Type::PhaseNoise: case Type::PhaseNoise:
setFrequency(parentTrace->findExtremumFreq(true)); setPosition(parentTrace->findExtremumFreq(true));
helperMarkers[0]->setFrequency(frequency + offset); helperMarkers[0]->setPosition(position + offset);
break; break;
} }
emit dataChanged(this); emit dataChanged(this);
@ -686,6 +798,15 @@ std::complex<double> TraceMarker::getData() const
return data; return data;
} }
Trace::TimedomainData TraceMarker::getTimeData() const
{
Trace::TimedomainData ret = {};
if(timeDomain) {
ret = timeData;
}
return ret;
}
bool TraceMarker::isMovable() bool TraceMarker::isMovable()
{ {
if(parent) { if(parent) {
@ -707,8 +828,8 @@ QPixmap &TraceMarker::getSymbol()
return symbol; return symbol;
} }
double TraceMarker::getFrequency() const double TraceMarker::getPosition() const
{ {
return frequency; return position;
} }

View File

@ -15,14 +15,16 @@ class TraceMarker : public QObject
public: public:
TraceMarker(TraceMarkerModel *model, int number = 1, TraceMarker *parent = nullptr, QString descr = QString()); TraceMarker(TraceMarkerModel *model, int number = 1, TraceMarker *parent = nullptr, QString descr = QString());
~TraceMarker(); ~TraceMarker();
void setTimeDomain(bool timeDomain);
void assignTrace(Trace *t); void assignTrace(Trace *t);
Trace* trace(); Trace* trace();
QString readableData(); QString readableData();
QString readableSettings(); QString readableSettings();
QString readableType(); QString readableType();
double getFrequency() const; double getPosition() const;
std::complex<double> getData() const; std::complex<double> getData() const;
Trace::TimedomainData getTimeData() const;
bool isMovable(); bool isMovable();
QPixmap& getSymbol(); QPixmap& getSymbol();
@ -46,8 +48,10 @@ public:
TraceMarker *helperMarker(unsigned int i); TraceMarker *helperMarker(unsigned int i);
QString getSuffix() const; QString getSuffix() const;
bool isTimeDomain() const;
public slots: public slots:
void setFrequency(double freq); void setPosition(double freq);
signals: signals:
void deleted(TraceMarker *m); void deleted(TraceMarker *m);
void dataChanged(TraceMarker *m); void dataChanged(TraceMarker *m);
@ -56,6 +60,7 @@ signals:
void traceChanged(TraceMarker *m); void traceChanged(TraceMarker *m);
void beginRemoveHelperMarkers(TraceMarker *m); void beginRemoveHelperMarkers(TraceMarker *m);
void endRemoveHelperMarkers(TraceMarker *m); void endRemoveHelperMarkers(TraceMarker *m);
void timeDomainChanged();
private slots: private slots:
void parentTraceDeleted(Trace *t); void parentTraceDeleted(Trace *t);
@ -95,7 +100,7 @@ private:
default: return QString(); default: return QString();
} }
} }
void constrainFrequency(); void constrainPosition();
void assignDeltaMarker(TraceMarker *m); void assignDeltaMarker(TraceMarker *m);
void deleteHelperMarkers(); void deleteHelperMarkers();
void setType(Type t); void setType(Type t);
@ -104,9 +109,12 @@ private:
TraceMarkerModel *model; TraceMarkerModel *model;
Trace *parentTrace; Trace *parentTrace;
double frequency; double position;
int number; int number;
// Frequency domain: S parameter
// Time domain: imag part is impulse response, real part is step response
std::complex<double> data; std::complex<double> data;
Trace::TimedomainData timeData;
QPixmap symbol; QPixmap symbol;
Type type; Type type;
QString suffix; QString suffix;
@ -120,6 +128,8 @@ private:
double peakThreshold; double peakThreshold;
double offset; double offset;
}; };
bool timeDomain;
}; };
#endif // TRACEMARKER_H #endif // TRACEMARKER_H

View File

@ -70,7 +70,7 @@ TraceMarker *TraceMarkerModel::createDefaultMarker()
} }
} while (used); } while (used);
auto marker = new TraceMarker(this, number); auto marker = new TraceMarker(this, number);
marker->setFrequency(2150000000); marker->setPosition(2150000000);
marker->assignTrace(model.trace(0)); marker->assignTrace(model.trace(0));
return marker; return marker;
} }
@ -199,8 +199,11 @@ bool TraceMarkerModel::setData(const QModelIndex &index, const QVariant &value,
} }
break; break;
case ColIndexTrace: { case ColIndexTrace: {
auto trace = qvariant_cast<Trace*>(value); auto info = qvariant_cast<MarkerWidgetTraceInfo>(value);
m->assignTrace(trace); // always disable timedomain before switching trace
m->setTimeDomain(false);
m->assignTrace(info.trace);
m->setTimeDomain(info.isTimeDomain);
} }
break; break;
case ColIndexSettings: { case ColIndexSettings: {
@ -283,7 +286,14 @@ QWidget *MarkerTraceDelegate::createEditor(QWidget *parent, const QStyleOptionVi
}); });
auto traces = model->getModel().getTraces(); auto traces = model->getModel().getTraces();
for(auto t : traces) { for(auto t : traces) {
c->addItem(t->name(), QVariant::fromValue<Trace*>(t)); MarkerWidgetTraceInfo info;
info.trace = t;
info.isTimeDomain = false;
c->addItem(t->name(), QVariant::fromValue(info));
if(t->TDRactive()) {
info.isTimeDomain = true;
c->addItem(t->name() + " Time Domain", QVariant::fromValue(info));
}
} }
return c; return c;
} }
@ -292,8 +302,12 @@ void MarkerTraceDelegate::setEditorData(QWidget *editor, const QModelIndex &inde
{ {
auto marker = static_cast<const TraceMarkerModel*>(index.model())->markerFromIndex(index); auto marker = static_cast<const TraceMarkerModel*>(index.model())->markerFromIndex(index);
auto c = (QComboBox*) editor; auto c = (QComboBox*) editor;
MarkerWidgetTraceInfo markerInfo;
markerInfo.trace = marker->trace();
markerInfo.isTimeDomain = marker->isTimeDomain();
for(int i=0;i<c->count();i++) { for(int i=0;i<c->count();i++) {
if(qvariant_cast<Trace*>(c->itemData(i)) == marker->trace()) { auto info = qvariant_cast<MarkerWidgetTraceInfo>(c->itemData(i));
if(info == markerInfo) {
c->setCurrentIndex(i); c->setCurrentIndex(i);
return; return;
} }

View File

@ -16,6 +16,19 @@ class MarkerTraceDelegate : public QStyledItemDelegate
void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override; void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override;
}; };
class MarkerWidgetTraceInfo {
public:
Trace *trace;
bool isTimeDomain;
};
inline bool operator==(const MarkerWidgetTraceInfo& lhs, const MarkerWidgetTraceInfo& rhs)
{
return lhs.trace == rhs.trace && lhs.isTimeDomain == rhs.isTimeDomain;
}
Q_DECLARE_METATYPE(MarkerWidgetTraceInfo)
class MarkerTypeDelegate : public QStyledItemDelegate class MarkerTypeDelegate : public QStyledItemDelegate
{ {
Q_OBJECT; Q_OBJECT;

View File

@ -23,6 +23,9 @@ void TraceModel::addTrace(Trace *t)
connect(t, &Trace::nameChanged, [=]() { connect(t, &Trace::nameChanged, [=]() {
emit traceNameChanged(t); emit traceNameChanged(t);
}); });
connect(t, &Trace::changedTDRstate, [=](bool enabled) {
emit traceTDRstateChanged(t, enabled);
});
traces.push_back(t); traces.push_back(t);
endInsertRows(); endInsertRows();
emit traceAdded(t); emit traceAdded(t);

View File

@ -33,6 +33,7 @@ signals:
void traceRemoved(Trace *t); void traceRemoved(Trace *t);
void requiredExcitation(bool excitePort1, bool excitePort2); void requiredExcitation(bool excitePort1, bool excitePort2);
void traceNameChanged(Trace *t); void traceNameChanged(Trace *t);
void traceTDRstateChanged(Trace *t, bool enabled);
public slots: public slots:
void clearVNAData(); void clearVNAData();

View File

@ -141,10 +141,7 @@ void TracePlot::mousePressEvent(QMouseEvent *event)
if(!m->isMovable()) { if(!m->isMovable()) {
continue; continue;
} }
Trace::Data d; auto markerPoint = markerToPixel(m);
d.S = m->getData();
d.frequency = m->getFrequency();
auto markerPoint = dataToPixel(d);
if(markerPoint.isNull()) { if(markerPoint.isNull()) {
// invalid, skip // invalid, skip
continue; continue;
@ -167,29 +164,9 @@ void TracePlot::mousePressEvent(QMouseEvent *event)
void TracePlot::mouseMoveEvent(QMouseEvent *event) void TracePlot::mouseMoveEvent(QMouseEvent *event)
{ {
if(selectedMarker) { if(selectedMarker) {
auto t = selectedMarker->trace();
auto clickPoint = event->pos() - QPoint(marginLeft, marginTop); auto clickPoint = event->pos() - QPoint(marginLeft, marginTop);
auto samples = t->size(); auto trace = selectedMarker->getTrace();
if(!samples) { selectedMarker->setPosition(nearestTracePoint(trace, clickPoint));
return;
}
double closestDistance = numeric_limits<double>::max();
unsigned int closestIndex = 0;
for(unsigned int i=0;i<samples;i++) {
auto data = t->sample(i);
auto plotPoint = dataToPixel(data);
if (plotPoint.isNull()) {
// destination point outside of currently displayed range
continue;
}
auto diff = plotPoint - clickPoint;
unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y();
if(distance < closestDistance) {
closestDistance = distance;
closestIndex = i;
}
}
selectedMarker->setFrequency(t->sample(closestIndex).frequency);
} }
} }

View File

@ -40,7 +40,8 @@ protected:
bool markedForDeletion; bool markedForDeletion;
static std::set<TracePlot*> plots; static std::set<TracePlot*> plots;
virtual QPoint dataToPixel(Trace::Data d) = 0; virtual QPoint markerToPixel(TraceMarker *m) = 0;
virtual double nearestTracePoint(Trace *t, QPoint pixel) = 0;
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;

View File

@ -46,6 +46,39 @@ QPoint TraceSmithChart::dataToPixel(Trace::Data d)
return transform.map(QPoint(d.S.real() * smithCoordMax, -d.S.imag() * smithCoordMax)); return transform.map(QPoint(d.S.real() * smithCoordMax, -d.S.imag() * smithCoordMax));
} }
QPoint TraceSmithChart::markerToPixel(TraceMarker *m)
{
QPoint ret = QPoint();
if(!m->isTimeDomain()) {
if(m->getPosition() >= sweep_fmin && m->getPosition() <= sweep_fmax) {
auto d = m->getData();
ret = transform.map(QPoint(d.real() * smithCoordMax, -d.imag() * smithCoordMax));
}
}
return ret;
}
double TraceSmithChart::nearestTracePoint(Trace *t, QPoint pixel)
{
double closestDistance = numeric_limits<double>::max();
unsigned int closestIndex = 0;
for(unsigned int i=0;i<t->size();i++) {
auto data = t->sample(i);
auto plotPoint = dataToPixel(data);
if (plotPoint.isNull()) {
// destination point outside of currently displayed range
continue;
}
auto diff = plotPoint - pixel;
unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y();
if(distance < closestDistance) {
closestDistance = distance;
closestIndex = i;
}
}
return t->sample(closestIndex).frequency;
}
void TraceSmithChart::draw(QPainter &p) { void TraceSmithChart::draw(QPainter &p) {
auto pref = Preferences::getInstance(); auto pref = Preferences::getInstance();
@ -120,7 +153,10 @@ void TraceSmithChart::draw(QPainter &p) {
// only draw markers if the trace has at least one point // only draw markers if the trace has at least one point
auto markers = t.first->getMarkers(); auto markers = t.first->getMarkers();
for(auto m : markers) { for(auto m : markers) {
if (limitToSpan && (m->getFrequency() < sweep_fmin || m->getFrequency() > sweep_fmax)) { if (m->isTimeDomain()) {
continue;
}
if (limitToSpan && (m->getPosition() < sweep_fmin || m->getPosition() > sweep_fmax)) {
continue; continue;
} }
auto coords = m->getData(); auto coords = m->getData();

View File

@ -18,7 +18,9 @@ protected:
static constexpr double screenUsage = 0.9; static constexpr double screenUsage = 0.9;
static constexpr double smithCoordMax = 4096; static constexpr double smithCoordMax = 4096;
QPoint dataToPixel(Trace::Data d) override; QPoint dataToPixel(Trace::Data d);
QPoint markerToPixel(TraceMarker *m) override;
double nearestTracePoint(Trace *t, QPoint pixel) override;
//void paintEvent(QPaintEvent *event) override; //void paintEvent(QPaintEvent *event) override;
virtual void updateContextMenu() override; virtual void updateContextMenu() override;

View File

@ -68,14 +68,17 @@ void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool
} }
} while(erased); } while(erased);
if(isTDRtype(YAxis[axis].type)) { auto oldType = YAxis[axis].type;
if(isTDRtype(YAxis[axis].type) && !isTDRtype(type)) {
// not TDR axis anymore
for(auto t : tracesAxis[axis]) { for(auto t : tracesAxis[axis]) {
t->removeTDRinterest(); t->removeTDRinterest();
} }
} }
YAxis[axis].type = type; YAxis[axis].type = type;
for(auto t : tracesAxis[axis]) { if(isTDRtype(type) && !isTDRtype(oldType)) {
if(isTDRtype(type)) { // now a TDR axis
for(auto t : tracesAxis[axis]) {
t->addTDRinterest(); t->addTDRinterest();
} }
} }
@ -256,7 +259,7 @@ void TraceXYPlot::draw(QPainter &p)
} }
for(auto t : XAxis.ticks) { for(auto t : XAxis.ticks) {
auto xCoord = Util::Scale<double>(t, XAxis.ticks.front(), XAxis.ticks.back(), plotAreaLeft, plotAreaLeft + plotAreaWidth); auto xCoord = Util::Scale<double>(t, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth);
auto tickValue = Unit::ToString(t, "", prefixes, significantDigits); auto tickValue = Unit::ToString(t, "", prefixes, significantDigits);
p.setPen(QPen(pref.General.graphColors.axis, 1)); p.setPen(QPen(pref.General.graphColors.axis, 1));
if(displayFullFreq) { if(displayFullFreq) {
@ -335,13 +338,6 @@ void TraceXYPlot::draw(QPainter &p)
} }
} }
auto toPlotCoordinates = [=](const QPointF& point, const class YAxis& ax) -> QPoint {
QPoint p;
p.setX(Util::Scale<double>(point.x(), XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth));
p.setY(Util::Scale<double>(point.y(), ax.rangeMin, ax.rangeMax, plotAreaBottom, 0));
return p;
};
// plot traces // plot traces
p.setClipRect(QRect(plotRect.x()+1, plotRect.y()+1, plotRect.width()-2, plotRect.height()-2)); p.setClipRect(QRect(plotRect.x()+1, plotRect.y()+1, plotRect.width()-2, plotRect.height()-2));
for(auto t : tracesAxis[i]) { for(auto t : tracesAxis[i]) {
@ -366,8 +362,8 @@ void TraceXYPlot::draw(QPainter &p)
} }
// scale to plot coordinates // scale to plot coordinates
auto p1 = toPlotCoordinates(last, YAxis[i]); auto p1 = plotValueToPixel(last, i);
auto p2 = toPlotCoordinates(now, YAxis[i]); auto p2 = plotValueToPixel(now, i);
if(!plotRect.contains(p1) && !plotRect.contains(p2)) { if(!plotRect.contains(p1) && !plotRect.contains(p2)) {
// completely out of frame // completely out of frame
continue; continue;
@ -379,11 +375,25 @@ void TraceXYPlot::draw(QPainter &p)
// only draw markers on primary YAxis and if the trace has at least one point // only draw markers on primary YAxis and if the trace has at least one point
auto markers = t->getMarkers(); auto markers = t->getMarkers();
for(auto m : markers) { for(auto m : markers) {
if ((m->getFrequency() < XAxis.rangeMin || m->getFrequency() > XAxis.rangeMax)) { if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) {
// wrong domain, skip this marker
continue; continue;
} }
QPointF markerPoint = QPointF(m->getFrequency(), transformY(m->getData(), YAxis[i].type)); double xPosition;
auto point = toPlotCoordinates(markerPoint, YAxis[i]); if(m->isTimeDomain()) {
if(XAxis.type == XAxisType::Distance) {
xPosition = m->getTimeData().distance;
} else {
xPosition = m->getTimeData().time;
}
} else {
xPosition = m->getPosition();
}
if (xPosition < XAxis.rangeMin || xPosition > XAxis.rangeMax) {
continue;
}
QPointF markerPoint = QPointF(xPosition, transformY(m->getData(), YAxis[i].type));
auto point = plotValueToPixel(markerPoint, i);
if(!plotRect.contains(point)) { if(!plotRect.contains(point)) {
// out of screen // out of screen
continue; continue;
@ -412,7 +422,7 @@ void TraceXYPlot::draw(QPainter &p)
auto text = "Drop here to add\n" + dropTrace->name() + "\nto XY-plot"; auto text = "Drop here to add\n" + dropTrace->name() + "\nto XY-plot";
p.drawText(plotRect, Qt::AlignCenter, text); p.drawText(plotRect, Qt::AlignCenter, text);
} else { } else {
// both axis enabled show regions // both axis enabled, show regions
auto leftRect = plotRect; auto leftRect = plotRect;
leftRect.setWidth(plotRect.width() * 0.3); leftRect.setWidth(plotRect.width() * 0.3);
auto centerRect = plotRect; auto centerRect = plotRect;
@ -621,6 +631,14 @@ double TraceXYPlot::transformY(std::complex<double> data, TraceXYPlot::YAxisType
return (1+abs(data)) / (1-abs(data)); return (1+abs(data)) / (1-abs(data));
} }
break; break;
case YAxisType::Step:
return data.real();
case YAxisType::Impulse:
return data.imag();
case YAxisType::Impedance:
if(abs(data.real()) < 1.0) {
return 50 * (1+data.real()) / (1-data.real());
}
default: default:
break; break;
} }
@ -656,10 +674,7 @@ QPointF TraceXYPlot::transformY(Trace *t, unsigned int sample, TraceXYPlot::YAxi
} }
break; break;
case YAxisType::Impedance: { case YAxisType::Impedance: {
auto step = t->getTDR()[sample].stepResponse; ret.setY(t->getTDR()[sample].impedance);
if(abs(step) < 1.0) {
ret.setY(50 * (1+step) / (1-step));
}
if(XAxis.type == XAxisType::Distance) { if(XAxis.type == XAxisType::Distance) {
ret.setX(t->getTDR()[sample].distance); ret.setX(t->getTDR()[sample].distance);
} else { } else {
@ -696,6 +711,61 @@ QPoint TraceXYPlot::dataToPixel(Trace::Data d)
return p; return p;
} }
QPoint TraceXYPlot::plotValueToPixel(QPointF plotValue, int Yaxis)
{
QPoint p;
p.setX(Util::Scale<double>(plotValue.x(), XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth));
p.setY(Util::Scale<double>(plotValue.y(), YAxis[Yaxis].rangeMin, YAxis[Yaxis].rangeMax, plotAreaBottom, 0));
return p;
}
QPoint TraceXYPlot::markerToPixel(TraceMarker *m)
{
QPoint ret = QPoint();
if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) {
// invalid domain
return ret;
}
QPointF plotPoint;
plotPoint.setY(transformY(m->getData(), YAxis[0].type));
if(m->isTimeDomain()) {
auto timedata = m->getTimeData();
if(XAxis.type == XAxisType::Distance) {
plotPoint.setX(timedata.distance);
} else {
plotPoint.setX(timedata.time);
}
} else {
plotPoint.setX(m->getPosition());
}
return plotValueToPixel(plotPoint, 0);
}
double TraceXYPlot::nearestTracePoint(Trace *t, QPoint pixel)
{
if(!tracesAxis[0].count(t)) {
// trace not enabled
return 0;
}
double closestDistance = numeric_limits<double>::max();
double closestXpos = 0;
auto samples = numTraceSamples(t);
for(unsigned int i=0;i<samples;i++) {
auto point = transformY(t, i, YAxis[0].type);
if(isnan(point.x()) || isnan(point.y())) {
continue;
}
auto plotPoint = plotValueToPixel(point, 0);
auto diff = plotPoint - pixel;
unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y();
if(distance < closestDistance) {
closestDistance = distance;
closestXpos = point.x();
}
}
return closestXpos;
}
void TraceXYPlot::traceDropped(Trace *t, QPoint position) void TraceXYPlot::traceDropped(Trace *t, QPoint position)
{ {
if(YAxis[0].type == YAxisType::Disabled && YAxis[1].type == YAxisType::Disabled) { if(YAxis[0].type == YAxisType::Disabled && YAxis[1].type == YAxisType::Disabled) {

View File

@ -61,7 +61,10 @@ private:
double transformY(std::complex<double> data, YAxisType type); double transformY(std::complex<double> data, YAxisType type);
QPointF transformY(Trace *t, unsigned int sample, YAxisType type); QPointF transformY(Trace *t, unsigned int sample, YAxisType type);
unsigned int numTraceSamples(Trace *t); unsigned int numTraceSamples(Trace *t);
QPoint dataToPixel(Trace::Data d) override; QPoint dataToPixel(Trace::Data d);
QPoint plotValueToPixel(QPointF plotValue, int Yaxis);
QPoint markerToPixel(TraceMarker *m) override;
double nearestTracePoint(Trace *t, QPoint pixel) override;
void traceDropped(Trace *t, QPoint position) override; void traceDropped(Trace *t, QPoint position) override;
std::set<Trace*> tracesAxis[2]; std::set<Trace*> tracesAxis[2];