From 49917c4b199f40f8dd0806c82388d259c47e4615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Sat, 3 Oct 2020 13:01:59 +0200 Subject: [PATCH] Through normalization and TRL calibration added --- .../Calibration/calibration.cpp | 320 +++-- .../PC_Application/Calibration/calibration.h | 9 +- .../Calibration/calibrationtracedialog.cpp | 4 +- .../PC_Application/Calibration/calkit.cpp | 102 +- Software/PC_Application/Calibration/calkit.h | 25 +- .../Calibration/calkitdialog.cpp | 49 +- .../Calibration/calkitdialog.ui | 1121 +++++++++++------ .../Calibration/measurementmodel.cpp | 2 - .../Calibration/measurementmodel.h | 1 - Software/PC_Application/VNA/vna.cpp | 9 +- 10 files changed, 1109 insertions(+), 533 deletions(-) diff --git a/Software/PC_Application/Calibration/calibration.cpp b/Software/PC_Application/Calibration/calibration.cpp index a7bc99c..6a7cfbe 100644 --- a/Software/PC_Application/Calibration/calibration.cpp +++ b/Software/PC_Application/Calibration/calibration.cpp @@ -42,31 +42,7 @@ void Calibration::addMeasurement(Calibration::Measurement type, Protocol::Datapo bool Calibration::calculationPossible(Calibration::Type type) { - std::vector 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 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 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(measurements[Measurement::Through].datapoints[i].real_S22, measurements[Measurement::Through].datapoints[i].imag_S22); auto S12_through = complex(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 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::Port1Short].datapoints[i].real_S11, measurements[Measurement::Port1Short].datapoints[i].imag_S11); auto S11_load = complex(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 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::Port2Short].datapoints[i].real_S22, measurements[Measurement::Port2Short].datapoints[i].imag_S22); auto S22_load = complex(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[i].real_S21, measurements[Measurement::Through].datapoints[i].imag_S21); + auto S12_through = complex(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 +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 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[i].real_S11, measurements[Measurement::Through].datapoints[i].imag_S11); + auto S21_through = complex(measurements[Measurement::Through].datapoints[i].real_S21, measurements[Measurement::Through].datapoints[i].imag_S21); + auto S22_through = complex(measurements[Measurement::Through].datapoints[i].real_S22, measurements[Measurement::Through].datapoints[i].imag_S22); + auto S12_through = complex(measurements[Measurement::Through].datapoints[i].real_S12, measurements[Measurement::Through].datapoints[i].imag_S12); + auto S11_line = complex(measurements[Measurement::Line].datapoints[i].real_S11, measurements[Measurement::Line].datapoints[i].imag_S11); + auto S21_line = complex(measurements[Measurement::Line].datapoints[i].real_S21, measurements[Measurement::Line].datapoints[i].imag_S21); + auto S22_line = complex(measurements[Measurement::Line].datapoints[i].real_S22, measurements[Measurement::Line].datapoints[i].imag_S22); + auto S12_line = complex(measurements[Measurement::Line].datapoints[i].real_S12, measurements[Measurement::Line].datapoints[i].imag_S12); + auto trl = kit.toTRL(p.frequency); + complex S11_reflection, S22_reflection; + if(trl.reflectionIsNegative) { + // used short + S11_reflection = complex(measurements[Measurement::Port1Short].datapoints[i].real_S11, measurements[Measurement::Port1Short].datapoints[i].imag_S11); + S22_reflection = complex(measurements[Measurement::Port2Short].datapoints[i].real_S22, measurements[Measurement::Port2Short].datapoints[i].imag_S22); + } else { + // used open + S11_reflection = complex(measurements[Measurement::Port1Open].datapoints[i].real_S11, measurements[Measurement::Port1Open].datapoints[i].imag_S11); + S22_reflection = complex(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>(); + auto R_D = Tparam>(); + 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 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(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>(r22 * a, r22 * b, r22 * c, r22); + auto Box_B = Tparam>(rho22 * alpha, rho22 * beta, rho22 * gamma, rho22); + // e00 is S11_A, S11 = T12/T22 = r22*a/r22 + complex 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>(r22 * a, r22 * b, r22 * c, r22); + Box_B = Tparam>(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 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::Types() { - const std::vector ret = {Type::Port1SOL, Type::Port2SOL, Type::FullSOLT}; + const std::vector ret = {Type::Port1SOL, Type::Port2SOL, Type::FullSOLT, Type::TransmissionNormalization, Type::TRL}; return ret; } -const std::vector Calibration::Measurements(Calibration::Type type) +const std::vector 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::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 &requiredMeasurements) +bool Calibration::SanityCheckSamples(const std::vector &requiredMeasurements) { // sanity check measurements, all need to be of the same size with the same frequencies (except for isolation which may be empty) vector freqs; diff --git a/Software/PC_Application/Calibration/calibration.h b/Software/PC_Application/Calibration/calibration.h index d329044..87d68a1 100644 --- a/Software/PC_Application/Calibration/calibration.h +++ b/Software/PC_Application/Calibration/calibration.h @@ -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 Types(); - static const std::vector Measurements(Type type = Type::None); + const std::vector 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 &requiredMeasurements); + void constructTransmissionNormalization(); + void constructTRL(); + bool SanityCheckSamples(const std::vector &requiredMeasurements); class Point { public: diff --git a/Software/PC_Application/Calibration/calibrationtracedialog.cpp b/Software/PC_Application/Calibration/calibrationtracedialog.cpp index 014976d..ed8e944 100644 --- a/Software/PC_Application/Calibration/calibrationtracedialog.cpp +++ b/Software/PC_Application/Calibration/calibrationtracedialog.cpp @@ -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() diff --git a/Software/PC_Application/Calibration/calkit.cpp b/Software/PC_Application/Calibration/calkit.cpp index 2e19691..09021c6 100644 --- a/Software/PC_Application/Calibration/calkit.cpp +++ b/Software/PC_Application/Calibration/calkit.cpp @@ -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::min(); - array 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::max(); - array 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::min(); + array 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::max(); + array 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() diff --git a/Software/PC_Application/Calibration/calkit.h b/Software/PC_Application/Calibration/calkit.h index 81f03f7..7b96b88 100644 --- a/Software/PC_Application/Calibration/calkit.h +++ b/Software/PC_Application/Calibration/calkit.h @@ -11,7 +11,7 @@ class Calkit public: Calkit(); - class Reflection { + class SOLT { public: std::complex Open; std::complex Short; @@ -19,13 +19,23 @@ public: std::complex ThroughS11, ThroughS12, ThroughS21, ThroughS22; }; + class TRL { + public: + bool reflectionIsNegative; + std::complex 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; diff --git a/Software/PC_Application/Calibration/calkitdialog.cpp b/Software/PC_Application/Calibration/calkitdialog.cpp index db49740..bc12da1 100644 --- a/Software/PC_Application/Calibration/calkitdialog.cpp +++ b/Software/PC_Application/Calibration/calkitdialog.cpp @@ -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(); + } } diff --git a/Software/PC_Application/Calibration/calkitdialog.ui b/Software/PC_Application/Calibration/calkitdialog.ui index c09e4c7..df9e004 100644 --- a/Software/PC_Application/Calibration/calkitdialog.ui +++ b/Software/PC_Application/Calibration/calkitdialog.ui @@ -9,8 +9,8 @@ 0 0 - 1121 - 345 + 1141 + 394 @@ -30,469 +30,774 @@ - - - + + + 0 + + + + SOLT + + - - - - 16 - - - - Open - - - Qt::AlignCenter - - - - - + - - - Coefficients + + + + 16 + + + + Short + + + Qt::AlignCenter - - OpenType - - - - Measurement file + + + + + Coefficients + + + ShortType + + + + + + + Measurement file + + + ShortType + + + + + + + + + 0 - - OpenType - + + + + + + + + Offset delay [ps]: + + + + + + + + + + Offset loss [GΩ/s]: + + + + + + + + + + <html><head/><body><p>L0 [10<span style=" vertical-align:super;">-12</span>F]:</p></body></html> + + + + + + + + + + <html><head/><body><p>L1 [10<span style=" vertical-align:super;">-24</span>F/Hz]:</p></body></html> + + + + + + + + + + <html><head/><body><p>L2 [10<span style=" vertical-align:super;">-33</span>F/Hz<span style=" vertical-align:super;">2</span>]:</p></body></html> + + + + + + + + + + <html><head/><body><p>L3 [10<span style=" vertical-align:super;">-42</span>F/Hz<span style=" vertical-align:super;">3</span>]:</p></body></html> + + + + + + + + + + false + + + + + + + Z0 [Ω]: + + + + + + + + + + + + + + - - - 0 - - - - - - - - - Offset delay [ps]: - - - - - - - - - - Offset loss [GΩ/s]: - - - - - - - - - - C0 [10<sup>-15</sup>F]: - - - - - - - - - - C1 [10<sup>-27</sup>F/Hz]: - - - - - - - - - - C2 [10<sup>-36</sup>F/Hz<sup>2</sup>]: - - - - - - - - - - C0 [10<sup>-45</sup>F/Hz<sup>3</sup>]: - - - - - - - - - - - - - Z0 [Ω]: - - - - - - - - - - - - - - - - - - - - - - Qt::Vertical - - - - - - - - - - 16 - - - - Short - - - Qt::AlignCenter + + + Qt::Vertical - + - - - Coefficients + + + + 16 + + + + Open + + + Qt::AlignCenter - - ShortType - - - - Measurement file + + + + + Coefficients + + + OpenType + + + + + + + Measurement file + + + OpenType + + + + + + + + + 0 - - ShortType - + + + + + + + + Offset delay [ps]: + + + + + + + + + + Offset loss [GΩ/s]: + + + + + + + + + + C0 [10<sup>-15</sup>F]: + + + + + + + + + + C1 [10<sup>-27</sup>F/Hz]: + + + + + + + + + + C2 [10<sup>-36</sup>F/Hz<sup>2</sup>]: + + + + + + + + + + C0 [10<sup>-45</sup>F/Hz<sup>3</sup>]: + + + + + + + + + + false + + + + + + + Z0 [Ω]: + + + + + + + + + + + + + + - - - 0 - - - - - - - - - Offset delay [ps]: - - - - - - - - - - Offset loss [GΩ/s]: - - - - - - - - - - <html><head/><body><p>L0 [10<span style=" vertical-align:super;">-12</span>F]:</p></body></html> - - - - - - - - - - <html><head/><body><p>L1 [10<span style=" vertical-align:super;">-24</span>F/Hz]:</p></body></html> - - - - - - - - - - <html><head/><body><p>L2 [10<span style=" vertical-align:super;">-33</span>F/Hz<span style=" vertical-align:super;">2</span>]:</p></body></html> - - - - - - - - - - <html><head/><body><p>L3 [10<span style=" vertical-align:super;">-42</span>F/Hz<span style=" vertical-align:super;">3</span>]:</p></body></html> - - - - - - - - - - - - - Z0 [Ω]: - - - - - - - - - - - - - - - - - - - - - - Qt::Vertical - - - - - - - - - - 16 - - - - Load - - - Qt::AlignCenter + + + Qt::Vertical - + - - - Coefficients + + + + 16 + + + + Load + + + Qt::AlignCenter - - LoadType - - - - Measurement file + + + + + Coefficients + + + LoadType + + + + + + + Measurement file + + + LoadType + + + + + + + + + 0 - - LoadType - + + + + + + + + Z0 [Ω]: + + + + + + + false + + + + + + + + + + + + + + - - - 0 - - - - - - - - - Z0 [Ω]: - - - - - - - - - - - - - - - - - - - - - - - - - Qt::Vertical - - - - - - - - - - 16 - - - - Through - - - Qt::AlignCenter + + + Qt::Vertical - + - - - Coefficients + + + + 16 + + + + Through + + + Qt::AlignCenter - - ThroughType - - - - Measurement file + + + + + Coefficients + + + ThroughType + + + + + + + Measurement file + + + ThroughType + + + + + + + + + 0 - - ThroughType - + + + + + + + + Z0 [Ω]: + + + + + + + false + + + + + + + Offset delay [ps]: + + + + + + + + + + Offset loss [GΩ/s]: + + + + + + + + + + + + + + + + + + + + + + TRL + + - - - 0 + + + + + + 0 + 0 + + + + + 16 + + + + Through + + + Qt::AlignCenter + + + + + + + + + Z0 [Ω]: + + + + + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Vertical - - - - - - - - Z0 [Ω]: - - - - - - - - - - Offset delay [ps]: - - - - - - - - - - Offset loss [GΩ/s]: - - - - - - - - - - - - - - - - - + + + + + + + 16 + + + + Reflection + + + Qt::AlignCenter + + + + + + + Short + + + TRL_Rtype + + + + + + + Open + + + TRL_Rtype + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Vertical + + + + + + + + + + 0 + 0 + + + + + 16 + + + + Line + + + Qt::AlignCenter + + + + + + + + + Delay [ps]: + + + + + + + true + + + + + + + + + + Min. frequency: + + + + + + + true + + + + + + + + + + Max. frequency: + + + + + + + true + + + + + + + + + + + + + + + + + 239 + 41 + 41 + + + + + + + + + 239 + 41 + 41 + + + + + + + + + 190 + 190 + 190 + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + - - + + @@ -516,6 +821,11 @@
CustomWidgets/touchstoneimport.h
1 + + SIUnitEdit + QLineEdit +
CustomWidgets/siunitedit.h
+
open_Z0 @@ -594,8 +904,9 @@ - + + diff --git a/Software/PC_Application/Calibration/measurementmodel.cpp b/Software/PC_Application/Calibration/measurementmodel.cpp index de31317..f0935ec 100644 --- a/Software/PC_Application/Calibration/measurementmodel.cpp +++ b/Software/PC_Application/Calibration/measurementmodel.cpp @@ -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 { diff --git a/Software/PC_Application/Calibration/measurementmodel.h b/Software/PC_Application/Calibration/measurementmodel.h index 42a0a18..ffb08eb 100644 --- a/Software/PC_Application/Calibration/measurementmodel.h +++ b/Software/PC_Application/Calibration/measurementmodel.h @@ -26,7 +26,6 @@ private: ColIndexDescription, ColIndexData, ColIndexDate, - ColIndexStatusSymbol, ColIndexLast }; Calibration *cal; diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index d9749c3..1e0e783 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -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);