From 0e0173f62a32cd780637e88a823527257e8fa721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Mon, 29 Aug 2022 20:07:07 +0200 Subject: [PATCH] switched to new calibration implementation --- .../Calibration/amplitudecaldialog.cpp | 1 + .../Calibration/calibration.cpp | 1834 ++++++++--------- .../PC_Application/Calibration/calibration.h | 248 +-- .../Calibration/calibration2.cpp | 686 ------ .../PC_Application/Calibration/calibration2.h | 81 - .../Calibration/calibrationmeasurement.cpp | 142 +- .../Calibration/calibrationmeasurement.h | 58 +- .../Calibration/calibrationtracedialog.cpp | 207 -- .../Calibration/calibrationtracedialog.h | 44 - .../Calibration/calibrationtracedialog.ui | 207 -- .../PC_Application/Calibration/calkit.cpp | 46 +- Software/PC_Application/Calibration/calkit.h | 9 - .../Calibration/manualcalibrationdialog.cpp | 2 +- .../Calibration/measurementmodel.cpp | 122 -- .../Calibration/measurementmodel.h | 39 - Software/PC_Application/LibreVNA-GUI.pro | 7 - .../SpectrumAnalyzer/spectrumanalyzer.cpp | 1 - .../VNA/Deembedding/portextension.cpp | 15 +- .../PC_Application/VNA/tracewidgetvna.cpp | 4 +- Software/PC_Application/VNA/vna.cpp | 208 +- Software/PC_Application/VNA/vna.h | 14 +- Software/PC_Application/appwindow.cpp | 2 +- Software/PC_Application/appwindow.h | 1 - 23 files changed, 1260 insertions(+), 2718 deletions(-) delete mode 100644 Software/PC_Application/Calibration/calibration2.cpp delete mode 100644 Software/PC_Application/Calibration/calibration2.h delete mode 100644 Software/PC_Application/Calibration/calibrationtracedialog.cpp delete mode 100644 Software/PC_Application/Calibration/calibrationtracedialog.h delete mode 100644 Software/PC_Application/Calibration/calibrationtracedialog.ui delete mode 100644 Software/PC_Application/Calibration/measurementmodel.cpp delete mode 100644 Software/PC_Application/Calibration/measurementmodel.h diff --git a/Software/PC_Application/Calibration/amplitudecaldialog.cpp b/Software/PC_Application/Calibration/amplitudecaldialog.cpp index 503822f..462aee8 100644 --- a/Software/PC_Application/Calibration/amplitudecaldialog.cpp +++ b/Software/PC_Application/Calibration/amplitudecaldialog.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include using namespace std; diff --git a/Software/PC_Application/Calibration/calibration.cpp b/Software/PC_Application/Calibration/calibration.cpp index 7cce183..c89d735 100644 --- a/Software/PC_Application/Calibration/calibration.cpp +++ b/Software/PC_Application/Calibration/calibration.cpp @@ -1,481 +1,456 @@ #include "calibration.h" - -#include "unit.h" -#include "Tools/parameters.h" +#include "ui_calibrationdialogui.h" #include "CustomWidgets/informationbox.h" +#include "Util/app_common.h" +#include "unit.h" + +#include "Eigen/Dense" -#include -#include -#include -#include #include +#include + +#include +#include +#include +#include +#include using namespace std; +using Eigen::MatrixXcd; -Calibration::Calibration() -{ - // Create vectors for measurements - measurements[Measurement::Port1Open].datapoints = vector(); - measurements[Measurement::Port1Short].datapoints = vector(); - measurements[Measurement::Port1Load].datapoints = vector(); - measurements[Measurement::Port2Open].datapoints = vector(); - measurements[Measurement::Port2Short].datapoints = vector(); - measurements[Measurement::Port2Load].datapoints = vector(); - measurements[Measurement::Isolation].datapoints = vector(); - measurements[Measurement::Through].datapoints = vector(); - measurements[Measurement::Line].datapoints = vector(); - - type = Type::None; - port1Standard = port2Standard = PortStandard::Male; - throughZeroLength = false; -} - -Calibration::Standard Calibration::getPort1Standard(Calibration::Measurement m) -{ - switch(m) { - case Measurement::Port1Open: return Standard::Open; - case Measurement::Port1Short: return Standard::Short; - case Measurement::Port1Load: return Standard::Load; - case Measurement::Port2Open: return Standard::Any; - case Measurement::Port2Short: return Standard::Any; - case Measurement::Port2Load: return Standard::Any; - case Measurement::Through: return Standard::Through; - case Measurement::Isolation: return Standard::Load; - case Measurement::Line: return Standard::Through; - default: return Standard::Any; - } -} - -Calibration::Standard Calibration::getPort2Standard(Calibration::Measurement m) -{ - switch(m) { - case Measurement::Port1Open: return Standard::Any; - case Measurement::Port1Short: return Standard::Any; - case Measurement::Port1Load: return Standard::Any; - case Measurement::Port2Open: return Standard::Open; - case Measurement::Port2Short: return Standard::Short; - case Measurement::Port2Load: return Standard::Load; - case Measurement::Through: return Standard::Through; - case Measurement::Isolation: return Standard::Load; - case Measurement::Line: return Standard::Through; - default: return Standard::Any; - } -} - -void Calibration::clearMeasurements() -{ - qDebug() << "Clearing all calibration measurements..."; - for(auto m : measurements) { - clearMeasurement(m.first); - } -} - -void Calibration::clearMeasurements(std::set types) -{ - for(auto t : types) { - clearMeasurement(t); - } -} - -void Calibration::clearMeasurement(Calibration::Measurement type) -{ - measurements[type].datapoints.clear(); - measurements[type].timestamp = QDateTime(); - qDebug() << "Deleted" << MeasurementToString(type) << "measurement"; -} - -void Calibration::addMeasurement(Calibration::Measurement type, VirtualDevice::VNAMeasurement &d) -{ - measurements[type].datapoints.push_back(d); - measurements[type].timestamp = QDateTime::currentDateTime(); -} - -void Calibration::addMeasurements(std::set types, VirtualDevice::VNAMeasurement &d) -{ - for(auto t : types) { - addMeasurement(t, d); - } -} - -bool Calibration::calculationPossible(Calibration::Type type) -{ - if(type == Type::None) { - // always possible to reset to None - return true; - } - qDebug() << "Checking if" << TypeToString(type) << "calibration is possible..."; - auto ret = SanityCheckSamples(Measurements(type, false)); - if(ret) { - qDebug() << "...calibration possible"; - } else { - qDebug() << "...calibration not possible"; - } - return SanityCheckSamples(Measurements(type, false)); -} - -bool Calibration::constructErrorTerms(Calibration::Type type) -{ - if(type == Type::None) { - resetErrorTerms(); - return true; - } - if(!calculationPossible(type)) { +bool operator==(const Calibration::CalType &lhs, const Calibration::CalType &rhs) { + if(lhs.type != rhs.type) { return false; } - qDebug() << "Constructing error terms for" << TypeToString(type) << "calibration"; - bool isTRL = type == Type::TRL; - bool uses_male = true; - bool uses_female = true; - if(!kit.checkIfValid(minFreq, maxFreq, isTRL, uses_male, uses_female)) { - // TODO adjust for male/female standards - // Calkit does not support complete calibration range - QString msg = QString("The calibration kit does not support the complete span.\n\n") - + "The measured calibration data covers " + Unit::ToString(minFreq, "Hz", " kMG", 4) + " to " + Unit::ToString(maxFreq, "Hz", " kMG", 4) - + ", however the calibration kit does not support the whole frequency range.\n\n" - + "Please adjust the calibration kit or the span and take the calibration measurements again."; - InformationBox::ShowError("Unable to perform calibration", msg); - qWarning() << msg; + if(lhs.usedPorts.size() != rhs.usedPorts.size()) { return false; } - // check calkit standards and adjust if necessary - if(!kit.hasSeparateMaleFemaleStandards()) { - port1Standard = PortStandard::Male; - port2Standard = PortStandard::Male; + for(unsigned int i=0;itype = type; + // all fields are equal return true; } -void Calibration::resetErrorTerms() +Calibration::Calibration() { - type = Type::None; - points.clear(); - qDebug() << "Error terms reset"; + caltype.type = Type::None; } -void Calibration::construct12TermPoints() +QString Calibration::TypeToString(Calibration::Type type) { - std::vector requiredMeasurements = Measurements(Type::FullSOLT); - requiredMeasurements.push_back(Measurement::Isolation); - bool isolation_measured = SanityCheckSamples(requiredMeasurements); - - points.clear(); - for(unsigned int i = 0;i(0,0); - auto S12_isolation = complex(0,0); - if(isolation_measured) { - S21_isolation = measurements[Measurement::Isolation].datapoints[i].measurements["S21"]; - S12_isolation = measurements[Measurement::Isolation].datapoints[i].measurements["S12"]; - } - auto S11_through = measurements[Measurement::Through].datapoints[i].measurements["S11"]; - auto S21_through = measurements[Measurement::Through].datapoints[i].measurements["S21"]; - auto S22_through = measurements[Measurement::Through].datapoints[i].measurements["S22"]; - auto S12_through = measurements[Measurement::Through].datapoints[i].measurements["S12"]; - - auto actual = kit.toSOLT(p.frequency, port1Standard == PortStandard::Male); - // Forward calibration - computeSOL(S11_short, S11_open, S11_load, p.fe00, p.fe11, p.fe10e01, actual.Open, actual.Short, actual.Load); - p.fe30 = S21_isolation; - // See page 18 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf - // Formulas for S11M and S21M solved for e22 and e10e32 - if (throughZeroLength) { - // use ideal through - actual.ThroughS11 = 0.0; - actual.ThroughS12 = 1.0; - actual.ThroughS21 = 1.0; - actual.ThroughS22 = 0.0; - } - - auto deltaS = actual.ThroughS11*actual.ThroughS22 - actual.ThroughS21 * actual.ThroughS12; - p.fe22 = ((S11_through - p.fe00)*(1.0 - p.fe11 * actual.ThroughS11)-actual.ThroughS11*p.fe10e01) - / ((S11_through - p.fe00)*(actual.ThroughS22-p.fe11*deltaS)-deltaS*p.fe10e01); - p.fe10e32 = (S21_through - p.fe30)*(1.0 - p.fe11*actual.ThroughS11 - p.fe22*actual.ThroughS22 + p.fe11*p.fe22*deltaS) / actual.ThroughS21; - // Reverse calibration - actual = kit.toSOLT(p.frequency, port2Standard == PortStandard::Male); - computeSOL(S22_short, S22_open, S22_load, p.re33, p.re22, p.re23e32, actual.Open, actual.Short, actual.Load); - p.re03 = S12_isolation; - p.re11 = ((S22_through - p.re33)*(1.0 - p.re22 * actual.ThroughS22)-actual.ThroughS22*p.re23e32) - / ((S22_through - p.re33)*(actual.ThroughS11-p.re22*deltaS)-deltaS*p.re23e32); - p.re23e01 = (S12_through - p.re03)*(1.0 - p.re11*actual.ThroughS11 - p.re22*actual.ThroughS22 + p.re11*p.re22*deltaS) / actual.ThroughS12; - - - - points.push_back(p); + switch(type) { + case Type::None: return "None"; + case Type::SOLT: return "SOLT"; + case Type::Last: return "Invalid"; } } -void Calibration::constructPort1SOL() +Calibration::Type Calibration::TypeFromString(QString s) { - points.clear(); - for(unsigned int i = 0;i void solveQuadratic(T a, T b, T c, T &result1, T &result2) -{ - T root = sqrt(b * b - T(4) * a * c); - result1 = (-b + root) / (T(2) * a); - result2 = (-b - root) / (T(2) * a); -} - -void Calibration::constructTRL() -{ - points.clear(); - for(unsigned int i = 0;i S11_reflection, S22_reflection; - if(trl.reflectionIsNegative) { - // used short - S11_reflection = measurements[Measurement::Port1Short].datapoints[i].measurements["S11"]; - S22_reflection = measurements[Measurement::Port2Short].datapoints[i].measurements["S22"]; - } else { - // used open - S11_reflection = measurements[Measurement::Port1Open].datapoints[i].measurements["S11"]; - S22_reflection = measurements[Measurement::Port2Open].datapoints[i].measurements["S22"]; + for(int i=0;i<(int) Type::Last;i++) { + if(TypeToString((Type) i) == s) { + return (Type) i; } - // calculate TRL calibration - // variable names and formulas according to http://emlab.uiuc.edu/ece451/notes/new_TRL.pdf - // page 19 - Sparam Sthrough(S11_through, S12_through, S21_through, S22_through); - Sparam Sline(S11_line, S12_line, S21_line, S22_line); - auto R_T = Tparam(Sthrough); - auto R_D = Tparam(Sline); - auto T = R_D*R_T.inverse(); - complex a_over_c, b; - // page 21-22 - solveQuadratic(T.m21, T.m22 - T.m11, -T.m12, b, a_over_c); - // ensure correct root selection - // page 23 - if(abs(b) >= abs(a_over_c)) { - swap(b, a_over_c); - } - // page 24 - auto g = R_T.m22; - auto d = R_T.m11 / g; - auto e = R_T.m12 / g; - auto f = R_T.m21 / g; - - // page 25 - auto r22_rho22 = g * (1.0 - e / a_over_c) / (1.0 - b / a_over_c); - auto gamma = (f - d / a_over_c) / (1.0 - e / a_over_c); - auto beta_over_alpha = (e - b) / (d - b * f); - // page 26 - auto alpha_a = (d - b * f) / (1.0 - e / a_over_c); - auto w1 = S11_reflection; - auto w2 = S22_reflection; - // page 28 - auto a = sqrt((w1 - b) / (w2 + gamma) * (1.0 + w2 * beta_over_alpha) / (1.0 - w1 / a_over_c) * alpha_a); - // page 29, check sign of a - auto reflection = (w1 - b) / (a * (1.0 - w1 / a_over_c)); - if((reflection.real() > 0 && trl.reflectionIsNegative) || (reflection.real() < 0 && !trl.reflectionIsNegative)) { - // wrong sign for a - a = -a; - } - // Revert back from error boxes with T parameters to S paramaters, - // page 17 + formulas for calculating S parameters from T parameters. - // Forward coefficients, normalize for S21 = 1.0 -> r22 = 1.0 - auto r22 = complex(1.0); - auto rho22 = r22_rho22 / r22; - auto alpha = alpha_a / a; - auto beta = beta_over_alpha * alpha; - auto c = a / a_over_c; - auto Box_A = Tparam(r22 * a, r22 * b, r22 * c, r22); - auto Box_B = Tparam(rho22 * alpha, rho22 * beta, rho22 * gamma, rho22); - auto S_A = Sparam(Box_A); - p.fe00 = S_A.m11; - p.fe10e01 = S_A.m12; - p.fe11 = S_A.m22; - auto S_B = Sparam(Box_B); - p.fe22 = S_B.m11; - p.fe10e32 = S_B.m21; - // no isolation measurement available - p.fe30 = 0.0; - p.fex = 0.0; - - // Reverse coefficients, normalize for S12 = 1.0 - // => det(T)/T22 = 1.0 - // => (rho22*alpa*rho22 - rho22*beta*rho*gamma)/rho22 = 1.0 - // => rho22*alpha - rho22*beta*gamma = 1.0 - // => rho22 = 1.0/(alpha - beta * gamma) - rho22 = 1.0/(alpha - beta * gamma); - r22 = r22_rho22 / rho22; - - Box_A = Tparam(r22 * a, r22 * b, r22 * c, r22); - Box_B = Tparam(rho22 * alpha, rho22 * beta, rho22 * gamma, rho22); - S_A = Sparam(Box_A); - p.re23e01 = S_A.m12; - p.re11 = S_A.m22; - S_B = Sparam(Box_B); - p.re22 = S_B.m11; - p.re23e32 = S_B.m21; - p.re33 = S_B.m22; - // no isolation measurement available - p.re03 = 0.0; - p.rex = 0.0; - - points.push_back(p); } + return Type::None; } void Calibration::correctMeasurement(VirtualDevice::VNAMeasurement &d) { - if(type == Type::None) { - // No calibration data, do nothing + if(caltype.type == Type::None) { + // no calibration active, nothing to do return; } - // Extract measurement S parameters - auto S11m = d.measurements["S11"]; - auto S21m = d.measurements["S21"]; - auto S22m = d.measurements["S22"]; - auto S12m = d.measurements["S12"]; + // formulas from "Multi-Port Calibration Techniques for Differential Parameter Measurements with Network Analyzers", variable names also losely follow this document + MatrixXcd S(caltype.usedPorts.size(), caltype.usedPorts.size()); + MatrixXcd a(caltype.usedPorts.size(), caltype.usedPorts.size()); + MatrixXcd b(caltype.usedPorts.size(), caltype.usedPorts.size()); - // find correct entry - auto p = getCalibrationPoint(d); + // gab point and interpolate + Point p; + if(d.frequency <= points.front().frequency) { + p = points.front(); + } else if(d.frequency >= points.back().frequency) { + p = points.back(); + } else { + // needs to interpolate + auto lower = lower_bound(points.begin(), points.end(), d.frequency, [](const Point &lhs, double rhs) -> bool { + return lhs.frequency < rhs; + }); + auto highPoint = *lower; + auto lowPoint = *prev(lower); + double alpha = (d.frequency - lowPoint.frequency) / (highPoint.frequency - lowPoint.frequency); - complex S11, S12, S21, S22; + p = lowPoint.interpolate(highPoint, alpha); + } - // equations from page 19 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf - auto denom = (1.0 + (S11m - p.fe00) / p.fe10e01 * p.fe11) * (1.0 + (S22m - p.re33) / p.re23e32 * p.re22) - - (S21m - p.fe30) / p.fe10e32 * (S12m - p.re03) / p.re23e01 * p.fe22 * p.re11; - S11 = ((S11m - p.fe00) / p.fe10e01 * (1.0 + (S22m - p.re33) / p.re23e32 * p.re22) - - p.fe22 * (S21m - p.fe30) / p.fe10e32 * (S12m - p.re03) / p.re23e01) / denom; - S21 = ((S21m - p.fe30) / p.fe10e32 * (1.0 + (S22m - p.re33) / p.re23e32 * (p.re22 - p.fe22))) / denom; - S22 = ((S22m - p.re33) / p.re23e32 * (1.0 + (S11m - p.fe00) / p.fe10e01 * p.fe11) - - p.re11 * (S21m - p.fe30) / p.fe10e32 * (S12m - p.re03) / p.re23e01) / denom; - S12 = ((S12m - p.re03) / p.re23e01 * (1.0 + (S11m - p.fe00) / p.fe10e01 * (p.fe11 - p.re11))) / denom; + // Grab measurements (easier to access by index later) + for(unsigned int i=0;isetAttribute(Qt::WA_DeleteOnClose); + auto ui = new Ui::CalibrationDialog; + ui->setupUi(d); + + ui->calMinFreq->setUnit("Hz"); + ui->calMinFreq->setPrecision(4); + ui->calMinFreq->setPrefixes(" kMG"); + ui->calMaxFreq->setUnit("Hz"); + ui->calMaxFreq->setPrecision(4); + ui->calMaxFreq->setPrefixes(" kMG"); + + // generate all possible calibration with the connected device + vector availableCals = getAvailableCalibrations(); + + for(auto c : availableCals) { + ui->calibrationList->addItem(c.getReadableDescription()); } + + auto updateCalStatistics = [=](){ + ui->activeCalibration->setText(caltype.getReadableDescription()); + ui->calPoints->setValue(points.size()); + if(points.size() > 0) { + ui->calMinFreq->setValue(points.front().frequency); + ui->calMaxFreq->setValue(points.back().frequency); + } else { + ui->calMinFreq->setValue(0); + ui->calMaxFreq->setValue(0); + } + }; + + auto updateCalButtons = [=](){ + auto row = ui->calibrationList->currentRow(); + if(row < 0) { + ui->activate->setEnabled(false); + ui->deactivate->setEnabled(false); + } else { + if(caltype == availableCals[row]) { + ui->deactivate->setEnabled(true); + ui->activate->setEnabled(false); + } else { + ui->deactivate->setEnabled(false); + ui->activate->setEnabled(canCompute(availableCals[row])); + } + } + }; + + auto updateCalibrationList = [=](){ + auto style = QApplication::style(); + for(int i=0;istandardIcon(QStyle::SP_DialogApplyButton); + } else { + icon = style->standardIcon(QStyle::SP_MessageBoxCritical); + } + ui->calibrationList->item(i)->setIcon(icon); + } + updateCalButtons(); + }; + + updateCalibrationList(); + updateCalStatistics(); + updateCalButtons(); + + connect(ui->calibrationList, &QListWidget::doubleClicked, [=](const QModelIndex &index) { + ui->activate->clicked(); + }); + + connect(ui->calibrationList, &QListWidget::currentRowChanged, [=](){ + updateCalButtons(); + }); + + connect(ui->activate, &QPushButton::clicked, [=](){ + auto cal = availableCals[ui->calibrationList->currentRow()]; + if(compute(cal)) { + updateCalibrationList(); + updateCalStatistics(); + updateCalButtons(); + } + }); + + connect(ui->deactivate, &QPushButton::clicked, [=](){ + deactivate(); + updateCalibrationList(); + updateCalStatistics(); + updateCalButtons(); + }); + + ui->table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + + auto updateTableEditButtons = [=](){ + ui->bDelete->setEnabled(ui->table->currentRow() >= 0); + ui->bMoveUp->setEnabled(ui->table->currentRow() >= 1); + ui->bMoveDown->setEnabled(ui->table->currentRow() >= 0 && ui->table->currentRow() < ui->table->rowCount() - 1); + }; + + auto updateMeasurementTable = [=](){ + int row = ui->table->currentRow(); + ui->table->clear(); + ui->table->setColumnCount(5); + ui->table->setHorizontalHeaderItem(0, new QTableWidgetItem("Type")); + ui->table->setHorizontalHeaderItem(1, new QTableWidgetItem("Calkit Standard")); + ui->table->setHorizontalHeaderItem(2, new QTableWidgetItem("Settings")); + ui->table->setHorizontalHeaderItem(3, new QTableWidgetItem("Statistics")); + ui->table->setHorizontalHeaderItem(4, new QTableWidgetItem("Timestamp")); + ui->table->setRowCount(measurements.size()); + for(unsigned int i=0;itable->setItem(i, 0, new QTableWidgetItem(CalibrationMeasurement::Base::TypeToString(measurements[i]->getType()))); + ui->table->setCellWidget(i, 1, measurements[i]->createStandardWidget()); + ui->table->setCellWidget(i, 2, measurements[i]->createSettingsWidget()); + ui->table->setItem(i, 3, new QTableWidgetItem(measurements[i]->getStatistics())); + ui->table->setItem(i, 4, new QTableWidgetItem(measurements[i]->getTimestamp().toString())); + } + ui->table->selectRow(row); + updateTableEditButtons(); + }; + + ui->createDefault->addItem(" "); + for(unsigned int i=0;i<(int) DefaultMeasurements::Last;i++) { + ui->createDefault->addItem(DefaultMeasurementsToString((DefaultMeasurements) i)); + } + + QObject::connect(ui->createDefault, qOverload(&QComboBox::currentIndexChanged), [=](){ + if(measurements.size() > 0) { + if(!InformationBox::AskQuestion("Create default entries?", "Do you want to remove all existing entries and create default calibration measurements instead?", true)) { + // user aborted + return; + } + measurements.clear(); + } + createDefaultMeasurements((DefaultMeasurements) (ui->createDefault->currentIndex() - 1)); + updateMeasurementTable(); + updateCalibrationList(); + ui->createDefault->blockSignals(true); + ui->createDefault->setCurrentIndex(0); + ui->createDefault->blockSignals(false); + }); + + QObject::connect(ui->bDelete, &QPushButton::clicked, [=](){ + auto selected = ui->table->selectionModel()->selectedRows(); + set toDelete; + for(auto s : selected) { + toDelete.insert(measurements[s.row()]); + } + while(toDelete.size() > 0) { + for(unsigned int i=0;ibMoveUp, &QPushButton::clicked, [=](){ + auto row = ui->table->currentRow(); + if(row >= 1) { + swap(measurements[row], measurements[row-1]); + ui->table->selectRow(row-1); + updateMeasurementTable(); + } + }); + + QObject::connect(ui->bMoveDown, &QPushButton::clicked, [=](){ + auto row = ui->table->currentRow(); + if(row >= 0) { + swap(measurements[row], measurements[row+1]); + ui->table->selectRow(row+1); + updateMeasurementTable(); + } + }); + + connect(ui->measure, &QPushButton::clicked, [=](){ + std::set m; + auto selected = ui->table->selectionModel()->selectedRows(); + for(auto s : selected) { + m.insert(measurements[s.row()]); + } + if(!CalibrationMeasurement::Base::canMeasureSimultaneously(m)) { + InformationBox::ShowError("Unable to measure", "Different selected measurements require the same port, unable to perform measurement"); + return; + } + emit startMeasurements(m); + }); + + connect(this, &Calibration::measurementsUpdated, d, [=](){ + updateMeasurementTable(); + updateCalibrationList(); + }); + + connect(ui->clearMeasurement, &QPushButton::clicked, [=](){ + auto selected = ui->table->selectionModel()->selectedRows(); + for(auto s : selected) { + measurements[s.row()]->clearPoints(); + } + updateMeasurementTable(); + updateCalibrationList(); + }); + + QObject::connect(ui->table, &QTableWidget::currentCellChanged, updateTableEditButtons); + + auto addMenu = new QMenu(); + for(auto t : CalibrationMeasurement::Base::availableTypes()) { + auto action = new QAction(CalibrationMeasurement::Base::TypeToString(t)); + QObject::connect(action, &QAction::triggered, [=](){ + auto newMeas = newMeasurement(t); + if(newMeas) { + measurements.push_back(newMeas); + updateMeasurementTable(); + } + }); + addMenu->addAction(action); + } + + ui->bAdd->setMenu(addMenu); + + updateMeasurementTable(); + + d->show(); +} + +CalibrationMeasurement::Base *Calibration::newMeasurement(CalibrationMeasurement::Base::Type type) +{ + CalibrationMeasurement::Base *m = nullptr; + switch(type) { + case CalibrationMeasurement::Base::Type::Open: m = new CalibrationMeasurement::Open(this); break; + case CalibrationMeasurement::Base::Type::Short: m = new CalibrationMeasurement::Short(this); break; + case CalibrationMeasurement::Base::Type::Load: m = new CalibrationMeasurement::Load(this); break; + case CalibrationMeasurement::Base::Type::Through: m = new CalibrationMeasurement::Through(this); break; + case CalibrationMeasurement::Base::Type::Isolation: m = new CalibrationMeasurement::Isolation(this); break; + } + return m; +} + +Calibration::Point Calibration::computeSOLT(double f) +{ + Point point; + point.frequency = f; + // resize vectors + point.D.resize(caltype.usedPorts.size()); + point.R.resize(caltype.usedPorts.size()); + point.S.resize(caltype.usedPorts.size()); + + point.L.resize(caltype.usedPorts.size()); + point.T.resize(caltype.usedPorts.size()); + point.I.resize(caltype.usedPorts.size()); + fill(point.L.begin(), point.L.end(), vector>(caltype.usedPorts.size())); + fill(point.T.begin(), point.T.end(), vector>(caltype.usedPorts.size())); + fill(point.I.begin(), point.I.end(), vector>(caltype.usedPorts.size())); + + // Calculate SOL coefficients + for(unsigned int i=0;i(findMeasurement(CalibrationMeasurement::Base::Type::Short, p)); + auto open = static_cast(findMeasurement(CalibrationMeasurement::Base::Type::Open, p)); + auto load = static_cast(findMeasurement(CalibrationMeasurement::Base::Type::Load, p)); + auto s_m = _short->getMeasured(f); + auto o_m = open->getMeasured(f); + auto l_m = load->getMeasured(f); + auto s_c = _short->getActual(f); + auto o_c = open->getActual(f); + auto l_c = load->getActual(f); + auto denom = l_c * o_c * (o_m - l_m) + l_c * s_c * (l_m - s_m) + o_c * s_c * (s_m - o_m); + point.D[i] = (l_c * o_m * (s_m * (o_c - s_c) + l_m * s_c) - l_c * o_c * l_m * s_m + o_c * l_m * s_c * (s_m - o_m)) / denom; + point.S[i] = (l_c * (o_m - s_m) + o_c * (s_m - l_m) + s_c * (l_m - o_m)) / denom; + auto delta = (l_c * l_m * (o_m - s_m) + o_c * o_m * (s_m - l_m) + s_c * s_m * (l_m - o_m)) / denom; + point.R[i] = point.D[i] * point.S[i] - delta; + } + // calculate forward match and transmission + for(unsigned int i=0;i(findMeasurement(CalibrationMeasurement::Base::Type::Through, p1, p2)); + auto throughReverse = static_cast(findMeasurement(CalibrationMeasurement::Base::Type::Through, p2, p1)); + complex S11, S21; + Sparam Sideal; + if(throughForward) { + S11 = throughForward->getMeasured(f).m11; + S21 = throughForward->getMeasured(f).m21; + Sideal = throughForward->getActual(f); + } else if(throughReverse) { + S11 = throughReverse->getMeasured(f).m22; + S21 = throughReverse->getMeasured(f).m12; + Sideal = throughReverse->getActual(f); + swap(Sideal.m11, Sideal.m22); + swap(Sideal.m12, Sideal.m21); + } + auto isoMeas = static_cast(findMeasurement(CalibrationMeasurement::Base::Type::Isolation)); + auto isolation = complex(0.0,0.0); + if(isoMeas) { + isolation = isoMeas->getMeasured(f, p2, p1); + } + auto deltaS = Sideal.m11*Sideal.m22 - Sideal.m21 * Sideal.m12; + point.L[i][j] = ((S11 - point.D[i])*(1.0 - point.S[i] * Sideal.m11)-Sideal.m11*point.R[i]) + / ((S11 - point.D[i])*(Sideal.m22-point.S[i]*deltaS)-deltaS*point.R[i]); + point.T[i][j] = (S21 - isolation)*(1.0 - point.S[i]*Sideal.m11 - point.L[i][j]*Sideal.m22 + point.S[i]*point.L[i][j]*deltaS) / Sideal.m21; + point.I[i][j] = isolation; + } + } + return point; +} + +Calibration::CalType Calibration::getCaltype() const +{ + return caltype; } Calibration::InterpolationType Calibration::getInterpolation(double f_start, double f_stop, int npoints) @@ -511,304 +486,128 @@ Calibration::InterpolationType Calibration::getInterpolation(double f_start, dou } } -Calibration::Measurement Calibration::MeasurementFromString(QString s) -{ - for(unsigned int i=0;i<(int)Measurement::Last;i++) { - auto m = (Measurement) i; - if(s.compare(MeasurementToString(m), Qt::CaseInsensitive)==0) { - return m; - } - } - return Measurement::Last; -} - -QString Calibration::MeasurementToString(Calibration::Measurement m) -{ - switch(m) { - case Measurement::Port1Open: - return "Port 1 Open"; - case Measurement::Port1Short: - return "Port 1 Short"; - case Measurement::Port1Load: - return "Port 1 Load"; - case Measurement::Port2Open: - return "Port 2 Open"; - case Measurement::Port2Short: - return "Port 2 Short"; - case Measurement::Port2Load: - return "Port 2 Load"; - case Measurement::Through: - return "Through"; - case Measurement::Isolation: - return "Isolation"; - case Measurement::Line: - return "Line"; - default: - return "Unknown"; - } -} - -Calibration::Type Calibration::TypeFromString(QString s) -{ - for(unsigned int i=0;i<(int)Type::Last;i++) { - auto t = (Type) i; - if(s.compare(TypeToString(t), Qt::CaseInsensitive)==0) { - return t; - } - } - return Type::Last; -} - -QString Calibration::TypeToString(Calibration::Type t) -{ - switch(t) { - case Type::Port1SOL: return "Port 1"; break; - case Type::Port2SOL: return "Port 2"; break; - case Type::FullSOLT: return "SOLT"; break; - case Type::TransmissionNormalization: return "Normalize"; break; - case Type::TRL: return "TRL"; break; - default: return "None"; break; - } -} - -const std::vector Calibration::Types() -{ - const std::vector ret = {Type::Port1SOL, Type::Port2SOL, Type::FullSOLT, Type::TransmissionNormalization, Type::TRL}; - return ret; -} - -const std::vector Calibration::Measurements(Calibration::Type type, bool optional_included) -{ - switch(type) { - case Type::None: - // all possible measurements - return {Measurement::Port1Short, Measurement::Port1Open, Measurement::Port1Load, - Measurement::Port2Short, Measurement::Port2Open, Measurement::Port2Load, - Measurement::Through, Measurement::Isolation, Measurement::Line}; - case Type::FullSOLT: - if(optional_included) { - return {Measurement::Port1Short, Measurement::Port1Open, Measurement::Port1Load, Measurement::Port2Short, Measurement::Port2Open, Measurement::Port2Load, Measurement::Through, Measurement::Isolation}; - } else { - return {Measurement::Port1Short, Measurement::Port1Open, Measurement::Port1Load, Measurement::Port2Short, Measurement::Port2Open, Measurement::Port2Load, Measurement::Through}; - } - break; - case Type::Port1SOL: - return {Measurement::Port1Short, Measurement::Port1Open, Measurement::Port1Load}; - break; - case Type::Port2SOL: - return {Measurement::Port2Short, Measurement::Port2Open, Measurement::Port2Load}; - break; - case Type::TransmissionNormalization: - return {Measurement::Through}; - break; - case Type::TRL: - if(kit.isTRLReflectionShort()) { - return {Measurement::Through, Measurement::Port1Short, Measurement::Port2Short, Measurement::Line}; - } else { - return {Measurement::Through, Measurement::Port1Open, Measurement::Port2Open, Measurement::Line}; - } - break; - default: - return {}; - break; - } -} - -Calibration::MeasurementInfo Calibration::getMeasurementInfo(Calibration::Measurement m) -{ - MeasurementInfo info; - switch(m) { - case Measurement::Port1Short: - info.name = "Port 1 short"; - info.prerequisites = "Short standard connected to port 1, port 2 open"; - break; - case Measurement::Port1Open: - info.name = "Port 1 open"; - info.prerequisites = "Open standard connected to port 1, port 2 open"; - break; - case Measurement::Port1Load: - info.name = "Port 1 load"; - info.prerequisites = "Load standard connected to port 1, port 2 open"; - break; - case Measurement::Port2Short: - info.name = "Port 2 short"; - info.prerequisites = "Port 1 open, short standard connected to port 2"; - break; - case Measurement::Port2Open: - info.name = "Port 2 open"; - info.prerequisites = "Port 1 open, open standard connected to port 2"; - break; - case Measurement::Port2Load: - info.name = "Port 2 load"; - info.prerequisites = "Port 1 open, load standard connected to port 2"; - break; - case Measurement::Through: - info.name = "Through"; - info.prerequisites = "Port 1 connected to port 2 via through standard"; - break; - case Measurement::Isolation: - info.name = "Isolation"; - info.prerequisites = "Both ports terminated into 50 ohm"; - break; - case Measurement::Line: - info.name = "Line"; - info.prerequisites = "Port 1 connected to port 2 via line standard"; - break; - default: - info.name = "Invalid"; - info.prerequisites = "Invalid"; - break; - } - info.points = measurements[m].datapoints.size(); - if(info.points > 0) { - info.fmin = measurements[m].datapoints.front().frequency; - info.fmax = measurements[m].datapoints.back().frequency; - info.points = measurements[m].datapoints.size(); - } - info.timestamp = measurements[m].timestamp; - return info; -} - std::vector Calibration::getErrorTermTraces() { - std::vector traces; - const QString traceNames[12] = {"e00", "F_e11", "e10e01", "e10e32", "F_e22", "e30", "e33", "R_e11", "e23e32", "e23e01", "R_e22", "e03"}; - constexpr bool reflection[12] = {true, true, false, false, true, false, true, true, false, false, true, false}; - for(int i=0;i<12;i++) { - auto t = new Trace(traceNames[i], Qt::red); - t->setCalibration(); - t->setReflection(reflection[i]); - traces.push_back(t); - } - for(auto p : points) { - Trace::Data d; - d.x = p.frequency; - for(int i=0;i<12;i++) { - switch(i) { - case 0: d.y = p.fe00; break; - case 1: d.y = p.fe11; break; - case 2: d.y = p.fe10e01; break; - case 3: d.y = p.fe10e32; break; - case 4: d.y = p.fe22; break; - case 5: d.y = p.fe30; break; - case 6: d.y = p.re33; break; - case 7: d.y = p.re11; break; - case 8: d.y = p.re23e32; break; - case 9: d.y = p.re23e01; break; - case 10: d.y = p.re22; break; - case 11: d.y = p.re03; break; - } - traces[i]->addData(d, TraceMath::DataType::Frequency); - } - } - return traces; + return vector(); // TODO } std::vector Calibration::getMeasurementTraces() { - std::vector traces; - for(auto m : measurements) { - auto info = getMeasurementInfo(m.first); - if(info.points > 0) { - vector usedPrefixes; - switch(m.first) { - case Measurement::Port1Load: - case Measurement::Port1Open: - case Measurement::Port1Short: - usedPrefixes = {"S11"}; - break; - case Measurement::Port2Load: - case Measurement::Port2Open: - case Measurement::Port2Short: - usedPrefixes = {"S22"}; - break; - case Measurement::Through: - case Measurement::Line: - case Measurement::Isolation: - usedPrefixes = {"S11", "S12", "S21", "S22"}; - break; - default: - break; - } - for(auto prefix : usedPrefixes) { - auto t = new Trace(prefix + " " + info.name); - t->setCalibration(); - t->setReflection(prefix == "S11" || prefix == "S22"); - for(auto p : m.second.datapoints) { - Trace::Data d; - d.x = p.frequency; - d.y = p.measurements[prefix]; - t->addData(d, TraceMath::DataType::Frequency); - } - traces.push_back(t); - } - } - } - return traces; + return vector(); // TODO } -bool Calibration::openFromFile(QString filename) +QString Calibration::getCurrentCalibrationFile() { - if(filename.isEmpty()) { - filename = QFileDialog::getOpenFileName(nullptr, "Load calibration data", "", "Calibration files (*.cal)", nullptr, QFileDialog::DontUseNativeDialog); - if(filename.isEmpty()) { - // aborted selection - return false; + return currentCalFile; +} + +double Calibration::getMinFreq() +{ + if(points.size() > 0) { + return points.front().frequency; + } else { + return numeric_limits::quiet_NaN(); + } +} + +double Calibration::getMaxFreq() +{ + if(points.size() > 0) { + return points.back().frequency; + } else { + return numeric_limits::quiet_NaN(); + } +} + +int Calibration::getNumPoints() +{ + return points.size(); +} + +QString Calibration::descriptiveCalName() +{ + if(points.size() == 0) { + return QString(); + } + int precision = 3; + QString lo = Unit::ToString(points.front().frequency, "", " kMG", precision); + QString hi = Unit::ToString(points.back().frequency, "", " kMG", precision); + // due to rounding up 123.66M and 123.99M -> we get lo="124M" and hi="124M" + // so let's add some precision + if (lo == hi) { + // Only in case of 123.66M and 123.69M we would need 5 digits, but that kind of narrow cal. is very unlikely. + precision = 4; + lo = Unit::ToString(points.front().frequency, "", " kMG", precision); + hi = Unit::ToString(points.back().frequency, "", " kMG", precision); + } + + QString tmp = + caltype.getReadableDescription() + + " " + + lo + "-" + hi + + " " + + QString::number(this->points.size()) + "pt"; + return tmp; +} + +Calkit &Calibration::getKit() +{ + return kit; +} + +nlohmann::json Calibration::toJSON() +{ + nlohmann::json j; + nlohmann::json jmeasurements; + for(auto m : measurements) { + nlohmann::json jmeas; + jmeas["type"] = CalibrationMeasurement::Base::TypeToString(m->getType()).toStdString(); + jmeas["data"] = m->toJSON(); + jmeasurements.push_back(jmeas); + } + j["measurements"] = jmeasurements; + j["calkit"] = kit.toJSON(); + j["type"] = TypeToString(caltype.type).toStdString(); + nlohmann::json jports; + for(auto p : caltype.usedPorts) { + jports.push_back(p); + } + j["ports"] = jports; + j["version"] = qlibrevnaApp->applicationVersion().toStdString(); + if(VirtualDevice::getConnected()) { + j["device"] = VirtualDevice::getConnected()->serial().toStdString(); + } + return j; +} + +void Calibration::fromJSON(nlohmann::json j) +{ + reset(); + if(j.contains("calkit")) { + kit.fromJSON(j["calkit"]); + } + if(j.contains("measurements")) { + for(auto jm : j["measurements"]) { + auto type = CalibrationMeasurement::Base::TypeFromString(QString::fromStdString(jm.value("type", ""))); + auto m = newMeasurement(type); + m->fromJSON(jm["data"]); + measurements.push_back(m); } } - // force correct file ending - if(filename.toLower().endsWith(".cal")) { - filename.chop(4); - filename += ".cal"; + CalType ct; + ct.type = TypeFromString(QString::fromStdString(j.value("type", ""))); + if(j.contains("ports")) { + for(auto jp : j["ports"]) { + ct.usedPorts.push_back(jp); + } } - - qDebug() << "Attempting to open calibration from file" << filename; - - // reset all data before loading new calibration - clearMeasurements(); - resetErrorTerms(); - - // attempt to load associated calibration kit first (needs to be available when performing calibration) - auto calkit_file = filename; - auto dotPos = calkit_file.lastIndexOf('.'); - if(dotPos >= 0) { - calkit_file.truncate(dotPos); + if(ct.type != Type::None) { + compute(ct); } - calkit_file.append(".calkit"); - qDebug() << "Associated calibration kit expected in" << calkit_file; - try { - kit = Calkit::fromFile(calkit_file); - } catch (runtime_error &e) { - InformationBox::ShowError("Missing calibration kit", "The calibration kit file associated with the selected calibration could not be parsed. The calibration might not be accurate. (" + QString(e.what()) + ")"); - qWarning() << "Parsing of calibration kit failed while opening calibration file: " << e.what(); - } - - ifstream file; - - file.open(filename.toStdString()); - if(!file.good()) { - QString msg = "Unable to open file: "+filename; - InformationBox::ShowError("Error", msg); - qWarning() << msg; - return false; - } - - try { - nlohmann::json j; - file >> j; - fromJSON(j); - } catch(exception &e) { - InformationBox::ShowError("File parsing error", e.what()); - qWarning() << "Calibration file parsing failed: " << e.what(); - return false; - } - this->currentCalFile = filename; // if all ok, remember this - - return true; } -bool Calibration::saveToFile(QString filename) +bool Calibration::toFile(QString filename) { if(filename.isEmpty()) { QString fn = descriptiveCalName(); @@ -835,277 +634,404 @@ bool Calibration::saveToFile(QString filename) return true; } -/** - * @brief Calibration::hzToString - * @param freqHz - input frequency in Hz - * @return descriptive name ie. "SOLT 40M-700M 1000pt" - */ -QString Calibration::descriptiveCalName(){ - int precision = 3; - QString lo = Unit::ToString(this->minFreq, "", " kMG", precision); - QString hi = Unit::ToString(this->maxFreq, "", " kMG", precision); - // due to rounding up 123.66M and 123.99M -> we get lo="124M" and hi="124M" - // so let's add some precision - if (lo == hi) { - // Only in case of 123.66M and 123.69M we would need 5 digits, but that kind of narrow cal. is very unlikely. - precision = 4; - lo = Unit::ToString(this->minFreq, "", " kMG", precision); - hi = Unit::ToString(this->maxFreq, "", " kMG", precision); - } - - QString tmp = - Calibration::TypeToString(this->getType()) - + " " - + lo + "-" + hi - + " " - + QString::number(this->points.size()) + "pt"; - return tmp; -} - -bool Calibration::getThroughZeroLength() const +bool Calibration::fromFile(QString filename) { - return throughZeroLength; -} - -void Calibration::setThroughZeroLength(bool value) -{ - throughZeroLength = value; -} - -double Calibration::getMinFreq(){ - return this->minFreq; -} -double Calibration::getMaxFreq(){ - return this->maxFreq; -} -int Calibration::getNumPoints(){ - return this->points.size(); -} - -nlohmann::json Calibration::toJSON() -{ - nlohmann::json j; - nlohmann::json j_measurements; - for(auto m : measurements) { - if(m.second.datapoints.size() > 0) { - nlohmann::json j_measurement; - j_measurement["name"] = MeasurementToString(m.first).toStdString(); - j_measurement["timestamp"] = m.second.timestamp.toSecsSinceEpoch(); - nlohmann::json j_points; - for(auto p : m.second.datapoints) { - nlohmann::json j_point; - j_point["frequency"] = p.frequency; - j_point["S11_real"] = p.measurements["S11"].real(); - j_point["S11_imag"] = p.measurements["S11"].imag(); - j_point["S12_real"] = p.measurements["S12"].real(); - j_point["S12_imag"] = p.measurements["S12"].imag(); - j_point["S21_real"] = p.measurements["S21"].real(); - j_point["S21_imag"] = p.measurements["S21"].imag(); - j_point["S22_real"] = p.measurements["S22"].real(); - j_point["S22_imag"] = p.measurements["S22"].imag(); - j_points.push_back(j_point); - } - j_measurement["points"] = j_points; - j_measurements.push_back(j_measurement); - } - } - j["measurements"] = j_measurements; - j["type"] = TypeToString(getType()).toStdString(); - j["port1StandardMale"] = port1Standard == PortStandard::Male; - j["port2StandardMale"] = port2Standard == PortStandard::Male; - j["throughZeroLength"] = throughZeroLength; - - return j; -} - -void Calibration::fromJSON(nlohmann::json j) -{ - clearMeasurements(); - resetErrorTerms(); - port1Standard = j.value("port1StandardMale", true) ? PortStandard::Male : PortStandard::Female; - port2Standard = j.value("port2StandardMale", true) ? PortStandard::Male : PortStandard::Female; - throughZeroLength = j.value("throughZeroLength", false); - if(j.contains("measurements")) { - // grab measurements - for(auto j_m : j["measurements"]) { - if(!j_m.contains("name")) { - throw runtime_error("Measurement without name given"); - } - auto m = MeasurementFromString(QString::fromStdString(j_m["name"])); - if(m == Measurement::Last) { - throw runtime_error("Measurement name unknown: "+std::string(j_m["name"])); - } - // get timestamp - measurements[m].timestamp = QDateTime::fromSecsSinceEpoch(j_m.value("timestamp", 0)); - // extract points - if(!j_m.contains("points")) { - throw runtime_error("Measurement "+MeasurementToString(m).toStdString()+" does not contain any points"); - } - int pointNum = 0; - for(auto j_p : j_m["points"]) { - VirtualDevice::VNAMeasurement p; - p.pointNum = pointNum++; - p.frequency = j_p.value("frequency", 0.0); - p.Z0 = 50.0; - p.measurements["S11"] = complex(j_p.value("S11_real", 0.0), j_p.value("S11_imag", 0.0)); - p.measurements["S12"] = complex(j_p.value("S12_real", 0.0), j_p.value("S12_imag", 0.0)); - p.measurements["S21"] = complex(j_p.value("S21_real", 0.0), j_p.value("S21_imag", 0.0)); - p.measurements["S22"] = complex(j_p.value("S22_real", 0.0), j_p.value("S22_imag", 0.0)); - measurements[m].datapoints.push_back(p); - } - } - } - // got all measurements, construct calibration according to type - if(j.contains("type")) { - auto t = TypeFromString(QString::fromStdString(j["type"])); - if(t == Type::Last) { - throw runtime_error("Calibration type unknown: "+std::string(j["type"])); - } - if(calculationPossible(t)) { - constructErrorTerms(t); - } else { - throw runtime_error("Incomplete calibration data, the requested calibration could not be performed."); - } - } -} - -QString Calibration::getCurrentCalibrationFile(){ - return this->currentCalFile; -} - -bool Calibration::SanityCheckSamples(const std::vector &requiredMeasurements) -{ - // sanity check measurements, all need to be of the same size with the same frequencies (except for isolation which may be empty) - vector freqs; - for(auto type : requiredMeasurements) { - auto m = measurements[type]; - if(m.datapoints.size() == 0) { - // empty required measurement + if(filename.isEmpty()) { + filename = QFileDialog::getOpenFileName(nullptr, "Load calibration data", "", "Calibration files (*.cal)", nullptr, QFileDialog::DontUseNativeDialog); + if(filename.isEmpty()) { + // aborted selection return false; } - if(freqs.size() == 0) { - // this is the first measurement, create frequency vector - for(auto p : m.datapoints) { - freqs.push_back(p.frequency); - } - } else { - // compare with already assembled frequency vector - if(m.datapoints.size() != freqs.size()) { - return false; - } - for(unsigned int i=0;i> j; + currentCalFile = filename; // if all ok, remember this + fromJSON(j); + } catch(exception &e) { + currentCalFile.clear(); + InformationBox::ShowError("File parsing error", e.what()); + qWarning() << "Calibration file parsing failed: " << e.what(); + return false; + } + return true; } -Calibration::Point Calibration::getCalibrationPoint(VirtualDevice::VNAMeasurement &d) +std::vector Calibration::getAvailableCalibrations() { - if(!points.size()) { - throw runtime_error("No calibration points available"); + int ports = 2; + if(VirtualDevice::getConnected()) { + ports = VirtualDevice::getConnected()->getInfo().ports; } - if(d.frequency <= points.front().frequency) { - // use first point even for lower frequencies - return points.front(); + vector ret; + for(auto t : getTypes()) { + CalType cal; + cal.type = t; + auto minPorts = minimumPorts(t); + for(int pnum = minPorts;pnum <= ports;pnum++) { + std::string bitmask(pnum, 1); + bitmask.resize(ports, 0); + // assemble selected ports and permute bitmask + do { + vector usedPorts; + for (int i = 0; i < ports; ++i) { + if (bitmask[i]) { + usedPorts.push_back(i+1); + } + } + cal.usedPorts = usedPorts; + ret.push_back(cal); + } while (std::prev_permutation(bitmask.begin(), bitmask.end())); + } } - if(d.frequency >= points.back().frequency) { - // use last point even for higher frequencies - return points.back(); - } - auto p = lower_bound(points.begin(), points.end(), d.frequency, [](Point p, uint64_t freq) -> bool { - return p.frequency < freq; - }); - if(p->frequency == d.frequency) { - // Exact match, return point - return *p; - } - // need to interpolate - auto high = p; - p--; - auto low = p; - double alpha = (d.frequency - low->frequency) / (high->frequency - low->frequency); - Point ret; - ret.frequency = d.frequency; - ret.fe00 = low->fe00 * (1 - alpha) + high->fe00 * alpha; - ret.fe11 = low->fe11 * (1 - alpha) + high->fe11 * alpha; - ret.fe22 = low->fe22 * (1 - alpha) + high->fe22 * alpha; - ret.fe30 = low->fe30 * (1 - alpha) + high->fe30 * alpha; - ret.fex = low->fex * (1 - alpha) + high->fex * alpha; - ret.re03 = low->re03 * (1 - alpha) + high->re03 * alpha; - ret.rex = low->rex * (1 - alpha) + high->rex * alpha; - ret.re11 = low->re11 * (1 - alpha) + high->re11 * alpha; - ret.re22 = low->re22 * (1 - alpha) + high->re22 * alpha; - ret.re33 = low->re33 * (1 - alpha) + high->re33 * alpha; - ret.fe10e01 = low->fe10e01 * (1 - alpha) + high->fe10e01 * alpha; - ret.fe10e32 = low->fe10e32 * (1 - alpha) + high->fe10e32 * alpha; - ret.re23e01 = low->re23e01 * (1 - alpha) + high->re23e01 * alpha; - ret.re23e32 = low->re23e32 * (1 - alpha) + high->re23e32 * alpha; return ret; } -void Calibration::computeSOL(std::complex s_m, std::complex o_m, std::complex l_m, - std::complex &directivity, std::complex &match, std::complex &tracking, - std::complex o_c, std::complex s_c, std::complex l_c) +std::vector Calibration::getTypes() { - // equations from page 13 of http://www2.electron.frba.utn.edu.ar/~jcecconi/Bibliografia/04%20-%20Param_S_y_VNA/Network_Analyzer_Error_Models_and_Calibration_Methods.pdf - // solved while taking non ideal o/s/l standards into account - auto denom = l_c * o_c * (o_m - l_m) + l_c * s_c * (l_m - s_m) + o_c * s_c * (s_m - o_m); - directivity = (l_c * o_m * (s_m * (o_c - s_c) + l_m * s_c) - l_c * o_c * l_m * s_m + o_c * l_m * s_c * (s_m - o_m)) / denom; - match = (l_c * (o_m - s_m) + o_c * (s_m - l_m) + s_c * (l_m - o_m)) / denom; - auto delta = (l_c * l_m * (o_m - s_m) + o_c * o_m * (s_m - l_m) + s_c * s_m * (l_m - o_m)) / denom; - tracking = directivity * match - delta; + vector types; + // Start at index 1, skip Type::None + for(int i=1;i<(int) Type::Last;i++) { + types.push_back((Type) i); + } + return types; } -void Calibration::computeIsolation(std::complex x0_m, std::complex x1_m, std::complex reverse_match, std::complex reverse_tracking, std::complex reverse_directivity, std::complex x0, std::complex x1, std::complex &internal_isolation, std::complex &external_isolation) +bool Calibration::canCompute(Calibration::CalType type, double *startFreq, double *stopFreq, int *points) { - external_isolation = (x1_m - x0_m)*(1.0 - reverse_match * (x1 - x0) + x1*x0*reverse_match*reverse_match) / (reverse_tracking * (x1 - x0)); - internal_isolation = x0_m - external_isolation*(reverse_directivity + reverse_tracking*x0 / (1.0 - x0*reverse_match)); + switch(type.type) { + case Type::None: + return true; // Always possible to reset the calibration + case Type::SOLT: { + using RequiredMeasurements = struct { + CalibrationMeasurement::Base::Type type; + int port1, port2; + }; + vector required; + // SOL measurements for every port + for(auto p : type.usedPorts) { + required.push_back({.type = CalibrationMeasurement::Base::Type::Short, .port1 = p}); + required.push_back({.type = CalibrationMeasurement::Base::Type::Open, .port1 = p}); + required.push_back({.type = CalibrationMeasurement::Base::Type::Load, .port1 = p}); + } + // through measurements between all ports + for(int i=1;i<=type.usedPorts.size();i++) { + for(int j=i+1;j<=type.usedPorts.size();j++) { + required.push_back({.type = CalibrationMeasurement::Base::Type::Through, .port1 = i, .port2 = j}); + } + } + vector foundMeasurements; + for(auto m : required) { + auto meas = findMeasurement(m.type, m.port1, m.port2); + if(!meas) { + // missing measurement + return false; + } else { + foundMeasurements.push_back(meas); + } + } + return hasFrequencyOverlap(foundMeasurements, startFreq, stopFreq, points); + } + break; + } + return false; } -std::complex Calibration::correctSOL(std::complex measured, std::complex directivity, std::complex match, std::complex tracking) +bool Calibration::compute(Calibration::CalType type) { - return (measured - directivity) / (measured * match - directivity * match + tracking); + if(type.type == Type::None) { + deactivate(); + return true; + } + double start, stop; + int numPoints; + if(!canCompute(type, &start, &stop, &numPoints)) { + return false; + } + caltype = type; + try { + points.clear(); + for(int i=0;i m, const VirtualDevice::VNAMeasurement &data) { - if(port == 1) { - port1Standard = standard; - } else if(port == 2) { - port2Standard = standard; + for(auto meas : m) { + meas->addPoint(data); } } -Calibration::PortStandard Calibration::getPortStandard(int port) +void Calibration::clearMeasurements(std::set m) { - if(port == 1) { - return port1Standard; - } else if(port == 2) { - return port2Standard; + for(auto meas : m) { + meas->clearPoints(); + } +} + +void Calibration::measurementsComplete() +{ + emit measurementsUpdated(); +} + +void Calibration::deactivate() +{ + points.clear(); + caltype.type = Type::None; + caltype.usedPorts.clear(); + emit deactivated(); +} + +QString Calibration::DefaultMeasurementsToString(Calibration::DefaultMeasurements dm) +{ + switch(dm) { + case DefaultMeasurements::SOL1Port: return "1 Port SOL"; + case DefaultMeasurements::SOLT2Port: return "2 Port SOLT"; + case DefaultMeasurements::SOLT3Port: return "3 Port SOLT"; + case DefaultMeasurements::SOLT4Port: return "4 Port SOLT"; + } +} + +void Calibration::createDefaultMeasurements(Calibration::DefaultMeasurements dm) +{ + auto createSOL = [=](int port) { + auto _short = new CalibrationMeasurement::Short(this); + _short->setPort(port); + measurements.push_back(_short); + auto open = new CalibrationMeasurement::Open(this); + open->setPort(port); + measurements.push_back(open); + auto load = new CalibrationMeasurement::Load(this); + load->setPort(port); + measurements.push_back(load); + }; + auto createThrough = [=](int port1, int port2) { + auto through = new CalibrationMeasurement::Through(this); + through->setPort1(port1); + through->setPort2(port2); + measurements.push_back(through); + }; + switch(dm) { + case DefaultMeasurements::SOL1Port: + createSOL(1); + break; + case DefaultMeasurements::SOLT2Port: + createSOL(1); + createSOL(2); + createThrough(1, 2); + break; + case DefaultMeasurements::SOLT3Port: + createSOL(1); + createSOL(2); + createSOL(3); + createThrough(1, 2); + createThrough(1, 3); + createThrough(2, 3); + break; + case DefaultMeasurements::SOLT4Port: + createSOL(1); + createSOL(2); + createSOL(3); + createSOL(4); + createThrough(1, 2); + createThrough(1, 3); + createThrough(1, 4); + createThrough(2, 3); + createThrough(2, 4); + createThrough(3, 4); + break; + } +} + +bool Calibration::hasFrequencyOverlap(std::vector m, double *startFreq, double *stopFreq, int *points) +{ + double minResolution = std::numeric_limits::max(); + double minFreq = 0; + double maxFreq = std::numeric_limits::max(); + for(auto meas : m) { + if(meas->numPoints() < 2) { + return false; + } + auto resolution = (meas->maxFreq() - meas->minFreq()) / (meas->numPoints() - 1); + if(meas->maxFreq() < maxFreq) { + maxFreq = meas->maxFreq(); + } + if(meas->minFreq() > minFreq) { + minFreq = meas->minFreq(); + } + if(resolution < minResolution) { + minResolution = resolution; + } + } + if(startFreq) { + *startFreq = minFreq; + } + if(stopFreq) { + *stopFreq = maxFreq; + } + if(points) { + *points = (maxFreq - minFreq) / minResolution + 1; + } + if(maxFreq > minFreq) { + return true; } else { - return PortStandard::Male; + return false; } } -Calibration::Type Calibration::getType() const +CalibrationMeasurement::Base *Calibration::findMeasurement(CalibrationMeasurement::Base::Type type, int port1, int port2) { - return type; + for(auto m : measurements) { + if(m->getType() != type) { + continue; + } + auto onePort = dynamic_cast(m); + if(onePort) { + if(onePort->getPort() != port1) { + continue; + } + } + auto twoPort = dynamic_cast(m); + if(twoPort) { + if(twoPort->getPort1() != port1 || twoPort->getPort2() != port2) { + continue; + } + } + // if we get here, we have a match + return m; + } + return nullptr; } +QString Calibration::CalType::getReadableDescription() +{ + QString ret = TypeToString(this->type); + if(usedPorts.size() == 1) { + ret += ", Port: "+QString::number(usedPorts[0]); + } else if(usedPorts.size() > 0) { + ret += ", Ports: ["; + for(auto p : usedPorts) { + ret += QString::number(p)+","; + } + // remove the last trailing comma + ret.chop(1); + ret += "]"; + } + return ret; +} +QString Calibration::CalType::getShortString() +{ + QString ret = TypeToString(this->type); + if(usedPorts.size() > 0) { + ret += "_"; + } + for(auto p : usedPorts) { + ret += QString::number(p); + } + return ret; +} + +Calibration::CalType Calibration::CalType::fromShortString(QString s) +{ + CalType ret; + auto list = s.split("_"); + if(list.size() != 2) { + ret.type = Type::None; + } else { + ret.type = TypeFromString(list[0]); + for(auto c : list[1]) { + ret.usedPorts.push_back(QString(c).toInt()); + } + } + return ret; +} + +Calibration::Point Calibration::Point::interpolate(const Calibration::Point &to, double alpha) +{ + Point ret; + ret.frequency = frequency * (1.0-alpha) + to.frequency * alpha; + ret.D.resize(D.size()); + for(unsigned int i=0;i -#include -#include -#include -#include -#include -#include - -class Calibration : public Savable +class Calibration : public QObject, public Savable { + Q_OBJECT public: Calibration(); - enum class Measurement { - Port1Open, - Port1Short, - Port1Load, - Port2Open, - Port2Short, - Port2Load, - Isolation, - Through, - Line, - Last, - }; - - enum class Standard { - Open, - Short, - Load, - Through, - Any, - }; - - static Standard getPort1Standard(Measurement m); - static Standard getPort2Standard(Measurement m); - - void clearMeasurements(); - void clearMeasurements(std::set types); - void clearMeasurement(Measurement type); - void addMeasurement(Measurement type, VirtualDevice::VNAMeasurement &d); - void addMeasurements(std::set types, VirtualDevice::VNAMeasurement &d); - enum class Type { - Port1SOL, - Port2SOL, - FullSOLT, - TransmissionNormalization, - TRL, None, + SOLT, Last, }; + class CalType { + public: + Type type; + std::vector usedPorts; + QString getReadableDescription(); + QString getShortString(); + static CalType fromShortString(QString s); - bool calculationPossible(Type type); - bool constructErrorTerms(Type type); - void resetErrorTerms(); + friend bool operator==(const CalType &lhs, const CalType &rhs); + }; + static QString TypeToString(Type type); + static Type TypeFromString(QString s); + + // Applies calculated calibration coefficients to measurement data void correctMeasurement(VirtualDevice::VNAMeasurement &d); - void correctTraces(Trace &S11, Trace &S12, Trace &S21, Trace &S22); + + // Starts the calibration edit dialog, allowing the user to make/delete measurements + void edit(); + + Calkit& getKit(); + + virtual nlohmann::json toJSON() override; + virtual void fromJSON(nlohmann::json j) override; + + bool toFile(QString filename = QString()); + bool fromFile(QString filename = QString()); + + // Returns all possible calibration types/port permutations for the currently connected device. + // If no device is connected, a two-port device is assumed + static std::vector getAvailableCalibrations(); + + // Returns vector of all calibration types (without 'Last') + static std::vector getTypes(); + // Checks whether all measurements for a specific calibration are available. + // If pointer to the frequency/points variables are given, the start/stop frequency and number of points the calibration will have after the calculation is stored there + bool canCompute(CalType type, double *startFreq = nullptr, double *stopFreq = nullptr, int *points = nullptr); + // Resets the calibration (deletes all measurements and calculated coefficients) + void reset(); + // Returns the minimum number of ports for a given calibration type. + // E.g. the SOL(T) calibration can work with only one port, a through normalization requires at least two + static int minimumPorts(Type type); + + // Adds a new measurement point (data) to all calibration measurements (m) + void addMeasurements(std::set m, const VirtualDevice::VNAMeasurement &data); + // Deletes all datapoints in the calibration measurements (m) + void clearMeasurements(std::set m); + CalType getCaltype() const; enum class InterpolationType { Unchanged, // Nothing has changed, settings and calibration points match @@ -74,119 +75,70 @@ public: NoCalibration, // No calibration available }; - InterpolationType getInterpolation(double f_start, double f_stop, int points); - - static Measurement MeasurementFromString(QString s); - static QString MeasurementToString(Measurement m); - static Type TypeFromString(QString s); - static QString TypeToString(Type t); - - class MeasurementInfo { - public: - QString name, prerequisites; - double fmin, fmax; - unsigned int points; - QDateTime timestamp; - }; - - static const std::vector Types(); - const std::vector Measurements(Type type = Type::None, bool optional_included = true); - MeasurementInfo getMeasurementInfo(Measurement m); - - friend std::istream& operator >> (std::istream &in, Calibration& c); - int nPoints() { - return points.size(); - } + InterpolationType getInterpolation(double f_start, double f_stop, int npoints); std::vector getErrorTermTraces(); std::vector getMeasurementTraces(); - bool openFromFile(QString filename = QString()); - bool saveToFile(QString filename = QString()); - Type getType() const; - - Calkit& getCalibrationKit(); - void setCalibrationKit(const Calkit &value); - - enum class PortStandard { - Male, - Female, - }; - void setPortStandard(int port, PortStandard standard); - PortStandard getPortStandard(int port); - bool getThroughZeroLength() const; - void setThroughZeroLength(bool value); - QString getCurrentCalibrationFile(); double getMinFreq(); double getMaxFreq(); int getNumPoints(); - nlohmann::json toJSON() override; - void fromJSON(nlohmann::json j) override; - +public slots: + // Call once all datapoints of the current span have been added + void measurementsComplete(); + // Attempts to calculate the calibration coefficients. If not enough measurements are available, false is returned and the currently used coefficients are not changed + bool compute(CalType type); + // Deactivates the calibration, resets the calibration coefficients. Calibration measurements are NOT deleted. + void deactivate(); +signals: + // emitted when the measurement of a set of calibration measurements should be started + void startMeasurements(std::set m); + // emitted whenever a measurement is complete (triggered by calling measurementsComplete()) + void measurementsUpdated(); + // emitted when calibration coefficients were calculated/updated successfully + void activated(CalType type); + // emitted when the calibrationo coefficients were reset + void deactivated(); private: - void construct12TermPoints(); - void constructPort1SOL(); - void constructPort2SOL(); - void constructTransmissionNormalization(); - void constructTRL(); - bool SanityCheckSamples(const std::vector &requiredMeasurements); - class Point - { + enum class DefaultMeasurements { + SOL1Port, + SOLT2Port, + SOLT3Port, + SOLT4Port, + Last + }; + static QString DefaultMeasurementsToString(DefaultMeasurements dm); + void createDefaultMeasurements(DefaultMeasurements dm); + + bool hasFrequencyOverlap(std::vector m, double *startFreq = nullptr, double *stopFreq = nullptr, int *points = nullptr); + CalibrationMeasurement::Base* findMeasurement(CalibrationMeasurement::Base::Type type, int port1 = 0, int port2 = 0); + + CalibrationMeasurement::Base *newMeasurement(CalibrationMeasurement::Base::Type type); + + class Point { public: double frequency; - // Forward error terms - std::complex fe00, fe11, fe10e01, fe10e32, fe22, fe30, fex; - // Reverse error terms - std::complex re33, re11, re23e32, re23e01, re22, re03, rex; + std::vector> D; // Directivity + std::vector> R; // Source Match + std::vector> S; // Reflection tracking + std::vector>> L; // Receiver Match + std::vector>> T; // Transmission tracking + std::vector>> I; // Transmission isolation + Point interpolate(const Point &to, double alpha); }; - Point getCalibrationPoint(VirtualDevice::VNAMeasurement &d); - /* - * Constructs directivity, match and tracking correction factors from measurements of three distinct impedances - * Normally, an open, short and load are used (with ideal reflection coefficients of 1, -1 and 0 respectively). - * The actual reflection coefficients can be passed on as optional arguments to take into account the non-ideal - * calibration kit. - */ - void computeSOL(std::complex s_m, - std::complex o_m, - std::complex l_m, - std::complex &directivity, - std::complex &match, - std::complex &tracking, - std::complex o_c = std::complex(1.0, 0), - std::complex s_c = std::complex(-1.0, 0), - std::complex l_c = std::complex(0, 0)); - void computeIsolation(std::complex x0_m, - std::complex x1_m, - std::complex reverse_match, - std::complex reverse_tracking, - std::complex reverse_directivity, - std::complex x0, - std::complex x1, - std::complex &internal_isolation, - std::complex &external_isolation); - std::complex correctSOL(std::complex measured, - std::complex directivity, - std::complex match, - std::complex tracking); - class MeasurementData { - public: - QDateTime timestamp; - std::vector datapoints; - }; - Type type; - - std::map measurements; - double minFreq, maxFreq; std::vector points; + Point computeSOLT(double f); + + std::vector measurements; + Calkit kit; + CalType caltype; + QString descriptiveCalName(); QString currentCalFile; - - PortStandard port1Standard, port2Standard; - bool throughZeroLength; }; -#endif // CALIBRATION_H +#endif // CALIBRATION2_H diff --git a/Software/PC_Application/Calibration/calibration2.cpp b/Software/PC_Application/Calibration/calibration2.cpp deleted file mode 100644 index e1af0d0..0000000 --- a/Software/PC_Application/Calibration/calibration2.cpp +++ /dev/null @@ -1,686 +0,0 @@ -#include "calibration2.h" -#include "ui_calibrationdialogui.h" -#include "CustomWidgets/informationbox.h" -#include "Util/app_common.h" - -#include "Eigen/Dense" - -#include -#include -#include -#include - -using namespace std; -using Eigen::MatrixXcd; - -Calibration2::Calibration2() -{ - caltype.type = Type::None; -} - -void Calibration2::correctMeasurement(VirtualDevice::VNAMeasurement &d) -{ - if(caltype.type == Type::None) { - // no calibration active, nothing to do - return; - } - // formulas from "Multi-Port Calibration Techniques for Differential Parameter Measurements with Network Analyzers", variable names also losely follow this document - MatrixXcd S(caltype.usedPorts.size(), caltype.usedPorts.size()); - MatrixXcd a(caltype.usedPorts.size(), caltype.usedPorts.size()); - MatrixXcd b(caltype.usedPorts.size(), caltype.usedPorts.size()); - // Grab measurements (easier to access by index later) - for(unsigned int i=0;i= points.back().frequency) { - p = points.back(); - } else { - // needs to interpolate - auto lower = lower_bound(points.begin(), points.end(), d.frequency, [](const Point &lhs, double rhs) -> bool { - return lhs.frequency < rhs; - }); - auto highPoint = *lower; - auto lowPoint = *prev(lower); - double alpha = (d.frequency - lowPoint.frequency) / (highPoint.frequency - lowPoint.frequency); - - p = lowPoint.interpolate(highPoint, alpha); - } - // assemble a (L) and b (K) matrices - for(unsigned int i=0;isetAttribute(Qt::WA_DeleteOnClose); - auto ui = new Ui::CalibrationDialog; - ui->setupUi(d); - - ui->calMinFreq->setUnit("Hz"); - ui->calMinFreq->setPrecision(4); - ui->calMinFreq->setPrefixes(" kMG"); - ui->calMaxFreq->setUnit("Hz"); - ui->calMaxFreq->setPrecision(4); - ui->calMaxFreq->setPrefixes(" kMG"); - - int ports = 2; - if(VirtualDevice::getConnected()) { - ports = VirtualDevice::getConnected()->getInfo().ports; - } - - // generate all possible calibration with the connected device - vector availableCals; - for(auto t : getTypes()) { - CalType cal; - cal.type = t; - auto minPorts = minimumPorts(t); - for(int pnum = minPorts;pnum <= ports;pnum++) { - std::string bitmask(pnum, 1); - bitmask.resize(ports, 0); - // assemble selected ports and permute bitmask - do { - vector usedPorts; - for (int i = 0; i < ports; ++i) { - if (bitmask[i]) { - usedPorts.push_back(i+1); - } - } - cal.usedPorts = usedPorts; - availableCals.push_back(cal); - } while (std::prev_permutation(bitmask.begin(), bitmask.end())); - } - } - - for(auto c : availableCals) { - ui->calibrationList->addItem(c.getDescription()); - } - - auto updateCalibrationList = [=](){ - auto style = QApplication::style(); - for(int i=0;istandardIcon(QStyle::SP_DialogApplyButton); - } else { - icon = style->standardIcon(QStyle::SP_MessageBoxCritical); - } - ui->calibrationList->item(i)->setIcon(icon); - } - }; - - updateCalibrationList(); - - connect(ui->calibrationList, &QListWidget::doubleClicked, [=](const QModelIndex &index) { - auto row = index.row(); - auto cal = availableCals[row]; - if(canCompute(cal)) { - compute(cal); - ui->activeCalibration->setText(cal.getDescription()); - ui->calMinFreq->setValue(points.front().frequency); - ui->calMaxFreq->setValue(points.back().frequency); - ui->calPoints->setValue(points.size()); - } - }); - - ui->table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - - auto updateTableEditButtons = [=](){ - ui->bDelete->setEnabled(ui->table->currentRow() >= 0); - ui->bMoveUp->setEnabled(ui->table->currentRow() >= 1); - ui->bMoveDown->setEnabled(ui->table->currentRow() >= 0 && ui->table->currentRow() < ui->table->rowCount() - 1); - }; - - auto updateMeasurementTable = [=](){ - int row = ui->table->currentRow(); - ui->table->clear(); - ui->table->setColumnCount(5); - ui->table->setHorizontalHeaderItem(0, new QTableWidgetItem("Type")); - ui->table->setHorizontalHeaderItem(1, new QTableWidgetItem("Calkit Standard")); - ui->table->setHorizontalHeaderItem(2, new QTableWidgetItem("Settings")); - ui->table->setHorizontalHeaderItem(3, new QTableWidgetItem("Statistics")); - ui->table->setHorizontalHeaderItem(4, new QTableWidgetItem("Timestamp")); - ui->table->setRowCount(measurements.size()); - for(unsigned int i=0;itable->setItem(i, 0, new QTableWidgetItem(CalibrationMeasurement::Base::TypeToString(measurements[i]->getType()))); - ui->table->setCellWidget(i, 1, measurements[i]->createStandardWidget()); - ui->table->setCellWidget(i, 2, measurements[i]->createSettingsWidget()); - ui->table->setItem(i, 3, new QTableWidgetItem(measurements[i]->getStatistics())); - ui->table->setItem(i, 4, new QTableWidgetItem(measurements[i]->getTimestamp().toString())); - } - ui->table->selectRow(row); - updateTableEditButtons(); - }; - - ui->createDefault->addItem(" "); - for(unsigned int i=0;i<(int) DefaultMeasurements::Last;i++) { - ui->createDefault->addItem(DefaultMeasurementsToString((DefaultMeasurements) i)); - } - - QObject::connect(ui->createDefault, qOverload(&QComboBox::currentIndexChanged), [=](){ - if(measurements.size() > 0) { - if(!InformationBox::AskQuestion("Create default entries?", "Do you want to remove all existing entries and create default calibration measurements instead?", true)) { - // user aborted - return; - } - measurements.clear(); - } - createDefaultMeasurements((DefaultMeasurements) (ui->createDefault->currentIndex() - 1)); - updateMeasurementTable(); - updateCalibrationList(); - ui->createDefault->blockSignals(true); - ui->createDefault->setCurrentIndex(0); - ui->createDefault->blockSignals(false); - }); - - QObject::connect(ui->bDelete, &QPushButton::clicked, [=](){ - auto selected = ui->table->selectionModel()->selectedRows(); - set toDelete; - for(auto s : selected) { - toDelete.insert(measurements[s.row()]); - } - while(toDelete.size() > 0) { - for(unsigned int i=0;ibMoveUp, &QPushButton::clicked, [=](){ - auto row = ui->table->currentRow(); - if(row >= 1) { - swap(measurements[row], measurements[row-1]); - ui->table->selectRow(row-1); - updateMeasurementTable(); - } - }); - - QObject::connect(ui->bMoveDown, &QPushButton::clicked, [=](){ - auto row = ui->table->currentRow(); - if(row >= 0) { - swap(measurements[row], measurements[row+1]); - ui->table->selectRow(row+1); - updateMeasurementTable(); - } - }); - - connect(ui->measure, &QPushButton::clicked, [=](){ - std::set m; - auto selected = ui->table->selectionModel()->selectedRows(); - for(auto s : selected) { - m.insert(measurements[s.row()]); - } - if(!CalibrationMeasurement::Base::canMeasureSimultaneously(m)) { - InformationBox::ShowError("Unable to measure", "Different selected measurements require the same port, unable to perform measurement"); - return; - } - emit startMeasurements(m); - }); - - connect(this, &Calibration2::measurementsUpdated, d, [=](){ - updateMeasurementTable(); - updateCalibrationList(); - }); - - connect(ui->clearMeasurement, &QPushButton::clicked, [=](){ - auto selected = ui->table->selectionModel()->selectedRows(); - for(auto s : selected) { - measurements[s.row()]->clearPoints(); - } - updateMeasurementTable(); - updateCalibrationList(); - }); - - QObject::connect(ui->table, &QTableWidget::currentCellChanged, updateTableEditButtons); - - auto addMenu = new QMenu(); - for(auto t : CalibrationMeasurement::Base::availableTypes()) { - auto action = new QAction(CalibrationMeasurement::Base::TypeToString(t)); - QObject::connect(action, &QAction::triggered, [=](){ - auto newMeas = newMeasurement(t); - if(newMeas) { - measurements.push_back(newMeas); - updateMeasurementTable(); - } - }); - addMenu->addAction(action); - } - - ui->bAdd->setMenu(addMenu); - - updateMeasurementTable(); - - d->show(); -} - -CalibrationMeasurement::Base *Calibration2::newMeasurement(CalibrationMeasurement::Base::Type type) -{ - CalibrationMeasurement::Base *m = nullptr; - switch(type) { - case CalibrationMeasurement::Base::Type::Open: m = new CalibrationMeasurement::Open(this); break; - case CalibrationMeasurement::Base::Type::Short: m = new CalibrationMeasurement::Short(this); break; - case CalibrationMeasurement::Base::Type::Load: m = new CalibrationMeasurement::Load(this); break; - case CalibrationMeasurement::Base::Type::Through: m = new CalibrationMeasurement::Through(this); break; - } - return m; -} - -Calibration2::Point Calibration2::computeSOLT(double f) -{ - Point point; - point.frequency = f; - // resize vectors - point.D.resize(caltype.usedPorts.size()); - point.R.resize(caltype.usedPorts.size()); - point.S.resize(caltype.usedPorts.size()); - - point.L.resize(caltype.usedPorts.size()); - point.T.resize(caltype.usedPorts.size()); - fill(point.L.begin(), point.L.end(), vector>(caltype.usedPorts.size())); - fill(point.T.begin(), point.T.end(), vector>(caltype.usedPorts.size())); - - // Calculate SOL coefficients - for(unsigned int i=0;i(findMeasurement(CalibrationMeasurement::Base::Type::Short, p)); - auto open = static_cast(findMeasurement(CalibrationMeasurement::Base::Type::Open, p)); - auto load = static_cast(findMeasurement(CalibrationMeasurement::Base::Type::Load, p)); - auto s_m = _short->getMeasured(f); - auto o_m = open->getMeasured(f); - auto l_m = load->getMeasured(f); - auto s_c = _short->getActual(f); - auto o_c = open->getActual(f); - auto l_c = load->getActual(f); - auto denom = l_c * o_c * (o_m - l_m) + l_c * s_c * (l_m - s_m) + o_c * s_c * (s_m - o_m); - point.D[i] = (l_c * o_m * (s_m * (o_c - s_c) + l_m * s_c) - l_c * o_c * l_m * s_m + o_c * l_m * s_c * (s_m - o_m)) / denom; - point.S[i] = (l_c * (o_m - s_m) + o_c * (s_m - l_m) + s_c * (l_m - o_m)) / denom; - auto delta = (l_c * l_m * (o_m - s_m) + o_c * o_m * (s_m - l_m) + s_c * s_m * (l_m - o_m)) / denom; - point.R[i] = point.D[i] * point.S[i] - delta; - } - // calculate forward match and transmission - for(unsigned int i=0;i(findMeasurement(CalibrationMeasurement::Base::Type::Through, p1, p2)); - auto S11 = through->getMeasured(f).m11; - auto S21 = through->getMeasured(f).m21; - auto Sideal = through->getActual(f); - auto deltaS = Sideal.m11*Sideal.m22 - Sideal.m21 * Sideal.m12; - auto isolation = complex(0.0, 0.0); - point.L[i][j] = ((S11 - point.D[i])*(1.0 - point.S[i] * Sideal.m11)-Sideal.m11*point.R[i]) - / ((S11 - point.D[i])*(Sideal.m22-point.S[i]*deltaS)-deltaS*point.R[i]); - point.T[i][j] = (S21 - isolation)*(1.0 - point.S[i]*Sideal.m11 - point.L[i][j]*Sideal.m22 + point.S[i]*point.L[i][j]*deltaS) / Sideal.m21; - } - } - return point; -} - -Calkit &Calibration2::getKit() -{ - return kit; -} - -nlohmann::json Calibration2::toJSON() -{ - nlohmann::json j; - nlohmann::json jmeasurements; - for(auto m : measurements) { - nlohmann::json jmeas; - jmeas["type"] = m->getType(); - jmeas["data"] = m->toJSON(); - jmeasurements.push_back(jmeas); - } - j["measurements"] = jmeasurements; - j["calkit"] = kit.toJSON(); - j["version"] = qlibrevnaApp->applicationVersion().toStdString(); - if(VirtualDevice::getConnected()) { - j["device"] = VirtualDevice::getConnected()->serial(); - } - return j; -} - -void Calibration2::fromJSON(nlohmann::json j) -{ - if(j.contains("calkit")) { - kit.fromJSON(j["calkit"]); - } - if(j.contains("measurements")) { - for(auto jm : j["measurements"]) { - auto type = CalibrationMeasurement::Base::TypeFromString(jm.value("type", "")); - auto m = newMeasurement(type); - m->fromJSON(jm["data"]); - measurements.push_back(m); - } - } -} - -std::vector Calibration2::getTypes() -{ - vector types; - // Start at index 1, skip Type::None - for(int i=1;i<(int) Type::Last;i++) { - types.push_back((Type) i); - } - return types; -} - -bool Calibration2::canCompute(Calibration2::CalType type, double *startFreq, double *stopFreq, int *points) -{ - switch(type.type) { - case Type::SOLT: { - using RequiredMeasurements = struct { - CalibrationMeasurement::Base::Type type; - int port1, port2; - }; - vector required; - // SOL measurements for every port - for(auto p : type.usedPorts) { - required.push_back({.type = CalibrationMeasurement::Base::Type::Short, .port1 = p}); - required.push_back({.type = CalibrationMeasurement::Base::Type::Open, .port1 = p}); - required.push_back({.type = CalibrationMeasurement::Base::Type::Load, .port1 = p}); - } - // through measurements between all ports - for(int i=1;i<=type.usedPorts.size();i++) { - for(int j=i+1;j<=type.usedPorts.size();j++) { - required.push_back({.type = CalibrationMeasurement::Base::Type::Through, .port1 = i, .port2 = j}); - } - } - vector foundMeasurements; - for(auto m : required) { - auto meas = findMeasurement(m.type, m.port1, m.port2); - if(!meas) { - // missing measurement - return false; - } else { - foundMeasurements.push_back(meas); - } - } - return hasFrequencyOverlap(foundMeasurements, startFreq, stopFreq, points); - } - break; - } -return false; -} - -bool Calibration2::compute(Calibration2::CalType type) -{ - double start, stop; - int numPoints; - if(!canCompute(type, &start, &stop, &numPoints)) { - return false; - } - caltype = type; - try { - points.clear(); - for(int i=0;i m, const VirtualDevice::VNAMeasurement &data) -{ - for(auto meas : m) { - meas->addPoint(data); - } -} - -void Calibration2::clearMeasurements(std::set m) -{ - for(auto meas : m) { - meas->clearPoints(); - } -} - -void Calibration2::measurementsComplete() -{ - emit measurementsUpdated(); -} - -QString Calibration2::DefaultMeasurementsToString(Calibration2::DefaultMeasurements dm) -{ - switch(dm) { - case DefaultMeasurements::SOL1Port: return "1 Port SOL"; - case DefaultMeasurements::SOLT2Port: return "2 Port SOLT"; - case DefaultMeasurements::SOLT3Port: return "3 Port SOLT"; - case DefaultMeasurements::SOLT4Port: return "4 Port SOLT"; - } -} - -void Calibration2::createDefaultMeasurements(Calibration2::DefaultMeasurements dm) -{ - auto createSOL = [=](int port) { - auto _short = new CalibrationMeasurement::Short(this); - _short->setPort(port); - measurements.push_back(_short); - auto open = new CalibrationMeasurement::Open(this); - open->setPort(port); - measurements.push_back(open); - auto load = new CalibrationMeasurement::Load(this); - load->setPort(port); - measurements.push_back(load); - }; - auto createThrough = [=](int port1, int port2) { - auto through = new CalibrationMeasurement::Through(this); - through->setPort1(port1); - through->setPort2(port2); - measurements.push_back(through); - }; - switch(dm) { - case DefaultMeasurements::SOL1Port: - createSOL(1); - break; - case DefaultMeasurements::SOLT2Port: - createSOL(1); - createSOL(2); - createThrough(1, 2); - break; - case DefaultMeasurements::SOLT3Port: - createSOL(1); - createSOL(2); - createSOL(3); - createThrough(1, 2); - createThrough(1, 3); - createThrough(2, 3); - break; - case DefaultMeasurements::SOLT4Port: - createSOL(1); - createSOL(2); - createSOL(3); - createSOL(4); - createThrough(1, 2); - createThrough(1, 3); - createThrough(1, 4); - createThrough(2, 3); - createThrough(2, 4); - createThrough(3, 4); - break; - } -} - -bool Calibration2::hasFrequencyOverlap(std::vector m, double *startFreq, double *stopFreq, int *points) -{ - double minResolution = std::numeric_limits::max(); - double minFreq = 0; - double maxFreq = std::numeric_limits::max(); - for(auto meas : m) { - if(meas->numPoints() < 2) { - return false; - } - auto resolution = (meas->maxFreq() - meas->minFreq()) / (meas->numPoints() - 1); - if(meas->maxFreq() < maxFreq) { - maxFreq = meas->maxFreq(); - } - if(meas->minFreq() > minFreq) { - minFreq = meas->minFreq(); - } - if(resolution < minResolution) { - minResolution = resolution; - } - } - if(startFreq) { - *startFreq = minFreq; - } - if(stopFreq) { - *stopFreq = maxFreq; - } - if(points) { - *points = (maxFreq - minFreq) / minResolution + 1; - } - if(maxFreq > minFreq) { - return true; - } else { - return false; - } -} - -CalibrationMeasurement::Base *Calibration2::findMeasurement(CalibrationMeasurement::Base::Type type, int port1, int port2) -{ - for(auto m : measurements) { - if(m->getType() != type) { - continue; - } - auto onePort = dynamic_cast(m); - if(onePort) { - if(onePort->getPort() != port1) { - continue; - } - } - auto twoPort = dynamic_cast(m); - if(twoPort) { - if(twoPort->getPort1() != port1 || twoPort->getPort2() != port2) { - continue; - } - } - // if we get here, we have a match - return m; - } - return nullptr; -} - -QString Calibration2::CalType::getDescription() -{ - switch(type) { - case Type::SOLT: - if(usedPorts.size() == 1) { - return "SOL, Port: "+QString::number(usedPorts[0]); - } else { - QString ret = "SOLT, Ports: ["; - for(auto p : usedPorts) { - ret += QString::number(p)+","; - } - // remove the last trailing comma - ret.chop(1); - ret += "]"; - return ret; - } - } -} - -Calibration2::Point Calibration2::Point::interpolate(const Calibration2::Point &to, double alpha) -{ - Point ret; - ret.frequency = frequency * (1.0-alpha) + to.frequency * alpha; - ret.D.resize(D.size()); - for(unsigned int i=0;i usedPorts; - QString getDescription(); - }; - - void correctMeasurement(VirtualDevice::VNAMeasurement &d); - - void edit(); - - Calkit& getKit(); - - virtual nlohmann::json toJSON() override; - virtual void fromJSON(nlohmann::json j) override; - - static std::vector getTypes(); - bool canCompute(CalType type, double *startFreq = nullptr, double *stopFreq = nullptr, int *points = nullptr); - bool compute(CalType type); - static int minimumPorts(Type type); - - void deleteMeasurements(); - void addMeasurements(std::set m, const VirtualDevice::VNAMeasurement &data); - void clearMeasurements(std::set m); -public slots: - void measurementsComplete(); -signals: - void startMeasurements(std::set m); - void measurementsUpdated(); -private: - enum class DefaultMeasurements { - SOL1Port, - SOLT2Port, - SOLT3Port, - SOLT4Port, - Last - }; - static QString DefaultMeasurementsToString(DefaultMeasurements dm); - void createDefaultMeasurements(DefaultMeasurements dm); - - bool hasFrequencyOverlap(std::vector m, double *startFreq = nullptr, double *stopFreq = nullptr, int *points = nullptr); - CalibrationMeasurement::Base* findMeasurement(CalibrationMeasurement::Base::Type type, int port1 = 0, int port2 = 0); - - CalibrationMeasurement::Base *newMeasurement(CalibrationMeasurement::Base::Type type); - - class Point { - public: - double frequency; - std::vector> D, R, S; - std::vector>> L, T; - Point interpolate(const Point &to, double alpha); - }; - std::vector points; - - Point computeSOLT(double f); - - std::vector measurements; - - Calkit kit; - CalType caltype; -}; - -#endif // CALIBRATION2_H diff --git a/Software/PC_Application/Calibration/calibrationmeasurement.cpp b/Software/PC_Application/Calibration/calibrationmeasurement.cpp index 1d910fb..17bed2c 100644 --- a/Software/PC_Application/Calibration/calibrationmeasurement.cpp +++ b/Software/PC_Application/Calibration/calibrationmeasurement.cpp @@ -1,6 +1,6 @@ #include "calibrationmeasurement.h" #include "unit.h" -#include "calibration2.h" +#include "calibration.h" #include #include @@ -9,7 +9,7 @@ using namespace std; -CalibrationMeasurement::Base::Base(Calibration2 *cal) +CalibrationMeasurement::Base::Base(Calibration *cal) : cal(cal) { standard = nullptr; @@ -86,6 +86,7 @@ QString CalibrationMeasurement::Base::TypeToString(CalibrationMeasurement::Base: case Type::Short: return "Short"; case Type::Load: return "Load"; case Type::Through: return "Through"; + case Type::Isolation: return "Isolation"; case Type::Last: return "Invalid"; } } @@ -137,7 +138,15 @@ nlohmann::json CalibrationMeasurement::Base::toJSON() void CalibrationMeasurement::Base::fromJSON(nlohmann::json j) { if(j.contains("standard")) { - // TODO find standard from ID + auto standards = cal->getKit().getStandards(); + standard = nullptr; + unsigned long long id = j.value("standard", 0ULL); + for(auto s : standards) { + if(s->getID() == id) { + setStandard(s); + break; + } + } } timestamp = QDateTime::fromSecsSinceEpoch(j.value("timestamp", 0)); } @@ -441,7 +450,7 @@ void CalibrationMeasurement::TwoPort::fromJSON(nlohmann::json j) for(auto jpoint : j["points"]) { Point p; p.frequency = jpoint.value("frequency", 0.0); - p.S.fromJSON(j["Sparam"]); + p.S.fromJSON(jpoint["Sparam"]); points.push_back(p); } } @@ -498,3 +507,128 @@ int CalibrationMeasurement::TwoPort::getPort1() const { return port1; } + +double CalibrationMeasurement::Isolation::minFreq() +{ + if(points.size() > 0) { + return points.front().frequency; + } else { + return numeric_limits::max(); + } +} + +double CalibrationMeasurement::Isolation::maxFreq() +{ + if(points.size() > 0) { + return points.back().frequency; + } else { + return 0; + } +} + +unsigned int CalibrationMeasurement::Isolation::numPoints() +{ + return points.size(); +} + +void CalibrationMeasurement::Isolation::clearPoints() +{ + points.clear(); + timestamp = QDateTime(); +} + +void CalibrationMeasurement::Isolation::addPoint(const VirtualDevice::VNAMeasurement &m) +{ + Point p; + p.frequency = m.frequency; + for(auto &meas : m.measurements) { + QString name = meas.first; + unsigned int rcv = name.mid(1, 1).toInt() - 1; + unsigned int src = name.mid(2, 1).toInt() - 1; + if(rcv >= p.S.size()) { + p.S.resize(rcv + 1); + } + if(src >= p.S[rcv].size()) { + p.S[rcv].resize(src + 1); + } + p.S[rcv][src] = meas.second; + } + points.push_back(p); + timestamp = QDateTime::currentDateTimeUtc(); +} + +QWidget *CalibrationMeasurement::Isolation::createStandardWidget() +{ + return new QLabel("Terminate all ports"); +} + +QWidget *CalibrationMeasurement::Isolation::createSettingsWidget() +{ + return new QLabel("No settings available"); +} + +nlohmann::json CalibrationMeasurement::Isolation::toJSON() +{ + auto j = Base::toJSON(); + nlohmann::json jpoints; + for(auto &p : points) { + nlohmann::json jpoint; + jpoint["frequency"] = p.frequency; + nlohmann::json jdest; + for(auto dst : p.S) { + nlohmann::json jsrc; + for(auto src : dst) { + nlohmann::json jiso; + jiso["real"] = src.real(); + jiso["imag"] = src.imag(); + jsrc.push_back(jiso); + } + jdest.push_back(jsrc); + } + jpoint["S"] = jdest; + jpoints.push_back(jpoint); + } + j["points"] = jpoints; + return j; +} + +void CalibrationMeasurement::Isolation::fromJSON(nlohmann::json j) +{ + clearPoints(); + Base::fromJSON(j); + if(j.contains("points")) { + for(auto jpoint : j["points"]) { + Point p; + p.frequency = jpoint.value("frequency", 0.0); + if(jpoint.contains("S")) { + for(auto jdest : jpoint["S"]) { + p.S.push_back(vector>()); + for(auto jsrc : jdest) { + auto S = complex(jsrc.value("real", 0.0), jsrc.value("imag", 0.0)); + p.S.back().push_back(S); + } + } + } + points.push_back(p); + } + } +} + +std::complex CalibrationMeasurement::Isolation::getMeasured(double frequency, unsigned int portRcv, unsigned int portSrc) +{ + if(points.size() == 0 || frequency < points.front().frequency || frequency > points.back().frequency) { + return numeric_limits>::quiet_NaN(); + } + portRcv--; + portSrc--; + // find correct point, no interpolation yet + auto lower = lower_bound(points.begin(), points.end(), frequency, [](const Point &lhs, double rhs) -> bool { + return lhs.frequency < rhs; + }); + Point p = *lower; + if(portRcv >= p.S.size() || portSrc >= p.S[portRcv].size()) { + return numeric_limits>::quiet_NaN(); + } else { + return p.S[portRcv][portSrc]; + } +} diff --git a/Software/PC_Application/Calibration/calibrationmeasurement.h b/Software/PC_Application/Calibration/calibrationmeasurement.h index 42ba9a4..a488ff5 100644 --- a/Software/PC_Application/Calibration/calibrationmeasurement.h +++ b/Software/PC_Application/Calibration/calibrationmeasurement.h @@ -1,4 +1,4 @@ -#ifndef CALIBRATIONMEASUREMENT_H +#ifndef CALIBRATIONMEASUREMENT_H #define CALIBRATIONMEASUREMENT_H #include "calstandard.h" @@ -7,7 +7,7 @@ #include #include -class Calibration2; +class Calibration; namespace CalibrationMeasurement { @@ -15,19 +15,20 @@ class Base : public QObject, public Savable { Q_OBJECT public: - Base(Calibration2 *cal); + Base(Calibration *cal); enum class Type { Open, Short, Load, Through, + Isolation, Last, }; std::vector supportedStandards(); - bool setFirstSupportedStandard(); - bool setStandard(CalStandard::Virtual *standard); + virtual bool setFirstSupportedStandard(); + virtual bool setStandard(CalStandard::Virtual *standard); QString getStatistics(); @@ -59,14 +60,14 @@ signals: protected: CalStandard::Virtual *standard; QDateTime timestamp; - Calibration2 *cal; + Calibration *cal; }; class OnePort : public Base { Q_OBJECT public: - OnePort(Calibration2 *cal) : + OnePort(Calibration *cal) : Base(cal), port(0) {} @@ -106,7 +107,7 @@ class Open : public OnePort { Q_OBJECT public: - Open(Calibration2 *cal) : + Open(Calibration *cal) : OnePort(cal){setFirstSupportedStandard();} virtual std::set supportedStandardTypes() override {return {CalStandard::Virtual::Type::Open};} @@ -117,7 +118,7 @@ class Short : public OnePort { Q_OBJECT public: - Short(Calibration2 *cal) : + Short(Calibration *cal) : OnePort(cal){setFirstSupportedStandard();} virtual std::set supportedStandardTypes() override {return {CalStandard::Virtual::Type::Short};} virtual Type getType() override {return Type::Short;} @@ -127,7 +128,7 @@ class Load : public OnePort { Q_OBJECT public: - Load(Calibration2 *cal) : + Load(Calibration *cal) : OnePort(cal){setFirstSupportedStandard();} virtual std::set supportedStandardTypes() override {return {CalStandard::Virtual::Type::Load};} virtual Type getType() override {return Type::Load;} @@ -137,7 +138,7 @@ class TwoPort : public Base { Q_OBJECT public: - TwoPort(Calibration2 *cal) : + TwoPort(Calibration *cal) : Base(cal), port1(0), port2(0){} @@ -181,11 +182,44 @@ class Through : public TwoPort { Q_OBJECT public: - Through(Calibration2 *cal) : + Through(Calibration *cal) : TwoPort(cal){setFirstSupportedStandard();} virtual std::set supportedStandardTypes() override {return {CalStandard::Virtual::Type::Through};} virtual Type getType() override {return Type::Through;} }; +class Isolation : public Base +{ +public: + Isolation(Calibration *cal) : + Base(cal){} + + virtual double minFreq() override; + virtual double maxFreq() override; + virtual unsigned int numPoints() override; + + virtual void clearPoints(); + virtual void addPoint(const VirtualDevice::VNAMeasurement &m); + + virtual QWidget* createStandardWidget() override; + virtual QWidget* createSettingsWidget() override; + + virtual nlohmann::json toJSON() override; + virtual void fromJSON(nlohmann::json j) override; + + std::complex getMeasured(double frequency, unsigned int portRcv, unsigned int portSrc); + + virtual std::set supportedStandardTypes() override {return {};} + virtual Type getType() override {return Type::Isolation;} + +protected: + class Point { + public: + double frequency; + std::vector>> S; + }; + std::vector points; +}; + } #endif // CALIBRATIONMEASUREMENT_H diff --git a/Software/PC_Application/Calibration/calibrationtracedialog.cpp b/Software/PC_Application/Calibration/calibrationtracedialog.cpp deleted file mode 100644 index 6aefba5..0000000 --- a/Software/PC_Application/Calibration/calibrationtracedialog.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#include "calibrationtracedialog.h" - -#include "ui_calibrationtracedialog.h" -#include "measurementmodel.h" -#include "CustomWidgets/informationbox.h" - -#include - -CalibrationTraceDialog::CalibrationTraceDialog(Calibration *cal, double f_min, double f_max, Calibration::Type type) : - QDialog(nullptr), - ui(new Ui::CalibrationTraceDialog), - cal(cal), - requestedType(type) -{ - ui->setupUi(this); - ui->bApply->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton)); - measurements = cal->Measurements(type); - if(requestedType == Calibration::Type::None) { - ui->bApply->setVisible(false); - } - model = new MeasurementModel(cal, measurements); - ui->tableView->setModel(model); - ui->tableView->setColumnWidth(0, 100); - ui->tableView->setColumnWidth(1, 80); - ui->tableView->setColumnWidth(2, 350); - ui->tableView->setColumnWidth(3, 320); - ui->tableView->setColumnWidth(4, 160); - UpdateCalibrationStatus(); - - auto updateThroughStandardUI = [=](){ - if(cal->getPortStandard(1) == cal->getPortStandard(2)) { - // same gender on both ports, can't use zero length through - ui->throughCalkit->click(); - ui->throughZero->setEnabled(false); - ui->throughCalkit->setEnabled(false); - } else { - // user may select option for through - ui->throughZero->setEnabled(true); - ui->throughCalkit->setEnabled(true); - } - model->genderUpdated(); - }; - - connect(ui->port1Group, qOverload(&QButtonGroup::buttonClicked), [=](){ - if(ui->port1Male->isChecked()) { - cal->setPortStandard(1, Calibration::PortStandard::Male); - } else { - cal->setPortStandard(1, Calibration::PortStandard::Female); - } - updateThroughStandardUI(); - UpdateCalibrationStatus(); - }); - - connect(ui->port2Group, qOverload(&QButtonGroup::buttonClicked), [=](){ - if(ui->port2Male->isChecked()) { - cal->setPortStandard(2, Calibration::PortStandard::Male); - } else { - cal->setPortStandard(2, Calibration::PortStandard::Female); - } - updateThroughStandardUI(); - UpdateCalibrationStatus(); - }); - - connect(ui->throughGroup, qOverload(&QButtonGroup::buttonClicked), [=](){ - if(ui->throughZero->isChecked()) { - cal->setThroughZeroLength(true); - } else { - cal->setThroughZeroLength(false); - } - UpdateCalibrationStatus(); - }); - - // hide selector if calkit does not have separate male/female standards - if(!cal->getCalibrationKit().hasSeparateMaleFemaleStandards()) { - ui->port1Standards->hide(); - ui->port2Standards->hide(); - ui->throughStandard->hide(); - ui->tableView->hideColumn((int) MeasurementModel::ColIndex::Gender); - // default selection is male - ui->port1Male->click(); - ui->port2Male->click(); - ui->throughCalkit->click(); - } else { - // separate standards defined - if(cal->getPortStandard(1) == Calibration::PortStandard::Male) { - ui->port1Male->setChecked(true); - } else { - ui->port1Female->setChecked(true); - } - if(cal->getPortStandard(2) == Calibration::PortStandard::Male) { - ui->port2Male->setChecked(true); - } else { - ui->port2Female->setChecked(true); - } - if(cal->getThroughZeroLength()) { - ui->throughZero->setChecked(true); - } else { - ui->throughCalkit->setChecked(true); - } - updateThroughStandardUI(); - } - - // Check calibration kit span - if(type != Calibration::Type::None) { - auto kit = cal->getCalibrationKit(); - auto isTRL = type == Calibration::Type::TRL; - if(isTRL && (kit.minFreqTRL() > f_min || kit.maxFreqTRL() < f_max)) { - // TODO check SOLT frequency range depending on selected male/female kit - InformationBox::ShowMessage("Warning", "The calibration kit does not completely cover the currently selected span. " - "Applying a calibration will not be possible for any measurements taken with these settings."); - } - } -} - -CalibrationTraceDialog::~CalibrationTraceDialog() -{ - delete ui; -} - -void CalibrationTraceDialog::measurementsComplete(std::set m) -{ - for(auto t : m) { - measurementComplete(t); - } -} - -void CalibrationTraceDialog::measurementComplete(Calibration::Measurement m) -{ - model->measurementUpdated(m); - UpdateCalibrationStatus(); -} - -void CalibrationTraceDialog::UpdateCalibrationStatus() -{ - if(!cal->calculationPossible(cal->getType())) { - // some trace for the current calibration was deleted - cal->resetErrorTerms(); - emit calibrationInvalidated(); - } else { - // update error terms as a measurement might have changed - cal->constructErrorTerms(cal->getType()); - } - ui->bApply->setEnabled(cal->calculationPossible(requestedType)); -} - -void CalibrationTraceDialog::on_bDelete_clicked() -{ - auto selected = ui->tableView->selectionModel()->selectedRows(); - for(auto s : selected) { - cal->clearMeasurement(measurements[s.row()]); - model->measurementUpdated(measurements[s.row()]); - } - UpdateCalibrationStatus(); -} - -void CalibrationTraceDialog::on_bMeasure_clicked() -{ - std::set m; - auto selected = ui->tableView->selectionModel()->selectedRows(); - for(auto s : selected) { - m.insert(measurements[s.row()]); - } - - // check if incompatible measurements are selected - auto p1Standard = Calibration::Standard::Any; - auto p2Standard = Calibration::Standard::Any; - - bool okay = true; - for(auto type : m) { - auto p1Required = Calibration::getPort1Standard(type); - auto p2Required = Calibration::getPort2Standard(type); - if(p1Required != Calibration::Standard::Any) { - if(p1Standard == Calibration::Standard::Any) { - // first calibration measurement type that needs a specific standard - p1Standard = p1Required; - } else if(p1Required != p1Standard) { - // needs different standard than other measurement that has also been selected - okay = false; - break; - } - } - // same check for port 2 - if(p2Required != Calibration::Standard::Any) { - if(p2Standard == Calibration::Standard::Any) { - // first calibration measurement type that needs a specific standard - p2Standard = p2Required; - } else if(p2Required != p2Standard) { - // needs different standard than other measurement that has also been selected - okay = false; - break; - } - } - } - if(!okay) { - // these measurements should not be taken at once, get user confirmation before continuing - okay = InformationBox::AskQuestion("Confirm selection", "The selected calibration measurements require different standards. Are you sure you want to measure them at the same time?", false); - } - if(okay) { - emit triggerMeasurements(m); - } -} - -void CalibrationTraceDialog::on_bApply_clicked() -{ - emit applyCalibration(requestedType); - accept(); -} diff --git a/Software/PC_Application/Calibration/calibrationtracedialog.h b/Software/PC_Application/Calibration/calibrationtracedialog.h deleted file mode 100644 index 001346f..0000000 --- a/Software/PC_Application/Calibration/calibrationtracedialog.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef CALIBRATIONTRACEDIALOG_H -#define CALIBRATIONTRACEDIALOG_H - -#include "calibration.h" -#include "measurementmodel.h" -#include "Device/device.h" - -#include - -namespace Ui { -class CalibrationTraceDialog; -} - -class CalibrationTraceDialog : public QDialog -{ - Q_OBJECT - -public: - explicit CalibrationTraceDialog(Calibration *cal, double f_min, double f_max, Calibration::Type type = Calibration::Type::None); - ~CalibrationTraceDialog(); - -public slots: - void measurementsComplete(std::set m); - void measurementComplete(Calibration::Measurement m); -signals: - void triggerMeasurements(std::set m); - void applyCalibration(Calibration::Type type); - void calibrationInvalidated(); - -private slots: - void on_bDelete_clicked(); - void on_bMeasure_clicked(); - void on_bApply_clicked(); - -private: - void UpdateCalibrationStatus(); - Ui::CalibrationTraceDialog *ui; - Calibration *cal; - Calibration::Type requestedType; - std::vector measurements; - MeasurementModel *model; -}; - -#endif // CALIBRATIONTRACEDIALOG_H diff --git a/Software/PC_Application/Calibration/calibrationtracedialog.ui b/Software/PC_Application/Calibration/calibrationtracedialog.ui deleted file mode 100644 index 58dd531..0000000 --- a/Software/PC_Application/Calibration/calibrationtracedialog.ui +++ /dev/null @@ -1,207 +0,0 @@ - - - CalibrationTraceDialog - - - - 0 - 0 - 1079 - 578 - - - - Calibration Measurements - - - true - - - - - - - - Port 1 Standards - - - - - - Male - - - port1Group - - - - - - - Female - - - port1Group - - - - - - - - - - Port 2 Standards - - - - - - Male - - - port2Group - - - - - - - Female - - - port2Group - - - - - - - - - - Through Standard - - - - - - From calibration kit - - - throughGroup - - - - - - - Zero-length through - - - throughGroup - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - QAbstractItemView::SelectRows - - - true - - - false - - - true - - - false - - - false - - - - - - - - - Measure - - - - :/icons/play.png:/icons/play.png - - - - - - - Delete - - - - :/icons/trash.png:/icons/trash.png - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Apply Calibration - - - - :/icons/ok.png:/icons/ok.png - - - - - - - - - - - - - - - - - diff --git a/Software/PC_Application/Calibration/calkit.cpp b/Software/PC_Application/Calibration/calkit.cpp index fc67d55..07c8748 100644 --- a/Software/PC_Application/Calibration/calkit.cpp +++ b/Software/PC_Application/Calibration/calkit.cpp @@ -341,51 +341,6 @@ void Calkit::edit(std::function updateCal) } } -bool Calkit::hasSeparateMaleFemaleStandards() -{ - return true; // TODO delete function -} - -class Calkit::SOLT Calkit::toSOLT(double frequency, bool male_standards) -{ - return SOLT(); // TODO delete function -} - -class Calkit::TRL Calkit::toTRL(double) -{ - return TRL(); // TODO delete function -} - -double Calkit::minFreqTRL() -{ - return 0; // TODO delete function -} - -double Calkit::maxFreqTRL() -{ - return std::numeric_limits::max(); // TODO delete function -} - -double Calkit::minFreqSOLT(bool male_standards) -{ - return 0; // TODO delete function -} - -double Calkit::maxFreqSOLT(bool male_standards) -{ - return std::numeric_limits::max(); // TODO delete function -} - -bool Calkit::checkIfValid(double min_freq, double max_freq, bool isTRL, bool include_male, bool include_female) -{ - return true; // TODO delete function -} - -bool Calkit::isTRLReflectionShort() const -{ - return true; // TODO delete function -} - void Calkit::clearStandards() { for(auto s : standards) { @@ -416,6 +371,7 @@ nlohmann::json Calkit::toJSON() void Calkit::fromJSON(nlohmann::json j) { + clearStandards(); Savable::parseJSON(j, descr); for(auto js : j["standards"]) { if(!js.contains("type") || !js.contains("params")) { diff --git a/Software/PC_Application/Calibration/calkit.h b/Software/PC_Application/Calibration/calkit.h index e58ec63..976ce8c 100644 --- a/Software/PC_Application/Calibration/calkit.h +++ b/Software/PC_Application/Calibration/calkit.h @@ -42,15 +42,6 @@ public: void toFile(QString filename); static Calkit fromFile(QString filename); void edit(std::function updateCal = nullptr); - bool hasSeparateMaleFemaleStandards(); - SOLT toSOLT(double frequency, bool male_standards = true); - TRL toTRL(double frequency); - double minFreqTRL(); - double maxFreqTRL(); - double minFreqSOLT(bool male_standards = true); - double maxFreqSOLT(bool male_standards = true); - bool checkIfValid(double min_freq, double max_freq, bool isTRL, bool include_male, bool include_female); - bool isTRLReflectionShort() const; std::vector getStandards() const; diff --git a/Software/PC_Application/Calibration/manualcalibrationdialog.cpp b/Software/PC_Application/Calibration/manualcalibrationdialog.cpp index da5ff0f..1157318 100644 --- a/Software/PC_Application/Calibration/manualcalibrationdialog.cpp +++ b/Software/PC_Application/Calibration/manualcalibrationdialog.cpp @@ -13,7 +13,7 @@ ManualCalibrationDialog::ManualCalibrationDialog(const TraceModel &model, Calibr connect(traceSelector, &SparamTraceSelector::selectionValid, ui->buttonBox, &QDialogButtonBox::setEnabled); connect(ui->buttonBox, &QDialogButtonBox::accepted, [=]() { auto t = traceSelector->getTraces(); - cal->correctTraces(*t[0], *t[1], *t[2], *t[3]); +// cal->correctTraces(*t[0], *t[1], *t[2], *t[3]); // TODO accept(); }); } diff --git a/Software/PC_Application/Calibration/measurementmodel.cpp b/Software/PC_Application/Calibration/measurementmodel.cpp deleted file mode 100644 index 92f8f7c..0000000 --- a/Software/PC_Application/Calibration/measurementmodel.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "measurementmodel.h" - -#include "../unit.h" - -#include - -MeasurementModel::MeasurementModel(Calibration *cal, std::vector measurements) : - QAbstractTableModel(), - cal(cal), - measurements(measurements) -{ - -} - -int MeasurementModel::rowCount(const QModelIndex &) const -{ - return measurements.size(); -} - -int MeasurementModel::columnCount(const QModelIndex &) const -{ - return (int) ColIndex::Last; -} - -QVariant MeasurementModel::data(const QModelIndex &index, int role) const -{ - auto info = cal->getMeasurementInfo(measurements[index.row()]); - if(role == Qt::DisplayRole) { - switch((ColIndex) index.column()) { - case ColIndex::Name: - return info.name; - break; - case ColIndex::Gender: - switch(measurements[index.row()]) { - case Calibration::Measurement::Port1Load: - case Calibration::Measurement::Port1Open: - case Calibration::Measurement::Port1Short: - if(cal->getPortStandard(1) == Calibration::PortStandard::Male) { - return "Male"; - } else { - return "Female"; - } - break; - case Calibration::Measurement::Port2Load: - case Calibration::Measurement::Port2Open: - case Calibration::Measurement::Port2Short: - if(cal->getPortStandard(2) == Calibration::PortStandard::Male) { - return "Male"; - } else { - return "Female"; - } - break; - default: - return ""; - } - - break; - case ColIndex::Description: - return info.prerequisites; - break; - case ColIndex::Data: - if(info.points > 0) { - QString data = QString::number(info.points); - data.append(" points from "); - data.append(Unit::ToString(info.fmin, "Hz", " kMG")); - data.append(" to "); - data.append(Unit::ToString(info.fmax, "Hz", " kMG")); - return data; - } else { - return "Not available"; - } - break; - case ColIndex::Date: - return info.timestamp.toString("dd.MM.yyyy hh:mm:ss"); - break; - case ColIndex::Last: - return ""; - } - } else if(role == Qt::SizeHintRole) { - switch((ColIndex) index.column()) { - case ColIndex::Name: return 200; break; - case ColIndex::Gender: return 150; break; - case ColIndex::Description: return 500; break; - case ColIndex::Data: return 300; break; - case ColIndex::Date: return 300; break; - default: return QVariant(); break; - } - } - - return QVariant(); -} - -QVariant MeasurementModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { - switch((ColIndex) section) { - case ColIndex::Name: return "Type"; break; - case ColIndex::Gender: return "Gender"; break; - case ColIndex::Description: return "Prerequisites"; break; - case ColIndex::Data: return "Statistics"; break; - case ColIndex::Date: return "Timestamp"; break; - default: return QVariant(); break; - } - } else { - return QVariant(); - } -} - -void MeasurementModel::measurementUpdated(Calibration::Measurement m) -{ - // find correct index in vector - auto it = std::find(measurements.begin(), measurements.end(), m); - if(it != measurements.end()) { - int row = it - measurements.begin(); - emit dataChanged(index(row, 0), index(row, (int) ColIndex::Last - 1)); - } -} - -void MeasurementModel::genderUpdated() -{ - emit dataChanged(index(0, (int) ColIndex::Gender), index(rowCount() - 1, (int) ColIndex::Gender)); -} diff --git a/Software/PC_Application/Calibration/measurementmodel.h b/Software/PC_Application/Calibration/measurementmodel.h deleted file mode 100644 index edbba05..0000000 --- a/Software/PC_Application/Calibration/measurementmodel.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef MEASUREMENTMODEL_H -#define MEASUREMENTMODEL_H - -#include "calibration.h" - -#include -#include -#include - -class MeasurementModel : public QAbstractTableModel -{ - Q_OBJECT -public: - enum class ColIndex { - Name, - Gender, - Description, - Data, - Date, - Last - }; - - MeasurementModel(Calibration *cal, std::vector measurements); - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - -public slots: - void measurementUpdated(Calibration::Measurement m); - void genderUpdated(); - -private: - Calibration *cal; - std::vector measurements; -}; - -#endif // MEASUREMENTMODEL_H diff --git a/Software/PC_Application/LibreVNA-GUI.pro b/Software/PC_Application/LibreVNA-GUI.pro index 6565f3d..87d493f 100644 --- a/Software/PC_Application/LibreVNA-GUI.pro +++ b/Software/PC_Application/LibreVNA-GUI.pro @@ -2,15 +2,12 @@ HEADERS += \ ../VNA_embedded/Application/Communication/Protocol.hpp \ Calibration/amplitudecaldialog.h \ Calibration/calibration.h \ - Calibration/calibration2.h \ Calibration/calibrationmeasurement.h \ - Calibration/calibrationtracedialog.h \ Calibration/calkit.h \ Calibration/calkitdialog.h \ Calibration/calstandard.h \ Calibration/frequencycaldialog.h \ Calibration/manualcalibrationdialog.h \ - Calibration/measurementmodel.h \ Calibration/receivercaldialog.h \ Calibration/sourcecaldialog.h \ CustomWidgets/colorpickerbutton.h \ @@ -144,15 +141,12 @@ SOURCES += \ ../VNA_embedded/Application/Communication/Protocol.cpp \ Calibration/amplitudecaldialog.cpp \ Calibration/calibration.cpp \ - Calibration/calibration2.cpp \ Calibration/calibrationmeasurement.cpp \ - Calibration/calibrationtracedialog.cpp \ Calibration/calkit.cpp \ Calibration/calkitdialog.cpp \ Calibration/calstandard.cpp \ Calibration/frequencycaldialog.cpp \ Calibration/manualcalibrationdialog.cpp \ - Calibration/measurementmodel.cpp \ Calibration/receivercaldialog.cpp \ Calibration/sourcecaldialog.cpp \ CustomWidgets/colorpickerbutton.cpp \ @@ -286,7 +280,6 @@ FORMS += \ Calibration/amplitudecaldialog.ui \ Calibration/automaticamplitudedialog.ui \ Calibration/calibrationdialogui.ui \ - Calibration/calibrationtracedialog.ui \ Calibration/calkitdialog.ui \ Calibration/frequencycaldialog.ui \ Calibration/manualcalibrationdialog.ui \ diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp index 75a65cd..513bf09 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -12,7 +12,6 @@ #include "CustomWidgets/siunitedit.h" #include "Traces/Marker/markerwidget.h" #include "Tools/impedancematchdialog.h" -#include "Calibration/calibrationtracedialog.h" #include "ui_main.h" #include "Device/virtualdevice.h" #include "preferences.h" diff --git a/Software/PC_Application/VNA/Deembedding/portextension.cpp b/Software/PC_Application/VNA/Deembedding/portextension.cpp index 8c45140..edcf601 100644 --- a/Software/PC_Application/VNA/Deembedding/portextension.cpp +++ b/Software/PC_Application/VNA/Deembedding/portextension.cpp @@ -211,13 +211,14 @@ void PortExtension::measurementCompleted(std::vector calStandard; - auto standards = kit->toSOLT(p.frequency); - if(isOpen) { - calStandard = standards.Open; - } else { - calStandard = standards.Short; - } + complex calStandard = 1.0; + // TODO +// auto standards = kit->toSOLT(p.frequency); +// if(isOpen) { +// calStandard = standards.Open; +// } else { +// calStandard = standards.Short; +// } // remove effect of calibration standard reflection /= calStandard; } diff --git a/Software/PC_Application/VNA/tracewidgetvna.cpp b/Software/PC_Application/VNA/tracewidgetvna.cpp index 27e8e1f..34b08fc 100644 --- a/Software/PC_Application/VNA/tracewidgetvna.cpp +++ b/Software/PC_Application/VNA/tracewidgetvna.cpp @@ -83,7 +83,7 @@ void TraceWidgetVNA::importDialog() connect(i, &TraceImportDialog::importFinsished, [=](const std::vector &traces) { if(traces.size() == 4) { // all traces imported, can calculate calibration/de-embedding - bool calAvailable = cal.nPoints() > 0; + bool calAvailable = cal.getNumPoints() > 0; bool deembedAvailable = deembed.getOptions().size() > 0; if(calAvailable || deembedAvailable) { // check if user wants to apply either one to the imported traces @@ -107,7 +107,7 @@ void TraceWidgetVNA::importDialog() dialog->exec(); } if(applyCal) { - cal.correctTraces(*traces[0], *traces[1], *traces[2], *traces[3]); +// cal.correctTraces(*traces[0], *traces[1], *traces[2], *traces[3]); // TODO } if(applyDeembed) { deembed.Deembed(*traces[0], *traces[1], *traces[2], *traces[3]); diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index 4975019..456f420 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -12,7 +12,6 @@ #include "CustomWidgets/siunitedit.h" #include "Traces/Marker/markerwidget.h" #include "Tools/impedancematchdialog.h" -#include "Calibration/calibrationtracedialog.h" #include "ui_main.h" #include "Device/firmwareupdatedialog.h" #include "preferences.h" @@ -59,7 +58,6 @@ VNA::VNA(AppWindow *window, QString name) { averages = 1; singleSweep = false; - calValid = false; calMeasuring = false; calWaitFirst = false; calDialog.reset(); @@ -89,27 +87,23 @@ VNA::VNA(AppWindow *window, QString name) }); connect(saveCal, &QAction::triggered, [=](){ - if(cal.saveToFile()) { + if(cal.toFile()) { calEdited = false; UpdateStatusbar(); } }); - connect(&cal2, &Calibration2::startMeasurements, this, &VNA::StartCalibrationMeasurements); + connect(&cal, &Calibration::startMeasurements, this, &VNA::StartCalibrationMeasurements); - auto calDisable = calMenu->addAction("Disabled"); - calDisable->setCheckable(true); - calDisable->setChecked(true); - calMenu->addSeparator(); auto calData = calMenu->addAction("Calibration Measurements"); connect(calData, &QAction::triggered, [=](){ - cal2.edit(); + cal.edit(); // StartCalibrationDialog(); }); auto calEditKit = calMenu->addAction("Edit Calibration Kit"); connect(calEditKit, &QAction::triggered, [=](){ - cal2.getKit().edit(); + cal.getKit().edit(); // cal.getCalibrationKit().edit([=](){ // if(calValid) { // ApplyCalibration(cal.getType()); @@ -394,42 +388,42 @@ VNA::VNA(AppWindow *window, QString name) auto cbEnableCal = new QCheckBox; tb_cal->addWidget(cbEnableCal); auto cbType = new QComboBox(); - auto calMenuGroup = new QActionGroup(this); - calMenuGroup->addAction(calDisable); - for(auto type : Calibration::Types()) { - cbType->addItem(Calibration::TypeToString(type), (int) type); - auto menuAction = new QAction(Calibration::TypeToString(type), calMenu); - calMenuGroup->addAction(menuAction); - connect(menuAction, &QAction::triggered, [=](){ - ApplyCalibration(type); - }); - connect(this, &VNA::CalibrationApplied, [=](Calibration::Type applied){ - if(type == applied) { - menuAction->setChecked(true); - } - }); - menuAction->setCheckable(true); - calMenu->insertAction(calDisable, menuAction); - } + + auto updateCalComboBox = [=](){ + auto cals = cal.getAvailableCalibrations(); + cbType->blockSignals(true); + cbType->clear(); + for(auto c : cals) { + if(c.type == Calibration::Type::None) { + continue; + } + cbType->addItem(c.getShortString()); + } + cbType->setCurrentText(cal.getCaltype().getShortString()); + cbType->blockSignals(false); + }; + + connect(this, &VNA::deviceInitialized, updateCalComboBox); + + updateCalComboBox(); auto calToolbarLambda = [=]() { if(cbEnableCal->isChecked()) { // Get requested calibration type from combobox - ApplyCalibration((Calibration::Type) cbType->itemData(cbType->currentIndex()).toInt()); + ApplyCalibration(Calibration::CalType::fromShortString(cbType->currentText())); } else { DisableCalibration(); } }; // Calibration connections - connect(this, &VNA::CalibrationApplied, this, &VNA::UpdateStatusbar); - connect(this, &VNA::CalibrationDisabled, this, &VNA::UpdateStatusbar); + connect(&cal, &Calibration::activated, this, &VNA::UpdateStatusbar); + connect(&cal, &Calibration::deactivated, this, &VNA::UpdateStatusbar); connect(cbEnableCal, &QCheckBox::stateChanged, calToolbarLambda); connect(cbType, qOverload(&QComboBox::currentIndexChanged), calToolbarLambda); - connect(this, &VNA::CalibrationDisabled, [=](){ + connect(&cal, &Calibration::deactivated, [=](){ cbType->blockSignals(true); cbEnableCal->blockSignals(true); - calDisable->setChecked(true); cbEnableCal->setCheckState(Qt::CheckState::Unchecked); // visually indicate loss of calibration // cal. file unknown at this moment @@ -441,16 +435,10 @@ VNA::VNA(AppWindow *window, QString name) calApplyToTraces->setEnabled(false); saveCal->setEnabled(false); }); - connect(calDisable, &QAction::triggered, this, &VNA::DisableCalibration); - connect(this, &VNA::CalibrationApplied, [=](Calibration::Type applied){ + connect(&cal, &Calibration::activated, [=](Calibration::CalType applied){ cbType->blockSignals(true); cbEnableCal->blockSignals(true); - for(int i=0;icount();i++) { - if(cbType->itemData(i).toInt() == (int) applied) { - cbType->setCurrentIndex(i); - break; - } - } + cbType->setCurrentText(applied.getShortString()); cbEnableCal->setCheckState(Qt::CheckState::Checked); // restore default look of widget // on hover, show name of active cal. file @@ -506,11 +494,6 @@ VNA::VNA(AppWindow *window, QString name) toolbars.insert(tb_cal); -// auto tb_portExtension = portExtension.createToolbar(); -// window->addToolBar(tb_portExtension); -// toolbars.insert(tb_portExtension); - - markerModel = new MarkerModel(traceModel, this); auto tracesDock = new QDockWidget("Traces"); @@ -590,7 +573,7 @@ Calibration::InterpolationType VNA::getCalInterpolation() QString VNA::getCalStyle() { - Calibration::InterpolationType interpol = getCalInterpolation(); + auto interpol = getCalInterpolation(); QString style = ""; switch (interpol) { @@ -612,7 +595,7 @@ QString VNA::getCalStyle() QString VNA::getCalToolTip() { - Calibration::InterpolationType interpol = getCalInterpolation(); + auto interpol = getCalInterpolation(); QString txt = ""; switch (interpol) { @@ -657,9 +640,7 @@ void VNA::initializeDevice() auto filename = s.value(key).toString(); qDebug() << "Attempting to load default calibration file " << filename; if(QFile::exists(filename)) { - if(cal.openFromFile(filename)) { - ApplyCalibration(cal.getType()); -// portExtension.setCalkit(&cal.getCalibrationKit()); + if(cal.fromFile(filename)) { qDebug() << "Calibration successful from " << filename; } else { qDebug() << "Calibration not successfull from: " << filename; @@ -674,6 +655,7 @@ void VNA::initializeDevice() } // Configure initial state of device SettingsChanged(); + emit deviceInitialized(); } void VNA::deviceDisconnected() @@ -683,10 +665,10 @@ void VNA::deviceDisconnected() void VNA::shutdown() { - if(calEdited && calValid) { + if(calEdited && cal.getCaltype().type != Calibration::Type::None) { auto save = InformationBox::AskQuestion("Save calibration?", "The calibration contains data that has not been saved yet. Do you want to save it before exiting?", false); if(save) { - cal.saveToFile(); + cal.toFile(); } } } @@ -820,19 +802,18 @@ void VNA::NewDatapoint(VirtualDevice::VNAMeasurement m) // this is the last averaging sweep, use values for calibration if(!calWaitFirst || m_avg.pointNum == 0) { calWaitFirst = false; - cal2.addMeasurements(calMeasurements, m_avg); + cal.addMeasurements(calMeasurements, m_avg); if(m_avg.pointNum == settings.npoints - 1) { calMeasuring = false; - cal2.measurementsComplete(); + cal.measurementsComplete(); } } } int percentage = (((average.currentSweep() - 1) * 100) + (m_avg.pointNum + 1) * 100 / settings.npoints) / averages; calDialog.setValue(percentage); } -// if(calValid) { - cal2.correctMeasurement(m_avg); -// } + + cal.correctMeasurement(m_avg); if(deembedding_active) { deembedding.Deembed(m_avg); @@ -1170,39 +1151,30 @@ void VNA::ExcitationRequired() } } -void VNA::DisableCalibration(bool force) +void VNA::DisableCalibration() { - if(calValid || force) { - calValid = false; - cal.resetErrorTerms(); - emit CalibrationDisabled(); - } + cal.deactivate(); } -void VNA::ApplyCalibration(Calibration::Type type) +void VNA::ApplyCalibration(Calibration::CalType type) { - if(cal.calculationPossible(type)) { + if(cal.canCompute(type)) { try { - if(cal.constructErrorTerms(type)) { - calValid = true; - emit CalibrationApplied(type); - } else { - DisableCalibration(true); - } + cal.compute(type); } catch (runtime_error &e) { InformationBox::ShowError("Calibration failure", e.what()); - DisableCalibration(true); + DisableCalibration(); } } else { if(settings.sweepType == SweepType::Frequency) { // Not all required traces available InformationBox::ShowMessageBlocking("Missing calibration measurements", "Not all calibration measurements for this type of calibration have been taken. The calibration can be enabled after the missing measurements have been acquired."); - DisableCalibration(true); - StartCalibrationDialog(type); + DisableCalibration(); + cal.edit(); } else { // Not all required traces available InformationBox::ShowMessageBlocking("Missing calibration measurements", "Not all calibration measurements for this type of calibration have been taken. Please switch to frequency sweep to take these measurements."); - DisableCalibration(true); + DisableCalibration(); } } } @@ -1216,7 +1188,7 @@ void VNA::StartCalibrationMeasurements(std::set m StopSweep(); calMeasurements = m; // Delete any already captured data of this measurement - cal2.clearMeasurements(m); + cal.clearMeasurements(m); calWaitFirst = true; // show messagebox QString text = "Measuring "; @@ -1237,7 +1209,7 @@ void VNA::StartCalibrationMeasurements(std::set m connect(&calDialog, &QProgressDialog::canceled, [=]() { // the user aborted the calibration measurement calMeasuring = false; - cal2.clearMeasurements(calMeasurements); + cal.clearMeasurements(calMeasurements); }); // Trigger sweep to start from beginning SettingsChanged(true, [=](bool){ @@ -1430,25 +1402,24 @@ void VNA::SetupSCPI() if(params.size() != 1) { return SCPI::getResultName(SCPI::Result::Error); } else { - auto type = Calibration::TypeFromString(params[0].replace('_', ' ')); - if(type == Calibration::Type::Last) { - // failed to parse string - return SCPI::getResultName(SCPI::Result::Error); - } else if(type == Calibration::Type::None) { - DisableCalibration(); - } else { - // check if calibration can be activated - if(cal.calculationPossible(type)) { - ApplyCalibration(type); - } else { - return SCPI::getResultName(SCPI::Result::Error); + auto availableCals = cal.getAvailableCalibrations(); + for(auto caltype : availableCals) { + if(caltype.getShortString().compare(params[0], Qt::CaseInsensitive) == 0) { + // found a match + // check if calibration can be activated + if(cal.canCompute(caltype)) { + ApplyCalibration(caltype); + } else { + return SCPI::getResultName(SCPI::Result::Error); + } } } + // if we get here, the supplied parameter did not match any of the available calibrations + return SCPI::getResultName(SCPI::Result::Error); } return SCPI::getResultName(SCPI::Result::Empty); }, [=](QStringList) -> QString { - auto ret = Calibration::TypeToString(cal.getType()); - ret.replace(' ', '_'); + auto ret = cal.getCaltype().getShortString(); return ret; })); scpi_cal->add(new SCPICommand("MEASure", [=](QStringList params) -> QString { @@ -1456,15 +1427,16 @@ void VNA::SetupSCPI() // no measurement specified, still busy or invalid mode return SCPI::getResultName(SCPI::Result::Error); } else { - auto meas = Calibration::MeasurementFromString(params[0].replace('_', ' ')); - if(meas == Calibration::Measurement::Last) { - // failed to parse string - return SCPI::getResultName(SCPI::Result::Error); - } else { - std::set m; - m.insert(meas); -// StartCalibrationMeasurements(m); // TODO - } + // TODO +// auto meas = Calibration::MeasurementFromString(params[0].replace('_', ' ')); +// if(meas == Calibration::Measurement::Last) { +// // failed to parse string +// return SCPI::getResultName(SCPI::Result::Error); +// } else { +// std::set m; +// m.insert(meas); +//// StartCalibrationMeasurements(m); +// } } return SCPI::getResultName(SCPI::Result::Empty); }, nullptr)); @@ -1472,11 +1444,11 @@ void VNA::SetupSCPI() return CalibrationMeasurementActive() ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False); })); scpi_cal->add(new SCPICommand("SAVE", [=](QStringList params) -> QString { - if(params.size() != 1 || !calValid) { + if(params.size() != 1 || cal.getCaltype().type == Calibration::Type::None) { // no filename given or no calibration active return SCPI::getResultName(SCPI::Result::Error); } - if(!cal.saveToFile(params[0])) { + if(!cal.toFile(params[0])) { // some error when writing the calibration file return SCPI::getResultName(SCPI::Result::Error); } @@ -1488,15 +1460,10 @@ void VNA::SetupSCPI() // no filename given or no calibration active return SCPI::getResultName(SCPI::Result::False); } - if(!cal.openFromFile(params[0])) { + if(!cal.fromFile(params[0])) { // some error when loading the calibration file return SCPI::getResultName(SCPI::Result::False); } - if(cal.getType() == Calibration::Type::None) { - DisableCalibration(); - } else { - ApplyCalibration(cal.getType()); - } calEdited = false; return SCPI::getResultName(SCPI::Result::True); })); @@ -1577,20 +1544,6 @@ void VNA::StopSweep() } } -void VNA::StartCalibrationDialog(Calibration::Type type) -{ - auto traceDialog = new CalibrationTraceDialog(&cal, settings.Freq.start, settings.Freq.stop, type); -// connect(traceDialog, &CalibrationTraceDialog::triggerMeasurements, this, &VNA::StartCalibrationMeasurements); - connect(traceDialog, &CalibrationTraceDialog::applyCalibration, this, &VNA::ApplyCalibration); - connect(traceDialog, &CalibrationTraceDialog::calibrationInvalidated, [=](){ - DisableCalibration(true); - InformationBox::ShowMessageBlocking("Calibration disabled", "The currently active calibration is no longer supported by the available measurements and was disabled."); - }); - if(AppWindow::showGUI()) { - traceDialog->show(); - } -} - void VNA::UpdateCalWidget() { calLabel->setStyleSheet(getCalStyle()); @@ -1730,7 +1683,7 @@ VNA::SweepType VNA::SweepTypeFromString(QString s) void VNA::UpdateStatusbar() { - if(calValid) { + if(cal.getCaltype().type != Calibration::Type::None) { QFileInfo fi(cal.getCurrentCalibrationFile()); auto filename = fi.fileName(); if(filename.isEmpty()) { @@ -1753,13 +1706,6 @@ void VNA::SetSingleSweep(bool single) bool VNA::LoadCalibration(QString filename) { - cal.openFromFile(filename); + cal.fromFile(filename); calEdited = false; - if(cal.getType() == Calibration::Type::None) { - DisableCalibration(); - return false; - } else { - ApplyCalibration(cal.getType()); - return true; - } } diff --git a/Software/PC_Application/VNA/vna.h b/Software/PC_Application/VNA/vna.h index fe2e33e..44b2587 100644 --- a/Software/PC_Application/VNA/vna.h +++ b/Software/PC_Application/VNA/vna.h @@ -8,7 +8,7 @@ #include "Deembedding/deembedding.h" #include "scpi.h" #include "Traces/tracewidget.h" -#include "Calibration/calibration2.h" +#include "Calibration/calibration.h" #include #include @@ -105,8 +105,8 @@ private slots: void SetAveraging(unsigned int averages); void ExcitationRequired(); // Calibration - void DisableCalibration(bool force = false); - void ApplyCalibration(Calibration::Type type); + void DisableCalibration(); + void ApplyCalibration(Calibration::CalType type); void StartCalibrationMeasurements(std::set m); @@ -121,7 +121,6 @@ private: void LoadSweepSettings(); void StoreSweepSettings(); void StopSweep(); - void StartCalibrationDialog(Calibration::Type type = Calibration::Type::None); void UpdateCalWidget(); void createDefaultTracesAndGraphs(int ports); @@ -140,9 +139,8 @@ private: // Calibration Calibration cal; - Calibration2 cal2; bool changingSettings; - bool calValid; +// bool calValid; bool calEdited; std::set calMeasurements; bool calMeasuring; @@ -168,6 +166,7 @@ private: TileWidget *central; signals: + void deviceInitialized(); void dataChanged(); void sweepTypeChanged(SweepType sw); void startFreqChanged(double freq); @@ -185,9 +184,6 @@ signals: void startPowerChanged(double level); void stopPowerChanged(double level); void powerSweepFrequencyChanged(double freq); - - void CalibrationDisabled(); - void CalibrationApplied(Calibration::Type type); }; #endif // VNA_H diff --git a/Software/PC_Application/appwindow.cpp b/Software/PC_Application/appwindow.cpp index 5c2e7c2..1c62f1d 100644 --- a/Software/PC_Application/appwindow.cpp +++ b/Software/PC_Application/appwindow.cpp @@ -12,7 +12,6 @@ #include "CustomWidgets/siunitedit.h" #include "Traces/Marker/markerwidget.h" #include "Tools/impedancematchdialog.h" -#include "Calibration/calibrationtracedialog.h" #include "ui_main.h" #include "Device/firmwareupdatedialog.h" #include "preferences.h" @@ -55,6 +54,7 @@ #include #include #include +#include #include #include diff --git a/Software/PC_Application/appwindow.h b/Software/PC_Application/appwindow.h index 85e00b3..f3d03ed 100644 --- a/Software/PC_Application/appwindow.h +++ b/Software/PC_Application/appwindow.h @@ -3,7 +3,6 @@ #include "Device/virtualdevice.h" #include "Traces/traceplot.h" -#include "Calibration/calibration.h" #include "Traces/tracemodel.h" #include "Traces/Marker/markermodel.h" #include "averaging.h"