diff --git a/Software/PC_Application/Calibration/CalStandardLoadEditDialog.ui b/Software/PC_Application/Calibration/CalStandardLoadEditDialog.ui
new file mode 100644
index 0000000..b3fc7b9
--- /dev/null
+++ b/Software/PC_Application/Calibration/CalStandardLoadEditDialog.ui
@@ -0,0 +1,244 @@
+
+
+ CalStandardLoadEditDialog
+
+
+
+ 0
+ 0
+ 349
+ 355
+
+
+
+ Edit Load Standard
+
+
+ true
+
+
+ -
+
+
-
+
+
+ Name:
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ Coefficients
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Measurement file
+
+
+ buttonGroup
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+
-
+
+
-
+
+
+ Resistance:
+
+
+
+ -
+
+
+ -
+
+
+ Z0:
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ Offset delay [ps]:
+
+
+
+ -
+
+
+ -
+
+
+ Parallel C:
+
+
+
+ -
+
+
+ -
+
+
+ Series L:
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Load Parameter Model
+
+
+
-
+
+
+ Series L first
+
+
+ buttonGroup_2
+
+
+
+ -
+
+
+ Shunt C first
+
+
+ buttonGroup_2
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+ Update from file
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+ SIUnitEdit
+ QLineEdit
+ CustomWidgets/siunitedit.h
+
+
+ TouchstoneImport
+ QWidget
+ CustomWidgets/touchstoneimport.h
+ 1
+
+
+
+
+
+ buttonBox
+ accepted()
+ CalStandardLoadEditDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ CalStandardLoadEditDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
+
+
+
+
diff --git a/Software/PC_Application/Calibration/CalStandardOpenEditDialog.ui b/Software/PC_Application/Calibration/CalStandardOpenEditDialog.ui
new file mode 100644
index 0000000..a1dd1f9
--- /dev/null
+++ b/Software/PC_Application/Calibration/CalStandardOpenEditDialog.ui
@@ -0,0 +1,230 @@
+
+
+ CalStandardOpenEditDialog
+
+
+
+ 0
+ 0
+ 358
+ 342
+
+
+
+ Edit Open Standard
+
+
+ true
+
+
+ -
+
+
-
+
+
+ Name:
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ Coefficients
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Measurement file
+
+
+ buttonGroup
+
+
+
+
+
+ -
+
+
+ 1
+
+
+
+
-
+
+
+ Z0:
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ Offset delay [ps]:
+
+
+
+ -
+
+
+ -
+
+
+ Offset loss [GΩ/s]:
+
+
+
+ -
+
+
+ -
+
+
+ C0 [10<sup>-15</sup>F]:
+
+
+
+ -
+
+
+ -
+
+
+ C1 [10<sup>-27</sup>F/Hz]:
+
+
+
+ -
+
+
+ -
+
+
+ C2 [10<sup>-36</sup>F/Hz<sup>2</sup>]:
+
+
+
+ -
+
+
+ -
+
+
+ <html><head/><body><p>C3 [10<span style=" vertical-align:super;">-45</span>F/Hz<span style=" vertical-align:super;">3</span>]:</p></body></html>
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+ Update from file
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+ SIUnitEdit
+ QLineEdit
+ CustomWidgets/siunitedit.h
+
+
+ TouchstoneImport
+ QWidget
+ CustomWidgets/touchstoneimport.h
+ 1
+
+
+
+
+
+ buttonBox
+ accepted()
+ CalStandardOpenEditDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ CalStandardOpenEditDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
+
+
+
diff --git a/Software/PC_Application/Calibration/CalStandardShortEditDialog.ui b/Software/PC_Application/Calibration/CalStandardShortEditDialog.ui
new file mode 100644
index 0000000..594cb06
--- /dev/null
+++ b/Software/PC_Application/Calibration/CalStandardShortEditDialog.ui
@@ -0,0 +1,230 @@
+
+
+ CalStandardShortEditDialog
+
+
+
+ 0
+ 0
+ 358
+ 342
+
+
+
+ Edit Short Standard
+
+
+ true
+
+
+ -
+
+
-
+
+
+ Name:
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ Coefficients
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ Measurement file
+
+
+ buttonGroup
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+
-
+
+
+ Z0:
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ Offset delay [ps]:
+
+
+
+ -
+
+
+ -
+
+
+ Offset loss [GΩ/s]:
+
+
+
+ -
+
+
+ -
+
+
+ <html><head/><body><p>L0 [10<span style=" vertical-align:super;">-12</span>H]:</p></body></html>
+
+
+
+ -
+
+
+ -
+
+
+ <html><head/><body><p>L1 [10<span style=" vertical-align:super;">-24</span>H/Hz]:</p></body></html>
+
+
+
+ -
+
+
+ -
+
+
+ <html><head/><body><p>L2 [10<span style=" vertical-align:super;">-33</span>H/Hz<span style=" vertical-align:super;">2</span>]:</p></body></html>
+
+
+
+ -
+
+
+ -
+
+
+ <html><head/><body><p>L3 [10<span style=" vertical-align:super;">-42</span>H/Hz<span style=" vertical-align:super;">3</span>]:</p></body></html>
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+ Update from file
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+ SIUnitEdit
+ QLineEdit
+ CustomWidgets/siunitedit.h
+
+
+ TouchstoneImport
+ QWidget
+ CustomWidgets/touchstoneimport.h
+ 1
+
+
+
+
+
+ buttonBox
+ accepted()
+ CalStandardShortEditDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ CalStandardShortEditDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
+
+
+
diff --git a/Software/PC_Application/Calibration/calkitdialog.ui b/Software/PC_Application/Calibration/calkitdialog.ui
index dee0f6d..e010eab 100644
--- a/Software/PC_Application/Calibration/calkitdialog.ui
+++ b/Software/PC_Application/Calibration/calkitdialog.ui
@@ -10,7 +10,7 @@
0
0
1213
- 626
+ 637
@@ -1505,14 +1505,14 @@
+
-
+
+
-
-
+
-
diff --git a/Software/PC_Application/Calibration/calstandard.cpp b/Software/PC_Application/Calibration/calstandard.cpp
new file mode 100644
index 0000000..39ccf82
--- /dev/null
+++ b/Software/PC_Application/Calibration/calstandard.cpp
@@ -0,0 +1,540 @@
+#include "calstandard.h"
+#include "ui_CalStandardOpenEditDialog.h"
+#include "ui_CalStandardShortEditDialog.h"
+#include "ui_CalStandardLoadEditDialog.h"
+#include "unit.h"
+
+using namespace std;
+using namespace CalStandard;
+
+QString Virtual::TypeToString(Virtual::Type type)
+{
+ switch(type) {
+ case Type::Open: return "Open";
+ case Type::Short: return "Short";
+ case Type::Load: return "Load";
+ case Type::Through: return "Through";
+ case Type::Line: return "Line";
+ case Type::Last: return "Invalid";
+ }
+}
+
+Virtual::Type Virtual::TypeFromString(QString s)
+{
+ for(int i=0;i<(int) Type::Last;i++) {
+ if(TypeToString((Type) i) == s) {
+ return (Type) i;
+ }
+ }
+ return Type::Last;
+}
+
+QString Virtual::getDescription()
+{
+ return TypeToString(getType())+", "+name;
+}
+
+nlohmann::json Virtual::toJSON()
+{
+ nlohmann::json j;
+ j["name"] = name.toStdString();
+ return j;
+}
+
+void Virtual::fromJSON(nlohmann::json j)
+{
+ name = QString::fromStdString(j.value("name", ""));
+}
+
+void OnePort::setMeasurement(const Touchstone &ts, int port)
+{
+ if(!touchstone) {
+ touchstone = new Touchstone(ts);
+ } else {
+ *touchstone = ts;
+ }
+ if(touchstone->ports() > 1) {
+ touchstone->reduceTo1Port(port);
+ }
+ minFreq = touchstone->minFreq();
+ maxFreq = touchstone->maxFreq();
+}
+
+void OnePort::clearMeasurement()
+{
+ delete touchstone;
+ touchstone = nullptr;
+ minFreq = std::numeric_limits::lowest();
+ minFreq = std::numeric_limits::max();
+}
+
+nlohmann::json OnePort::toJSON()
+{
+ auto j = Virtual::toJSON();
+ if(touchstone) {
+ j["touchstone"] = touchstone->toJSON();
+ }
+ return j;
+}
+
+void OnePort::fromJSON(nlohmann::json j)
+{
+ Virtual::fromJSON(j);
+ if(j.contains("touchstone")) {
+ Touchstone ts(1);
+ ts.fromJSON(j["touchstone"]);
+ setMeasurement(ts);
+ }
+}
+
+std::complex OnePort::addTransmissionLine(std::complex termination_reflection, double offset_impedance, double offset_delay, double offset_loss, double frequency)
+{
+ // nomenclature and formulas from https://loco.lab.asu.edu/loco-memos/edges_reports/report_20130807.pdf
+ auto Gamma_T = termination_reflection;
+ auto f = frequency;
+ auto w = 2.0 * M_PI * frequency;
+ auto f_sqrt = sqrt(f / 1e9);
+
+ auto Z_c = complex(offset_impedance + (offset_loss / (2*w)) * f_sqrt, -(offset_loss / (2*w)) * f_sqrt);
+ auto gamma_l = complex(offset_loss*offset_delay/(2*offset_impedance)*f_sqrt, w*offset_delay+offset_loss*offset_delay/(2*offset_impedance)*f_sqrt);
+
+ auto Z_r = complex(50.0);
+
+ auto Gamma_1 = (Z_c - Z_r) / (Z_c + Z_r);
+
+ auto Gamma_i = (Gamma_1*(1.0-exp(-2.0*gamma_l)-Gamma_1*Gamma_T)+exp(-2.0*gamma_l)*Gamma_T)
+ / (1.0-Gamma_1*(exp(-2.0*gamma_l)*Gamma_1+Gamma_T*(1.0-exp(-2.0*gamma_l))));
+
+ return Gamma_i;
+}
+
+Open::Open()
+{
+ Z0 = 50.0;
+ delay = loss = C0 = C1 = C2 = C3 = 0.0;
+}
+
+std::complex Open::toS11(double freq)
+{
+ if(touchstone) {
+ return touchstone->interpolate(freq).S[0];
+ } else {
+ // calculate fringing capacitance for open
+ double Cfringing = C0 * 1e-15 + C1 * 1e-27 * freq + C2 * 1e-36 * pow(freq, 2) + C3 * 1e-45 * pow(freq, 3);
+ // convert to impedance
+ complex open;
+ if (Cfringing == 0) {
+ // special case to avoid issues with infinity
+ open = complex(1.0, 0);
+ } else {
+ auto imp_open = complex(0, -1.0 / (freq * 2 * M_PI * Cfringing));
+ open = (imp_open - complex(50.0)) / (imp_open + complex(50.0));
+ }
+ return addTransmissionLine(open, Z0, delay*1e-12, loss*1e9, freq);
+ }
+}
+
+void Open::edit()
+{
+ auto d = new QDialog;
+ auto ui = new Ui::CalStandardOpenEditDialog;
+ ui->setupUi(d);
+
+ ui->name->setText(name);
+ ui->Z0->setUnit("Ω");
+ ui->Z0->setPrecision(2);
+ ui->Z0->setValue(Z0);
+ ui->delay->setValue(delay);
+ ui->loss->setValue(loss);
+ ui->C0->setValue(C0);
+ ui->C1->setValue(C1);
+ ui->C2->setValue(C2);
+ ui->C3->setValue(C3);
+
+ auto updateMeasurementLabel = [=](){
+ QString label;
+ if(touchstone) {
+ label = QString::number(touchstone->points())+" points from "+Unit::ToString(touchstone->minFreq(), "Hz", " kMG")+" to "+Unit::ToString(touchstone->maxFreq(), "Hz", " kMG");
+ } else {
+ label = "No measurements stored yet";
+ }
+ ui->measurementLabel->setText(label);
+ };
+
+ QObject::connect(ui->coefficients, &QRadioButton::toggled, [=](bool checked) {
+ if(checked) {
+ clearMeasurement();
+ }
+ ui->stackedWidget->setCurrentIndex(checked ? 0 : 1);
+ });
+ QObject::connect(ui->measurement, &QRadioButton::toggled, [=](bool checked) {
+ updateMeasurementLabel();
+ ui->stackedWidget->setCurrentIndex(checked ? 1 : 0);
+ });
+
+ QObject::connect(ui->touchstoneImport, &TouchstoneImport::statusChanged, ui->updateFile, &QPushButton::setEnabled);
+
+ ui->touchstoneImport->setPorts(1);
+ if(touchstone) {
+ ui->measurement->setChecked(true);
+ ui->touchstoneImport->setFile(touchstone->getFilename());
+ } else {
+ ui->coefficients->setChecked(true);
+ }
+
+ QObject::connect(ui->updateFile, &QPushButton::clicked, [=](){
+ setMeasurement(ui->touchstoneImport->getTouchstone(), ui->touchstoneImport->getPorts()[0]);
+ updateMeasurementLabel();
+ });
+
+ QObject::connect(d, &QDialog::accepted, [=](){
+ name = ui->name->text();
+ Z0 = ui->Z0->value();
+ delay = ui->delay->value();
+ loss = ui->loss->value();
+ C0 = ui->C0->value();
+ C1 = ui->C1->value();
+ C2 = ui->C2->value();
+ C3 = ui->C3->value();
+ });
+
+ d->show();
+}
+
+nlohmann::json Open::toJSON()
+{
+ auto j = OnePort::toJSON();
+ j["Z0"] = Z0;
+ j["delay"] = delay;
+ j["loss"] = loss;
+ j["C0"] = C0;
+ j["C1"] = C1;
+ j["C2"] = C2;
+ j["C3"] = C3;
+ return j;
+}
+
+void Open::fromJSON(nlohmann::json j)
+{
+ OnePort::fromJSON(j);
+ Z0 = j.value("Z0", 50.0);
+ delay = j.value("delay", 0.0);
+ loss = j.value("loss", 0.0);
+ C0 = j.value("C0", 0.0);
+ C1 = j.value("C1", 0.0);
+ C2 = j.value("C2", 0.0);
+ C3 = j.value("C3", 0.0);
+}
+
+Short::Short()
+{
+ Z0 = 50.0;
+ delay = loss = L0 = L1 = L2 = L3 = 0.0;
+}
+
+std::complex Short::toS11(double freq)
+{
+ if(touchstone) {
+ return touchstone->interpolate(freq).S[0];
+ } else {
+ // calculate inductance for short
+ double Lseries = L0 * 1e-12 + L1 * 1e-24 * freq + L2 * 1e-33 * pow(freq, 2) + L3 * 1e-42 * pow(freq, 3);
+ // convert to impedance
+ auto imp_short = complex(0, freq * 2 * M_PI * Lseries);
+ complex _short = (imp_short - complex(50.0)) / (imp_short + complex(50.0));
+ return addTransmissionLine(_short, Z0, delay*1e-12, loss*1e9, freq);
+ }
+}
+
+void Short::edit()
+{
+ auto d = new QDialog;
+ auto ui = new Ui::CalStandardShortEditDialog;
+ ui->setupUi(d);
+
+ ui->name->setText(name);
+ ui->Z0->setUnit("Ω");
+ ui->Z0->setPrecision(2);
+ ui->Z0->setValue(Z0);
+ ui->delay->setValue(delay);
+ ui->loss->setValue(loss);
+ ui->L0->setValue(L0);
+ ui->L1->setValue(L1);
+ ui->L2->setValue(L2);
+ ui->L3->setValue(L3);
+
+ auto updateMeasurementLabel = [=](){
+ QString label;
+ if(touchstone) {
+ label = QString::number(touchstone->points())+" points from "+Unit::ToString(touchstone->minFreq(), "Hz", " kMG")+" to "+Unit::ToString(touchstone->maxFreq(), "Hz", " kMG");
+ } else {
+ label = "No measurements stored yet";
+ }
+ ui->measurementLabel->setText(label);
+ };
+
+ QObject::connect(ui->coefficients, &QRadioButton::toggled, [=](bool checked) {
+ if(checked) {
+ clearMeasurement();
+ }
+ ui->stackedWidget->setCurrentIndex(checked ? 0 : 1);
+ });
+ QObject::connect(ui->measurement, &QRadioButton::toggled, [=](bool checked) {
+ updateMeasurementLabel();
+ ui->stackedWidget->setCurrentIndex(checked ? 1 : 0);
+ });
+
+ QObject::connect(ui->touchstoneImport, &TouchstoneImport::statusChanged, ui->updateFile, &QPushButton::setEnabled);
+
+ ui->touchstoneImport->setPorts(1);
+ if(touchstone) {
+ ui->measurement->setChecked(true);
+ ui->touchstoneImport->setFile(touchstone->getFilename());
+ } else {
+ ui->coefficients->setChecked(true);
+ }
+
+ QObject::connect(ui->updateFile, &QPushButton::clicked, [=](){
+ setMeasurement(ui->touchstoneImport->getTouchstone(), ui->touchstoneImport->getPorts()[0]);
+ updateMeasurementLabel();
+ });
+
+ QObject::connect(d, &QDialog::accepted, [=](){
+ name = ui->name->text();
+ Z0 = ui->Z0->value();
+ delay = ui->delay->value();
+ loss = ui->loss->value();
+ L0 = ui->L0->value();
+ L1 = ui->L1->value();
+ L2 = ui->L2->value();
+ L3 = ui->L3->value();
+ });
+
+ d->show();
+}
+
+nlohmann::json Short::toJSON()
+{
+ auto j = OnePort::toJSON();
+ j["Z0"] = Z0;
+ j["delay"] = delay;
+ j["loss"] = loss;
+ j["L0"] = L0;
+ j["L1"] = L1;
+ j["L2"] = L2;
+ j["L3"] = L3;
+ return j;
+}
+
+void Short::fromJSON(nlohmann::json j)
+{
+ OnePort::fromJSON(j);
+ Z0 = j.value("Z0", 50.0);
+ delay = j.value("delay", 0.0);
+ loss = j.value("loss", 0.0);
+ L0 = j.value("L0", 0.0);
+ L1 = j.value("L1", 0.0);
+ L2 = j.value("L2", 0.0);
+ L3 = j.value("L3", 0.0);
+}
+
+Load::Load()
+{
+ Z0 = 50.0;
+ resistance = 50.0;
+ delay = Cparallel = Lseries = 0;
+ Cfirst = true;
+}
+
+std::complex Load::toS11(double freq)
+{
+ if(touchstone) {
+ return touchstone->interpolate(freq).S[0];
+ } else {
+ auto imp_load = complex(resistance, 0);
+ if (Cfirst) {
+ // C is the first parameter starting from the VNA port. But the load is modeled here starting from
+ // the other end, so we need to start with the inductor
+ imp_load += complex(0, freq * 2 * M_PI * Lseries);
+ }
+ // Add parallel capacitor to impedance
+ if(Cparallel > 0) {
+ auto imp_C = complex(0, -1.0 / (freq * 2 * M_PI * Cparallel));
+ imp_load = (imp_load * imp_C) / (imp_load + imp_C);
+ }
+ if (!Cfirst) {
+ // inductor not added yet, do so now
+ imp_load += complex(0, freq * 2 * M_PI * Lseries);
+ }
+ complex load = (imp_load - complex(50.0)) / (imp_load + complex(50.0));
+ return addTransmissionLine(load, Z0, delay*1e-12, 0, freq);
+ }
+}
+
+void Load::edit()
+{
+ auto d = new QDialog;
+ auto ui = new Ui::CalStandardLoadEditDialog;
+ ui->setupUi(d);
+
+ ui->name->setText(name);
+ ui->resistance->setUnit("Ω");
+ ui->resistance->setPrecision(2);
+ ui->resistance->setValue(resistance);
+ ui->Z0->setUnit("Ω");
+ ui->Z0->setPrecision(2);
+ ui->Z0->setValue(Z0);
+ ui->delay->setValue(delay);
+ ui->parC->setUnit("F");
+ ui->parC->setPrefixes("pnum ");
+ ui->parC->setPrecision(3);
+ ui->parC->setValue(Cparallel);
+ ui->serL->setUnit("H");
+ ui->serL->setPrefixes("num ");
+ ui->serL->setPrecision(3);
+ ui->serL->setValue(Lseries);
+
+ if(Cfirst) {
+ ui->C_first->setChecked(true);
+ } else {
+ ui->L_first->setChecked(true);
+ }
+
+ auto updateMeasurementLabel = [=](){
+ QString label;
+ if(touchstone) {
+ label = QString::number(touchstone->points())+" points from "+Unit::ToString(touchstone->minFreq(), "Hz", " kMG")+" to "+Unit::ToString(touchstone->maxFreq(), "Hz", " kMG");
+ } else {
+ label = "No measurements stored yet";
+ }
+ ui->measurementLabel->setText(label);
+ };
+
+ QObject::connect(ui->coefficients, &QRadioButton::toggled, [=](bool checked) {
+ if(checked) {
+ clearMeasurement();
+ }
+ ui->stackedWidget->setCurrentIndex(checked ? 0 : 1);
+ });
+ QObject::connect(ui->measurement, &QRadioButton::toggled, [=](bool checked) {
+ updateMeasurementLabel();
+ ui->stackedWidget->setCurrentIndex(checked ? 1 : 0);
+ });
+
+ QObject::connect(ui->touchstoneImport, &TouchstoneImport::statusChanged, ui->updateFile, &QPushButton::setEnabled);
+
+ ui->touchstoneImport->setPorts(1);
+ if(touchstone) {
+ ui->measurement->setChecked(true);
+ ui->touchstoneImport->setFile(touchstone->getFilename());
+ } else {
+ ui->coefficients->setChecked(true);
+ }
+
+ QObject::connect(ui->updateFile, &QPushButton::clicked, [=](){
+ setMeasurement(ui->touchstoneImport->getTouchstone(), ui->touchstoneImport->getPorts()[0]);
+ updateMeasurementLabel();
+ });
+
+ QObject::connect(d, &QDialog::accepted, [=](){
+ name = ui->name->text();
+ resistance = ui->resistance->value();
+ Z0 = ui->Z0->value();
+ delay = ui->delay->value();
+ Cparallel = ui->parC->value();
+ Lseries = ui->serL->value();
+ Cfirst = ui->C_first->isChecked();
+ });
+
+ d->show();
+}
+
+nlohmann::json Load::toJSON()
+{
+ auto j = OnePort::toJSON();
+ j["Z0"] = Z0;
+ j["delay"] = delay;
+ j["resistance"] = resistance;
+ j["Cparallel"] = Cparallel;
+ j["Lseries"] = Lseries;
+ j["Cfirst"] = Cfirst;
+ return j;
+}
+
+void Load::fromJSON(nlohmann::json j)
+{
+ OnePort::fromJSON(j);
+ Z0 = j.value("Z0", 50.0);
+ delay = j.value("delay", 0.0);
+ resistance = j.value("resistance", 0.0);
+ Cparallel = j.value("Cparallel", 0.0);
+ Lseries = j.value("Lseries", 0.0);
+ Cfirst = j.value("Cfirst", true);
+}
+
+void TwoPort::setMeasurement(const Touchstone &ts, int port1, int port2)
+{
+ if(!touchstone) {
+ touchstone = new Touchstone(ts);
+ } else {
+ *touchstone = ts;
+ }
+ if(touchstone->ports() > 2) {
+ touchstone->reduceTo2Port(port1, port2);
+ }
+ minFreq = touchstone->minFreq();
+ maxFreq = touchstone->maxFreq();
+}
+
+void TwoPort::clearMeasurement()
+{
+ delete touchstone;
+ touchstone = nullptr;
+ minFreq = std::numeric_limits::lowest();
+ minFreq = std::numeric_limits::max();
+}
+
+nlohmann::json TwoPort::toJSON()
+{
+ auto j = Virtual::toJSON();
+ if(touchstone) {
+ j["touchstone"] = touchstone->toJSON();
+ }
+ return j;
+}
+
+void TwoPort::fromJSON(nlohmann::json j)
+{
+ Virtual::fromJSON(j);
+ if(j.contains("touchstone")) {
+ Touchstone ts(1);
+ ts.fromJSON(j["touchstone"]);
+ setMeasurement(ts);
+ }
+}
+
+Sparam Through::toSparam(double freq)
+{
+ if(touchstone) {
+ auto interp = touchstone->interpolate(freq).S;
+ return Sparam(interp[0], interp[1], interp[2], interp[3]);
+ } else {
+ // calculate effect of through
+ double through_phaseshift = -2 * M_PI * freq * delay * 1e-12;
+ double through_att_db = loss * 1e9 * 4.3429 * delay * 1e-12 / Z0 * sqrt(freq / 1e9);;
+ double through_att = pow(10.0, -through_att_db / 10.0);
+ auto through = polar(through_att, through_phaseshift);
+ // Assume symmetric and perfectly matched through for other parameters
+ return Sparam(0.0, through, through, 0.0);
+ }
+}
+
+nlohmann::json Through::toJSON()
+{
+
+}
+
+void Through::fromJSON(nlohmann::json j)
+{
+
+}
diff --git a/Software/PC_Application/Calibration/calstandard.h b/Software/PC_Application/Calibration/calstandard.h
new file mode 100644
index 0000000..2e00d63
--- /dev/null
+++ b/Software/PC_Application/Calibration/calstandard.h
@@ -0,0 +1,147 @@
+#ifndef CALSTANDARD_H
+#define CALSTANDARD_H
+
+#include "savable.h"
+#include "touchstone.h"
+#include "Tools/parameters.h"
+
+#include
+
+namespace CalStandard
+{
+
+class Virtual : public Savable
+{
+public:
+ Virtual() :
+ minFreq(std::numeric_limits::lowest()),
+ maxFreq(std::numeric_limits::max()){}
+
+ enum class Type {
+ Open,
+ Short,
+ Load,
+ Through,
+ Line,
+ Last
+ };
+
+ static Virtual* create(Type type);
+
+ static QString TypeToString(Type type);
+ static Type TypeFromString(QString s);
+
+ virtual Type getType() = 0;
+ double minFrequency() {return minFreq;}
+ double maxFrequency() {return maxFreq;}
+
+ virtual void edit() = 0;
+ virtual QString getDescription();
+
+ virtual nlohmann::json toJSON() override;
+ virtual void fromJSON(nlohmann::json j) override;
+
+protected:
+ QString name;
+ double minFreq;
+ double maxFreq;
+};
+
+class OnePort : public Virtual
+{
+public:
+ OnePort() :
+ touchstone(nullptr){}
+
+ virtual std::complex toS11(double freq) = 0;
+
+ void setMeasurement(const Touchstone &ts, int port = 0);
+ void clearMeasurement();
+
+ virtual nlohmann::json toJSON() override;
+ virtual void fromJSON(nlohmann::json j) override;
+
+protected:
+ std::complex addTransmissionLine(std::complex termination_reflection,
+ double offset_impedance, double offset_delay,
+ double offset_loss, double frequency);
+ Touchstone *touchstone;
+};
+
+class Open : public OnePort
+{
+public:
+ Open();
+
+ virtual std::complex toS11(double freq) override;
+ virtual void edit() override;
+ virtual Type getType() override {return Type::Open;}
+ virtual nlohmann::json toJSON() override;
+ virtual void fromJSON(nlohmann::json j) override;
+private:
+ double Z0, delay, loss, C0, C1, C2, C3;
+};
+
+class Short : public OnePort
+{
+public:
+ Short();
+
+ virtual std::complex toS11(double freq) override;
+ virtual void edit() override;
+ virtual Type getType() override {return Type::Short;}
+ virtual nlohmann::json toJSON() override;
+ virtual void fromJSON(nlohmann::json j) override;
+private:
+ double Z0, delay, loss, L0, L1, L2, L3;
+};
+
+class Load : public OnePort
+{
+public:
+ Load();
+
+ virtual std::complex toS11(double freq) override;
+ virtual void edit() override;
+ virtual Type getType() override {return Type::Load;}
+ virtual nlohmann::json toJSON() override;
+ virtual void fromJSON(nlohmann::json j) override;
+private:
+ double Z0, delay, resistance, Cparallel, Lseries;
+ bool Cfirst;
+};
+
+class TwoPort : public Virtual
+{
+public:
+ TwoPort() :
+ touchstone(nullptr){}
+
+ virtual Sparam toSparam(double freq) = 0;
+
+ void setMeasurement(const Touchstone &ts, int port1 = 0, int port2 = 1);
+ void clearMeasurement();
+
+ virtual nlohmann::json toJSON() override;
+ virtual void fromJSON(nlohmann::json j) override;
+
+protected:
+ Touchstone *touchstone;
+};
+
+class Through : public TwoPort
+{
+public:
+ Through();
+
+ virtual Sparam toSparam(double freq) override;
+ virtual Type getType() override {return Type::Through;}
+ virtual nlohmann::json toJSON() override;
+ virtual void fromJSON(nlohmann::json j) override;
+private:
+ double Z0, delay, loss;
+};
+
+}
+
+#endif // CALSTANDARD_H
diff --git a/Software/PC_Application/LibreVNA-GUI.pro b/Software/PC_Application/LibreVNA-GUI.pro
index 27bb59d..53599b6 100644
--- a/Software/PC_Application/LibreVNA-GUI.pro
+++ b/Software/PC_Application/LibreVNA-GUI.pro
@@ -5,6 +5,7 @@ HEADERS += \
Calibration/calibrationtracedialog.h \
Calibration/calkit.h \
Calibration/calkitdialog.h \
+ Calibration/calstandard.h \
Calibration/frequencycaldialog.h \
Calibration/manualcalibrationdialog.h \
Calibration/measurementmodel.h \
@@ -144,6 +145,7 @@ SOURCES += \
Calibration/calibrationtracedialog.cpp \
Calibration/calkit.cpp \
Calibration/calkitdialog.cpp \
+ Calibration/calstandard.cpp \
Calibration/frequencycaldialog.cpp \
Calibration/manualcalibrationdialog.cpp \
Calibration/measurementmodel.cpp \
@@ -272,6 +274,9 @@ osx:LIBS += $(shell pkg-config --libs libusb-1.0)
QT += widgets network
FORMS += \
+ Calibration/CalStandardLoadEditDialog.ui \
+ Calibration/CalStandardOpenEditDialog.ui \
+ Calibration/CalStandardShortEditDialog.ui \
Calibration/addamplitudepointsdialog.ui \
Calibration/amplitudecaldialog.ui \
Calibration/automaticamplitudedialog.ui \
diff --git a/Software/PC_Application/Tools/parameters.h b/Software/PC_Application/Tools/parameters.h
index 4850eb4..81ca684 100644
--- a/Software/PC_Application/Tools/parameters.h
+++ b/Software/PC_Application/Tools/parameters.h
@@ -8,8 +8,8 @@ using Type = std::complex;
class Parameters {
public:
Parameters(Type m11, Type m12, Type m21, Type m22)
- : m11(m11), m12(m12), m21(m21), m22(m22){};
- Parameters(){};
+ : m11(m11), m12(m12), m21(m21), m22(m22){}
+ Parameters(){}
Type m11, m12, m21, m22;
};
diff --git a/Software/PC_Application/touchstone.h b/Software/PC_Application/touchstone.h
index 3cdf1bd..10ae410 100644
--- a/Software/PC_Application/touchstone.h
+++ b/Software/PC_Application/touchstone.h
@@ -31,15 +31,15 @@ public:
};
Touchstone(unsigned int m_ports);
- virtual ~Touchstone(){};
+ virtual ~Touchstone(){}
void AddDatapoint(Datapoint p);
void toFile(QString filename, Scale unit = Scale::GHz, Format format = Format::RealImaginary);
std::stringstream toString(Scale unit = Scale::GHz, Format format = Format::RealImaginary);
static Touchstone fromFile(std::string filename);
double minFreq();
double maxFreq();
- unsigned int points() { return m_datapoints.size(); };
- Datapoint point(int index) { return m_datapoints.at(index); };
+ unsigned int points() { return m_datapoints.size(); }
+ Datapoint point(int index) { return m_datapoints.at(index); }
Datapoint interpolate(double frequency);
// remove all paramaters except the ones regarding port1 and port2 (port cnt starts at 0)
void reduceTo2Port(unsigned int port1, unsigned int port2);