time domain markers
This commit is contained in:
parent
c22d576984
commit
93bf7255c3
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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];
|
||||||
|
Loading…
Reference in New Issue
Block a user