Calkit with flexible number of standards

This commit is contained in:
Jan Käberich 2022-08-26 00:46:53 +02:00
parent 1c607d2c42
commit 623aa2b29a
9 changed files with 592 additions and 2329 deletions

View File

@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CalStandardThroughEditDialog</class>
<widget class="QDialog" name="CalStandardThroughEditDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>358</width>
<height>342</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Through 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_35">
<property name="text">
<string>Z0:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="SIUnitEdit" name="Z0">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>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_22">
<property name="text">
<string>Loss [GΩ/s]: </string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="loss"/>
</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>CalStandardThroughEditDialog</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>CalStandardThroughEditDialog</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

@ -15,14 +15,6 @@ using json = nlohmann::json;
using namespace std;
Calkit::Calkit()
: ts_open_m(nullptr),
ts_short_m(nullptr),
ts_load_m(nullptr),
ts_open_f(nullptr),
ts_short_f(nullptr),
ts_load_f(nullptr),
ts_through(nullptr),
ts_cached(false)
{
// set default values
@ -39,15 +31,11 @@ void Calkit::toFile(QString filename)
qDebug() << "Saving calkit to file" << filename;
TransformPathsToRelative(filename);
json j = Savable::createJSON(descr);
ofstream file;
file.open(filename.toStdString());
file << setw(4) << j << endl;
file.close();
TransformPathsToAbsolute(filename);
}
static QString readLine(ifstream &file) {
@ -74,93 +62,87 @@ Calkit Calkit::fromFile(QString filename)
throw runtime_error("JSON parsing error: " + string(e.what()));
}
if(j.contains("SOLT")) {
// older file versions specify Z0 for resistance. Set resistance to Nan to detect missing values later
c.SOLT.load_m.resistance = std::numeric_limits<double>::quiet_NaN();
c.SOLT.load_f.resistance = std::numeric_limits<double>::quiet_NaN();
// // older file versions specify Z0 for resistance. Set resistance to Nan to detect missing values later
// c.SOLT.load_m.resistance = std::numeric_limits<double>::quiet_NaN();
// c.SOLT.load_f.resistance = std::numeric_limits<double>::quiet_NaN();
qDebug() << "JSON format detected";
// calkit file uses json format, parse
Savable::parseJSON(j, c.descr);
auto jSOLT = j["SOLT"];
if (!jSOLT.contains("loadModelCFirst")) {
// older version which did not allow the user to choose the load model. CFirst seems to be the more
// used standard so it is the default for newer calkits. However, old calkits used LFirst so we need
// to keep that to not mess with older calkit files
c.SOLT.loadModelCFirst = false;
}
// adjust Z0/resistance in case of older calkit file version with missing resistance entries
if(isnan(c.SOLT.load_f.resistance)) {
c.SOLT.load_f.resistance = c.SOLT.load_f.Z0;
c.SOLT.load_f.Z0 = 50.0;
}
if(isnan(c.SOLT.load_m.resistance)) {
c.SOLT.load_m.resistance = c.SOLT.load_m.Z0;
c.SOLT.load_m.Z0 = 50.0;
}
// qDebug() << "JSON format detected";
// // calkit file uses json format, parse
// Savable::parseJSON(j, c.descr);
// auto jSOLT = j["SOLT"];
// if (!jSOLT.contains("loadModelCFirst")) {
// // older version which did not allow the user to choose the load model. CFirst seems to be the more
// // used standard so it is the default for newer calkits. However, old calkits used LFirst so we need
// // to keep that to not mess with older calkit files
// c.SOLT.loadModelCFirst = false;
// }
// // adjust Z0/resistance in case of older calkit file version with missing resistance entries
// if(isnan(c.SOLT.load_f.resistance)) {
// c.SOLT.load_f.resistance = c.SOLT.load_f.Z0;
// c.SOLT.load_f.Z0 = 50.0;
// }
// if(isnan(c.SOLT.load_m.resistance)) {
// c.SOLT.load_m.resistance = c.SOLT.load_m.Z0;
// c.SOLT.load_m.Z0 = 50.0;
// }
} else {
qDebug() << "Legacy format detected";
// legacy file format, return to beginning of file
file.clear();
file.seekg(0);
c.SOLT.open_m.useMeasurements = readLine(file).toInt();
c.SOLT.short_m.useMeasurements = readLine(file).toInt();
c.SOLT.load_m.useMeasurements = readLine(file).toInt();
c.SOLT.Through.useMeasurements = readLine(file).toInt();
c.SOLT.open_m.Z0 = readLine(file).toDouble();
c.SOLT.open_m.delay = readLine(file).toDouble();
c.SOLT.open_m.loss = readLine(file).toDouble();
c.SOLT.open_m.C0 = readLine(file).toDouble();
c.SOLT.open_m.C1 = readLine(file).toDouble();
c.SOLT.open_m.C2 = readLine(file).toDouble();
c.SOLT.open_m.C3 = readLine(file).toDouble();
c.SOLT.short_m.Z0 = readLine(file).toDouble();
c.SOLT.short_m.delay = readLine(file).toDouble();
c.SOLT.short_m.loss = readLine(file).toDouble();
c.SOLT.short_m.L0 = readLine(file).toDouble();
c.SOLT.short_m.L1 = readLine(file).toDouble();
c.SOLT.short_m.L2 = readLine(file).toDouble();
c.SOLT.short_m.L3 = readLine(file).toDouble();
c.SOLT.load_m.resistance = readLine(file).toDouble();
c.SOLT.Through.Z0 = readLine(file).toDouble();
c.SOLT.Through.delay = readLine(file).toDouble();
c.SOLT.Through.loss = readLine(file).toDouble();
if(c.SOLT.open_m.useMeasurements) {
c.SOLT.open_m.file = readLine(file);
c.SOLT.open_m.Sparam = readLine(file).toInt();
}
if(c.SOLT.short_m.useMeasurements) {
c.SOLT.short_m.file = readLine(file);
c.SOLT.short_m.Sparam = readLine(file).toInt();
}
if(c.SOLT.load_m.useMeasurements) {
c.SOLT.load_m.file = readLine(file);
c.SOLT.load_m.Sparam = readLine(file).toInt();
}
if(c.SOLT.Through.useMeasurements) {
c.SOLT.Through.file = readLine(file);
c.SOLT.Through.Sparam1 = readLine(file).toInt();
c.SOLT.Through.Sparam2 = readLine(file).toInt();
}
c.TRL.Through.Z0 = readLine(file).toDouble();
c.TRL.Reflection.isShort = readLine(file).toDouble();
c.TRL.Line.delay = readLine(file).toDouble();
c.TRL.Line.minFreq = readLine(file).toDouble();
c.TRL.Line.maxFreq = readLine(file).toDouble();
// qDebug() << "Legacy format detected";
// // legacy file format, return to beginning of file
// file.clear();
// file.seekg(0);
// c.SOLT.open_m.useMeasurements = readLine(file).toInt();
// c.SOLT.short_m.useMeasurements = readLine(file).toInt();
// c.SOLT.load_m.useMeasurements = readLine(file).toInt();
// c.SOLT.Through.useMeasurements = readLine(file).toInt();
// c.SOLT.open_m.Z0 = readLine(file).toDouble();
// c.SOLT.open_m.delay = readLine(file).toDouble();
// c.SOLT.open_m.loss = readLine(file).toDouble();
// c.SOLT.open_m.C0 = readLine(file).toDouble();
// c.SOLT.open_m.C1 = readLine(file).toDouble();
// c.SOLT.open_m.C2 = readLine(file).toDouble();
// c.SOLT.open_m.C3 = readLine(file).toDouble();
// c.SOLT.short_m.Z0 = readLine(file).toDouble();
// c.SOLT.short_m.delay = readLine(file).toDouble();
// c.SOLT.short_m.loss = readLine(file).toDouble();
// c.SOLT.short_m.L0 = readLine(file).toDouble();
// c.SOLT.short_m.L1 = readLine(file).toDouble();
// c.SOLT.short_m.L2 = readLine(file).toDouble();
// c.SOLT.short_m.L3 = readLine(file).toDouble();
// c.SOLT.load_m.resistance = readLine(file).toDouble();
// c.SOLT.Through.Z0 = readLine(file).toDouble();
// c.SOLT.Through.delay = readLine(file).toDouble();
// c.SOLT.Through.loss = readLine(file).toDouble();
// if(c.SOLT.open_m.useMeasurements) {
// c.SOLT.open_m.file = readLine(file);
// c.SOLT.open_m.Sparam = readLine(file).toInt();
// }
// if(c.SOLT.short_m.useMeasurements) {
// c.SOLT.short_m.file = readLine(file);
// c.SOLT.short_m.Sparam = readLine(file).toInt();
// }
// if(c.SOLT.load_m.useMeasurements) {
// c.SOLT.load_m.file = readLine(file);
// c.SOLT.load_m.Sparam = readLine(file).toInt();
// }
// if(c.SOLT.Through.useMeasurements) {
// c.SOLT.Through.file = readLine(file);
// c.SOLT.Through.Sparam1 = readLine(file).toInt();
// c.SOLT.Through.Sparam2 = readLine(file).toInt();
// }
// c.TRL.Through.Z0 = readLine(file).toDouble();
// c.TRL.Reflection.isShort = readLine(file).toDouble();
// c.TRL.Line.delay = readLine(file).toDouble();
// c.TRL.Line.minFreq = readLine(file).toDouble();
// c.TRL.Line.maxFreq = readLine(file).toDouble();
c.SOLT.separate_male_female = false;
// c.SOLT.separate_male_female = false;
InformationBox::ShowMessage("Loading calkit file", "The file \"" + filename + "\" is stored in a deprecated"
" calibration kit format. Future versions of this application might not support"
" it anymore. Please save the calibration kit to update to the new format");
// InformationBox::ShowMessage("Loading calkit file", "The file \"" + filename + "\" is stored in a deprecated"
// " calibration kit format. Future versions of this application might not support"
// " it anymore. Please save the calibration kit to update to the new format");
}
file.close();
c.TransformPathsToAbsolute(filename);
// set default values for non-editable items (for now)
c.TRL.Through.Z0 = 50.0;
c.SOLT.Through.Z0 = 50.0;
return c;
}
@ -179,305 +161,45 @@ void Calkit::edit(std::function<void (void)> updateCal)
bool Calkit::hasSeparateMaleFemaleStandards()
{
return SOLT.separate_male_female;
return true; // TODO delete function
}
class Calkit::SOLT Calkit::toSOLT(double frequency, bool male_standards)
{
auto addTransmissionLine = [](complex<double> termination_reflection, double offset_impedance, double offset_delay, double offset_loss, double frequency) -> complex<double> {
// 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;
};
auto Load = male_standards ? SOLT.load_m : SOLT.load_f;
auto Short = male_standards ? SOLT.short_m : SOLT.short_f;
auto Open = male_standards ? SOLT.open_m : SOLT.open_f;
auto ts_load = male_standards ? ts_load_m : ts_load_f;
auto ts_short = male_standards ? ts_short_m : ts_short_f;
auto ts_open = male_standards ? ts_open_m : ts_open_f;
fillTouchstoneCache();
class SOLT ref;
if(Load.useMeasurements) {
ref.Load = ts_load->interpolate(frequency).S[0];
} else {
auto imp_load = complex<double>(Load.resistance, 0);
if (SOLT.loadModelCFirst) {
// 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, frequency * 2 * M_PI * Load.Lseries);
}
// Add parallel capacitor to impedance
if(Load.Cparallel > 0) {
auto imp_C = complex<double>(0, -1.0 / (frequency * 2 * M_PI * Load.Cparallel));
imp_load = (imp_load * imp_C) / (imp_load + imp_C);
}
if (!SOLT.loadModelCFirst) {
// inductor not added yet, do so now
imp_load += complex<double>(0, frequency * 2 * M_PI * Load.Lseries);
}
ref.Load = (imp_load - complex<double>(50.0)) / (imp_load + complex<double>(50.0));
ref.Load = addTransmissionLine(ref.Load, Load.Z0, Load.delay*1e-12, 0, frequency);
}
if(Open.useMeasurements) {
ref.Open = ts_open->interpolate(frequency).S[0];
} else {
// calculate fringing capacitance for open
double Cfringing = Open.C0 * 1e-15 + Open.C1 * 1e-27 * frequency + Open.C2 * 1e-36 * pow(frequency, 2) + Open.C3 * 1e-45 * pow(frequency, 3);
// convert to impedance
if (Cfringing == 0) {
// special case to avoid issues with infinity
ref.Open = complex<double>(1.0, 0);
} else {
auto imp_open = complex<double>(0, -1.0 / (frequency * 2 * M_PI * Cfringing));
ref.Open = (imp_open - complex<double>(50.0)) / (imp_open + complex<double>(50.0));
}
ref.Open = addTransmissionLine(ref.Open, Open.Z0, Open.delay*1e-12, Open.loss*1e9, frequency);
}
if(Short.useMeasurements) {
ref.Short = ts_short->interpolate(frequency).S[0];
} else {
// calculate inductance for short
double Lseries = Short.L0 * 1e-12 + Short.L1 * 1e-24 * frequency + Short.L2 * 1e-33 * pow(frequency, 2) + Short.L3 * 1e-42 * pow(frequency, 3);
// convert to impedance
auto imp_short = complex<double>(0, frequency * 2 * M_PI * Lseries);
ref.Short = (imp_short - complex<double>(50.0)) / (imp_short + complex<double>(50.0));
ref.Short = addTransmissionLine(ref.Short, Short.Z0, Short.delay*1e-12, Short.loss*1e9, frequency);
}
if(SOLT.Through.useMeasurements) {
auto interp = ts_through->interpolate(frequency);
ref.ThroughS11 = interp.S[0];
ref.ThroughS12 = interp.S[1];
ref.ThroughS21 = interp.S[2];
ref.ThroughS22 = interp.S[3];
} else {
// calculate effect of through
double through_phaseshift = -2 * M_PI * frequency * SOLT.Through.delay * 1e-12;
double through_att_db = SOLT.Through.loss * 1e9 * 4.3429 * SOLT.Through.delay * 1e-12 / SOLT.Through.Z0 * sqrt(frequency / 1e9);;
double through_att = pow(10.0, -through_att_db / 10.0);
ref.ThroughS12 = polar<double>(through_att, through_phaseshift);
// Assume symmetric and perfectly matched through for other parameters
ref.ThroughS21 = ref.ThroughS12;
ref.ThroughS11 = 0.0;
ref.ThroughS22 = 0.0;
}
return ref;
return SOLT(); // TODO delete function
}
class Calkit::TRL Calkit::toTRL(double)
{
class TRL trl;
// reflection coefficent sign depends on whether an open or short is used
trl.reflectionIsNegative = TRL.Reflection.isShort;
// assume ideal through for now
trl.ThroughS11 = 0.0;
trl.ThroughS12 = 1.0;
trl.ThroughS21 = 1.0;
trl.ThroughS22 = 0.0;
return trl;
return TRL(); // TODO delete function
}
double Calkit::minFreqTRL()
{
return TRL.Line.minFreq;
return 0; // TODO delete function
}
double Calkit::maxFreqTRL()
{
return TRL.Line.maxFreq;
return std::numeric_limits<double>::max(); // TODO delete function
}
double Calkit::minFreqSOLT(bool male_standards)
{
fillTouchstoneCache();
double min = 0;
auto ts_load = male_standards ? ts_load_m : ts_load_f;
auto ts_short = male_standards ? ts_short_m : ts_short_f;
auto ts_open = male_standards ? ts_open_m : ts_open_f;
array<Touchstone*, 4> ts_list = {ts_open, ts_short, ts_load, ts_through};
// find the highest minimum frequency in all measurement files
for(auto ts : ts_list) {
if(!ts) {
// this calibration standard is defined by coefficients, no minimum frequency
continue;
}
if(ts->minFreq() > min) {
min = ts->minFreq();
}
}
return min;
return 0; // TODO delete function
}
double Calkit::maxFreqSOLT(bool male_standards)
{
fillTouchstoneCache();
double max = std::numeric_limits<double>::max();
auto ts_load = male_standards ? ts_load_m : ts_load_f;
auto ts_short = male_standards ? ts_short_m : ts_short_f;
auto ts_open = male_standards ? ts_open_m : ts_open_f;
array<Touchstone*, 4> ts_list = {ts_open, ts_short, ts_load, ts_through};
// find the highest minimum frequency in all measurement files
for(auto ts : ts_list) {
if(!ts) {
// this calibration standard is defined by coefficients, no minimum frequency
continue;
}
if(ts->maxFreq() < max) {
max = ts->maxFreq();
}
}
return max;
return std::numeric_limits<double>::max(); // TODO delete function
}
bool Calkit::checkIfValid(double min_freq, double max_freq, bool isTRL, bool include_male, bool include_female)
{
auto min_supported = std::numeric_limits<double>::min();
auto max_supported = std::numeric_limits<double>::max();
if(isTRL) {
min_supported = TRL.Line.minFreq;
max_supported = TRL.Line.maxFreq;
} else {
if(include_male) {
auto min_male = minFreqSOLT(true);
auto max_male = maxFreqSOLT(true);
if(min_male > min_supported) {
min_supported = min_male;
}
if(max_male > max_supported) {
max_supported = max_male;
}
}
if(include_female) {
auto min_female = minFreqSOLT(false);
auto max_female = maxFreqSOLT(false);
if(min_female > min_supported) {
min_supported = min_female;
}
if(max_female > max_supported) {
max_supported = max_female;
}
}
}
if(min_supported <= min_freq && max_supported >= max_freq) {
return true;
} else {
return false;
}
return true; // TODO delete function
}
bool Calkit::isTRLReflectionShort() const
{
return TRL.Reflection.isShort;
}
void Calkit::TransformPathsToRelative(QFileInfo d)
{
vector<QString*> filenames = {&SOLT.short_m.file, &SOLT.open_m.file, &SOLT.load_m.file, &SOLT.short_f.file, &SOLT.open_f.file, &SOLT.load_f.file, &SOLT.Through.file};
for(auto f : filenames) {
if(f->isEmpty()) {
continue;
}
if(QFileInfo(*f).isAbsolute()) {
QString buf = *f;
*f = d.dir().relativeFilePath(*f);
qDebug() << "Transformed" << buf << "to" << *f << "(to relative)";
}
}
}
void Calkit::TransformPathsToAbsolute(QFileInfo d)
{
vector<QString*> filenames = {&SOLT.short_m.file, &SOLT.open_m.file, &SOLT.load_m.file, &SOLT.short_f.file, &SOLT.open_f.file, &SOLT.load_f.file, &SOLT.Through.file};
for(auto f : filenames) {
if(f->isEmpty()) {
continue;
}
if(QFileInfo(*f).isRelative()) {
auto absDir = QDir(d.dir().path() + "/" + *f);
QString buf = *f;
*f = absDir.absolutePath();
qDebug() << "Transformed" << buf << "to" << *f << "(to absolute)";
}
}
}
void Calkit::clearTouchstoneCache()
{
delete ts_open_m;
ts_open_m = nullptr;
delete ts_short_m;
ts_short_m = nullptr;
delete ts_load_m;
ts_load_m = nullptr;
delete ts_open_f;
ts_open_f = nullptr;
delete ts_short_f;
ts_short_f = nullptr;
delete ts_load_f;
ts_load_f = nullptr;
delete ts_through;
ts_through = nullptr;
ts_cached = false;
}
void Calkit::fillTouchstoneCache()
{
if(ts_cached) {
return;
}
if(SOLT.open_m.useMeasurements) {
ts_open_m = new Touchstone(1);
*ts_open_m = Touchstone::fromFile(SOLT.open_m.file.toStdString());
ts_open_m->reduceTo1Port(SOLT.open_m.Sparam);
}
if(SOLT.short_m.useMeasurements) {
ts_short_m = new Touchstone(1);
*ts_short_m = Touchstone::fromFile(SOLT.short_m.file.toStdString());
ts_short_m->reduceTo1Port(SOLT.short_m.Sparam);
}
if(SOLT.load_m.useMeasurements) {
ts_load_m = new Touchstone(1);
*ts_load_m = Touchstone::fromFile(SOLT.load_m.file.toStdString());
ts_load_m->reduceTo1Port(SOLT.load_m.Sparam);
}
if(SOLT.open_f.useMeasurements) {
ts_open_f = new Touchstone(1);
*ts_open_f = Touchstone::fromFile(SOLT.open_f.file.toStdString());
ts_open_f->reduceTo1Port(SOLT.open_f.Sparam);
}
if(SOLT.short_f.useMeasurements) {
ts_short_f = new Touchstone(1);
*ts_short_f = Touchstone::fromFile(SOLT.short_f.file.toStdString());
ts_short_f->reduceTo1Port(SOLT.short_f.Sparam);
}
if(SOLT.load_f.useMeasurements) {
ts_load_f = new Touchstone(1);
*ts_load_f = Touchstone::fromFile(SOLT.load_f.file.toStdString());
ts_load_f->reduceTo1Port(SOLT.load_f.Sparam);
}
if(SOLT.Through.useMeasurements) {
ts_through = new Touchstone(2);
*ts_through = Touchstone::fromFile(SOLT.Through.file.toStdString());
ts_through->reduceTo2Port(SOLT.Through.Sparam1, SOLT.Through.Sparam2);
}
ts_cached = true;
return true; // TODO delete function
}

View File

@ -3,6 +3,7 @@
#include "touchstone.h"
#include "Util/qpointervariant.h"
#include "calstandard.h"
#include <string>
#include <complex>
@ -19,9 +20,7 @@ public:
this->manufacturer = other.manufacturer;
this->serialnumber = other.serialnumber;
this->description = other.description;
this->SOLT = other.SOLT;
this->TRL = other.TRL;
this->startDialogWithSOLT = other.startDialogWithSOLT;
this->standards = other.standards;
return *this;
}
@ -53,146 +52,14 @@ public:
bool isTRLReflectionShort() const;
private:
void TransformPathsToRelative(QFileInfo d);
void TransformPathsToAbsolute(QFileInfo d);
QString manufacturer, serialnumber, description;
// SOLT standard definitions
struct {
using Open = struct {
double Z0, delay, loss, C0, C1, C2, C3;
QString file;
bool useMeasurements;
int Sparam;
};
Open open_m, open_f;
using Short = struct {
double Z0, delay, loss, L0, L1, L2, L3;
QString file;
bool useMeasurements;
int Sparam;
};
Short short_m, short_f;
using Load = struct {
double resistance, Z0, delay, Cparallel, Lseries;
QString file;
bool useMeasurements;
int Sparam;
};
Load load_m, load_f;
bool loadModelCFirst;
struct {
double Z0, delay, loss;
QString file;
bool useMeasurements;
int Sparam1, Sparam2;
} Through;
bool separate_male_female;
} SOLT;
struct {
struct {
double Z0;
} Through;
struct {
bool isShort;
} Reflection;
struct {
double delay, minFreq, maxFreq;
} Line;
} TRL;
bool startDialogWithSOLT;
Touchstone *ts_open_m, *ts_short_m, *ts_load_m;
Touchstone *ts_open_f, *ts_short_f, *ts_load_f;
Touchstone *ts_through;
bool ts_cached;
std::vector<CalStandard::Virtual*> standards;
const std::vector<Savable::SettingDescription> descr = {{
{&manufacturer, "Manufacturer", ""},
{&serialnumber, "Serialnumber", ""},
{&description, "Description", ""},
{&SOLT.open_m.Z0, "SOLT.Open.Param.Z0", 50.0},
{&SOLT.open_m.delay, "SOLT.Open.Param.Delay", 0.0},
{&SOLT.open_m.loss, "SOLT.Open.Param.Loss", 0.0},
{&SOLT.open_m.C0, "SOLT.Open.Param.C0", 0.0},
{&SOLT.open_m.C1, "SOLT.Open.Param.C1", 0.0},
{&SOLT.open_m.C2, "SOLT.Open.Param.C2", 0.0},
{&SOLT.open_m.C3, "SOLT.Open.Param.C3", 0.0},
{&SOLT.open_m.useMeasurements, "SOLT.Open.Measurements.Use", false},
{&SOLT.open_m.file, "SOLT.Open.Measurements.File", ""},
{&SOLT.open_m.Sparam, "SOLT.Open.Measurements.Port", 0},
{&SOLT.open_f.Z0, "SOLT.Open.Param.Z0_Female", 50.0},
{&SOLT.open_f.delay, "SOLT.Open.Param.Delay_Female", 0.0},
{&SOLT.open_f.loss, "SOLT.Open.Param.Loss_Female", 0.0},
{&SOLT.open_f.C0, "SOLT.Open.Param.C0_Female", 0.0},
{&SOLT.open_f.C1, "SOLT.Open.Param.C1_Female", 0.0},
{&SOLT.open_f.C2, "SOLT.Open.Param.C2_Female", 0.0},
{&SOLT.open_f.C3, "SOLT.Open.Param.C3_Female", 0.0},
{&SOLT.open_f.useMeasurements, "SOLT.Open.Measurements.Use_Female", false},
{&SOLT.open_f.file, "SOLT.Open.Measurements.File_Female", ""},
{&SOLT.open_f.Sparam, "SOLT.Open.Measurements.Port_Female", 0},
{&SOLT.short_m.Z0, "SOLT.Short.Param.Z0", 50.0},
{&SOLT.short_m.delay, "SOLT.Short.Param.Delay", 0.0},
{&SOLT.short_m.loss, "SOLT.Short.Param.Loss", 0.0},
{&SOLT.short_m.L0, "SOLT.Short.Param.L0", 0.0},
{&SOLT.short_m.L1, "SOLT.Short.Param.L1", 0.0},
{&SOLT.short_m.L2, "SOLT.Short.Param.L2", 0.0},
{&SOLT.short_m.L3, "SOLT.Short.Param.L3", 0.0},
{&SOLT.short_m.useMeasurements, "SOLT.Short.Measurements.Use", false},
{&SOLT.short_m.file, "SOLT.Short.Measurements.File", ""},
{&SOLT.short_m.Sparam, "SOLT.Short.Measurements.Port", 0},
{&SOLT.short_f.Z0, "SOLT.Short.Param.Z0_Female", 50.0},
{&SOLT.short_f.delay, "SOLT.Short.Param.Delay_Female", 0.0},
{&SOLT.short_f.loss, "SOLT.Short.Param.Loss_Female", 0.0},
{&SOLT.short_f.L0, "SOLT.Short.Param.L0_Female", 0.0},
{&SOLT.short_f.L1, "SOLT.Short.Param.L1_Female", 0.0},
{&SOLT.short_f.L2, "SOLT.Short.Param.L2_Female", 0.0},
{&SOLT.short_f.L3, "SOLT.Short.Param.L3_Female", 0.0},
{&SOLT.short_f.useMeasurements, "SOLT.Short.Measurements.Use_Female", false},
{&SOLT.short_f.file, "SOLT.Short.Measurements.File_Female", ""},
{&SOLT.short_f.Sparam, "SOLT.Short.Measurements.Port_Female", 0},
{&SOLT.loadModelCFirst, "SOLT.loadModelCFirst", true},
{&SOLT.load_m.resistance, "SOLT.Load.Param.Resistance", 50.0},
{&SOLT.load_m.Z0, "SOLT.Load.Param.Z0", 50.0},
{&SOLT.load_m.delay, "SOLT.Load.Param.Delay", 0.0},
{&SOLT.load_m.Cparallel, "SOLT.Load.Param.C", 0.0},
{&SOLT.load_m.Lseries, "SOLT.Load.Param.L", 0.0},
{&SOLT.load_m.useMeasurements, "SOLT.Load.Measurements.Use", false},
{&SOLT.load_m.file, "SOLT.Load.Measurements.File", ""},
{&SOLT.load_m.Sparam, "SOLT.Load.Measurements.Port", 0},
{&SOLT.load_f.resistance, "SOLT.Load.Param.Resistance_Female", 50.0},
{&SOLT.load_f.Z0, "SOLT.Load.Param.Z0_Female", 50.0},
{&SOLT.load_f.delay, "SOLT.Load.Param.Delay_Female", 0.0},
{&SOLT.load_f.Cparallel, "SOLT.Load.Param.C_Female", 0.0},
{&SOLT.load_f.Lseries, "SOLT.Load.Param.L_Female", 0.0},
{&SOLT.load_f.useMeasurements, "SOLT.Load.Measurements.Use_Female", false},
{&SOLT.load_f.file, "SOLT.Load.Measurements.File_Female", ""},
{&SOLT.load_f.Sparam, "SOLT.Load.Measurements.Port_Female", 0},
{&SOLT.Through.Z0, "SOLT.Through.Param.Z0", 50.0},
{&SOLT.Through.delay, "SOLT.Through.Param.Delay", 0.0},
{&SOLT.Through.loss, "SOLT.Through.Param.Loss", 0.0},
{&SOLT.Through.useMeasurements, "SOLT.Through.Measurements.Use", false},
{&SOLT.Through.file, "SOLT.Through.Measurements.File", ""},
{&SOLT.Through.Sparam1, "SOLT.Through.Measurements.Port1", 0},
{&SOLT.Through.Sparam2, "SOLT.Through.Measurements.Port2", 1},
{&SOLT.separate_male_female, "SOLT.SeparateMaleFemale", false},
{&TRL.Through.Z0, "TRL.Through.Z0", 50.0},
{&TRL.Reflection.isShort, "TRL.Reflect.isShort", false},
{&TRL.Line.delay, "TRL.Line.Delay", 74.0},
{&TRL.Line.minFreq, "TRL.Line.minFreq", 751000000.0},
{&TRL.Line.maxFreq, "TRL.Line.maxFreq", 6000000000.0},
{&startDialogWithSOLT, "StartDialogWithSOLT", true}
}};
void clearTouchstoneCache();
void fillTouchstoneCache();
};
#endif // CALKIT_H

View File

@ -9,192 +9,87 @@
#include <fstream>
#include <touchstone.h>
#include <QtGlobal>
#include <QAction>
#include <QMenu>
using namespace std;
CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
QDialog(parent),
ui(new Ui::CalkitDialog),
open_ok(true),
short_ok(true),
load_ok(true),
through_ok(true),
editKit(c)
kit(c)
{
ui->setupUi(this);
ui->OpenType->setId(ui->open_coefficients, 0);
ui->OpenType->setId(ui->open_measurement, 1);
ui->ShortType->setId(ui->short_coefficients, 0);
ui->ShortType->setId(ui->short_measurement, 1);
ui->LoadType->setId(ui->load_coefficients, 0);
ui->LoadType->setId(ui->load_measurement, 1);
ui->ThroughType->setId(ui->through_coefficients, 0);
ui->ThroughType->setId(ui->through_measurement, 1);
ui->open_touchstone->setPorts(1);
ui->short_touchstone->setPorts(1);
ui->load_touchstone->setPorts(1);
ui->through_touchstone->setPorts(2);
ui->short_Z0->setUnit("Ω");
ui->open_Z0->setUnit("Ω");
ui->load_resistance->setUnit("Ω");
ui->load_Z0->setUnit("Ω");
ui->load_parC->setUnit("F");
ui->load_parC->setPrefixes("fpnum ");
ui->load_serL->setUnit("H");
ui->load_serL->setPrefixes("fpnum ");
// Same setup for female standards
ui->OpenType_f->setId(ui->open_coefficients_f, 0);
ui->OpenType_f->setId(ui->open_measurement_f, 1);
ui->ShortType_f->setId(ui->short_coefficients_f, 0);
ui->ShortType_f->setId(ui->short_measurement_f, 1);
ui->LoadType_f->setId(ui->load_coefficients_f, 0);
ui->LoadType_f->setId(ui->load_measurement_f, 1);
ui->open_touchstone_f->setPorts(1);
ui->short_touchstone_f->setPorts(1);
ui->load_touchstone_f->setPorts(1);
ui->short_Z0_f->setUnit("Ω");
ui->open_Z0_f->setUnit("Ω");
ui->load_resistance_f->setUnit("Ω");
ui->load_Z0_f->setUnit("Ω");
ui->load_parC_f->setUnit("F");
ui->load_parC_f->setPrefixes("fpnum ");
ui->load_serL_f->setUnit("H");
ui->load_serL_f->setPrefixes("fpnum ");
ui->through_Z0->setUnit("Ω");
ui->TRL_through_Z0->setUnit("Ω");
ui->TRL_line_max->setUnit("Hz");
ui->TRL_line_max->setPrecision(4);
ui->TRL_line_max->setPrefixes(" kMG");
ui->TRL_line_min->setUnit("Hz");
ui->TRL_line_min->setPrecision(4);
ui->TRL_line_min->setPrefixes(" kMG");
editKit.clearTouchstoneCache();
ownKit = editKit;
connect(ui->cbStandardDefinition, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index){
if (index == 0) {
// common definition, hide tab bars, set all to male tab
ui->mf_short->setCurrentIndex(0);
ui->mf_short->tabBar()->hide();
ui->mf_open->setCurrentIndex(0);
ui->mf_open->tabBar()->hide();
ui->mf_load->setCurrentIndex(0);
ui->mf_load->tabBar()->hide();
} else {
// separate definitions for male/female standards
ui->mf_short->tabBar()->show();
ui->mf_open->tabBar()->show();
ui->mf_load->tabBar()->show();
}
});
updateEntries();
connect(ui->TRL_line_min, &SIUnitEdit::valueChanged, [=](double newval){
ownKit.TRL.Line.minFreq = newval;
updateEntries();
});
connect(ui->TRL_line_max, &SIUnitEdit::valueChanged, [=](double newval){
ownKit.TRL.Line.maxFreq = newval;
updateEntries();
});
connect(ui->TRL_line_delay, &QLineEdit::editingFinished, [=](){
ownKit.TRL.Line.delay = ui->TRL_line_delay->value();
updateEntries();
connect(ui->bDelete, &QPushButton::clicked, [=](){
auto row = ui->list->currentRow();
if(row >= 0) {
delete kit.standards[row];
kit.standards.erase(kit.standards.begin() + row);
updateStandardList();
}
});
auto UpdateStatus = [=]() {
bool ok = true;
if(ui->open_measurement->isChecked() && !ui->open_touchstone->getStatus()) {
ok = false;
connect(ui->bMoveUp, &QPushButton::clicked, [=](){
auto row = ui->list->currentRow();
if(row >= 1) {
swap(kit.standards[row], kit.standards[row-1]);
ui->list->setCurrentRow(row-1);
updateStandardList();
}
if(ui->short_measurement->isChecked() && !ui->short_touchstone->getStatus()) {
ok = false;
}
if(ui->load_measurement->isChecked() && !ui->load_touchstone->getStatus()) {
ok = false;
}
if(ui->open_measurement_f->isChecked() && !ui->open_touchstone_f->getStatus()) {
ok = false;
}
if(ui->short_measurement_f->isChecked() && !ui->short_touchstone_f->getStatus()) {
ok = false;
}
if(ui->load_measurement_f->isChecked() && !ui->load_touchstone_f->getStatus()) {
ok = false;
}
if(ui->through_measurement->isChecked() && !ui->through_touchstone->getStatus()) {
ok = false;
}
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok);
ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(ok);
};
});
connect(ui->open_touchstone, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->short_touchstone, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->load_touchstone, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->open_touchstone_f, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->short_touchstone_f, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->load_touchstone_f, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->through_touchstone, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->bMoveDown, &QPushButton::clicked, [=](){
auto row = ui->list->currentRow();
if(row < ui->list->count() - 1) {
swap(kit.standards[row], kit.standards[row+1]);
ui->list->setCurrentRow(row+1);
updateStandardList();
}
});
connect(ui->OpenType, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
});
connect(ui->ShortType, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
});
connect(ui->LoadType, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
});
connect(ui->OpenType_f, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
});
connect(ui->ShortType_f, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
});
connect(ui->LoadType_f, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
});
connect(ui->ThroughType, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
connect(ui->list, &QListWidget::currentRowChanged, this, &CalkitDialog::updateListEditButtons);
auto addMenu = new QMenu();
for(auto t : CalStandard::Virtual::availableTypes()) {
auto action = new QAction(CalStandard::Virtual::TypeToString(t));
connect(action, &QAction::triggered, [=](){
auto newStandard = CalStandard::Virtual::create(t);
if(newStandard) {
kit.standards.push_back(newStandard);
updateStandardList();
// start the edit dialog of the newly created standard
kit.standards.back()->edit(bind(&CalkitDialog::updateStandardList, this));
}
});
addMenu->addAction(action);
}
ui->bAdd->setMenu(addMenu);
connect(ui->list, &QListWidget::doubleClicked, [=](const QModelIndex &index){
if(!index.isValid()) {
return;
}
kit.standards[index.row()]->edit(bind(&CalkitDialog::updateStandardList, this));
});
connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, [this]() {
parseEntries();
editKit = ownKit;
editKit.clearTouchstoneCache();
emit settingsChanged();
});
connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, [this]() {
parseEntries();
editKit = ownKit;
editKit.clearTouchstoneCache();
emit settingsChanged();
accept();
});
connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, [this]() {
reject();
});
connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getOpenFileName(this, "Open calibration kit coefficients", "", "Calibration kit files (*.calkit)", nullptr, QFileDialog::DontUseNativeDialog);
if(filename.length() > 0) {
try {
ownKit = Calkit::fromFile(filename);
kit = Calkit::fromFile(filename);
} catch (runtime_error &e) {
InformationBox::ShowError("Error", "The calibration kit file could not be parsed (" + QString(e.what()) + ")");
qWarning() << "Parsing of calibration kit failed while opening calibration file: " << e.what();
@ -207,7 +102,7 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
auto filename = QFileDialog::getSaveFileName(this, "Save calibration kit coefficients", "", "Calibration kit files (*.calkit)", nullptr, QFileDialog::DontUseNativeDialog);
if(filename.length() > 0) {
parseEntries();
ownKit.toFile(filename);
kit.toFile(filename);
}
});
}
@ -217,256 +112,40 @@ CalkitDialog::~CalkitDialog()
delete ui;
}
void CalkitDialog::updateListEditButtons()
{
ui->bDelete->setEnabled(ui->list->currentRow() >= 0);
ui->bMoveUp->setEnabled(ui->list->currentRow() >= 1);
ui->bMoveDown->setEnabled(ui->list->currentRow() >= 0 && ui->list->currentRow() < ui->list->count() - 1);
}
void CalkitDialog::parseEntries()
{
ownKit.manufacturer = ui->manufacturer->text();
ownKit.serialnumber = ui->serialnumber->text();
ownKit.description = ui->description->toPlainText();
// type
ownKit.SOLT.open_m.useMeasurements = ui->open_measurement->isChecked();
ownKit.SOLT.short_m.useMeasurements = ui->short_measurement->isChecked();
ownKit.SOLT.load_m.useMeasurements = ui->load_measurement->isChecked();
ownKit.SOLT.open_f.useMeasurements = ui->open_measurement_f->isChecked();
ownKit.SOLT.short_f.useMeasurements = ui->short_measurement_f->isChecked();
ownKit.SOLT.load_f.useMeasurements = ui->load_measurement_f->isChecked();
ownKit.SOLT.Through.useMeasurements = ui->through_measurement->isChecked();
// coefficients
ownKit.SOLT.open_m.Z0 = ui->open_Z0->value();
ownKit.SOLT.open_m.delay = ui->open_delay->value();
ownKit.SOLT.open_m.loss = ui->open_loss->value();
ownKit.SOLT.open_m.C0 = ui->open_C0->value();
ownKit.SOLT.open_m.C1 = ui->open_C1->value();
ownKit.SOLT.open_m.C2 = ui->open_C2->value();
ownKit.SOLT.open_m.C3 = ui->open_C3->value();
ownKit.SOLT.short_m.Z0 = ui->short_Z0->value();
ownKit.SOLT.short_m.delay = ui->short_delay->value();
ownKit.SOLT.short_m.loss = ui->short_loss->value();
ownKit.SOLT.short_m.L0 = ui->short_L0->value();
ownKit.SOLT.short_m.L1 = ui->short_L1->value();
ownKit.SOLT.short_m.L2 = ui->short_L2->value();
ownKit.SOLT.short_m.L3 = ui->short_L3->value();
ownKit.SOLT.loadModelCFirst = ui->load_C_first->isChecked();
ownKit.SOLT.load_m.resistance = ui->load_resistance->value();
ownKit.SOLT.load_m.Z0 = ui->load_Z0->value();
ownKit.SOLT.load_m.delay = ui->load_delay->value();
ownKit.SOLT.load_m.Cparallel = ui->load_parC->value();
ownKit.SOLT.load_m.Lseries = ui->load_serL->value();
ownKit.SOLT.open_f.Z0 = ui->open_Z0_f->value();
ownKit.SOLT.open_f.delay = ui->open_delay_f->value();
ownKit.SOLT.open_f.loss = ui->open_loss_f->value();
ownKit.SOLT.open_f.C0 = ui->open_C0_f->value();
ownKit.SOLT.open_f.C1 = ui->open_C1_f->value();
ownKit.SOLT.open_f.C2 = ui->open_C2_f->value();
ownKit.SOLT.open_f.C3 = ui->open_C3_f->value();
ownKit.SOLT.short_f.Z0 = ui->short_Z0_f->value();
ownKit.SOLT.short_f.delay = ui->short_delay_f->value();
ownKit.SOLT.short_f.loss = ui->short_loss_f->value();
ownKit.SOLT.short_f.L0 = ui->short_L0_f->value();
ownKit.SOLT.short_f.L1 = ui->short_L1_f->value();
ownKit.SOLT.short_f.L2 = ui->short_L2_f->value();
ownKit.SOLT.short_f.L3 = ui->short_L3_f->value();
ownKit.SOLT.load_f.resistance = ui->load_resistance_f->value();
ownKit.SOLT.load_f.Z0 = ui->load_Z0_f->value();
ownKit.SOLT.load_f.delay = ui->load_delay_f->value();
ownKit.SOLT.load_f.Cparallel = ui->load_parC_f->value();
ownKit.SOLT.load_f.Lseries = ui->load_serL_f->value();
ownKit.SOLT.Through.Z0 = ui->through_Z0->value();
ownKit.SOLT.Through.delay = ui->through_delay->value();
ownKit.SOLT.Through.loss = ui->through_loss->value();
ownKit.SOLT.separate_male_female = ui->cbStandardDefinition->currentIndex() == 1;
// file
ownKit.SOLT.open_m.file = ui->open_touchstone->getFilename();
ownKit.SOLT.short_m.file = ui->short_touchstone->getFilename();
ownKit.SOLT.load_m.file = ui->load_touchstone->getFilename();
ownKit.SOLT.Through.file = ui->through_touchstone->getFilename();
ownKit.SOLT.open_m.Sparam = ui->open_touchstone->getPorts()[0];
ownKit.SOLT.short_m.Sparam = ui->short_touchstone->getPorts()[0];
ownKit.SOLT.load_m.Sparam = ui->load_touchstone->getPorts()[0];
ownKit.SOLT.Through.Sparam1 = ui->through_touchstone->getPorts()[0];
ownKit.SOLT.Through.Sparam2 = ui->through_touchstone->getPorts()[1];
ownKit.SOLT.open_f.file = ui->open_touchstone_f->getFilename();
ownKit.SOLT.short_f.file = ui->short_touchstone_f->getFilename();
ownKit.SOLT.load_f.file = ui->load_touchstone_f->getFilename();
ownKit.SOLT.open_f.Sparam = ui->open_touchstone_f->getPorts()[0];
ownKit.SOLT.short_f.Sparam = ui->short_touchstone_f->getPorts()[0];
ownKit.SOLT.load_f.Sparam = ui->load_touchstone_f->getPorts()[0];
// TRL
ownKit.TRL.Through.Z0 = ui->TRL_through_Z0->value();
ownKit.TRL.Reflection.isShort = ui->TRL_R_short->isChecked();
ownKit.TRL.Line.delay = ui->TRL_line_delay->value();
ownKit.TRL.Line.minFreq = ui->TRL_line_min->value();
ownKit.TRL.Line.maxFreq = ui->TRL_line_max->value();
ownKit.startDialogWithSOLT = ui->tabWidget->currentIndex() == 0;
kit.manufacturer = ui->manufacturer->text();
kit.serialnumber = ui->serialnumber->text();
kit.description = ui->description->toPlainText();
}
void CalkitDialog::updateEntries()
{
ui->manufacturer->setText(ownKit.manufacturer);
ui->serialnumber->setText(ownKit.serialnumber);
ui->description->setPlainText(ownKit.description);
// Coefficients
ui->open_Z0->setValueQuiet(ownKit.SOLT.open_m.Z0);
ui->open_delay->setValueQuiet(ownKit.SOLT.open_m.delay);
ui->open_loss->setValueQuiet(ownKit.SOLT.open_m.loss);
ui->open_C0->setValueQuiet(ownKit.SOLT.open_m.C0);
ui->open_C1->setValueQuiet(ownKit.SOLT.open_m.C1);
ui->open_C2->setValueQuiet(ownKit.SOLT.open_m.C2);
ui->open_C3->setValueQuiet(ownKit.SOLT.open_m.C3);
ui->short_Z0->setValueQuiet(ownKit.SOLT.short_m.Z0);
ui->short_delay->setValueQuiet(ownKit.SOLT.short_m.delay);
ui->short_loss->setValueQuiet(ownKit.SOLT.short_m.loss);
ui->short_L0->setValueQuiet(ownKit.SOLT.short_m.L0);
ui->short_L1->setValueQuiet(ownKit.SOLT.short_m.L1);
ui->short_L2->setValueQuiet(ownKit.SOLT.short_m.L2);
ui->short_L3->setValueQuiet(ownKit.SOLT.short_m.L3);
ui->load_C_first->setChecked(ownKit.SOLT.loadModelCFirst);
ui->load_L_first->setChecked(!ownKit.SOLT.loadModelCFirst);
ui->load_resistance->setValueQuiet(ownKit.SOLT.load_m.resistance);
ui->load_Z0->setValueQuiet(ownKit.SOLT.load_m.Z0);
ui->load_delay->setValueQuiet(ownKit.SOLT.load_m.delay);
ui->load_parC->setValueQuiet(ownKit.SOLT.load_m.Cparallel);
ui->load_serL->setValueQuiet(ownKit.SOLT.load_m.Lseries);
ui->open_Z0_f->setValueQuiet(ownKit.SOLT.open_f.Z0);
ui->open_delay_f->setValueQuiet(ownKit.SOLT.open_f.delay);
ui->open_loss_f->setValueQuiet(ownKit.SOLT.open_f.loss);
ui->open_C0_f->setValueQuiet(ownKit.SOLT.open_f.C0);
ui->open_C1_f->setValueQuiet(ownKit.SOLT.open_f.C1);
ui->open_C2_f->setValueQuiet(ownKit.SOLT.open_f.C2);
ui->open_C3_f->setValueQuiet(ownKit.SOLT.open_f.C3);
ui->short_Z0_f->setValueQuiet(ownKit.SOLT.short_f.Z0);
ui->short_delay_f->setValueQuiet(ownKit.SOLT.short_f.delay);
ui->short_loss_f->setValueQuiet(ownKit.SOLT.short_f.loss);
ui->short_L0_f->setValueQuiet(ownKit.SOLT.short_f.L0);
ui->short_L1_f->setValueQuiet(ownKit.SOLT.short_f.L1);
ui->short_L2_f->setValueQuiet(ownKit.SOLT.short_f.L2);
ui->short_L3_f->setValueQuiet(ownKit.SOLT.short_f.L3);
ui->load_resistance_f->setValueQuiet(ownKit.SOLT.load_f.resistance);
ui->load_Z0_f->setValueQuiet(ownKit.SOLT.load_f.Z0);
ui->load_delay_f->setValueQuiet(ownKit.SOLT.load_f.delay);
ui->load_parC_f->setValueQuiet(ownKit.SOLT.load_f.Cparallel);
ui->load_serL_f->setValueQuiet(ownKit.SOLT.load_f.Lseries);
ui->through_Z0->setValueQuiet(ownKit.SOLT.Through.Z0);
ui->through_delay->setValueQuiet(ownKit.SOLT.Through.delay);
ui->through_loss->setValueQuiet(ownKit.SOLT.Through.loss);
// Measurements
ui->open_touchstone->setFile(ownKit.SOLT.open_m.file);
ui->open_touchstone->selectPort(0, ownKit.SOLT.open_m.Sparam);
ui->short_touchstone->setFile(ownKit.SOLT.short_m.file);
ui->short_touchstone->selectPort(0, ownKit.SOLT.short_m.Sparam);
ui->load_touchstone->setFile(ownKit.SOLT.load_m.file);
ui->load_touchstone->selectPort(0, ownKit.SOLT.load_m.Sparam);
ui->open_touchstone_f->setFile(ownKit.SOLT.open_f.file);
ui->open_touchstone_f->selectPort(0, ownKit.SOLT.open_f.Sparam);
ui->short_touchstone_f->setFile(ownKit.SOLT.short_f.file);
ui->short_touchstone_f->selectPort(0, ownKit.SOLT.short_f.Sparam);
ui->load_touchstone_f->setFile(ownKit.SOLT.load_f.file);
ui->load_touchstone_f->selectPort(0, ownKit.SOLT.load_f.Sparam);
ui->through_touchstone->setFile(ownKit.SOLT.Through.file);
ui->through_touchstone->selectPort(0, ownKit.SOLT.Through.Sparam1);
ui->through_touchstone->selectPort(1, ownKit.SOLT.Through.Sparam2);
// Type
if (ownKit.SOLT.open_m.useMeasurements) {
ui->open_measurement->click();
} else {
ui->open_coefficients->click();
}
if (ownKit.SOLT.short_m.useMeasurements) {
ui->short_measurement->click();
} else {
ui->short_coefficients->click();
}
if (ownKit.SOLT.load_m.useMeasurements) {
ui->load_measurement->click();
} else {
ui->load_coefficients->click();
}
if (ownKit.SOLT.open_f.useMeasurements) {
ui->open_measurement_f->click();
} else {
ui->open_coefficients_f->click();
}
if (ownKit.SOLT.short_f.useMeasurements) {
ui->short_measurement_f->click();
} else {
ui->short_coefficients_f->click();
}
if (ownKit.SOLT.load_f.useMeasurements) {
ui->load_measurement_f->click();
} else {
ui->load_coefficients_f->click();
}
if (ownKit.SOLT.Through.useMeasurements) {
ui->through_measurement->click();
} else {
ui->through_coefficients->click();
}
if (ownKit.SOLT.separate_male_female) {
ui->cbStandardDefinition->setCurrentIndex(1);
} else {
ui->cbStandardDefinition->setCurrentIndex(0);
}
// TRL
ui->TRL_through_Z0->setValueQuiet(ownKit.TRL.Through.Z0);
if(ownKit.TRL.Reflection.isShort) {
ui->TRL_R_short->setChecked(true);
} else {
ui->TRL_R_open->setChecked(true);
}
ui->TRL_line_delay->setValueQuiet(ownKit.TRL.Line.delay);
ui->TRL_line_min->setValueQuiet(ownKit.TRL.Line.minFreq);
ui->TRL_line_max->setValueQuiet(ownKit.TRL.Line.maxFreq);
// Check if line length is appropriate for frequencies
auto minDelay = 20.0/(ownKit.TRL.Line.minFreq * 360.0) * 1e12;
auto maxDelay = 160.0/(ownKit.TRL.Line.maxFreq * 360.0) * 1e12;
if(ownKit.TRL.Line.delay < minDelay) {
ui->TRL_line_warning->setText("Line too short, minimum required delay is "+QString::number(minDelay, 'g', 3) + "ps");
} else if(ownKit.TRL.Line.delay > maxDelay) {
ui->TRL_line_warning->setText("Line too long, maximum allowed delay is "+QString::number(maxDelay, 'g', 3) + "ps");
} else {
ui->TRL_line_warning->clear();
}
if (ownKit.startDialogWithSOLT) {
ui->tabWidget->setCurrentIndex(0);
} else {
ui->tabWidget->setCurrentIndex(1);
}
ui->manufacturer->setText(kit.manufacturer);
ui->serialnumber->setText(kit.serialnumber);
ui->description->setPlainText(kit.description);
}
void CalkitDialog::updateStandardList()
{
auto row = ui->list->currentRow();
ui->list->clear();
for(auto s : kit.standards) {
ui->list->addItem(s->getDescription());
}
if(row >= 0) {
if(row < ui->list->count()) {
ui->list->setCurrentRow(row);
} else if(ui->list->count() > 0) {
ui->list->setCurrentRow(ui->list->count() - 1);
}
}
updateListEditButtons();
}

View File

@ -22,15 +22,15 @@ public:
signals:
void settingsChanged();
private:
private slots:
void updateListEditButtons();
private:
void parseEntries();
void updateEntries();
void updateStandardList();
Ui::CalkitDialog *ui;
bool open_ok, short_ok, load_ok, through_ok;
Calkit ownKit;
Calkit &editKit;
Calkit &kit;
};
#endif // CALKITDIALOG_H

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,33 @@
#include "ui_CalStandardOpenEditDialog.h"
#include "ui_CalStandardShortEditDialog.h"
#include "ui_CalStandardLoadEditDialog.h"
#include "ui_CalStandardThroughEditDialog.h"
#include "unit.h"
using namespace std;
using namespace CalStandard;
Virtual *Virtual::create(Virtual::Type type)
{
Virtual *ret = nullptr;
switch(type) {
case Type::Open: ret = new Open; break;
case Type::Short: ret = new Short; break;
case Type::Load: ret = new Load; break;
case Type::Through: ret = new Through; break;
}
return ret;
}
std::vector<Virtual::Type> Virtual::availableTypes()
{
std::vector<Type> ret;
for(int i=0;i<(int) Type::Last;i++) {
ret.push_back((Type) i);
}
return ret;
}
QString Virtual::TypeToString(Virtual::Type type)
{
switch(type) {
@ -134,7 +156,7 @@ std::complex<double> Open::toS11(double freq)
}
}
void Open::edit()
void Open::edit(std::function<void(void)> finishedCallback)
{
auto d = new QDialog;
auto ui = new Ui::CalStandardOpenEditDialog;
@ -196,6 +218,9 @@ void Open::edit()
C1 = ui->C1->value();
C2 = ui->C2->value();
C3 = ui->C3->value();
if(finishedCallback) {
finishedCallback();
}
});
d->show();
@ -246,7 +271,7 @@ std::complex<double> Short::toS11(double freq)
}
}
void Short::edit()
void Short::edit(std::function<void(void)> finishedCallback)
{
auto d = new QDialog;
auto ui = new Ui::CalStandardShortEditDialog;
@ -308,6 +333,9 @@ void Short::edit()
L1 = ui->L1->value();
L2 = ui->L2->value();
L3 = ui->L3->value();
if(finishedCallback) {
finishedCallback();
}
});
d->show();
@ -371,7 +399,7 @@ std::complex<double> Load::toS11(double freq)
}
}
void Load::edit()
void Load::edit(std::function<void(void)> finishedCallback)
{
auto d = new QDialog;
auto ui = new Ui::CalStandardLoadEditDialog;
@ -444,6 +472,9 @@ void Load::edit()
Cparallel = ui->parC->value();
Lseries = ui->serL->value();
Cfirst = ui->C_first->isChecked();
if(finishedCallback) {
finishedCallback();
}
});
d->show();
@ -513,6 +544,13 @@ void TwoPort::fromJSON(nlohmann::json j)
}
}
Through::Through()
{
Z0 = 50.0;
delay = 0.0;
loss = 0.0;
}
Sparam Through::toSparam(double freq)
{
if(touchstone) {
@ -529,12 +567,81 @@ Sparam Through::toSparam(double freq)
}
}
void Through::edit(std::function<void(void)> finishedCallback)
{
auto d = new QDialog;
auto ui = new Ui::CalStandardThroughEditDialog;
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);
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();
if(finishedCallback) {
finishedCallback();
}
});
d->show();
}
nlohmann::json Through::toJSON()
{
auto j = TwoPort::toJSON();
j["Z0"] = Z0;
j["delay"] = delay;
j["loss"] = loss;
return j;
}
void Through::fromJSON(nlohmann::json j)
{
TwoPort::fromJSON(j);
Z0 = j.value("Z0", 50.0);
delay = j.value("delay", 0.0);
loss = j.value("loss", 0.0);
}

View File

@ -6,6 +6,7 @@
#include "Tools/parameters.h"
#include <complex>
#include <functional>
namespace CalStandard
{
@ -28,6 +29,7 @@ public:
static Virtual* create(Type type);
static std::vector<Type> availableTypes();
static QString TypeToString(Type type);
static Type TypeFromString(QString s);
@ -35,7 +37,7 @@ public:
double minFrequency() {return minFreq;}
double maxFrequency() {return maxFreq;}
virtual void edit() = 0;
virtual void edit(std::function<void(void)> finishedCallback) = 0;
virtual QString getDescription();
virtual nlohmann::json toJSON() override;
@ -74,7 +76,7 @@ public:
Open();
virtual std::complex<double> toS11(double freq) override;
virtual void edit() override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
virtual Type getType() override {return Type::Open;}
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
@ -88,7 +90,7 @@ public:
Short();
virtual std::complex<double> toS11(double freq) override;
virtual void edit() override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
virtual Type getType() override {return Type::Short;}
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
@ -102,7 +104,7 @@ public:
Load();
virtual std::complex<double> toS11(double freq) override;
virtual void edit() override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
virtual Type getType() override {return Type::Load;}
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
@ -135,6 +137,7 @@ public:
Through();
virtual Sparam toSparam(double freq) override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
virtual Type getType() override {return Type::Through;}
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;

View File

@ -277,6 +277,7 @@ FORMS += \
Calibration/CalStandardLoadEditDialog.ui \
Calibration/CalStandardOpenEditDialog.ui \
Calibration/CalStandardShortEditDialog.ui \
Calibration/CalStandardThroughEditDialog.ui \
Calibration/addamplitudepointsdialog.ui \
Calibration/amplitudecaldialog.ui \
Calibration/automaticamplitudedialog.ui \