diff --git a/Software/PC_Application/Application.pro b/Software/PC_Application/Application.pro index ebc52e2..d80f365 100644 --- a/Software/PC_Application/Application.pro +++ b/Software/PC_Application/Application.pro @@ -258,6 +258,7 @@ FORMS += \ VNA/Deembedding/deembeddingdialog.ui \ VNA/Deembedding/form.ui \ VNA/Deembedding/matchingnetworkdialog.ui \ + VNA/Deembedding/measurementdialog.ui \ VNA/Deembedding/portextensioneditdialog.ui \ VNA/Deembedding/twothrudialog.ui \ main.ui \ diff --git a/Software/PC_Application/VNA/Deembedding/deembedding.cpp b/Software/PC_Application/VNA/Deembedding/deembedding.cpp index 10593f3..5d9c6f0 100644 --- a/Software/PC_Application/VNA/Deembedding/deembedding.cpp +++ b/Software/PC_Application/VNA/Deembedding/deembedding.cpp @@ -1,6 +1,7 @@ #include "deembedding.h" #include "deembeddingdialog.h" #include +#include "ui_measurementdialog.h" using namespace std; @@ -10,9 +11,259 @@ void Deembedding::configure() d->show(); } +void Deembedding::measurementCompleted() +{ + // pass on the measurement result to the option that triggered the measurement + if (measuringOption) { + measuringOption->measurementCompleted(measurements); + measuringOption = nullptr; + } + + delete measurementDialog; + measurementDialog = nullptr; + measurementUI = nullptr; +} + +void Deembedding::setInitialTraceSelections() +{ + // all checkboxes initially set to none + measurementUI->cS11->blockSignals(true); + measurementUI->cS12->blockSignals(true); + measurementUI->cS21->blockSignals(true); + measurementUI->cS22->blockSignals(true); + measurementUI->cS11->clear(); + measurementUI->cS12->clear(); + measurementUI->cS21->clear(); + measurementUI->cS22->clear(); + measurementUI->cS11->addItem("None"); + measurementUI->cS12->addItem("None"); + measurementUI->cS21->addItem("None"); + measurementUI->cS22->addItem("None"); + // add applicable traces + for(auto t : tm.getTraces()) { + if(t->isReflection()) { + measurementUI->cS11->addItem(t->name(), QVariant::fromValue(t)); + measurementUI->cS22->addItem(t->name(), QVariant::fromValue(t)); + } else { + measurementUI->cS12->addItem(t->name(), QVariant::fromValue(t)); + measurementUI->cS21->addItem(t->name(), QVariant::fromValue(t)); + } + } + measurementUI->cS11->blockSignals(false); + measurementUI->cS12->blockSignals(false); + measurementUI->cS21->blockSignals(false); + measurementUI->cS22->blockSignals(false); +} + +void Deembedding::traceSelectionChanged(QComboBox *w) +{ + vector cbs; + if (measurementUI->cS11->isVisible()) { + cbs.push_back(measurementUI->cS11); + } + if (measurementUI->cS12->isVisible()) { + cbs.push_back(measurementUI->cS12); + } + if (measurementUI->cS21->isVisible()) { + cbs.push_back(measurementUI->cS21); + } + if (measurementUI->cS22->isVisible()) { + cbs.push_back(measurementUI->cS22); + } + + // update available traces in combo boxes + if(w->currentIndex() != 0 && points == 0) { + // the first trace has been selected, extract frequency info + Trace *t = qvariant_cast(w->itemData(w->currentIndex())); + points = t->size(); + if(points > 0) { + minFreq = t->minX(); + maxFreq = t->maxX(); + } + // remove all trace options with incompatible frequencies + for(auto c : cbs) { + for(int i=1;icount();i++) { + Trace *t = qvariant_cast(c->itemData(i)); + if(t->size() != points || (points > 0 && (t->minX() != minFreq || t->maxX() != maxFreq))) { + // this trace is not available anymore + c->removeItem(i); + // decrement to check the next index in the next loop iteration + i--; + } + } + } + } else if(w->currentIndex() == 0 && points > 0) { + measurementUI->buttonBox->setEnabled(false); + // Check if all trace selections are set for none + for(auto c : cbs) { + if(c->currentIndex() != 0) { + // some trace is still selected, abort + return; + } + } + // all traces set for none + points = 0; + minFreq = 0; + maxFreq = 0; + setInitialTraceSelections(); + } + bool allSelectionsValid = true; + for(auto c : cbs) { + if (c->currentIndex() == 0) { + allSelectionsValid = false; + break; + } + } + if(allSelectionsValid) { + measurementUI->buttonBox->setEnabled(true); + } +} + +void Deembedding::startMeasurementDialog(bool S11, bool S12, bool S21, bool S22) +{ + measurements.clear(); + + points = 0; + minFreq = 0.0; + maxFreq = 0.0; + + measurementDialog = new QDialog; + auto ui = new Ui_DeembeddingMeasurementDialog; + measurementUI = ui; + ui->setupUi(measurementDialog); + // disable not needed GUI elements + if(!S11) { + ui->lS11->setVisible(false); + ui->cS11->setVisible(false); + } + if(!S12) { + ui->lS12->setVisible(false); + ui->cS12->setVisible(false); + } + if(!S21) { + ui->lS21->setVisible(false); + ui->cS21->setVisible(false); + } + if(!S22) { + ui->lS22->setVisible(false); + ui->cS22->setVisible(false); + } + + connect(ui->bMeasure, &QPushButton::clicked, [=](){ + ui->bMeasure->setEnabled(false); + ui->cS11->setEnabled(false); + ui->cS12->setEnabled(false); + ui->cS21->setEnabled(false); + ui->cS22->setEnabled(false); + ui->buttonBox->setEnabled(false); + measuring = true; + }); + + connect(ui->cS11, qOverload(&QComboBox::currentIndexChanged), [=](int){ + traceSelectionChanged(ui->cS11); + }); + connect(ui->cS12, qOverload(&QComboBox::currentIndexChanged), [=](int){ + traceSelectionChanged(ui->cS12); + }); + connect(ui->cS21, qOverload(&QComboBox::currentIndexChanged), [=](int){ + traceSelectionChanged(ui->cS21); + }); + connect(ui->cS22, qOverload(&QComboBox::currentIndexChanged), [=](int){ + traceSelectionChanged(ui->cS22); + }); + connect(ui->buttonBox, &QDialogButtonBox::accepted, [=](){ + // create datapoints from individual traces + measurements.clear(); + Trace *S11 = nullptr, *S12 = nullptr, *S21 = nullptr, *S22 = nullptr; + if (ui->cS11->currentIndex() != 0) { + S11 = qvariant_cast(ui->cS11->itemData(ui->cS11->currentIndex())); + } + if (ui->cS12->currentIndex() != 0) { + S12 = qvariant_cast(ui->cS12->itemData(ui->cS12->currentIndex())); + } + if (ui->cS21->currentIndex() != 0) { + S21 = qvariant_cast(ui->cS21->itemData(ui->cS21->currentIndex())); + } + if (ui->cS22->currentIndex() != 0) { + S22 = qvariant_cast(ui->cS22->itemData(ui->cS22->currentIndex())); + } + for(unsigned int i=0;isample(i); + p.imag_S11 = sample.y.imag(); + p.real_S11 = sample.y.real(); + p.frequency = sample.x; + } + if(S12) { + auto sample = S12->sample(i); + p.imag_S12 = sample.y.imag(); + p.real_S12 = sample.y.real(); + p.frequency = sample.x; + } + if(S21) { + auto sample = S21->sample(i); + p.imag_S21 = sample.y.imag(); + p.real_S21 = sample.y.real(); + p.frequency = sample.x; + } + if(S22) { + auto sample = S22->sample(i); + p.imag_S22 = sample.y.imag(); + p.real_S22 = sample.y.real(); + p.frequency = sample.x; + } + measurements.push_back(p); + } + measurementCompleted(); + }); + + setInitialTraceSelections(); + + measurementDialog->show(); +} + +Deembedding::Deembedding(TraceModel &tm) + : tm(tm), + measuring(false), + points(0) +{ + +} + void Deembedding::Deembed(Protocol::Datapoint &d) { + // figure out the point in one sweep based on the incomig pointNums + static unsigned lastPointNum; + if (d.pointNum == 0) { + sweepPoints = lastPointNum; + } else if(d.pointNum > sweepPoints) { + sweepPoints = d.pointNum; + } + lastPointNum = d.pointNum; + for(auto it = options.begin();it != options.end();it++) { + if (measuring && measuringOption == *it) { + // this option needs a measurement + if (d.pointNum == 0) { + if(measurements.size() == 0) { + // this is the first point of the measurement + measurements.push_back(d); + } else { + // this is the first point of the next sweep, measurement complete + measuring = false; + measurementCompleted(); + } + } else if(measurements.size() > 0) { + // in the middle of the measurement, add point + measurements.push_back(d); + } + + if(measurementUI) { + measurementUI->progress->setValue(100 * measurements.size() / sweepPoints); + } + } (*it)->transformDatapoint(d); } } @@ -35,6 +286,10 @@ void Deembedding::addOption(DeembeddingOption *option) options.erase(pos); } }); + connect(option, &DeembeddingOption::triggerMeasurement, [=](bool S11, bool S12, bool S21, bool S22) { + measuringOption = option; + startMeasurementDialog(S11, S12, S21, S22); + }); } void Deembedding::swapOptions(unsigned int index) diff --git a/Software/PC_Application/VNA/Deembedding/deembedding.h b/Software/PC_Application/VNA/Deembedding/deembedding.h index ab3c417..88cbced 100644 --- a/Software/PC_Application/VNA/Deembedding/deembedding.h +++ b/Software/PC_Application/VNA/Deembedding/deembedding.h @@ -5,12 +5,17 @@ #include #include #include "savable.h" +#include "Traces/tracemodel.h" +#include +#include + +class Ui_DeembeddingMeasurementDialog; class Deembedding : public QObject, public Savable { Q_OBJECT public: - Deembedding(){}; + Deembedding(TraceModel &tm); ~Deembedding(){}; void Deembed(Protocol::Datapoint &d); @@ -23,9 +28,27 @@ public: void fromJSON(nlohmann::json j) override; public slots: void configure(); - +signals: + void triggerMeasurement(bool S11 = true, bool S12 = true, bool S21 = true, bool S22 = true); private: + void measurementCompleted(); + void setInitialTraceSelections(); + void traceSelectionChanged(QComboBox *w); + void startMeasurementDialog(bool S11, bool S12, bool S21, bool S22); std::vector options; + DeembeddingOption *measuringOption; + TraceModel &tm; + + bool measuring; + std::vector measurements; + QDialog *measurementDialog; + Ui_DeembeddingMeasurementDialog *measurementUI; + // parameters of the selected traces for the measurement + double minFreq, maxFreq; + unsigned long points; + + unsigned long sweepPoints; + }; #endif // DEEMBEDDING_H diff --git a/Software/PC_Application/VNA/Deembedding/deembeddingdialog.ui b/Software/PC_Application/VNA/Deembedding/deembeddingdialog.ui index 1d6374e..1cbf9d2 100644 --- a/Software/PC_Application/VNA/Deembedding/deembeddingdialog.ui +++ b/Software/PC_Application/VNA/Deembedding/deembeddingdialog.ui @@ -13,6 +13,9 @@ De-embedding + + true + diff --git a/Software/PC_Application/VNA/Deembedding/deembeddingoption.h b/Software/PC_Application/VNA/Deembedding/deembeddingoption.h index 7016370..337884a 100644 --- a/Software/PC_Application/VNA/Deembedding/deembeddingoption.h +++ b/Software/PC_Application/VNA/Deembedding/deembeddingoption.h @@ -23,9 +23,14 @@ public: virtual void transformDatapoint(Protocol::Datapoint &p) = 0; virtual void edit(){}; virtual Type getType() = 0; + +public slots: + virtual void measurementCompleted(std::vector m){Q_UNUSED(m)}; signals: // Deembedding option may selfdestruct if not applicable with current settings. It should emit this signal before deleting itself void deleted(DeembeddingOption *option); + + void triggerMeasurement(bool S11 = true, bool S12 = true, bool S21 = true, bool S22 = true); }; #endif // DEEMBEDDING_H diff --git a/Software/PC_Application/VNA/Deembedding/matchingnetwork.cpp b/Software/PC_Application/VNA/Deembedding/matchingnetwork.cpp index ef332c6..f5b3bfa 100644 --- a/Software/PC_Application/VNA/Deembedding/matchingnetwork.cpp +++ b/Software/PC_Application/VNA/Deembedding/matchingnetwork.cpp @@ -67,6 +67,7 @@ void MatchingNetwork::edit() auto dialog = new QDialog(); auto ui = new Ui::MatchingNetworkDialog(); ui->setupUi(dialog); + dialog->setModal(true); graph = new QWidget(); ui->scrollArea->setWidget(graph); diff --git a/Software/PC_Application/VNA/Deembedding/measurementdialog.ui b/Software/PC_Application/VNA/Deembedding/measurementdialog.ui new file mode 100644 index 0000000..5bf9e39 --- /dev/null +++ b/Software/PC_Application/VNA/Deembedding/measurementdialog.ui @@ -0,0 +1,144 @@ + + + DeembeddingMeasurementDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 582 + 228 + + + + De-embedding Measurement + + + true + + + + + + Either take a new measurement with the currently active frequency/span settings... + + + + + + + + + 0 + + + + + + + Measure + + + + + + + + + Qt::Horizontal + + + + + + + ... or select already existing traces for the measurement: + + + + + + + + + + + S11: + + + + + + + + + + S12: + + + + + + + + + + S21: + + + + + + + + + + S22: + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + Qt::Vertical + + + QDialogButtonBox::Ok + + + + + + + + + + + + diff --git a/Software/PC_Application/VNA/Deembedding/portextension.cpp b/Software/PC_Application/VNA/Deembedding/portextension.cpp index b4daf1b..666fe8f 100644 --- a/Software/PC_Application/VNA/Deembedding/portextension.cpp +++ b/Software/PC_Application/VNA/Deembedding/portextension.cpp @@ -22,111 +22,11 @@ PortExtension::PortExtension() port2.delay = 0; port2.velocityFactor = 0.66; - measuring = false; kit = nullptr; } void PortExtension::transformDatapoint(Protocol::Datapoint &d) { - if(measuring) { - if(measurements.size() > 0) { - if(d.pointNum == 0) { - // sweep complete, evaluate measurement - - double last_phase = 0.0; - double phasediff_sum = 0.0; - vector att_x, att_y; - double avg_x = 0.0, avg_y = 0.0; - for(auto m : measurements) { - // grab correct measurement - complex reflection; - if(isPort1) { - reflection = complex(m.real_S11, m.imag_S11); - } else { - reflection = complex(m.real_S22, m.imag_S22); - } - // remove calkit if specified - if(!isIdeal) { - complex calStandard; - auto standards = kit->toSOLT(m.frequency); - if(isOpen) { - calStandard = standards.Open; - } else { - calStandard = standards.Short; - } - // remove effect of calibration standard - reflection /= calStandard; - } - // sum phase differences to previous point - auto phase = arg(reflection); - if(m.pointNum == 0) { - last_phase = phase; - } else { - auto phasediff = phase - last_phase; - last_phase = phase; - if(phasediff > M_PI) { - phasediff -= 2 * M_PI; - } else if(phasediff <= -M_PI) { - phasediff += 2 * M_PI; - } - phasediff_sum += phasediff; - qDebug() << phasediff; - } - - double x = sqrt(m.frequency / measurements.back().frequency); - double y = 20*log10(abs(reflection)); - att_x.push_back(x); - att_y.push_back(y); - avg_x += x; - avg_y += y; - } - auto phase = phasediff_sum / (measurements.size() - 1); - auto freq_diff = measurements[1].frequency - measurements[0].frequency; - auto delay = -phase / (2 * M_PI * freq_diff); - // measured delay is two-way but port extension expects one-way - delay /= 2; - - // calculate linear regression with transformed square root model - avg_x /= measurements.size(); - avg_y /= measurements.size(); - double sum_top = 0.0; - double sum_bottom = 0.0; - for(unsigned int i=0;iP1Time->setValue(delay); - ui->P1DCloss->setValue(DCloss); - ui->P1Loss->setValue(loss); - ui->P1Frequency->setValue(freq); - } else { - ui->P2Time->setValue(delay); - ui->P2DCloss->setValue(DCloss); - ui->P2Loss->setValue(loss); - ui->P2Frequency->setValue(freq); - } - - if(msgBox) { - msgBox->close(); - msgBox = nullptr; - } - measurements.clear(); - } else { - measurements.push_back(d); - } - } else if(d.pointNum == 0) { - // first point of sweep, start measurement - measurements.push_back(d); - } - } - if(port1.enabled || port2.enabled) { // Convert measurements to complex variables auto S11 = complex(d.real_S11, d.imag_S11); @@ -299,16 +199,94 @@ void PortExtension::edit() dialog->show(); } +void PortExtension::measurementCompleted(std::vector m) +{ + if(m.size() > 0) { + double last_phase = 0.0; + double phasediff_sum = 0.0; + vector att_x, att_y; + double avg_x = 0.0, avg_y = 0.0; + for(auto p : m) { + // grab correct measurement + complex reflection; + if(isPort1) { + reflection = complex(p.real_S11, p.imag_S11); + } else { + reflection = complex(p.real_S22, p.imag_S22); + } + // remove calkit if specified + if(!isIdeal) { + complex calStandard; + auto standards = kit->toSOLT(p.frequency); + if(isOpen) { + calStandard = standards.Open; + } else { + calStandard = standards.Short; + } + // remove effect of calibration standard + reflection /= calStandard; + } + // sum phase differences to previous point + auto phase = arg(reflection); + if(p.pointNum == 0) { + last_phase = phase; + } else { + auto phasediff = phase - last_phase; + last_phase = phase; + if(phasediff > M_PI) { + phasediff -= 2 * M_PI; + } else if(phasediff <= -M_PI) { + phasediff += 2 * M_PI; + } + phasediff_sum += phasediff; + qDebug() << phasediff; + } + + double x = sqrt(p.frequency / m.back().frequency); + double y = 20*log10(abs(reflection)); + att_x.push_back(x); + att_y.push_back(y); + avg_x += x; + avg_y += y; + } + auto phase = phasediff_sum / (m.size() - 1); + auto freq_diff = m[1].frequency - m[0].frequency; + auto delay = -phase / (2 * M_PI * freq_diff); + // measured delay is two-way but port extension expects one-way + delay /= 2; + + // calculate linear regression with transformed square root model + avg_x /= m.size(); + avg_y /= m.size(); + double sum_top = 0.0; + double sum_bottom = 0.0; + for(unsigned int i=0;iP1Time->setValue(delay); + ui->P1DCloss->setValue(DCloss); + ui->P1Loss->setValue(loss); + ui->P1Frequency->setValue(freq); + } else { + ui->P2Time->setValue(delay); + ui->P2DCloss->setValue(DCloss); + ui->P2Loss->setValue(loss); + ui->P2Frequency->setValue(freq); + } + } +} + void PortExtension::startMeasurement() { - measurements.clear(); - msgBox = new QMessageBox(QMessageBox::Information, "Auto port extension", "Taking measurement...", QMessageBox::Cancel); - connect(msgBox, &QMessageBox::rejected, [=]() { - measuring = false; - measurements.clear(); - }); - msgBox->show(); - measuring = true; + emit triggerMeasurement(isPort1, false, false, !isPort1); } void PortExtension::setCalkit(Calkit *kit) diff --git a/Software/PC_Application/VNA/Deembedding/portextension.h b/Software/PC_Application/VNA/Deembedding/portextension.h index a474064..5c7348c 100644 --- a/Software/PC_Application/VNA/Deembedding/portextension.h +++ b/Software/PC_Application/VNA/Deembedding/portextension.h @@ -24,6 +24,7 @@ public: void fromJSON(nlohmann::json j) override; public slots: void edit() override; + void measurementCompleted(std::vector m) override; private: void startMeasurement(); @@ -40,11 +41,11 @@ private: // status variables for automatic measurements Calkit *kit; - bool measuring; +// bool measuring; bool isPort1; bool isOpen; bool isIdeal; - std::vector measurements; +// std::vector measurements; QMessageBox *msgBox; Ui::PortExtensionEditDialog *ui; }; diff --git a/Software/PC_Application/VNA/Deembedding/portextensioneditdialog.ui b/Software/PC_Application/VNA/Deembedding/portextensioneditdialog.ui index f35e06c..a245e38 100644 --- a/Software/PC_Application/VNA/Deembedding/portextensioneditdialog.ui +++ b/Software/PC_Application/VNA/Deembedding/portextensioneditdialog.ui @@ -2,12 +2,15 @@ PortExtensionEditDialog + + Qt::ApplicationModal + 0 0 318 - 476 + 505 diff --git a/Software/PC_Application/VNA/Deembedding/twothru.cpp b/Software/PC_Application/VNA/Deembedding/twothru.cpp index 2dce573..08143b2 100644 --- a/Software/PC_Application/VNA/Deembedding/twothru.cpp +++ b/Software/PC_Application/VNA/Deembedding/twothru.cpp @@ -2,227 +2,56 @@ #include "CustomWidgets/informationbox.h" #include "ui_twothrudialog.h" #include "Traces/fftcomplex.h" +#include +#include "unit.h" using namespace std; TwoThru::TwoThru() { - measuring = false; + Z0 = 50.0; } void TwoThru::transformDatapoint(Protocol::Datapoint &p) { - auto S11 = complex(p.real_S11, p.imag_S11); - auto S12 = complex(p.real_S12, p.imag_S12); - auto S21 = complex(p.real_S21, p.imag_S21); - auto S22 = complex(p.real_S22, p.imag_S22); - Sparam S(S11, S12, S21, S22); - Tparam meas(S); - if(measuring) { - if(measurements.size() > 0 && p.pointNum == 0) { - // complete sweep measured, exit measurement mode - measuring = false; - // calculate error boxes, see https://www.freelists.org/post/si-list/IEEE-P370-Opensource-Deembedding-MATLAB-functions - // create vectors of S parameters - vector> S11, S12, S21, S22; - vector f; - for(auto m : measurements) { - if(m.frequency == 0) { - // ignore possible DC point - continue; - } - S11.push_back(complex(m.real_S11, m.imag_S11)); - S12.push_back(complex(m.real_S12, m.imag_S12)); - S21.push_back(complex(m.real_S21, m.imag_S21)); - S22.push_back(complex(m.real_S22, m.imag_S22)); - f.push_back(m.frequency); - } - auto n = f.size(); - - auto makeSymmetric = [](const vector> &in) -> vector> { - auto abs_DC = 2.0 * abs(in[0]) - abs(in[1]); - auto phase_DC = 2.0 * arg(in[0]) - arg(in[1]); - auto DC = polar(abs_DC, phase_DC); - vector> ret; - ret.push_back(DC); - // add non-symmetric part - ret.insert(ret.end(), in.begin(), in.end()); - // add flipped complex conjugate values - for(auto it = in.rbegin(); it != in.rend(); it++) { - ret.push_back(conj(*it)); - } - return ret; - }; - - auto makeRealAndScale = [](vector> &in) { - for(unsigned int i=0;i data_side1, data_side2; - - { - auto p112x = makeSymmetric(S11); - auto p212x = makeSymmetric(S21); - - // transform into time domain and calculate step responses - auto t112x = p112x; - Fft::transform(t112x, true); - makeRealAndScale(t112x); - Fft::shift(t112x, false); - partial_sum(t112x.begin(), t112x.end(), t112x.begin()); - auto t212x = p212x; - Fft::transform(t212x, true); - makeRealAndScale(t212x); - Fft::shift(t212x, false); - partial_sum(t212x.begin(), t212x.end(), t212x.begin()); - - // find the midpoint of the trace - double threshold = 0.5*real(t212x.back()); - auto mid = lower_bound(t212x.begin(), t212x.end(), threshold, [](complex p, double c) -> bool { - return real(p) < c; - }) - t212x.begin(); - - // mask step response - vector> t111xStep(2*n + 1, 0.0); - copy(t112x.begin() + n, t112x.begin() + mid, t111xStep.begin() + n); - Fft::shift(t111xStep, true); - // create impulse response from masked step response - adjacent_difference(t111xStep.begin(), t111xStep.end(), t111xStep.begin()); - Fft::transform(t111xStep, false); - auto &p111x = t111xStep; - - // calculate p221x and p211x - vector> p221x; - vector> p211x; - double k = 1.0; - complex test, last_test; - for(unsigned int i=0;i 0) { - if(arg(test) - arg(last_test) > 0) { - k = -k; - } - } - last_test = test; - p211x.push_back(k*test); - } - - // create S parameter errorbox - for(unsigned int i=1;i<=n;i++) { - data_side1.push_back(Sparam(p111x[i], p211x[i], p211x[i], p221x[i])); - } - } - - // same thing for error box 2. Variable names get a bit confusing because they are viewed from port 2 (S22 is now called p112x, ...). - // All variable names follow https://gitlab.com/IEEE-SA/ElecChar/P370/-/blob/master/TG1/IEEEP3702xThru_Octave.m - { - auto p112x = makeSymmetric(S22); - auto p212x = makeSymmetric(S12); - - // transform into time domain and calculate step responses - auto t112x = p112x; - Fft::transform(t112x, true); - makeRealAndScale(t112x); - Fft::shift(t112x, false); - partial_sum(t112x.begin(), t112x.end(), t112x.begin()); - auto t212x = p212x; - Fft::transform(t212x, true); - makeRealAndScale(t212x); - Fft::shift(t212x, false); - partial_sum(t212x.begin(), t212x.end(), t212x.begin()); - - // find the midpoint of the trace - double threshold = 0.5*real(t212x.back()); - auto mid = lower_bound(t212x.begin(), t212x.end(), threshold, [](complex p, double c) -> bool { - return real(p) < c; - }) - t212x.begin(); - - // mask step response - vector> t111xStep(2*n + 1, 0.0); - copy(t112x.begin() + n, t112x.begin() + mid, t111xStep.begin() + n); - Fft::shift(t111xStep, true); - // create impulse response from masked step response - adjacent_difference(t111xStep.begin(), t111xStep.end(), t111xStep.begin()); - Fft::transform(t111xStep, false); - auto &p111x = t111xStep; - - // calculate p221x and p211x - vector> p221x; - vector> p211x; - double k = 1.0; - complex test, last_test; - for(unsigned int i=0;i 0) { - if(arg(test) - arg(last_test) > 0) { - k = -k; - } - } - last_test = test; - p211x.push_back(k*test); - } - - // create S parameter errorbox - for(unsigned int i=1;i<=n;i++) { - data_side2.push_back(Sparam(data_side1[i-1].m22, p211x[i], p211x[i], p111x[i])); - data_side1[i-1].m22 = p221x[i]; - } - } - - // got the error boxes, convert to T parameters and invert - for(unsigned int i=0;iaccept(); - msgBox = nullptr; - } - updateLabel(); - } else if(measurements.size() > 0 || p.pointNum == 0) { - measurements.push_back(p); - } - } - // correct measurement if(points.size() > 0) { - if(p.frequency != 0 && (p.frequency < points.front().freq || p.frequency > points.back().freq)) { - // No exact match, measurement no longer valid - points.clear(); - InformationBox::ShowMessage("Warning", "2xThru measurement cleared because it no longer matches the selected span"); - return; - } - // find correct measurement point - auto point = lower_bound(points.begin(), points.end(), p.frequency, [](Point p, uint64_t freq) -> bool { - return p.freq < freq; - }); + auto S11 = complex(p.real_S11, p.imag_S11); + auto S12 = complex(p.real_S12, p.imag_S12); + auto S21 = complex(p.real_S21, p.imag_S21); + auto S22 = complex(p.real_S22, p.imag_S22); + Sparam S(S11, S12, S21, S22); + Tparam meas(S); + Tparam inv1, inv2; - if(point->freq == p.frequency) { - inv1 = point->inverseP1; - inv2 = point->inverseP2; + if(p.frequency < points.front().freq) { + inv1 = points.front().inverseP1; + inv2 = points.front().inverseP2; + } else if(p.frequency > points.back().freq) { + inv1 = points.back().inverseP1; + inv2 = points.back().inverseP2; } else { - // need to interpolate - auto high = point; - point--; - auto low = point; - double alpha = (p.frequency - low->freq) / (high->freq - low->freq); - inv1 = low->inverseP1 * (1 - alpha) + high->inverseP1 * alpha; - inv2 = low->inverseP2 * (1 - alpha) + high->inverseP2 * alpha; + // find correct measurement point + auto point = lower_bound(points.begin(), points.end(), p.frequency, [](Point p, uint64_t freq) -> bool { + return p.freq < freq; + }); + if(point->freq == p.frequency) { + inv1 = point->inverseP1; + inv2 = point->inverseP2; + } else { + // need to interpolate + auto high = point; + point--; + auto low = point; + double alpha = (p.frequency - low->freq) / (high->freq - low->freq); + inv1 = low->inverseP1 * (1 - alpha) + high->inverseP1 * alpha; + inv2 = low->inverseP2 * (1 - alpha) + high->inverseP2 * alpha; + } } // perform correction Tparam corrected = inv1*meas*inv2; // transform back into S parameters - Sparam S(corrected); + S = Sparam(corrected); p.real_S11 = real(S.m11); p.imag_S11 = imag(S.m11); p.real_S12 = real(S.m12); @@ -236,27 +65,58 @@ void TwoThru::transformDatapoint(Protocol::Datapoint &p) void TwoThru::startMeasurement() { - points.clear(); - measurements.clear(); - updateLabel(); - msgBox = new QMessageBox(QMessageBox::Information, "2xThru", "Taking measurement...", QMessageBox::Cancel); - connect(msgBox, &QMessageBox::rejected, [=]() { - measuring = false; - points.clear(); - measurements.clear(); - updateLabel(); - }); - msgBox->show(); - measuring = true; + emit triggerMeasurement(); } -void TwoThru::updateLabel() +void TwoThru::updateGUI() { - if(points.size() > 0) { - ui->lInfo->setText("Got "+QString::number(points.size())+" points"); + if(measurements2xthru.size() > 0) { + ui->l2xthru->setText(QString::number(measurements2xthru.size())+" points from " + +Unit::ToString(measurements2xthru.front().frequency, "Hz", " kMG", 4)+" to " + +Unit::ToString(measurements2xthru.back().frequency, "Hz", " kMG", 4)); } else { - ui->lInfo->setText("No measurement, not deembedding"); + ui->l2xthru->setText("Not available, not de-embedding"); } + + if(measurementsDUT.size() > 0) { + ui->lDUT->setText(QString::number(measurementsDUT.size())+" points from " + +Unit::ToString(measurementsDUT.front().frequency, "Hz", " kMG", 4)+" to " + +Unit::ToString(measurementsDUT.back().frequency, "Hz", " kMG", 4)); + } else { + ui->lDUT->setText("Not available, not de-embedding"); + } + + if(points.size() > 0) { + ui->lPoints->setText(QString::number(points.size())+" points from " + +Unit::ToString(points.front().freq, "Hz", " kMG", 4)+" to " + +Unit::ToString(points.back().freq, "Hz", " kMG", 4)); + } else { + ui->lPoints->setText("Not available, not de-embedding"); + } + + if (measurementsDUT.size() > 0 && measurements2xthru.size() > 0) { + // correction using both measurements is available + ui->Z0->setEnabled(true); + ui->bCalc->setEnabled(true); + } else if(measurements2xthru.size() > 0) { + // simpler correction using only 2xthru measurement available + ui->Z0->setEnabled(false); + ui->bCalc->setEnabled(true); + } else { + // no correction available + ui->Z0->setEnabled(false); + ui->bCalc->setEnabled(false); + } +} + +void TwoThru::measurementCompleted(std::vector m) +{ + if (measuring2xthru) { + measurements2xthru = m; + } else if(measuringDUT) { + measurementsDUT = m; + } + updateGUI(); } void TwoThru::edit() @@ -264,11 +124,48 @@ void TwoThru::edit() auto dialog = new QDialog(); ui = new Ui::TwoThruDialog(); ui->setupUi(dialog); + ui->Z0->setUnit("Ω"); + ui->Z0->setPrecision(4); + ui->Z0->setValue(Z0); - connect(ui->bMeasure, &QPushButton::clicked, this, &TwoThru::startMeasurement); - connect(ui->buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept); + // choice of Z0 does not seem to make any difference, hide from user + ui->Z0->setVisible(false); + ui->lZ0->setVisible(false); - updateLabel(); + connect(ui->bMeasure, &QPushButton::clicked, [=](){ + measuringDUT = false; + measuring2xthru = true; + startMeasurement(); + }); + + connect(ui->bMeasureDUT, &QPushButton::clicked, [=](){ + measuringDUT = true; + measuring2xthru = false; + startMeasurement(); + }); + + connect(ui->bClear, &QPushButton::clicked, [=](){ + measurements2xthru.clear(); + updateGUI(); + }); + + connect(ui->bClearDUT, &QPushButton::clicked, [=](){ + measurementsDUT.clear(); + updateGUI(); + }); + + connect(ui->bCalc, &QPushButton::clicked, [=](){ + ui->lPoints->setText("Calculating..."); + qApp->processEvents(); + if(measurementsDUT.size() > 0) { + points = calculateErrorBoxes(measurements2xthru, measurementsDUT, ui->Z0->value()); + } else { + points = calculateErrorBoxes(measurements2xthru); + } + updateGUI(); + }); + + updateGUI(); dialog->show(); } @@ -317,3 +214,509 @@ void TwoThru::fromJSON(nlohmann::json j) points.push_back(p); } } + +std::vector TwoThru::calculateErrorBoxes(std::vector data_2xthru) +{ + // calculate error boxes, see https://www.freelists.org/post/si-list/IEEE-P370-Opensource-Deembedding-MATLAB-functions + // create vectors of S parameters + vector> S11, S12, S21, S22; + vector f; + + // remove DC point if present + if(data_2xthru[0].frequency == 0) { + data_2xthru.erase(data_2xthru.begin()); + } + + data_2xthru = interpolateEvenFrequencySteps(data_2xthru); + + for(auto m : data_2xthru) { + if(m.frequency == 0) { + // ignore possible DC point + continue; + } + S11.push_back(complex(m.real_S11, m.imag_S11)); + S12.push_back(complex(m.real_S12, m.imag_S12)); + S21.push_back(complex(m.real_S21, m.imag_S21)); + S22.push_back(complex(m.real_S22, m.imag_S22)); + f.push_back(m.frequency); + } + auto n = f.size(); + + auto makeSymmetric = [](const vector> &in) -> vector> { + auto abs_DC = 2.0 * abs(in[0]) - abs(in[1]); + auto phase_DC = 2.0 * arg(in[0]) - arg(in[1]); + auto DC = polar(abs_DC, phase_DC); + vector> ret; + ret.push_back(DC); + // add non-symmetric part + ret.insert(ret.end(), in.begin(), in.end()); + // add flipped complex conjugate values + for(auto it = in.rbegin(); it != in.rend(); it++) { + ret.push_back(conj(*it)); + } + return ret; + }; + + auto makeRealAndScale = [](vector> &in) { + for(unsigned int i=0;i data_side1, data_side2; + + { + auto p112x = makeSymmetric(S11); + auto p212x = makeSymmetric(S21); + + // transform into time domain and calculate step responses + auto t112x = p112x; + Fft::transform(t112x, true); + makeRealAndScale(t112x); + Fft::shift(t112x, false); + partial_sum(t112x.begin(), t112x.end(), t112x.begin()); + auto t212x = p212x; + Fft::transform(t212x, true); + makeRealAndScale(t212x); + Fft::shift(t212x, false); + partial_sum(t212x.begin(), t212x.end(), t212x.begin()); + + // find the midpoint of the trace + double threshold = 0.5*real(t212x.back()); + auto mid = lower_bound(t212x.begin(), t212x.end(), threshold, [](complex p, double c) -> bool { + return real(p) < c; + }) - t212x.begin(); + + // mask step response + vector> t111xStep(2*n + 1, 0.0); + copy(t112x.begin() + n, t112x.begin() + mid, t111xStep.begin() + n); + Fft::shift(t111xStep, true); + // create impulse response from masked step response + adjacent_difference(t111xStep.begin(), t111xStep.end(), t111xStep.begin()); + Fft::transform(t111xStep, false); + auto &p111x = t111xStep; + + // calculate p221x and p211x + vector> p221x; + vector> p211x; + double k = 1.0; + complex test, last_test; + for(unsigned int i=0;i 0) { + if(arg(test) - arg(last_test) > 0) { + k = -k; + } + } + last_test = test; + p211x.push_back(k*test); + } + + // create S parameter errorbox + for(unsigned int i=1;i<=n;i++) { + data_side1.push_back(Sparam(p111x[i], p211x[i], p211x[i], p221x[i])); + } + } + + // same thing for error box 2. Variable names get a bit confusing because they are viewed from port 2 (S22 is now called p112x, ...). + // All variable names follow https://gitlab.com/IEEE-SA/ElecChar/P370/-/blob/master/TG1/IEEEP3702xThru_Octave.m + { + auto p112x = makeSymmetric(S22); + auto p212x = makeSymmetric(S12); + + // transform into time domain and calculate step responses + auto t112x = p112x; + Fft::transform(t112x, true); + makeRealAndScale(t112x); + Fft::shift(t112x, false); + partial_sum(t112x.begin(), t112x.end(), t112x.begin()); + auto t212x = p212x; + Fft::transform(t212x, true); + makeRealAndScale(t212x); + Fft::shift(t212x, false); + partial_sum(t212x.begin(), t212x.end(), t212x.begin()); + + // find the midpoint of the trace + double threshold = 0.5*real(t212x.back()); + auto mid = lower_bound(t212x.begin(), t212x.end(), threshold, [](complex p, double c) -> bool { + return real(p) < c; + }) - t212x.begin(); + + // mask step response + vector> t111xStep(2*n + 1, 0.0); + copy(t112x.begin() + n, t112x.begin() + mid, t111xStep.begin() + n); + Fft::shift(t111xStep, true); + // create impulse response from masked step response + adjacent_difference(t111xStep.begin(), t111xStep.end(), t111xStep.begin()); + Fft::transform(t111xStep, false); + auto &p111x = t111xStep; + + // calculate p221x and p211x + vector> p221x; + vector> p211x; + double k = 1.0; + complex test, last_test; + for(unsigned int i=0;i 0) { + if(arg(test) - arg(last_test) > 0) { + k = -k; + } + } + last_test = test; + p211x.push_back(k*test); + } + + // create S parameter errorbox + for(unsigned int i=1;i<=n;i++) { + data_side2.push_back(Sparam(data_side1[i-1].m22, p211x[i], p211x[i], p111x[i])); + data_side1[i-1].m22 = p221x[i]; + } + } + + // got the error boxes, convert to T parameters and invert + vector ret; + for(unsigned int i=0;i TwoThru::calculateErrorBoxes(std::vector data_2xthru, std::vector data_fix_dut_fix, double z0) +{ + vector ret; + + if(data_2xthru.size() != data_fix_dut_fix.size()) { + InformationBox::ShowMessage("Unable to calculate", "The DUT and 2xthru measurements do not have the same amount of points, calculation not possible"); + return ret; + } + + // check if frequencies are the same (measurements must be taken with identical span settings) + for(unsigned int i=0;i (double) data_2xthru[i].frequency / 1e9) { + InformationBox::ShowMessage("Unable to calculate", "The DUT and 2xthru measurements do not have identical frequencies for all points, calculation not possible"); + return ret; + } + } + + data_2xthru = interpolateEvenFrequencySteps(data_2xthru); + data_fix_dut_fix = interpolateEvenFrequencySteps(data_fix_dut_fix); + + // Variable names and order of calulation follows https://gitlab.com/IEEE-SA/ElecChar/P370/-/blob/master/TG1/IEEEP370Zc2xThru_Octave.m as close as possible + vector p; + vector f; + for(auto d : data_2xthru) { + p.push_back(Sparam(complex(d.real_S11, d.imag_S11), + complex(d.real_S12, d.imag_S12), + complex(d.real_S21, d.imag_S21), + complex(d.real_S22, d.imag_S22))); + f.push_back(d.frequency); + } + auto data_2xthru_Sparam = p; + vector data_fix_dut_fix_Sparam; + for(auto d : data_fix_dut_fix) { + data_fix_dut_fix_Sparam.push_back(Sparam(complex(d.real_S11, d.imag_S11), + complex(d.real_S12, d.imag_S12), + complex(d.real_S21, d.imag_S21), + complex(d.real_S22, d.imag_S22))); + } + + // grabbing S21 + vector> s212x; + for(auto s : p) { + s212x.push_back(s.m21); + } + + // get the attenuation and phase constant per length + vector> gamma; + double last_angle = 0.0; + for(auto s : s212x) { + // unwrap phase + double angle = arg(s); + while(angle - last_angle > M_PI) { + angle -= 2 * M_PI; + } + while(angle - last_angle < -M_PI) { + angle += 2 * M_PI; + } + last_angle = angle; + double beta_per_length = -angle; + double alpha_per_length = 20 * log10(abs(s))/-8.686; + + // assume no bandwidth limit (==0) + gamma.push_back(complex(alpha_per_length, beta_per_length)); + } + + // helper function lambdas + auto makeSymmetric = [](const vector> &in) -> vector> { + auto ret = in; + for(auto it = in.rbegin();it != in.rend();it++) { + ret.push_back(conj(*it)); + } + // went one step too far, remove the DC point from the symmetric data + ret.pop_back(); + return ret; + }; + + auto makeRealAndScale = [](vector> &in) { + for(unsigned int i=0;i> &s, const vector &f) -> complex { + auto simple_filter = [](const vector &f, double f0) -> vector> { + vector> ret; + for(auto v : f) { + ret.push_back(1.0/complex(1.0, pow(v/f0, 4))); + } + return ret; + }; + + complex DCpoint = 0.002; // seed for the algorithm + double err = 1; // error seed + double allowedError = 1e-10; // allowable error + long cnt = 0; + auto df = f[1] - f[0]; + auto n = f.size(); + unsigned int ts = round((-3e-9) / ((2.0/df)/(n*2+1)) + (n*2+1)/2); + auto Hr = simple_filter(f, f.back()/2); + while (err > allowedError) { + vector> f1; + f1.push_back(DCpoint); + for(unsigned int i=0;i> f2; + f2.push_back(DCpoint+0.001); + for(unsigned int i=0;i> &gamma, double l, complex zLine, complex z0) -> vector { + vector ret; + for(auto g : gamma) { + auto s11 = ((zLine*zLine-z0*z0)*sinh(g*l))/((zLine*zLine+z0*z0)*sinh(g*l)+2.0*z0*zLine*cosh(g*l)); + auto s21 = (2.0*z0*zLine)/((zLine*zLine + z0*z0)*sinh(g*l)+2.0*z0*zLine*cosh(g*l)); + ret.push_back(Sparam(s11, s21, s21, s11)); + } + return ret; + }; + + auto hybrid = [](const vector &errorbox, const vector &data_2xthru, const vector &freq_2xthru) -> vector { + // taking the errorbox created by peeling and using it only for e00 and e11 + + // grab s11 and s22 of errorbox model + vector> s111x, s221x; + for(auto s : errorbox) { + s111x.push_back(s.m11); + s221x.push_back(s.m22); + } + + // grab s21 of the 2x thru measurement + vector> s212x; + for(auto s : data_2xthru) { + s212x.push_back(s.m21); + } + auto f = freq_2xthru; + + double k = 1.0; + complex test, last_test; + vector> s211x; + vector ret; + for(unsigned int i=0;i 0) { + if(arg(test) - arg(last_test) > 0) { + k = -k; + } + } + last_test = test; + s211x.push_back(k*test); + + // create the error box and make the s-parameter block + ret.push_back(Sparam(s111x[i], s211x[i], s211x[i], s221x[i])); + } + return ret; + }; + + auto makeErrorbox = [=](const vector &data_dut, const vector &data_2xthru, const vector &freq_2xthru, const vector> &gamma, complex z0) -> vector { + auto f = freq_2xthru; + auto n = f.size(); + + vector> s212x; + // add the DC point + s212x.push_back(1.0); + for(auto p : data_2xthru) { + s212x.push_back(p.m21); + } + // extract the mid point from the 2x thru + auto t212x = makeSymmetric(s212x); + Fft::transform(t212x, true); + makeRealAndScale(t212x); + auto x = max_element(t212x.begin(), t212x.end(), [](complex a, complex b) -> bool { + return abs(a) < abs(b); + }) - t212x.begin() + 1; + + // define the relative length + double l = 1.0/(2*x); + + // peel away the fixture and create the errorbox + + // create the errorbox seed (a perfect transmission line with no delay) + vector abcd_errorbox(n, ABCDparam(Sparam(0.0, 1.0, 1.0, 0.0), z0)); + + + for(unsigned int i=0;i> s_dut; + for(auto s : data_dut) { + s_dut.push_back(s.m11); + } + + // define the point for extraction + s_dut.insert(s_dut.begin(), DC2(s_dut, f)); + auto dc11 = makeSymmetric(s_dut); + Fft::transform(dc11, true); + makeRealAndScale(dc11); + Fft::shift(dc11, false); + partial_sum(dc11.begin(), dc11.end(), dc11.begin()); + auto t11dutStep = dc11; + vector> z11dutStep; + for(auto s : t11dutStep) { + z11dutStep.push_back(-z0 * (s+1.0)/(s-1.0)); + } + Fft::shift(z11dutStep, true); + auto zLine = z11dutStep; + + // create the TL + auto TL = makeTL(gamma, l, zLine[0], z0); + + for(unsigned int i=0;i errorbox; + for(auto abcd : abcd_errorbox) { + errorbox.push_back(Sparam(abcd, z0)); + } + return hybrid(errorbox, data_2xthru, f); + }; + + // make the first error box + auto data_side1 = makeErrorbox(data_fix_dut_fix_Sparam, data_2xthru_Sparam, f, gamma, z0); + + // reverse the port order of fixture-dut-fixture and 2x thru + vector data_fix_dut_fix_reversed; + for(auto s : data_fix_dut_fix_Sparam) { + data_fix_dut_fix_reversed.push_back(Sparam(s.m22, s.m21, s.m12, s.m11)); + } + vector data_2xthru_reversed; + for(auto s : data_2xthru_Sparam) { + data_2xthru_reversed.push_back(Sparam(s.m22, s.m21, s.m12, s.m11)); + } + + // make the second error box + auto data_side2 = makeErrorbox(data_fix_dut_fix_reversed, data_2xthru_reversed, f, gamma, z0); + + // got the error boxes, convert to T parameters and invert + for(unsigned int i=0;i TwoThru::interpolateEvenFrequencySteps(std::vector input) +{ + vector ret; + if(input.size() > 1) { + int size = input.size(); + double freqStep = 0.0; + if(input.front().frequency == 0) { + freqStep = input[1].frequency; + size--; + } else { + freqStep = input[0].frequency; + } + if(freqStep * size == input.back().frequency) { + // already correct spacing, no interpolation necessary + for(auto d : input) { + if(d.frequency == 0) { + continue; + } + ret.push_back(d); + } + } else { + // needs to interpolate + double freq = freqStep; + while(freq <= input.back().frequency) { + Protocol::Datapoint interp; + auto it = lower_bound(input.begin(), input.end(), freq, [](const Protocol::Datapoint &lhs, const double f) -> bool { + return lhs.frequency < f; + }); + if(it->frequency == freq) { + interp = *it; + } else { + // no exact match, needs to interpolate + auto high = *it; + it--; + auto low = *it; + double alpha = (freq - low.frequency) / (high.frequency - low.frequency); + interp.real_S11 = low.real_S11 * (1.0 - alpha) + high.real_S11 * alpha; + interp.imag_S11 = low.imag_S11 * (1.0 - alpha) + high.imag_S11 * alpha; + interp.real_S12 = low.real_S12 * (1.0 - alpha) + high.real_S12 * alpha; + interp.imag_S12 = low.imag_S12 * (1.0 - alpha) + high.imag_S12 * alpha; + interp.real_S21 = low.real_S21 * (1.0 - alpha) + high.real_S21 * alpha; + interp.imag_S21 = low.imag_S21 * (1.0 - alpha) + high.imag_S21 * alpha; + interp.real_S22 = low.real_S22 * (1.0 - alpha) + high.real_S22 * alpha; + interp.imag_S22 = low.imag_S22 * (1.0 - alpha) + high.imag_S22 * alpha; + } + interp.frequency = freq; + ret.push_back(interp); + freq += freqStep; + } + } + } + return ret; +} diff --git a/Software/PC_Application/VNA/Deembedding/twothru.h b/Software/PC_Application/VNA/Deembedding/twothru.h index b1f2c70..786cb2b 100644 --- a/Software/PC_Application/VNA/Deembedding/twothru.h +++ b/Software/PC_Application/VNA/Deembedding/twothru.h @@ -23,15 +23,24 @@ public: private slots: void startMeasurement(); - void updateLabel(); + void updateGUI(); + void measurementCompleted(std::vector m) override; private: using Point = struct { double freq; Tparam inverseP1, inverseP2; }; - std::vector measurements; + + static std::vector interpolateEvenFrequencySteps(std::vector input); + static std::vector calculateErrorBoxes(std::vector data_2xthru); + static std::vector calculateErrorBoxes(std::vector data_2xthru, std::vector data_fix_dut_fix, double z0); + + std::vector measurements2xthru; + std::vector measurementsDUT; + double Z0; std::vector points; - bool measuring; + bool measuring2xthru; + bool measuringDUT; QMessageBox *msgBox; Ui::TwoThruDialog *ui; }; diff --git a/Software/PC_Application/VNA/Deembedding/twothrudialog.ui b/Software/PC_Application/VNA/Deembedding/twothrudialog.ui index b667119..da25a51 100644 --- a/Software/PC_Application/VNA/Deembedding/twothrudialog.ui +++ b/Software/PC_Application/VNA/Deembedding/twothrudialog.ui @@ -9,8 +9,8 @@ 0 0 - 233 - 103 + 742 + 198 @@ -21,41 +21,112 @@ - - - + + + Measurements + + + + + 2xThru (mandatory): + + + + + + + Fixture-DUT-Fixture (optional): + + + + + + + Measure + + + + + + + + + + + + + + + + + + + + + Measure + + + + + + + Clear + + + + + + + Clear + + + + - - - Measure - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - QDialogButtonBox::Ok + + + Calculated de-embedding parameters + + + + + + + + + + + + Z0: + + + + + + + + + + Calculate + + + + + + + SIUnitEdit + QLineEdit +
CustomWidgets/siunitedit.h
+
+
diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index 94d04da..b5e7ee9 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -47,6 +47,7 @@ VNA::VNA(AppWindow *window) : Mode(window, "Vector Network Analyzer"), + deembedding(traceModel), central(new TileWidget(traceModel)) { averages = 1;