Preparations for new calkit format: individual cal standards

This commit is contained in:
Jan Käberich 2022-08-25 16:59:11 +02:00
parent 0ca87ec867
commit 1c607d2c42
9 changed files with 1406 additions and 10 deletions

View File

@ -0,0 +1,244 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CalStandardLoadEditDialog</class>
<widget class="QDialog" name="CalStandardLoadEditDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>349</width>
<height>355</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Load Standard</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="name"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="coefficients">
<property name="text">
<string>Coefficients</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="measurement">
<property name="text">
<string>Measurement file</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Resistance:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="SIUnitEdit" name="resistance"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_30">
<property name="text">
<string>Z0:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="Z0">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Offset delay [ps]:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="delay"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Parallel C:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="parC"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Series L:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="SIUnitEdit" name="serL"/>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Load Parameter Model</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_15">
<item>
<widget class="QRadioButton" name="L_first">
<property name="text">
<string>Series L first</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup_2</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="C_first">
<property name="text">
<string>Shunt C first</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup_2</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
<item>
<widget class="QLabel" name="measurementLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="updateFile">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Update from file</string>
</property>
</widget>
</item>
<item>
<widget class="TouchstoneImport" name="touchstoneImport" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SIUnitEdit</class>
<extends>QLineEdit</extends>
<header>CustomWidgets/siunitedit.h</header>
</customwidget>
<customwidget>
<class>TouchstoneImport</class>
<extends>QWidget</extends>
<header>CustomWidgets/touchstoneimport.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CalStandardLoadEditDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CalStandardLoadEditDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="buttonGroup"/>
<buttongroup name="buttonGroup_2"/>
</buttongroups>
</ui>

View File

@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CalStandardOpenEditDialog</class>
<widget class="QDialog" name="CalStandardOpenEditDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>358</width>
<height>342</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Open Standard</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="name"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="coefficients">
<property name="text">
<string>Coefficients</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="measurement">
<property name="text">
<string>Measurement file</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="page">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Z0:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="SIUnitEdit" name="Z0">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Offset delay [ps]:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="delay"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Offset loss [GΩ/s]: </string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="loss"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>C0 [10&lt;sup&gt;-15&lt;/sup&gt;F]:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="C0"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>C1 [10&lt;sup&gt;-27&lt;/sup&gt;F/Hz]:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="SIUnitEdit" name="C1"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>C2 [10&lt;sup&gt;-36&lt;/sup&gt;F/Hz&lt;sup&gt;2&lt;/sup&gt;]:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="SIUnitEdit" name="C2"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;C3 [10&lt;span style=&quot; vertical-align:super;&quot;&gt;-45&lt;/span&gt;F/Hz&lt;span style=&quot; vertical-align:super;&quot;&gt;3&lt;/span&gt;]:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="SIUnitEdit" name="C3"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
<item>
<widget class="QLabel" name="measurementLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="updateFile">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Update from file</string>
</property>
</widget>
</item>
<item>
<widget class="TouchstoneImport" name="touchstoneImport" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SIUnitEdit</class>
<extends>QLineEdit</extends>
<header>CustomWidgets/siunitedit.h</header>
</customwidget>
<customwidget>
<class>TouchstoneImport</class>
<extends>QWidget</extends>
<header>CustomWidgets/touchstoneimport.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CalStandardOpenEditDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CalStandardOpenEditDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="buttonGroup"/>
</buttongroups>
</ui>

View File

@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CalStandardShortEditDialog</class>
<widget class="QDialog" name="CalStandardShortEditDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>358</width>
<height>342</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Short Standard</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="name"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="coefficients">
<property name="text">
<string>Coefficients</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="measurement">
<property name="text">
<string>Measurement file</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Z0:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="SIUnitEdit" name="Z0">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Offset delay [ps]:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="delay"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Offset loss [GΩ/s]: </string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="loss"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;L0 [10&lt;span style=&quot; vertical-align:super;&quot;&gt;-12&lt;/span&gt;H]:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="L0"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;L1 [10&lt;span style=&quot; vertical-align:super;&quot;&gt;-24&lt;/span&gt;H/Hz]:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="SIUnitEdit" name="L1"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;L2 [10&lt;span style=&quot; vertical-align:super;&quot;&gt;-33&lt;/span&gt;H/Hz&lt;span style=&quot; vertical-align:super;&quot;&gt;2&lt;/span&gt;]:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="SIUnitEdit" name="L2"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;L3 [10&lt;span style=&quot; vertical-align:super;&quot;&gt;-42&lt;/span&gt;H/Hz&lt;span style=&quot; vertical-align:super;&quot;&gt;3&lt;/span&gt;]:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="SIUnitEdit" name="L3"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
<item>
<widget class="QLabel" name="measurementLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="updateFile">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Update from file</string>
</property>
</widget>
</item>
<item>
<widget class="TouchstoneImport" name="touchstoneImport" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SIUnitEdit</class>
<extends>QLineEdit</extends>
<header>CustomWidgets/siunitedit.h</header>
</customwidget>
<customwidget>
<class>TouchstoneImport</class>
<extends>QWidget</extends>
<header>CustomWidgets/touchstoneimport.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CalStandardShortEditDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CalStandardShortEditDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="buttonGroup"/>
</buttongroups>
</ui>

View File

@ -10,7 +10,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1213</width> <width>1213</width>
<height>626</height> <height>637</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -1505,14 +1505,14 @@
</connection> </connection>
</connections> </connections>
<buttongroups> <buttongroups>
<buttongroup name="ShortType_f"/>
<buttongroup name="LoadType_f"/> <buttongroup name="LoadType_f"/>
<buttongroup name="ShortType"/> <buttongroup name="LoadModel"/>
<buttongroup name="LoadType"/>
<buttongroup name="OpenType_f"/> <buttongroup name="OpenType_f"/>
<buttongroup name="TRL_Rtype"/> <buttongroup name="TRL_Rtype"/>
<buttongroup name="LoadType"/>
<buttongroup name="ShortType_f"/>
<buttongroup name="OpenType"/> <buttongroup name="OpenType"/>
<buttongroup name="ShortType"/>
<buttongroup name="ThroughType"/> <buttongroup name="ThroughType"/>
<buttongroup name="LoadModel"/>
</buttongroups> </buttongroups>
</ui> </ui>

View File

@ -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<double>::lowest();
minFreq = std::numeric_limits<double>::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<double> OnePort::addTransmissionLine(std::complex<double> 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<double>(offset_impedance + (offset_loss / (2*w)) * f_sqrt, -(offset_loss / (2*w)) * f_sqrt);
auto gamma_l = complex<double>(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<double>(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<double> 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<double> open;
if (Cfringing == 0) {
// special case to avoid issues with infinity
open = complex<double>(1.0, 0);
} else {
auto imp_open = complex<double>(0, -1.0 / (freq * 2 * M_PI * Cfringing));
open = (imp_open - complex<double>(50.0)) / (imp_open + complex<double>(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<double> 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<double>(0, freq * 2 * M_PI * Lseries);
complex<double> _short = (imp_short - complex<double>(50.0)) / (imp_short + complex<double>(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<double> Load::toS11(double freq)
{
if(touchstone) {
return touchstone->interpolate(freq).S[0];
} else {
auto imp_load = complex<double>(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<double>(0, freq * 2 * M_PI * Lseries);
}
// Add parallel capacitor to impedance
if(Cparallel > 0) {
auto imp_C = complex<double>(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<double>(0, freq * 2 * M_PI * Lseries);
}
complex<double> load = (imp_load - complex<double>(50.0)) / (imp_load + complex<double>(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<double>::lowest();
minFreq = std::numeric_limits<double>::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<double>(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)
{
}

View File

@ -0,0 +1,147 @@
#ifndef CALSTANDARD_H
#define CALSTANDARD_H
#include "savable.h"
#include "touchstone.h"
#include "Tools/parameters.h"
#include <complex>
namespace CalStandard
{
class Virtual : public Savable
{
public:
Virtual() :
minFreq(std::numeric_limits<double>::lowest()),
maxFreq(std::numeric_limits<double>::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<double> 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<double> addTransmissionLine(std::complex<double> termination_reflection,
double offset_impedance, double offset_delay,
double offset_loss, double frequency);
Touchstone *touchstone;
};
class Open : public OnePort
{
public:
Open();
virtual std::complex<double> 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<double> 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<double> 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

View File

@ -5,6 +5,7 @@ HEADERS += \
Calibration/calibrationtracedialog.h \ Calibration/calibrationtracedialog.h \
Calibration/calkit.h \ Calibration/calkit.h \
Calibration/calkitdialog.h \ Calibration/calkitdialog.h \
Calibration/calstandard.h \
Calibration/frequencycaldialog.h \ Calibration/frequencycaldialog.h \
Calibration/manualcalibrationdialog.h \ Calibration/manualcalibrationdialog.h \
Calibration/measurementmodel.h \ Calibration/measurementmodel.h \
@ -144,6 +145,7 @@ SOURCES += \
Calibration/calibrationtracedialog.cpp \ Calibration/calibrationtracedialog.cpp \
Calibration/calkit.cpp \ Calibration/calkit.cpp \
Calibration/calkitdialog.cpp \ Calibration/calkitdialog.cpp \
Calibration/calstandard.cpp \
Calibration/frequencycaldialog.cpp \ Calibration/frequencycaldialog.cpp \
Calibration/manualcalibrationdialog.cpp \ Calibration/manualcalibrationdialog.cpp \
Calibration/measurementmodel.cpp \ Calibration/measurementmodel.cpp \
@ -272,6 +274,9 @@ osx:LIBS += $(shell pkg-config --libs libusb-1.0)
QT += widgets network QT += widgets network
FORMS += \ FORMS += \
Calibration/CalStandardLoadEditDialog.ui \
Calibration/CalStandardOpenEditDialog.ui \
Calibration/CalStandardShortEditDialog.ui \
Calibration/addamplitudepointsdialog.ui \ Calibration/addamplitudepointsdialog.ui \
Calibration/amplitudecaldialog.ui \ Calibration/amplitudecaldialog.ui \
Calibration/automaticamplitudedialog.ui \ Calibration/automaticamplitudedialog.ui \

View File

@ -8,8 +8,8 @@ using Type = std::complex<double>;
class Parameters { class Parameters {
public: public:
Parameters(Type m11, Type m12, Type m21, Type m22) Parameters(Type m11, Type m12, Type m21, Type m22)
: m11(m11), m12(m12), m21(m21), m22(m22){}; : m11(m11), m12(m12), m21(m21), m22(m22){}
Parameters(){}; Parameters(){}
Type m11, m12, m21, m22; Type m11, m12, m21, m22;
}; };

View File

@ -31,15 +31,15 @@ public:
}; };
Touchstone(unsigned int m_ports); Touchstone(unsigned int m_ports);
virtual ~Touchstone(){}; virtual ~Touchstone(){}
void AddDatapoint(Datapoint p); void AddDatapoint(Datapoint p);
void toFile(QString filename, Scale unit = Scale::GHz, Format format = Format::RealImaginary); void toFile(QString filename, Scale unit = Scale::GHz, Format format = Format::RealImaginary);
std::stringstream toString(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); static Touchstone fromFile(std::string filename);
double minFreq(); double minFreq();
double maxFreq(); double maxFreq();
unsigned int points() { return m_datapoints.size(); }; unsigned int points() { return m_datapoints.size(); }
Datapoint point(int index) { return m_datapoints.at(index); }; Datapoint point(int index) { return m_datapoints.at(index); }
Datapoint interpolate(double frequency); Datapoint interpolate(double frequency);
// remove all paramaters except the ones regarding port1 and port2 (port cnt starts at 0) // remove all paramaters except the ones regarding port1 and port2 (port cnt starts at 0)
void reduceTo2Port(unsigned int port1, unsigned int port2); void reduceTo2Port(unsigned int port1, unsigned int port2);