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) bool Calibration::calculationPossible(Calibration::Type type)
{ {
std::vector<Measurement> requiredMeasurements; return SanityCheckSamples(Measurements(type, false));
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);
} }
bool Calibration::constructErrorTerms(Calibration::Type type) bool Calibration::constructErrorTerms(Calibration::Type type)
@ -74,23 +50,19 @@ bool Calibration::constructErrorTerms(Calibration::Type type)
if(!calculationPossible(type)) { if(!calculationPossible(type)) {
return false; 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 // 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."); 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; return false;
} }
switch(type) { switch(type) {
case Type::Port1SOL: case Type::Port1SOL: constructPort1SOL(); break;
constructPort1SOL(); case Type::Port2SOL: constructPort2SOL(); break;
break; case Type::FullSOLT: construct12TermPoints(); break;
case Type::Port2SOL: case Type::TransmissionNormalization: constructTransmissionNormalization(); break;
constructPort2SOL(); case Type::TRL: constructTRL(); break;
break; case Type::None: break;
case Type::FullSOLT:
construct12TermPoints();
break;
case Type::None:
break;
} }
this->type = type; this->type = type;
return true; return true;
@ -104,17 +76,7 @@ void Calibration::resetErrorTerms()
void Calibration::construct12TermPoints() void Calibration::construct12TermPoints()
{ {
std::vector<Measurement> requiredMeasurements; std::vector<Measurement> requiredMeasurements = Measurements(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);
if(!SanityCheckSamples(requiredMeasurements)) {
throw runtime_error("Missing/wrong calibration measurement");
}
requiredMeasurements.push_back(Measurement::Isolation); requiredMeasurements.push_back(Measurement::Isolation);
bool isolation_measured = SanityCheckSamples(requiredMeasurements); 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 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 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 // Forward calibration
computeSOL(S11_short, S11_open, S11_load, p.fe00, p.fe11, p.fe10e01, actual.Open, actual.Short, actual.Load); computeSOL(S11_short, S11_open, S11_load, p.fe00, p.fe11, p.fe10e01, actual.Open, actual.Short, actual.Load);
p.fe30 = S21_isolation; 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 // Formulas for S11M and S21M solved for e22 and e10e32
auto deltaS = actual.ThroughS11*actual.ThroughS22 - actual.ThroughS21 * actual.ThroughS12; 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) 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() 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(); points.clear();
for(unsigned int i = 0;i<measurements[Measurement::Port1Open].datapoints.size();i++) { for(unsigned int i = 0;i<measurements[Measurement::Port1Open].datapoints.size();i++) {
Point p; 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_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); auto S11_load = complex<double>(measurements[Measurement::Port1Load].datapoints[i].real_S11, measurements[Measurement::Port1Load].datapoints[i].imag_S11);
// OSL port1 // OSL port1
auto actual = kit.toReflection(p.frequency); auto actual = kit.toSOLT(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 // 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); 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 // All other calibration coefficients to ideal values
p.fe30 = 0.0; p.fe30 = 0.0;
@ -200,15 +153,6 @@ void Calibration::constructPort1SOL()
void Calibration::constructPort2SOL() 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(); points.clear();
for(unsigned int i = 0;i<measurements[Measurement::Port2Open].datapoints.size();i++) { for(unsigned int i = 0;i<measurements[Measurement::Port2Open].datapoints.size();i++) {
Point p; 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_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); auto S22_load = complex<double>(measurements[Measurement::Port2Load].datapoints[i].real_S22, measurements[Measurement::Port2Load].datapoints[i].imag_S22);
// OSL port2 // OSL port2
auto actual = kit.toReflection(p.frequency); auto actual = kit.toSOLT(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 // 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); 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 // All other calibration coefficients to ideal values
p.fe30 = 0.0; 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) void Calibration::correctMeasurement(Protocol::Datapoint &d)
{ {
if(type == Type::None) { if(type == Type::None) {
@ -250,15 +375,17 @@ void Calibration::correctMeasurement(Protocol::Datapoint &d)
// find correct entry // find correct entry
auto p = getCalibrationPoint(d); 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) 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; - (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; - 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; 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) 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; - 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.real_S11 = S11.real();
d.imag_S11 = S11.imag(); d.imag_S11 = S11.imag();
@ -319,6 +446,8 @@ QString Calibration::MeasurementToString(Calibration::Measurement m)
return "Through"; return "Through";
case Measurement::Isolation: case Measurement::Isolation:
return "Isolation"; return "Isolation";
case Measurement::Line:
return "Line";
default: default:
return "Unknown"; return "Unknown";
} }
@ -330,22 +459,32 @@ QString Calibration::TypeToString(Calibration::Type t)
case Type::Port1SOL: return "Port 1"; break; case Type::Port1SOL: return "Port 1"; break;
case Type::Port2SOL: return "Port 2"; break; case Type::Port2SOL: return "Port 2"; break;
case Type::FullSOLT: return "SOLT"; break; case Type::FullSOLT: return "SOLT"; break;
case Type::TransmissionNormalization: return "Normalize"; break;
case Type::TRL: return "TRL"; break;
default: return "None"; break; default: return "None"; break;
} }
} }
const std::vector<Calibration::Type> Calibration::Types() 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; 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) { switch(type) {
case Type::FullSOLT:
case Type::None: 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; break;
case Type::Port1SOL: case Type::Port1SOL:
return {Measurement::Port1Short, Measurement::Port1Open, Measurement::Port1Load}; return {Measurement::Port1Short, Measurement::Port1Open, Measurement::Port1Load};
@ -353,6 +492,16 @@ const std::vector<Calibration::Measurement> Calibration::Measurements(Calibratio
case Type::Port2SOL: case Type::Port2SOL:
return {Measurement::Port2Short, Measurement::Port2Open, Measurement::Port2Load}; return {Measurement::Port2Short, Measurement::Port2Open, Measurement::Port2Load};
break; 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: default:
return {}; return {};
break; break;
@ -394,6 +543,11 @@ Calibration::MeasurementInfo Calibration::getMeasurementInfo(Calibration::Measur
case Measurement::Isolation: case Measurement::Isolation:
info.name = "Isolation"; info.name = "Isolation";
info.prerequisites = "Both ports terminated into 50 ohm"; 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(); info.points = measurements[m].datapoints.size();
if(info.points > 0) { if(info.points > 0) {
@ -524,7 +678,7 @@ istream& operator >>(istream &in, Calibration &c)
{ {
std::string line; std::string line;
while(getline(in, line)) { while(getline(in, line)) {
for(auto m : Calibration::Measurements()) { for(auto m : c.Measurements()) {
if(Calibration::MeasurementToString(m) == QString::fromStdString(line)) { if(Calibration::MeasurementToString(m) == QString::fromStdString(line)) {
// this is the correct measurement // this is the correct measurement
c.clearMeasurement(m); c.clearMeasurement(m);
@ -561,7 +715,7 @@ istream& operator >>(istream &in, Calibration &c)
return in; 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) // 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; vector<uint64_t> freqs;

View File

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

View File

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

View File

@ -51,6 +51,12 @@ Calkit::Calkit()
load_Sparam = 0; load_Sparam = 0;
through_Sparam1 = 0; through_Sparam1 = 0;
through_Sparam2 = 1; 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) void Calkit::toFile(std::string filename)
@ -75,6 +81,11 @@ void Calkit::toFile(std::string filename)
if(through_measurements) { if(through_measurements) {
file << through_file << "\n" << through_Sparam1 << "\n" << through_Sparam2 << "\n"; 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(); file.close();
} }
@ -125,6 +136,11 @@ Calkit Calkit::fromFile(std::string filename)
file >> c.through_Sparam1; file >> c.through_Sparam1;
file >> c.through_Sparam2; 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(); file.close();
return c; return c;
} }
@ -135,10 +151,10 @@ void Calkit::edit()
dialog->show(); dialog->show();
} }
Calkit::Reflection Calkit::toReflection(double frequency) Calkit::SOLT Calkit::toSOLT(double frequency)
{ {
fillTouchstoneCache(); fillTouchstoneCache();
Reflection ref; SOLT ref;
if(load_measurements) { if(load_measurements) {
ref.Load = ts_load->interpolate(frequency).S[0]; ref.Load = ts_load->interpolate(frequency).S[0];
} else { } else {
@ -204,40 +220,66 @@ Calkit::Reflection Calkit::toReflection(double frequency)
return ref; return ref;
} }
double Calkit::minFreq() Calkit::TRL Calkit::toTRL(double)
{ {
fillTouchstoneCache(); TRL trl;
double min = std::numeric_limits<double>::min(); // reflection coefficent sign depends on whether an open or short is used
array<Touchstone*, 4> ts_list = {ts_open, ts_short, ts_load, ts_through}; trl.reflectionIsNegative = TRL_reflection_short;
// find the highest minimum frequency in all measurement files // assume ideal through for now
for(auto ts : ts_list) { trl.ThroughS11 = 0.0;
if(!ts) { trl.ThroughS12 = 1.0;
// this calibration standard is defined by coefficients, no minimum frequency trl.ThroughS21 = 1.0;
continue; trl.ThroughS22 = 0.0;
} return trl;
if(ts->minFreq() > min) {
min = ts->minFreq();
}
}
return min;
} }
double Calkit::maxFreq() double Calkit::minFreq(bool TRL)
{ {
fillTouchstoneCache(); if(TRL) {
double max = std::numeric_limits<double>::max(); return TRL_line_minfreq;
array<Touchstone*, 4> ts_list = {ts_open, ts_short, ts_load, ts_through}; } else {
// find the highest minimum frequency in all measurement files fillTouchstoneCache();
for(auto ts : ts_list) { double min = std::numeric_limits<double>::min();
if(!ts) { array<Touchstone*, 4> ts_list = {ts_open, ts_short, ts_load, ts_through};
// this calibration standard is defined by coefficients, no minimum frequency // find the highest minimum frequency in all measurement files
continue; for(auto ts : ts_list) {
} if(!ts) {
if(ts->maxFreq() < max) { // this calibration standard is defined by coefficients, no minimum frequency
max = ts->maxFreq(); 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() void Calkit::clearTouchstoneCache()

View File

@ -11,7 +11,7 @@ class Calkit
public: public:
Calkit(); Calkit();
class Reflection { class SOLT {
public: public:
std::complex<double> Open; std::complex<double> Open;
std::complex<double> Short; std::complex<double> Short;
@ -19,13 +19,23 @@ public:
std::complex<double> ThroughS11, ThroughS12, ThroughS21, ThroughS22; std::complex<double> ThroughS11, ThroughS12, ThroughS21, ThroughS22;
}; };
class TRL {
public:
bool reflectionIsNegative;
std::complex<double> ThroughS11, ThroughS12, ThroughS21, ThroughS22;
};
void toFile(std::string filename); void toFile(std::string filename);
static Calkit fromFile(std::string filename); static Calkit fromFile(std::string filename);
void edit(); void edit();
Reflection toReflection(double frequency); SOLT toSOLT(double frequency);
double minFreq(); TRL toTRL(double frequency);
double maxFreq(); double minFreq(bool TRL = false);
double maxFreq(bool TRL = false);
bool isTRLReflectionShort() const;
private: private:
// SOLT standard definitions
double open_Z0, open_delay, open_loss, open_C0, open_C1, open_C2, open_C3; 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 short_Z0, short_delay, short_loss, short_L0, short_L1, short_L2, short_L3;
double load_Z0; double load_Z0;
@ -37,6 +47,13 @@ private:
bool load_measurements; bool load_measurements;
bool through_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; std::string open_file, short_file, load_file, through_file;
int open_Sparam, short_Sparam, load_Sparam, through_Sparam1, through_Sparam2; 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->load_touchstone->setPorts(1);
ui->through_touchstone->setPorts(2); 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(); editKit.clearTouchstoneCache();
ownKit = editKit; ownKit = editKit;
updateEntries(); 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 = [=]() { auto UpdateStatus = [=]() {
bool ok = true; bool ok = true;
if(ui->open_measurement->isChecked() && !ui->open_touchstone->getStatus()) { if(ui->open_measurement->isChecked() && !ui->open_touchstone->getStatus()) {
@ -110,7 +130,6 @@ CalkitDialog::~CalkitDialog()
void CalkitDialog::parseEntries() void CalkitDialog::parseEntries()
{ {
// type // type
ownKit.open_measurements = ui->open_measurement->isChecked(); ownKit.open_measurements = ui->open_measurement->isChecked();
ownKit.short_measurements = ui->short_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.load_Sparam = ui->load_touchstone->getPorts()[0];
ownKit.through_Sparam1 = ui->through_touchstone->getPorts()[0]; ownKit.through_Sparam1 = ui->through_touchstone->getPorts()[0];
ownKit.through_Sparam2 = ui->through_touchstone->getPorts()[1]; 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() void CalkitDialog::updateEntries()
@ -216,4 +242,25 @@ void CalkitDialog::updateEntries()
} else { } else {
ui->through_coefficients->click(); 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 ColIndexDescription: return 500; break;
case ColIndexData: return 300; break; case ColIndexData: return 300; break;
case ColIndexDate: return 300; break; case ColIndexDate: return 300; break;
case ColIndexStatusSymbol: return 150; break;
default: return QVariant(); break; default: return QVariant(); break;
} }
} }
@ -69,7 +68,6 @@ QVariant MeasurementModel::headerData(int section, Qt::Orientation orientation,
case ColIndexDescription: return "Prerequisites"; break; case ColIndexDescription: return "Prerequisites"; break;
case ColIndexData: return "Statistics"; break; case ColIndexData: return "Statistics"; break;
case ColIndexDate: return "Timestamp"; break; case ColIndexDate: return "Timestamp"; break;
case ColIndexStatusSymbol: return "Status"; break;
default: return QVariant(); break; default: return QVariant(); break;
} }
} else { } else {

View File

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

View File

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