Through normalization and TRL calibration added

This commit is contained in:
Jan Käberich 2020-10-03 13:01:59 +02:00
parent c6d9796a11
commit 49917c4b19
10 changed files with 1109 additions and 533 deletions

View File

@ -42,31 +42,7 @@ void Calibration::addMeasurement(Calibration::Measurement type, Protocol::Datapo
bool Calibration::calculationPossible(Calibration::Type type)
{
std::vector<Measurement> requiredMeasurements;
switch(type) {
case Type::Port1SOL:
requiredMeasurements.push_back(Measurement::Port1Open);
requiredMeasurements.push_back(Measurement::Port1Short);
requiredMeasurements.push_back(Measurement::Port1Load);
break;
case Type::Port2SOL:
requiredMeasurements.push_back(Measurement::Port2Open);
requiredMeasurements.push_back(Measurement::Port2Short);
requiredMeasurements.push_back(Measurement::Port2Load);
break;
case Type::FullSOLT:
requiredMeasurements.push_back(Measurement::Port1Open);
requiredMeasurements.push_back(Measurement::Port1Short);
requiredMeasurements.push_back(Measurement::Port1Load);
requiredMeasurements.push_back(Measurement::Port2Open);
requiredMeasurements.push_back(Measurement::Port2Short);
requiredMeasurements.push_back(Measurement::Port2Load);
requiredMeasurements.push_back(Measurement::Through);
break;
case Type::None:
return false;
}
return SanityCheckSamples(requiredMeasurements);
return SanityCheckSamples(Measurements(type, false));
}
bool Calibration::constructErrorTerms(Calibration::Type type)
@ -74,23 +50,19 @@ bool Calibration::constructErrorTerms(Calibration::Type type)
if(!calculationPossible(type)) {
return false;
}
if(minFreq < kit.minFreq() || maxFreq > kit.maxFreq()) {
bool isTRL = type == Type::TRL;
if(minFreq < kit.minFreq(isTRL) || maxFreq > kit.maxFreq(isTRL)) {
// Calkit does not support complete calibration range
QMessageBox::critical(nullptr, "Unable to perform calibration", "The calibration kit does not support the complete span. Please choose a different calibration kit or a narrower span.");
return false;
}
switch(type) {
case Type::Port1SOL:
constructPort1SOL();
break;
case Type::Port2SOL:
constructPort2SOL();
break;
case Type::FullSOLT:
construct12TermPoints();
break;
case Type::None:
break;
case Type::Port1SOL: constructPort1SOL(); break;
case Type::Port2SOL: constructPort2SOL(); break;
case Type::FullSOLT: construct12TermPoints(); break;
case Type::TransmissionNormalization: constructTransmissionNormalization(); break;
case Type::TRL: constructTRL(); break;
case Type::None: break;
}
this->type = type;
return true;
@ -104,17 +76,7 @@ void Calibration::resetErrorTerms()
void Calibration::construct12TermPoints()
{
std::vector<Measurement> requiredMeasurements;
requiredMeasurements.push_back(Measurement::Port1Open);
requiredMeasurements.push_back(Measurement::Port1Short);
requiredMeasurements.push_back(Measurement::Port1Load);
requiredMeasurements.push_back(Measurement::Port2Open);
requiredMeasurements.push_back(Measurement::Port2Short);
requiredMeasurements.push_back(Measurement::Port2Load);
requiredMeasurements.push_back(Measurement::Through);
if(!SanityCheckSamples(requiredMeasurements)) {
throw runtime_error("Missing/wrong calibration measurement");
}
std::vector<Measurement> requiredMeasurements = Measurements(Type::FullSOLT);
requiredMeasurements.push_back(Measurement::Isolation);
bool isolation_measured = SanityCheckSamples(requiredMeasurements);
@ -141,11 +103,11 @@ void Calibration::construct12TermPoints()
auto S22_through = complex<double>(measurements[Measurement::Through].datapoints[i].real_S22, measurements[Measurement::Through].datapoints[i].imag_S22);
auto S12_through = complex<double>(measurements[Measurement::Through].datapoints[i].real_S12, measurements[Measurement::Through].datapoints[i].imag_S12);
auto actual = kit.toReflection(p.frequency);
auto actual = kit.toSOLT(p.frequency);
// Forward calibration
computeSOL(S11_short, S11_open, S11_load, p.fe00, p.fe11, p.fe10e01, actual.Open, actual.Short, actual.Load);
p.fe30 = S21_isolation;
// See page 17 of http://www2.electron.frba.utn.edu.ar/~jcecconi/Bibliografia/04%20-%20Param_S_y_VNA/Network_Analyzer_Error_Models_and_Calibration_Methods.pdf
// See page 18 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf
// Formulas for S11M and S21M solved for e22 and e10e32
auto deltaS = actual.ThroughS11*actual.ThroughS22 - actual.ThroughS21 * actual.ThroughS12;
p.fe22 = ((S11_through - p.fe00)*(1.0 - p.fe11 * actual.ThroughS11)-actual.ThroughS11*p.fe10e01)
@ -163,15 +125,6 @@ void Calibration::construct12TermPoints()
void Calibration::constructPort1SOL()
{
std::vector<Measurement> requiredMeasurements;
requiredMeasurements.push_back(Measurement::Port1Open);
requiredMeasurements.push_back(Measurement::Port1Short);
requiredMeasurements.push_back(Measurement::Port1Load);
if(!SanityCheckSamples(requiredMeasurements)) {
throw runtime_error("Missing/wrong calibration measurement");
}
// If we get here the calibration measurements are all okay
points.clear();
for(unsigned int i = 0;i<measurements[Measurement::Port1Open].datapoints.size();i++) {
Point p;
@ -181,8 +134,8 @@ void Calibration::constructPort1SOL()
auto S11_short = complex<double>(measurements[Measurement::Port1Short].datapoints[i].real_S11, measurements[Measurement::Port1Short].datapoints[i].imag_S11);
auto S11_load = complex<double>(measurements[Measurement::Port1Load].datapoints[i].real_S11, measurements[Measurement::Port1Load].datapoints[i].imag_S11);
// OSL port1
auto actual = kit.toReflection(p.frequency);
// See page 19 of http://www2.electron.frba.utn.edu.ar/~jcecconi/Bibliografia/04%20-%20Param_S_y_VNA/Network_Analyzer_Error_Models_and_Calibration_Methods.pdf
auto actual = kit.toSOLT(p.frequency);
// See page 13 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf
computeSOL(S11_short, S11_open, S11_load, p.fe00, p.fe11, p.fe10e01, actual.Open, actual.Short, actual.Load);
// All other calibration coefficients to ideal values
p.fe30 = 0.0;
@ -200,15 +153,6 @@ void Calibration::constructPort1SOL()
void Calibration::constructPort2SOL()
{
std::vector<Measurement> requiredMeasurements;
requiredMeasurements.push_back(Measurement::Port2Open);
requiredMeasurements.push_back(Measurement::Port2Short);
requiredMeasurements.push_back(Measurement::Port2Load);
if(!SanityCheckSamples(requiredMeasurements)) {
throw runtime_error("Missing/wrong calibration measurement");
}
// If we get here the calibration measurements are all okay
points.clear();
for(unsigned int i = 0;i<measurements[Measurement::Port2Open].datapoints.size();i++) {
Point p;
@ -218,8 +162,8 @@ void Calibration::constructPort2SOL()
auto S22_short = complex<double>(measurements[Measurement::Port2Short].datapoints[i].real_S22, measurements[Measurement::Port2Short].datapoints[i].imag_S22);
auto S22_load = complex<double>(measurements[Measurement::Port2Load].datapoints[i].real_S22, measurements[Measurement::Port2Load].datapoints[i].imag_S22);
// OSL port2
auto actual = kit.toReflection(p.frequency);
// See page 19 of http://www2.electron.frba.utn.edu.ar/~jcecconi/Bibliografia/04%20-%20Param_S_y_VNA/Network_Analyzer_Error_Models_and_Calibration_Methods.pdf
auto actual = kit.toSOLT(p.frequency);
// See page 19 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf
computeSOL(S22_short, S22_open, S22_load, p.re33, p.re22, p.re23e32, actual.Open, actual.Short, actual.Load);
// All other calibration coefficients to ideal values
p.fe30 = 0.0;
@ -235,6 +179,187 @@ void Calibration::constructPort2SOL()
}
}
void Calibration::constructTransmissionNormalization()
{
points.clear();
for(unsigned int i = 0;i<measurements[Measurement::Through].datapoints.size();i++) {
Point p;
p.frequency = measurements[Measurement::Through].datapoints[i].frequency;
// extract required complex reflection/transmission factors from datapoints
auto S21_through = complex<double>(measurements[Measurement::Through].datapoints[i].real_S21, measurements[Measurement::Through].datapoints[i].imag_S21);
auto S12_through = complex<double>(measurements[Measurement::Through].datapoints[i].real_S12, measurements[Measurement::Through].datapoints[i].imag_S12);
auto actual = kit.toSOLT(p.frequency);
p.fe10e32 = S21_through / actual.ThroughS21;
p.re23e01 = S12_through / actual.ThroughS12;
// All other calibration coefficients to ideal values
p.fe30 = 0.0;
p.fe22 = 0.0;
p.fe00 = 0.0;
p.fe11 = 0.0;
p.fe10e01 = 1.0;
p.re03 = 0.0;
p.re11 = 0.0;
p.re33 = 0.0;
p.re22 = 0.0;
p.re23e32 = 1.0;
points.push_back(p);
}
}
template<typename T>
class Tparam {
public:
Tparam(){};
Tparam(T t11, T t12, T t21, T t22)
: t11(t11), t12(t12), t21(t21), t22(t22){};
void fromSparam(T S11, T S21, T S12, T S22) {
t11 = -(S11*S22 - S12*S21) / S21;
t12 = S11 / S21;
t21 = -S22 / S21;
t22 = 1.0 / S21;
}
void toSparam(T &S11, T &S21, T &S12, T &S22) {
S11 = t12 / t22;
S21 = T(1) / t22;
S12 = (t11*t22 - t12*t21) / t22;
S22 = -t21 / t22;
}
Tparam inverse() {
Tparam i;
T det = t11*t22 - t12*t21;
i.t11 = t22 / det;
i.t12 = -t12 / det;
i.t21 = -t21 / det;
i.t22 = t11 / det;
return i;
}
Tparam operator*(const Tparam &r) {
Tparam p;
p.t11 = t11*r.t11 + t12*r.t21;
p.t12 = t11*r.t12 + t12*r.t22;
p.t21 = t21*r.t11 + t22*r.t21;
p.t22 = t21*r.t12 + t22*r.t22;
return p;
}
Tparam operator*(const T &r) {
Tparam p;
p.t11 = t11 * r;
p.t12 = t12 * r;
p.t21 = t21 * r;
p.t22 = t22 * r;
return p;
}
T t11, t12, t21, t22;
};
template<typename T> void solveQuadratic(T a, T b, T c, T &result1, T &result2)
{
T root = sqrt(b * b - T(4) * a * c);
result1 = (-b + root) / (T(2) * a);
result2 = (-b - root) / (T(2) * a);
}
void Calibration::constructTRL()
{
points.clear();
for(unsigned int i = 0;i<measurements[Measurement::Through].datapoints.size();i++) {
Point p;
p.frequency = measurements[Measurement::Through].datapoints[i].frequency;
// grab raw measurements
auto S11_through = complex<double>(measurements[Measurement::Through].datapoints[i].real_S11, measurements[Measurement::Through].datapoints[i].imag_S11);
auto S21_through = complex<double>(measurements[Measurement::Through].datapoints[i].real_S21, measurements[Measurement::Through].datapoints[i].imag_S21);
auto S22_through = complex<double>(measurements[Measurement::Through].datapoints[i].real_S22, measurements[Measurement::Through].datapoints[i].imag_S22);
auto S12_through = complex<double>(measurements[Measurement::Through].datapoints[i].real_S12, measurements[Measurement::Through].datapoints[i].imag_S12);
auto S11_line = complex<double>(measurements[Measurement::Line].datapoints[i].real_S11, measurements[Measurement::Line].datapoints[i].imag_S11);
auto S21_line = complex<double>(measurements[Measurement::Line].datapoints[i].real_S21, measurements[Measurement::Line].datapoints[i].imag_S21);
auto S22_line = complex<double>(measurements[Measurement::Line].datapoints[i].real_S22, measurements[Measurement::Line].datapoints[i].imag_S22);
auto S12_line = complex<double>(measurements[Measurement::Line].datapoints[i].real_S12, measurements[Measurement::Line].datapoints[i].imag_S12);
auto trl = kit.toTRL(p.frequency);
complex<double> S11_reflection, S22_reflection;
if(trl.reflectionIsNegative) {
// used short
S11_reflection = complex<double>(measurements[Measurement::Port1Short].datapoints[i].real_S11, measurements[Measurement::Port1Short].datapoints[i].imag_S11);
S22_reflection = complex<double>(measurements[Measurement::Port2Short].datapoints[i].real_S22, measurements[Measurement::Port2Short].datapoints[i].imag_S22);
} else {
// used open
S11_reflection = complex<double>(measurements[Measurement::Port1Open].datapoints[i].real_S11, measurements[Measurement::Port1Open].datapoints[i].imag_S11);
S22_reflection = complex<double>(measurements[Measurement::Port2Open].datapoints[i].real_S22, measurements[Measurement::Port2Open].datapoints[i].imag_S22);
}
// calculate TRL calibration
// variable names and formulas according to http://emlab.uiuc.edu/ece451/notes/new_TRL.pdf
// page 19
auto R_T = Tparam<complex<double>>();
auto R_D = Tparam<complex<double>>();
R_T.fromSparam(S11_through, S21_through, S12_through, S22_through);
R_D.fromSparam(S11_line, S21_line, S12_line, S22_line);
auto T = R_D*R_T.inverse();
complex<double> a_over_c, b;
// page 21-22
solveQuadratic(T.t21, T.t22 - T.t11, -T.t12, b, a_over_c);
// ensure correct root selection
// page 23
if(abs(b) >= abs(a_over_c)) {
swap(b, a_over_c);
}
// page 24
auto g = R_T.t22;
auto d = R_T.t11 / g;
auto e = R_T.t12 / g;
auto f = R_T.t21 / g;
// page 25
auto r22_rho22 = g * (1.0 - e / a_over_c) / (1.0 - b / a_over_c);
auto gamma = (f - d / a_over_c) / (1.0 - e / a_over_c);
auto beta_over_alpha = (e - b) / (d - b * f);
// page 26
auto alpha_a = (d - b * f) / (1.0 - e / a_over_c);
auto w1 = S11_reflection;
auto w2 = S22_reflection;
// page 28
auto a = sqrt((w1 - b) / (w2 + gamma) * (1.0 + w2 * beta_over_alpha) / (1.0 - w1 / a_over_c) * alpha_a);
// page 29, check sign of a
auto reflection = (w1 - b) / (a * (1.0 - w1 / a_over_c));
if((reflection.real() > 0 && trl.reflectionIsNegative) || (reflection.real() < 0 && !trl.reflectionIsNegative)) {
// wrong sign for a
a = -a;
}
// Revert back from error boxes with T parameters to S paramaters,
// page 17 + formulas for calculating S parameters from T parameters.
// Forward coefficients, normalize for S21 = 1.0 -> r22 = 1.0
auto r22 = complex<double>(1.0);
auto rho22 = r22_rho22 / r22;
auto alpha = alpha_a / a;
auto beta = beta_over_alpha * alpha;
auto c = a / a_over_c;
auto Box_A = Tparam<complex<double>>(r22 * a, r22 * b, r22 * c, r22);
auto Box_B = Tparam<complex<double>>(rho22 * alpha, rho22 * beta, rho22 * gamma, rho22);
// e00 is S11_A, S11 = T12/T22 = r22*a/r22
complex<double> dummy1, dummy2;
Box_A.toSparam(p.fe00, dummy1, p.fe10e01, p.fe11);
Box_B.toSparam(p.fe22, p.fe10e32, dummy1, dummy2);
// no isolation measurement available
p.fe30 = 0.0;
// Reverse coefficients, normalize for S12 = 1.0
// => det(T)/T22 = 1.0
// => (rho22*alpa*rho22 - rho22*beta*rho*gamma)/rho22 = 1.0
// => rho22*alpha - rho22*beta*gamma = 1.0
// => rho22 = 1.0/(alpha - beta * gamma)
rho22 = 1.0/(alpha - beta * gamma);
r22 = r22_rho22 / rho22;
Box_A = Tparam<complex<double>>(r22 * a, r22 * b, r22 * c, r22);
Box_B = Tparam<complex<double>>(rho22 * alpha, rho22 * beta, rho22 * gamma, rho22);
Box_A.toSparam(dummy1, dummy2, p.re23e01, p.re11);
Box_B.toSparam(p.re22, p.re23e32, dummy1, p.re33);
// no isolation measurement available
p.re03 = 0.0;
points.push_back(p);
}
}
void Calibration::correctMeasurement(Protocol::Datapoint &d)
{
if(type == Type::None) {
@ -250,15 +375,17 @@ void Calibration::correctMeasurement(Protocol::Datapoint &d)
// find correct entry
auto p = getCalibrationPoint(d);
// equations from page 20 of http://www2.electron.frba.utn.edu.ar/~jcecconi/Bibliografia/04%20-%20Param_S_y_VNA/Network_Analyzer_Error_Models_and_Calibration_Methods.pdf
complex<double> S11, S12, S21, S22;
// equations from page 19 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf
auto denom = (1.0 + (S11m - p.fe00) / p.fe10e01 * p.fe11) * (1.0 + (S22m - p.re33) / p.re23e32 * p.re22)
- (S21m - p.fe30) / p.fe10e32 * (S12m - p.re03) / p.re23e01 * p.fe22 * p.re11;
auto S11 = ((S11m - p.fe00) / p.fe10e01 * (1.0 + (S22m - p.re33) / p.re23e32 * p.re22)
S11 = ((S11m - p.fe00) / p.fe10e01 * (1.0 + (S22m - p.re33) / p.re23e32 * p.re22)
- p.fe22 * (S21m - p.fe30) / p.fe10e32 * (S12m - p.re03) / p.re23e01) / denom;
auto S21 = ((S21m - p.fe30) / p.fe10e32 * (1.0 + (S22m - p.re33) / p.re23e32 * (p.re22 - p.fe22))) / denom;
auto S22 = ((S22m - p.re33) / p.re23e32 * (1.0 + (S11m - p.fe00) / p.fe10e01 * p.fe11)
S21 = ((S21m - p.fe30) / p.fe10e32 * (1.0 + (S22m - p.re33) / p.re23e32 * (p.re22 - p.fe22))) / denom;
S22 = ((S22m - p.re33) / p.re23e32 * (1.0 + (S11m - p.fe00) / p.fe10e01 * p.fe11)
- p.re11 * (S21m - p.fe30) / p.fe10e32 * (S12m - p.re03) / p.re23e01) / denom;
auto S12 = ((S12m - p.re03) / p.re23e01 * (1.0 + (S11m - p.fe00) / p.fe10e01 * (p.fe11 - p.re11))) / denom;
S12 = ((S12m - p.re03) / p.re23e01 * (1.0 + (S11m - p.fe00) / p.fe10e01 * (p.fe11 - p.re11))) / denom;
d.real_S11 = S11.real();
d.imag_S11 = S11.imag();
@ -319,6 +446,8 @@ QString Calibration::MeasurementToString(Calibration::Measurement m)
return "Through";
case Measurement::Isolation:
return "Isolation";
case Measurement::Line:
return "Line";
default:
return "Unknown";
}
@ -330,22 +459,32 @@ QString Calibration::TypeToString(Calibration::Type t)
case Type::Port1SOL: return "Port 1"; break;
case Type::Port2SOL: return "Port 2"; break;
case Type::FullSOLT: return "SOLT"; break;
case Type::TransmissionNormalization: return "Normalize"; break;
case Type::TRL: return "TRL"; break;
default: return "None"; break;
}
}
const std::vector<Calibration::Type> Calibration::Types()
{
const std::vector<Calibration::Type> ret = {Type::Port1SOL, Type::Port2SOL, Type::FullSOLT};
const std::vector<Calibration::Type> ret = {Type::Port1SOL, Type::Port2SOL, Type::FullSOLT, Type::TransmissionNormalization, Type::TRL};
return ret;
}
const std::vector<Calibration::Measurement> Calibration::Measurements(Calibration::Type type)
const std::vector<Calibration::Measurement> Calibration::Measurements(Calibration::Type type, bool optional_included)
{
switch(type) {
case Type::FullSOLT:
case Type::None:
return {Measurement::Port1Short, Measurement::Port1Open, Measurement::Port1Load, Measurement::Port2Short, Measurement::Port2Open, Measurement::Port2Load, Measurement::Through, Measurement::Isolation};
// all possible measurements
return {Measurement::Port1Short, Measurement::Port1Open, Measurement::Port1Load,
Measurement::Port2Short, Measurement::Port2Open, Measurement::Port2Load,
Measurement::Through, Measurement::Isolation, Measurement::Line};
case Type::FullSOLT:
if(optional_included) {
return {Measurement::Port1Short, Measurement::Port1Open, Measurement::Port1Load, Measurement::Port2Short, Measurement::Port2Open, Measurement::Port2Load, Measurement::Through, Measurement::Isolation};
} else {
return {Measurement::Port1Short, Measurement::Port1Open, Measurement::Port1Load, Measurement::Port2Short, Measurement::Port2Open, Measurement::Port2Load, Measurement::Through};
}
break;
case Type::Port1SOL:
return {Measurement::Port1Short, Measurement::Port1Open, Measurement::Port1Load};
@ -353,6 +492,16 @@ const std::vector<Calibration::Measurement> Calibration::Measurements(Calibratio
case Type::Port2SOL:
return {Measurement::Port2Short, Measurement::Port2Open, Measurement::Port2Load};
break;
case Type::TransmissionNormalization:
return {Measurement::Through};
break;
case Type::TRL:
if(kit.isTRLReflectionShort()) {
return {Measurement::Through, Measurement::Port1Short, Measurement::Port2Short, Measurement::Line};
} else {
return {Measurement::Through, Measurement::Port1Open, Measurement::Port2Open, Measurement::Line};
}
break;
default:
return {};
break;
@ -394,6 +543,11 @@ Calibration::MeasurementInfo Calibration::getMeasurementInfo(Calibration::Measur
case Measurement::Isolation:
info.name = "Isolation";
info.prerequisites = "Both ports terminated into 50 ohm";
break;
case Measurement::Line:
info.name = "Line";
info.prerequisites = "Port 1 connected to port 2 via line standard";
break;
}
info.points = measurements[m].datapoints.size();
if(info.points > 0) {
@ -524,7 +678,7 @@ istream& operator >>(istream &in, Calibration &c)
{
std::string line;
while(getline(in, line)) {
for(auto m : Calibration::Measurements()) {
for(auto m : c.Measurements()) {
if(Calibration::MeasurementToString(m) == QString::fromStdString(line)) {
// this is the correct measurement
c.clearMeasurement(m);
@ -561,7 +715,7 @@ istream& operator >>(istream &in, Calibration &c)
return in;
}
bool Calibration::SanityCheckSamples(std::vector<Calibration::Measurement> &requiredMeasurements)
bool Calibration::SanityCheckSamples(const std::vector<Calibration::Measurement> &requiredMeasurements)
{
// sanity check measurements, all need to be of the same size with the same frequencies (except for isolation which may be empty)
vector<uint64_t> freqs;

View File

@ -26,6 +26,7 @@ public:
Port2Load,
Isolation,
Through,
Line,
};
void clearMeasurements();
void clearMeasurement(Measurement type);
@ -35,6 +36,8 @@ public:
Port1SOL,
Port2SOL,
FullSOLT,
TransmissionNormalization,
TRL,
None,
};
@ -67,7 +70,7 @@ public:
};
static const std::vector<Type> Types();
static const std::vector<Measurement> Measurements(Type type = Type::None);
const std::vector<Measurement> Measurements(Type type = Type::None, bool optional_included = true);
MeasurementInfo getMeasurementInfo(Measurement m);
friend std::ostream& operator<<(std::ostream& os, const Calibration& c);
@ -89,7 +92,9 @@ private:
void construct12TermPoints();
void constructPort1SOL();
void constructPort2SOL();
bool SanityCheckSamples(std::vector<Measurement> &requiredMeasurements);
void constructTransmissionNormalization();
void constructTRL();
bool SanityCheckSamples(const std::vector<Measurement> &requiredMeasurements);
class Point
{
public:

View File

@ -64,7 +64,9 @@ void CalibrationTraceDialog::on_bOpen_clicked()
{
cal->openFromFile();
UpdateApplyButton();
emit applyCalibration(cal->getType());
if(cal->getType() != Calibration::Type::None) {
emit applyCalibration(cal->getType());
}
}
void CalibrationTraceDialog::on_bSave_clicked()

View File

@ -51,6 +51,12 @@ Calkit::Calkit()
load_Sparam = 0;
through_Sparam1 = 0;
through_Sparam2 = 1;
TRL_through_Z0 = 50.0;
TRL_reflection_short = true;
TRL_line_delay = 74;
TRL_line_maxfreq = 6000000000;
TRL_line_minfreq = 751000000;
}
void Calkit::toFile(std::string filename)
@ -75,6 +81,11 @@ void Calkit::toFile(std::string filename)
if(through_measurements) {
file << through_file << "\n" << through_Sparam1 << "\n" << through_Sparam2 << "\n";
}
file << TRL_through_Z0 << "\n";
file << TRL_reflection_short << "\n";
file << TRL_line_delay << "\n";
file << TRL_line_minfreq << "\n";
file << TRL_line_maxfreq << "\n";
file.close();
}
@ -125,6 +136,11 @@ Calkit Calkit::fromFile(std::string filename)
file >> c.through_Sparam1;
file >> c.through_Sparam2;
}
file >> c.TRL_through_Z0;
file >> c.TRL_reflection_short;
file >> c.TRL_line_delay;
file >> c.TRL_line_minfreq;
file >> c.TRL_line_maxfreq;
file.close();
return c;
}
@ -135,10 +151,10 @@ void Calkit::edit()
dialog->show();
}
Calkit::Reflection Calkit::toReflection(double frequency)
Calkit::SOLT Calkit::toSOLT(double frequency)
{
fillTouchstoneCache();
Reflection ref;
SOLT ref;
if(load_measurements) {
ref.Load = ts_load->interpolate(frequency).S[0];
} else {
@ -204,40 +220,66 @@ Calkit::Reflection Calkit::toReflection(double frequency)
return ref;
}
double Calkit::minFreq()
Calkit::TRL Calkit::toTRL(double)
{
fillTouchstoneCache();
double min = std::numeric_limits<double>::min();
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;
TRL trl;
// reflection coefficent sign depends on whether an open or short is used
trl.reflectionIsNegative = TRL_reflection_short;
// assume ideal through for now
trl.ThroughS11 = 0.0;
trl.ThroughS12 = 1.0;
trl.ThroughS21 = 1.0;
trl.ThroughS22 = 0.0;
return trl;
}
double Calkit::maxFreq()
double Calkit::minFreq(bool TRL)
{
fillTouchstoneCache();
double max = std::numeric_limits<double>::max();
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();
if(TRL) {
return TRL_line_minfreq;
} else {
fillTouchstoneCache();
double min = std::numeric_limits<double>::min();
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 max;
}
double Calkit::maxFreq(bool TRL)
{
if(TRL) {
return TRL_line_maxfreq;
} else {
fillTouchstoneCache();
double max = std::numeric_limits<double>::max();
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;
}
}
bool Calkit::isTRLReflectionShort() const
{
return TRL_reflection_short;
}
void Calkit::clearTouchstoneCache()

View File

@ -11,7 +11,7 @@ class Calkit
public:
Calkit();
class Reflection {
class SOLT {
public:
std::complex<double> Open;
std::complex<double> Short;
@ -19,13 +19,23 @@ public:
std::complex<double> ThroughS11, ThroughS12, ThroughS21, ThroughS22;
};
class TRL {
public:
bool reflectionIsNegative;
std::complex<double> ThroughS11, ThroughS12, ThroughS21, ThroughS22;
};
void toFile(std::string filename);
static Calkit fromFile(std::string filename);
void edit();
Reflection toReflection(double frequency);
double minFreq();
double maxFreq();
SOLT toSOLT(double frequency);
TRL toTRL(double frequency);
double minFreq(bool TRL = false);
double maxFreq(bool TRL = false);
bool isTRLReflectionShort() const;
private:
// SOLT standard definitions
double open_Z0, open_delay, open_loss, open_C0, open_C1, open_C2, open_C3;
double short_Z0, short_delay, short_loss, short_L0, short_L1, short_L2, short_L3;
double load_Z0;
@ -37,6 +47,13 @@ private:
bool load_measurements;
bool through_measurements;
// TRL standard definitions
double TRL_through_Z0;
bool TRL_reflection_short;
double TRL_line_delay;
double TRL_line_minfreq;
double TRL_line_maxfreq;
std::string open_file, short_file, load_file, through_file;
int open_Sparam, short_Sparam, load_Sparam, through_Sparam1, through_Sparam2;

View File

@ -38,10 +38,30 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
ui->load_touchstone->setPorts(1);
ui->through_touchstone->setPorts(2);
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;
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->text().toDouble();
updateEntries();
});
auto UpdateStatus = [=]() {
bool ok = true;
if(ui->open_measurement->isChecked() && !ui->open_touchstone->getStatus()) {
@ -110,7 +130,6 @@ CalkitDialog::~CalkitDialog()
void CalkitDialog::parseEntries()
{
// type
ownKit.open_measurements = ui->open_measurement->isChecked();
ownKit.short_measurements = ui->short_measurement->isChecked();
@ -151,6 +170,13 @@ void CalkitDialog::parseEntries()
ownKit.load_Sparam = ui->load_touchstone->getPorts()[0];
ownKit.through_Sparam1 = ui->through_touchstone->getPorts()[0];
ownKit.through_Sparam2 = ui->through_touchstone->getPorts()[1];
// TRL
ownKit.TRL_through_Z0 = ui->TRL_through_Z0->text().toDouble();
ownKit.TRL_reflection_short = ui->TRL_R_short->isChecked();
ownKit.TRL_line_delay = ui->TRL_line_delay->text().toDouble();
ownKit.TRL_line_minfreq = ui->TRL_line_min->value();
ownKit.TRL_line_maxfreq = ui->TRL_line_max->value();
}
void CalkitDialog::updateEntries()
@ -216,4 +242,25 @@ void CalkitDialog::updateEntries()
} else {
ui->through_coefficients->click();
}
// TRL
ui->TRL_through_Z0->setText(QString::number(ownKit.TRL_through_Z0));
if(ownKit.TRL_reflection_short) {
ui->TRL_R_short->setChecked(true);
} else {
ui->TRL_R_open->setChecked(true);
}
ui->TRL_line_delay->setText(QString::number(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();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,6 @@ QVariant MeasurementModel::data(const QModelIndex &index, int role) const
case ColIndexDescription: return 500; break;
case ColIndexData: return 300; break;
case ColIndexDate: return 300; break;
case ColIndexStatusSymbol: return 150; break;
default: return QVariant(); break;
}
}
@ -69,7 +68,6 @@ QVariant MeasurementModel::headerData(int section, Qt::Orientation orientation,
case ColIndexDescription: return "Prerequisites"; break;
case ColIndexData: return "Statistics"; break;
case ColIndexDate: return "Timestamp"; break;
case ColIndexStatusSymbol: return "Status"; break;
default: return QVariant(); break;
}
} else {

View File

@ -26,7 +26,6 @@ private:
ColIndexDescription,
ColIndexData,
ColIndexDate,
ColIndexStatusSymbol,
ColIndexLast
};
Calibration *cal;

View File

@ -702,10 +702,11 @@ void VNA::ApplyCalibration(Calibration::Type type)
{
if(cal.calculationPossible(type)) {
try {
cal.constructErrorTerms(type);
calValid = true;
average.reset();
emit CalibrationApplied(type);
if(cal.constructErrorTerms(type)) {
calValid = true;
average.reset();
emit CalibrationApplied(type);
}
} catch (runtime_error e) {
QMessageBox::critical(this, "Calibration failure", e.what());
DisableCalibration(true);