2020-08-31 04:03:41 +08:00
# include "calibration.h"
2021-10-21 19:00:34 +08:00
# include "unit.h"
# include "Tools/parameters.h"
# include "CustomWidgets/informationbox.h"
# include <QDebug>
2020-08-31 04:03:41 +08:00
# include <algorithm>
# include <QMessageBox>
# include <QFileDialog>
# include <fstream>
using namespace std ;
Calibration : : Calibration ( )
{
2020-10-04 03:56:09 +08:00
// Create vectors for measurements
2022-03-18 01:24:18 +08:00
measurements [ Measurement : : Port1Open ] . datapoints = vector < VNAData > ( ) ;
measurements [ Measurement : : Port1Short ] . datapoints = vector < VNAData > ( ) ;
measurements [ Measurement : : Port1Load ] . datapoints = vector < VNAData > ( ) ;
measurements [ Measurement : : Port2Open ] . datapoints = vector < VNAData > ( ) ;
measurements [ Measurement : : Port2Short ] . datapoints = vector < VNAData > ( ) ;
measurements [ Measurement : : Port2Load ] . datapoints = vector < VNAData > ( ) ;
measurements [ Measurement : : Isolation ] . datapoints = vector < VNAData > ( ) ;
measurements [ Measurement : : Through ] . datapoints = vector < VNAData > ( ) ;
measurements [ Measurement : : Line ] . datapoints = vector < VNAData > ( ) ;
2020-08-31 04:03:41 +08:00
type = Type : : None ;
2021-12-02 06:21:13 +08:00
port1Standard = port2Standard = PortStandard : : Male ;
2021-12-03 05:41:51 +08:00
throughZeroLength = false ;
2020-08-31 04:03:41 +08:00
}
2021-09-03 19:13:33 +08:00
Calibration : : Standard Calibration : : getPort1Standard ( Calibration : : Measurement m )
{
switch ( m ) {
case Measurement : : Port1Open : return Standard : : Open ;
case Measurement : : Port1Short : return Standard : : Short ;
case Measurement : : Port1Load : return Standard : : Load ;
case Measurement : : Port2Open : return Standard : : Any ;
case Measurement : : Port2Short : return Standard : : Any ;
case Measurement : : Port2Load : return Standard : : Any ;
case Measurement : : Through : return Standard : : Through ;
case Measurement : : Isolation : return Standard : : Load ;
case Measurement : : Line : return Standard : : Through ;
default : return Standard : : Any ;
}
}
Calibration : : Standard Calibration : : getPort2Standard ( Calibration : : Measurement m )
{
switch ( m ) {
case Measurement : : Port1Open : return Standard : : Any ;
case Measurement : : Port1Short : return Standard : : Any ;
case Measurement : : Port1Load : return Standard : : Any ;
case Measurement : : Port2Open : return Standard : : Open ;
case Measurement : : Port2Short : return Standard : : Short ;
case Measurement : : Port2Load : return Standard : : Load ;
case Measurement : : Through : return Standard : : Through ;
case Measurement : : Isolation : return Standard : : Load ;
case Measurement : : Line : return Standard : : Through ;
default : return Standard : : Any ;
}
}
2020-08-31 04:03:41 +08:00
void Calibration : : clearMeasurements ( )
{
2020-11-12 02:13:53 +08:00
qDebug ( ) < < " Clearing all calibration measurements... " ;
2020-08-31 04:03:41 +08:00
for ( auto m : measurements ) {
2020-11-11 02:16:16 +08:00
clearMeasurement ( m . first ) ;
2020-08-31 04:03:41 +08:00
}
}
2021-09-03 19:13:33 +08:00
void Calibration : : clearMeasurements ( std : : set < Calibration : : Measurement > types )
{
for ( auto t : types ) {
clearMeasurement ( t ) ;
}
}
2020-08-31 04:03:41 +08:00
void Calibration : : clearMeasurement ( Calibration : : Measurement type )
{
measurements [ type ] . datapoints . clear ( ) ;
measurements [ type ] . timestamp = QDateTime ( ) ;
2020-11-12 02:13:53 +08:00
qDebug ( ) < < " Deleted " < < MeasurementToString ( type ) < < " measurement " ;
2020-08-31 04:03:41 +08:00
}
2022-03-18 01:24:18 +08:00
void Calibration : : addMeasurement ( Calibration : : Measurement type , VNAData & d )
2020-08-31 04:03:41 +08:00
{
measurements [ type ] . datapoints . push_back ( d ) ;
measurements [ type ] . timestamp = QDateTime : : currentDateTime ( ) ;
}
2022-03-18 01:24:18 +08:00
void Calibration : : addMeasurements ( std : : set < Calibration : : Measurement > types , VNAData & d )
2021-09-03 19:13:33 +08:00
{
for ( auto t : types ) {
addMeasurement ( t , d ) ;
}
}
2020-08-31 04:03:41 +08:00
bool Calibration : : calculationPossible ( Calibration : : Type type )
{
2020-11-11 02:16:16 +08:00
if ( type = = Type : : None ) {
// always possible to reset to None
return true ;
}
2020-11-12 02:13:53 +08:00
qDebug ( ) < < " Checking if " < < TypeToString ( type ) < < " calibration is possible... " ;
auto ret = SanityCheckSamples ( Measurements ( type , false ) ) ;
if ( ret ) {
qDebug ( ) < < " ...calibration possible " ;
} else {
qDebug ( ) < < " ...calibration not possible " ;
}
2020-10-03 19:01:59 +08:00
return SanityCheckSamples ( Measurements ( type , false ) ) ;
2020-08-31 04:03:41 +08:00
}
2020-11-12 02:13:53 +08:00
2020-08-31 04:03:41 +08:00
bool Calibration : : constructErrorTerms ( Calibration : : Type type )
{
2020-11-11 02:16:16 +08:00
if ( type = = Type : : None ) {
resetErrorTerms ( ) ;
return true ;
}
2020-08-31 04:03:41 +08:00
if ( ! calculationPossible ( type ) ) {
return false ;
}
2020-11-12 02:13:53 +08:00
qDebug ( ) < < " Constructing error terms for " < < TypeToString ( type ) < < " calibration " ;
2020-10-03 19:01:59 +08:00
bool isTRL = type = = Type : : TRL ;
2021-11-28 06:32:41 +08:00
bool uses_male = true ;
bool uses_female = true ;
2021-12-02 06:21:13 +08:00
if ( ! kit . checkIfValid ( minFreq , maxFreq , isTRL , uses_male , uses_female ) ) {
2021-11-28 06:32:41 +08:00
// TODO adjust for male/female standards
2020-08-31 04:03:41 +08:00
// Calkit does not support complete calibration range
2020-11-12 02:13:53 +08:00
QString msg = QString ( " The calibration kit does not support the complete span. \n \n " )
+ " The measured calibration data covers " + Unit : : ToString ( minFreq , " Hz " , " kMG " , 4 ) + " to " + Unit : : ToString ( maxFreq , " Hz " , " kMG " , 4 )
2021-11-28 06:32:41 +08:00
+ " , however the calibration kit does not support the whole frequency range. \n \n "
2020-11-12 02:13:53 +08:00
+ " Please adjust the calibration kit or the span and take the calibration measurements again. " ;
2021-10-13 03:58:44 +08:00
InformationBox : : ShowError ( " Unable to perform calibration " , msg ) ;
2020-11-12 02:13:53 +08:00
qWarning ( ) < < msg ;
2020-08-31 04:03:41 +08:00
return false ;
}
2021-12-02 06:21:13 +08:00
// check calkit standards and adjust if necessary
if ( ! kit . hasSeparateMaleFemaleStandards ( ) ) {
port1Standard = PortStandard : : Male ;
port2Standard = PortStandard : : Male ;
}
2021-12-03 05:41:51 +08:00
if ( port1Standard = = port2Standard ) {
// unable to use zero-length through
throughZeroLength = false ;
}
2020-08-31 04:03:41 +08:00
switch ( type ) {
2020-10-03 19:01:59 +08:00
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 ;
2021-04-16 01:24:11 +08:00
default : break ;
2020-08-31 04:03:41 +08:00
}
this - > type = type ;
return true ;
}
void Calibration : : resetErrorTerms ( )
{
type = Type : : None ;
points . clear ( ) ;
2020-11-12 02:13:53 +08:00
qDebug ( ) < < " Error terms reset " ;
2020-08-31 04:03:41 +08:00
}
void Calibration : : construct12TermPoints ( )
{
2020-10-03 19:01:59 +08:00
std : : vector < Measurement > requiredMeasurements = Measurements ( Type : : FullSOLT ) ;
2020-08-31 04:03:41 +08:00
requiredMeasurements . push_back ( Measurement : : Isolation ) ;
bool isolation_measured = SanityCheckSamples ( requiredMeasurements ) ;
points . clear ( ) ;
for ( unsigned int i = 0 ; i < measurements [ Measurement : : Port1Open ] . datapoints . size ( ) ; i + + ) {
Point p ;
p . frequency = measurements [ Measurement : : Port1Open ] . datapoints [ i ] . frequency ;
// extract required complex reflection/transmission factors from datapoints
2022-03-18 01:24:18 +08:00
auto S11_open = measurements [ Measurement : : Port1Open ] . datapoints [ i ] . S . m11 ;
auto S11_short = measurements [ Measurement : : Port1Short ] . datapoints [ i ] . S . m11 ;
auto S11_load = measurements [ Measurement : : Port1Load ] . datapoints [ i ] . S . m11 ;
auto S22_open = measurements [ Measurement : : Port2Open ] . datapoints [ i ] . S . m22 ;
auto S22_short = measurements [ Measurement : : Port2Short ] . datapoints [ i ] . S . m22 ;
auto S22_load = measurements [ Measurement : : Port2Load ] . datapoints [ i ] . S . m22 ;
2020-08-31 04:03:41 +08:00
auto S21_isolation = complex < double > ( 0 , 0 ) ;
auto S12_isolation = complex < double > ( 0 , 0 ) ;
if ( isolation_measured ) {
2022-03-18 01:24:18 +08:00
S21_isolation = measurements [ Measurement : : Isolation ] . datapoints [ i ] . S . m21 ;
S12_isolation = measurements [ Measurement : : Isolation ] . datapoints [ i ] . S . m12 ;
2020-08-31 04:03:41 +08:00
}
2022-03-18 01:24:18 +08:00
auto S11_through = measurements [ Measurement : : Through ] . datapoints [ i ] . S . m11 ;
auto S21_through = measurements [ Measurement : : Through ] . datapoints [ i ] . S . m21 ;
auto S22_through = measurements [ Measurement : : Through ] . datapoints [ i ] . S . m22 ;
auto S12_through = measurements [ Measurement : : Through ] . datapoints [ i ] . S . m12 ;
2020-08-31 04:03:41 +08:00
2021-12-02 06:21:13 +08:00
auto actual = kit . toSOLT ( p . frequency , port1Standard = = PortStandard : : Male ) ;
2020-08-31 04:03:41 +08:00
// 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 ;
2020-10-03 19:01:59 +08:00
// See page 18 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf
2020-08-31 04:03:41 +08:00
// Formulas for S11M and S21M solved for e22 and e10e32
2021-12-03 05:41:51 +08:00
if ( throughZeroLength ) {
// use ideal through
actual . ThroughS11 = 0.0 ;
actual . ThroughS12 = 1.0 ;
actual . ThroughS21 = 1.0 ;
actual . ThroughS22 = 0.0 ;
}
2020-08-31 04:03:41 +08:00
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 )
/ ( ( S11_through - p . fe00 ) * ( actual . ThroughS22 - p . fe11 * deltaS ) - deltaS * p . fe10e01 ) ;
p . fe10e32 = ( S21_through - p . fe30 ) * ( 1.0 - p . fe11 * actual . ThroughS11 - p . fe22 * actual . ThroughS22 + p . fe11 * p . fe22 * deltaS ) / actual . ThroughS21 ;
// Reverse calibration
2021-12-02 06:21:13 +08:00
actual = kit . toSOLT ( p . frequency , port2Standard = = PortStandard : : Male ) ;
2020-08-31 04:03:41 +08:00
computeSOL ( S22_short , S22_open , S22_load , p . re33 , p . re22 , p . re23e32 , actual . Open , actual . Short , actual . Load ) ;
p . re03 = S12_isolation ;
p . re11 = ( ( S22_through - p . re33 ) * ( 1.0 - p . re22 * actual . ThroughS22 ) - actual . ThroughS22 * p . re23e32 )
/ ( ( S22_through - p . re33 ) * ( actual . ThroughS11 - p . re22 * deltaS ) - deltaS * p . re23e32 ) ;
p . re23e01 = ( S12_through - p . re03 ) * ( 1.0 - p . re11 * actual . ThroughS11 - p . re22 * actual . ThroughS22 + p . re11 * p . re22 * deltaS ) / actual . ThroughS12 ;
2021-12-05 04:16:45 +08:00
2020-08-31 04:03:41 +08:00
points . push_back ( p ) ;
}
}
void Calibration : : constructPort1SOL ( )
{
points . clear ( ) ;
for ( unsigned int i = 0 ; i < measurements [ Measurement : : Port1Open ] . datapoints . size ( ) ; i + + ) {
Point p ;
p . frequency = measurements [ Measurement : : Port1Open ] . datapoints [ i ] . frequency ;
// extract required complex reflection/transmission factors from datapoints
2022-03-18 01:24:18 +08:00
auto S11_open = measurements [ Measurement : : Port1Open ] . datapoints [ i ] . S . m11 ;
auto S11_short = measurements [ Measurement : : Port1Short ] . datapoints [ i ] . S . m11 ;
auto S11_load = measurements [ Measurement : : Port1Load ] . datapoints [ i ] . S . m11 ;
2020-08-31 04:03:41 +08:00
// OSL port1
2021-12-02 06:21:13 +08:00
auto actual = kit . toSOLT ( p . frequency , port1Standard = = PortStandard : : Male ) ;
2020-10-03 19:01:59 +08:00
// See page 13 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf
2020-08-31 04:03:41 +08:00
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
2021-12-05 04:16:45 +08:00
p . fex = 0.0 ;
2020-08-31 04:03:41 +08:00
p . fe30 = 0.0 ;
p . fe22 = 0.0 ;
p . fe10e32 = 1.0 ;
p . re33 = 0.0 ;
p . re22 = 0.0 ;
p . re23e32 = 1.0 ;
p . re03 = 0.0 ;
2021-12-05 04:16:45 +08:00
p . rex = 0.0 ;
2020-08-31 04:03:41 +08:00
p . re11 = 0.0 ;
p . re23e01 = 1.0 ;
points . push_back ( p ) ;
}
}
void Calibration : : constructPort2SOL ( )
{
points . clear ( ) ;
for ( unsigned int i = 0 ; i < measurements [ Measurement : : Port2Open ] . datapoints . size ( ) ; i + + ) {
Point p ;
p . frequency = measurements [ Measurement : : Port2Open ] . datapoints [ i ] . frequency ;
// extract required complex reflection/transmission factors from datapoints
2022-03-18 01:24:18 +08:00
auto S22_open = measurements [ Measurement : : Port2Open ] . datapoints [ i ] . S . m22 ;
auto S22_short = measurements [ Measurement : : Port2Short ] . datapoints [ i ] . S . m22 ;
auto S22_load = measurements [ Measurement : : Port2Load ] . datapoints [ i ] . S . m22 ;
2020-08-31 04:03:41 +08:00
// OSL port2
2021-12-02 06:21:13 +08:00
auto actual = kit . toSOLT ( p . frequency , port1Standard = = PortStandard : : Male ) ;
2020-10-03 19:01:59 +08:00
// See page 19 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf
2020-08-31 04:03:41 +08:00
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
2021-12-05 04:16:45 +08:00
p . fex = 0.0 ;
2020-08-31 04:03:41 +08:00
p . fe30 = 0.0 ;
p . fe22 = 0.0 ;
p . fe10e32 = 1.0 ;
p . fe00 = 0.0 ;
p . fe11 = 0.0 ;
p . fe10e01 = 1.0 ;
p . re03 = 0.0 ;
2021-12-05 04:16:45 +08:00
p . rex = 0.0 ;
2020-08-31 04:03:41 +08:00
p . re11 = 0.0 ;
p . re23e01 = 1.0 ;
points . push_back ( p ) ;
}
}
2020-10-03 19:01:59 +08:00
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
2022-03-18 01:24:18 +08:00
auto S21_through = measurements [ Measurement : : Through ] . datapoints [ i ] . S . m21 ;
auto S12_through = measurements [ Measurement : : Through ] . datapoints [ i ] . S . m12 ;
2020-10-03 19:01:59 +08:00
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 ;
2021-12-05 04:16:45 +08:00
p . fex = 0.0 ;
2020-10-03 19:01:59 +08:00
p . fe22 = 0.0 ;
p . fe00 = 0.0 ;
p . fe11 = 0.0 ;
p . fe10e01 = 1.0 ;
p . re03 = 0.0 ;
2021-12-05 04:16:45 +08:00
p . rex = 0.0 ;
2020-10-03 19:01:59 +08:00
p . re11 = 0.0 ;
p . re33 = 0.0 ;
p . re22 = 0.0 ;
p . re23e32 = 1.0 ;
points . push_back ( p ) ;
}
}
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
2022-03-18 01:24:18 +08:00
auto S11_through = measurements [ Measurement : : Through ] . datapoints [ i ] . S . m11 ;
auto S21_through = measurements [ Measurement : : Through ] . datapoints [ i ] . S . m21 ;
auto S22_through = measurements [ Measurement : : Through ] . datapoints [ i ] . S . m22 ;
auto S12_through = measurements [ Measurement : : Through ] . datapoints [ i ] . S . m12 ;
auto S11_line = measurements [ Measurement : : Line ] . datapoints [ i ] . S . m11 ;
auto S21_line = measurements [ Measurement : : Line ] . datapoints [ i ] . S . m21 ;
auto S22_line = measurements [ Measurement : : Line ] . datapoints [ i ] . S . m22 ;
auto S12_line = measurements [ Measurement : : Line ] . datapoints [ i ] . S . m12 ;
2020-10-03 19:01:59 +08:00
auto trl = kit . toTRL ( p . frequency ) ;
complex < double > S11_reflection , S22_reflection ;
if ( trl . reflectionIsNegative ) {
// used short
2022-03-18 01:24:18 +08:00
S11_reflection = measurements [ Measurement : : Port1Short ] . datapoints [ i ] . S . m11 ;
S22_reflection = measurements [ Measurement : : Port2Short ] . datapoints [ i ] . S . m22 ;
2020-10-03 19:01:59 +08:00
} else {
// used open
2022-03-18 01:24:18 +08:00
S11_reflection = measurements [ Measurement : : Port1Open ] . datapoints [ i ] . S . m11 ;
S22_reflection = measurements [ Measurement : : Port2Open ] . datapoints [ i ] . S . m22 ;
2020-10-03 19:01:59 +08:00
}
// calculate TRL calibration
// variable names and formulas according to http://emlab.uiuc.edu/ece451/notes/new_TRL.pdf
// page 19
2021-01-30 04:44:44 +08:00
Sparam Sthrough ( S11_through , S12_through , S21_through , S22_through ) ;
Sparam Sline ( S11_line , S12_line , S21_line , S22_line ) ;
auto R_T = Tparam ( Sthrough ) ;
auto R_D = Tparam ( Sline ) ;
2020-10-03 19:01:59 +08:00
auto T = R_D * R_T . inverse ( ) ;
complex < double > a_over_c , b ;
// page 21-22
2021-01-30 04:44:44 +08:00
solveQuadratic ( T . m21 , T . m22 - T . m11 , - T . m12 , b , a_over_c ) ;
2020-10-03 19:01:59 +08:00
// ensure correct root selection
// page 23
if ( abs ( b ) > = abs ( a_over_c ) ) {
swap ( b , a_over_c ) ;
}
// page 24
2021-01-30 04:44:44 +08:00
auto g = R_T . m22 ;
auto d = R_T . m11 / g ;
auto e = R_T . m12 / g ;
auto f = R_T . m21 / g ;
2020-10-03 19:01:59 +08:00
// 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 ;
2021-01-30 04:44:44 +08:00
auto Box_A = Tparam ( r22 * a , r22 * b , r22 * c , r22 ) ;
auto Box_B = Tparam ( rho22 * alpha , rho22 * beta , rho22 * gamma , rho22 ) ;
auto S_A = Sparam ( Box_A ) ;
p . fe00 = S_A . m11 ;
p . fe10e01 = S_A . m12 ;
p . fe11 = S_A . m22 ;
auto S_B = Sparam ( Box_B ) ;
p . fe22 = S_B . m11 ;
p . fe10e32 = S_B . m21 ;
2020-10-03 19:01:59 +08:00
// no isolation measurement available
p . fe30 = 0.0 ;
2021-12-05 04:16:45 +08:00
p . fex = 0.0 ;
2020-10-03 19:01:59 +08:00
// 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 ;
2021-01-30 04:44:44 +08:00
Box_A = Tparam ( r22 * a , r22 * b , r22 * c , r22 ) ;
Box_B = Tparam ( rho22 * alpha , rho22 * beta , rho22 * gamma , rho22 ) ;
S_A = Sparam ( Box_A ) ;
p . re23e01 = S_A . m12 ;
p . re11 = S_A . m22 ;
S_B = Sparam ( Box_B ) ;
p . re22 = S_B . m11 ;
p . re23e32 = S_B . m21 ;
p . re33 = S_B . m22 ;
2020-10-03 19:01:59 +08:00
// no isolation measurement available
p . re03 = 0.0 ;
2021-12-05 04:16:45 +08:00
p . rex = 0.0 ;
2020-10-03 19:01:59 +08:00
points . push_back ( p ) ;
}
}
2022-03-18 01:24:18 +08:00
void Calibration : : correctMeasurement ( VNAData & d )
2020-08-31 04:03:41 +08:00
{
if ( type = = Type : : None ) {
// No calibration data, do nothing
return ;
}
// Convert measurements to complex variables
2022-03-18 01:24:18 +08:00
auto S11m = d . S . m11 ;
auto S21m = d . S . m21 ;
auto S22m = d . S . m22 ;
auto S12m = d . S . m12 ;
2020-08-31 04:03:41 +08:00
// find correct entry
auto p = getCalibrationPoint ( d ) ;
2020-10-03 19:01:59 +08:00
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
2020-08-31 04:03:41 +08:00
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 ;
2020-10-03 19:01:59 +08:00
S11 = ( ( S11m - p . fe00 ) / p . fe10e01 * ( 1.0 + ( S22m - p . re33 ) / p . re23e32 * p . re22 )
2020-08-31 04:03:41 +08:00
- p . fe22 * ( S21m - p . fe30 ) / p . fe10e32 * ( S12m - p . re03 ) / p . re23e01 ) / denom ;
2020-10-03 19:01:59 +08:00
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 )
2020-08-31 04:03:41 +08:00
- p . re11 * ( S21m - p . fe30 ) / p . fe10e32 * ( S12m - p . re03 ) / p . re23e01 ) / denom ;
2020-10-03 19:01:59 +08:00
S12 = ( ( S12m - p . re03 ) / p . re23e01 * ( 1.0 + ( S11m - p . fe00 ) / p . fe10e01 * ( p . fe11 - p . re11 ) ) ) / denom ;
2020-08-31 04:03:41 +08:00
2022-03-18 01:24:18 +08:00
d . S = Sparam ( S11 , S12 , S21 , S22 ) ;
2020-08-31 04:03:41 +08:00
}
2021-03-12 04:52:58 +08:00
void Calibration : : correctTraces ( Trace & S11 , Trace & S12 , Trace & S21 , Trace & S22 )
{
auto points = Trace : : assembleDatapoints ( S11 , S12 , S21 , S22 ) ;
if ( points . size ( ) ) {
// succeeded in assembling datapoints
for ( auto & p : points ) {
correctMeasurement ( p ) ;
}
Trace : : fillFromDatapoints ( S11 , S12 , S21 , S22 , points ) ;
}
}
2021-07-10 04:26:44 +08:00
Calibration : : InterpolationType Calibration : : getInterpolation ( double f_start , double f_stop , int npoints )
2020-08-31 04:03:41 +08:00
{
if ( ! points . size ( ) ) {
return InterpolationType : : NoCalibration ;
}
2021-07-10 04:26:44 +08:00
if ( f_start < points . front ( ) . frequency | | f_stop > points . back ( ) . frequency ) {
2020-08-31 04:03:41 +08:00
return InterpolationType : : Extrapolate ;
}
// Either exact or interpolation, check individual frequencies
2020-09-16 05:22:08 +08:00
uint32_t f_step ;
2021-07-10 04:26:44 +08:00
if ( npoints > 1 ) {
f_step = ( f_stop - f_start ) / ( npoints - 1 ) ;
2020-09-16 05:22:08 +08:00
} else {
2021-07-10 04:26:44 +08:00
f_step = f_stop - f_start ;
2020-09-16 05:22:08 +08:00
}
2021-07-10 04:26:44 +08:00
uint64_t f = f_start ;
do {
2020-08-31 04:03:41 +08:00
if ( find_if ( points . begin ( ) , points . end ( ) , [ & f ] ( const Point & p ) {
return abs ( f - p . frequency ) < 100 ;
} ) = = points . end ( ) ) {
return InterpolationType : : Interpolate ;
}
2021-07-10 04:26:44 +08:00
f + = f_step ;
2021-07-10 20:04:05 +08:00
} while ( f < = f_stop & & f_step > std : : numeric_limits < double > : : epsilon ( ) ) ;
2021-07-10 04:26:44 +08:00
2020-08-31 04:03:41 +08:00
// if we get here all frequency points were matched
2021-07-10 04:26:44 +08:00
if ( points . front ( ) . frequency = = f_start & & points . back ( ) . frequency = = f_stop ) {
2020-08-31 04:03:41 +08:00
return InterpolationType : : Unchanged ;
} else {
return InterpolationType : : Exact ;
}
}
2021-04-16 01:24:11 +08:00
Calibration : : Measurement Calibration : : MeasurementFromString ( QString s )
{
for ( unsigned int i = 0 ; i < ( int ) Measurement : : Last ; i + + ) {
auto m = ( Measurement ) i ;
if ( s . compare ( MeasurementToString ( m ) , Qt : : CaseInsensitive ) = = 0 ) {
return m ;
}
}
return Measurement : : Last ;
}
2020-08-31 04:03:41 +08:00
QString Calibration : : MeasurementToString ( Calibration : : Measurement m )
{
switch ( m ) {
case Measurement : : Port1Open :
return " Port 1 Open " ;
case Measurement : : Port1Short :
return " Port 1 Short " ;
case Measurement : : Port1Load :
return " Port 1 Load " ;
case Measurement : : Port2Open :
return " Port 2 Open " ;
case Measurement : : Port2Short :
return " Port 2 Short " ;
case Measurement : : Port2Load :
return " Port 2 Load " ;
case Measurement : : Through :
return " Through " ;
case Measurement : : Isolation :
return " Isolation " ;
2020-10-03 19:01:59 +08:00
case Measurement : : Line :
return " Line " ;
2020-08-31 04:03:41 +08:00
default :
return " Unknown " ;
}
}
2021-04-16 01:24:11 +08:00
Calibration : : Type Calibration : : TypeFromString ( QString s )
{
for ( unsigned int i = 0 ; i < ( int ) Type : : Last ; i + + ) {
auto t = ( Type ) i ;
if ( s . compare ( TypeToString ( t ) , Qt : : CaseInsensitive ) = = 0 ) {
return t ;
}
}
return Type : : Last ;
}
2020-08-31 04:03:41 +08:00
QString Calibration : : TypeToString ( Calibration : : Type t )
{
switch ( t ) {
case Type : : Port1SOL : return " Port 1 " ; break ;
case Type : : Port2SOL : return " Port 2 " ; break ;
case Type : : FullSOLT : return " SOLT " ; break ;
2020-10-03 19:01:59 +08:00
case Type : : TransmissionNormalization : return " Normalize " ; break ;
case Type : : TRL : return " TRL " ; break ;
2020-08-31 04:03:41 +08:00
default : return " None " ; break ;
}
}
const std : : vector < Calibration : : Type > Calibration : : Types ( )
{
2020-10-03 19:01:59 +08:00
const std : : vector < Calibration : : Type > ret = { Type : : Port1SOL , Type : : Port2SOL , Type : : FullSOLT , Type : : TransmissionNormalization , Type : : TRL } ;
2020-08-31 04:03:41 +08:00
return ret ;
}
2020-10-03 19:01:59 +08:00
const std : : vector < Calibration : : Measurement > Calibration : : Measurements ( Calibration : : Type type , bool optional_included )
2020-08-31 04:03:41 +08:00
{
switch ( type ) {
case Type : : None :
2020-10-03 19:01:59 +08:00
// 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 } ;
}
2020-08-31 04:03:41 +08:00
break ;
case Type : : Port1SOL :
return { Measurement : : Port1Short , Measurement : : Port1Open , Measurement : : Port1Load } ;
break ;
case Type : : Port2SOL :
return { Measurement : : Port2Short , Measurement : : Port2Open , Measurement : : Port2Load } ;
break ;
2020-10-03 19:01:59 +08:00
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 ;
2020-08-31 04:03:41 +08:00
default :
return { } ;
break ;
}
}
Calibration : : MeasurementInfo Calibration : : getMeasurementInfo ( Calibration : : Measurement m )
{
MeasurementInfo info ;
switch ( m ) {
case Measurement : : Port1Short :
info . name = " Port 1 short " ;
info . prerequisites = " Short standard connected to port 1, port 2 open " ;
break ;
case Measurement : : Port1Open :
info . name = " Port 1 open " ;
info . prerequisites = " Open standard connected to port 1, port 2 open " ;
break ;
case Measurement : : Port1Load :
info . name = " Port 1 load " ;
info . prerequisites = " Load standard connected to port 1, port 2 open " ;
break ;
case Measurement : : Port2Short :
info . name = " Port 2 short " ;
info . prerequisites = " Port 1 open, short standard connected to port 2 " ;
break ;
case Measurement : : Port2Open :
info . name = " Port 2 open " ;
info . prerequisites = " Port 1 open, open standard connected to port 2 " ;
break ;
case Measurement : : Port2Load :
info . name = " Port 2 load " ;
info . prerequisites = " Port 1 open, load standard connected to port 2 " ;
break ;
case Measurement : : Through :
info . name = " Through " ;
info . prerequisites = " Port 1 connected to port 2 via through standard " ;
break ;
case Measurement : : Isolation :
info . name = " Isolation " ;
info . prerequisites = " Both ports terminated into 50 ohm " ;
2020-10-03 19:01:59 +08:00
break ;
case Measurement : : Line :
info . name = " Line " ;
info . prerequisites = " Port 1 connected to port 2 via line standard " ;
break ;
2021-04-16 01:24:11 +08:00
default :
info . name = " Invalid " ;
info . prerequisites = " Invalid " ;
break ;
2020-08-31 04:03:41 +08:00
}
info . points = measurements [ m ] . datapoints . size ( ) ;
if ( info . points > 0 ) {
info . fmin = measurements [ m ] . datapoints . front ( ) . frequency ;
info . fmax = measurements [ m ] . datapoints . back ( ) . frequency ;
info . points = measurements [ m ] . datapoints . size ( ) ;
}
info . timestamp = measurements [ m ] . timestamp ;
return info ;
}
std : : vector < Trace * > Calibration : : getErrorTermTraces ( )
{
std : : vector < Trace * > traces ;
const QString traceNames [ 12 ] = { " e00 " , " F_e11 " , " e10e01 " , " e10e32 " , " F_e22 " , " e30 " , " e33 " , " R_e11 " , " e23e32 " , " e23e01 " , " R_e22 " , " e03 " } ;
constexpr bool reflection [ 12 ] = { true , true , false , false , true , false , true , true , false , false , true , false } ;
for ( int i = 0 ; i < 12 ; i + + ) {
auto t = new Trace ( traceNames [ i ] , Qt : : red ) ;
t - > setCalibration ( true ) ;
t - > setReflection ( reflection [ i ] ) ;
traces . push_back ( t ) ;
}
for ( auto p : points ) {
Trace : : Data d ;
2020-11-25 23:47:29 +08:00
d . x = p . frequency ;
2020-08-31 04:03:41 +08:00
for ( int i = 0 ; i < 12 ; i + + ) {
switch ( i ) {
2020-11-25 23:47:29 +08:00
case 0 : d . y = p . fe00 ; break ;
case 1 : d . y = p . fe11 ; break ;
case 2 : d . y = p . fe10e01 ; break ;
case 3 : d . y = p . fe10e32 ; break ;
case 4 : d . y = p . fe22 ; break ;
case 5 : d . y = p . fe30 ; break ;
case 6 : d . y = p . re33 ; break ;
case 7 : d . y = p . re11 ; break ;
case 8 : d . y = p . re23e32 ; break ;
case 9 : d . y = p . re23e01 ; break ;
case 10 : d . y = p . re22 ; break ;
case 11 : d . y = p . re03 ; break ;
2020-08-31 04:03:41 +08:00
}
2021-07-10 04:26:44 +08:00
traces [ i ] - > addData ( d , TraceMath : : DataType : : Frequency ) ;
2020-08-31 04:03:41 +08:00
}
}
return traces ;
}
2020-11-28 01:18:31 +08:00
std : : vector < Trace * > Calibration : : getMeasurementTraces ( )
{
std : : vector < Trace * > traces ;
for ( auto m : measurements ) {
auto info = getMeasurementInfo ( m . first ) ;
if ( info . points > 0 ) {
vector < QString > usedPrefixes ;
switch ( m . first ) {
case Measurement : : Port1Load :
case Measurement : : Port1Open :
case Measurement : : Port1Short :
usedPrefixes = { " S11 " } ;
break ;
case Measurement : : Port2Load :
case Measurement : : Port2Open :
case Measurement : : Port2Short :
usedPrefixes = { " S22 " } ;
break ;
case Measurement : : Through :
case Measurement : : Line :
case Measurement : : Isolation :
usedPrefixes = { " S11 " , " S12 " , " S21 " , " S22 " } ;
break ;
2021-04-16 01:24:11 +08:00
default :
break ;
2020-11-28 01:18:31 +08:00
}
for ( auto prefix : usedPrefixes ) {
auto t = new Trace ( prefix + " " + info . name ) ;
t - > setCalibration ( true ) ;
t - > setReflection ( prefix = = " S11 " | | prefix = = " S22 " ) ;
for ( auto p : m . second . datapoints ) {
Trace : : Data d ;
2020-11-28 20:57:22 +08:00
d . x = p . frequency ;
2020-11-28 01:18:31 +08:00
if ( prefix = = " S11 " ) {
2022-03-18 01:24:18 +08:00
d . y = p . S . m11 ;
2020-11-28 01:18:31 +08:00
} else if ( prefix = = " S12 " ) {
2022-03-18 01:24:18 +08:00
d . y = p . S . m12 ;
2020-11-28 01:18:31 +08:00
} else if ( prefix = = " S21 " ) {
2022-03-18 01:24:18 +08:00
d . y = p . S . m21 ;
2020-11-28 01:18:31 +08:00
} else {
2022-03-18 01:24:18 +08:00
d . y = p . S . m22 ;
2020-11-28 01:18:31 +08:00
}
2021-07-10 04:26:44 +08:00
t - > addData ( d , TraceMath : : DataType : : Frequency ) ;
2020-11-28 01:18:31 +08:00
}
traces . push_back ( t ) ;
}
}
}
return traces ;
}
2020-08-31 04:03:41 +08:00
bool Calibration : : openFromFile ( QString filename )
{
if ( filename . isEmpty ( ) ) {
filename = QFileDialog : : getOpenFileName ( nullptr , " Load calibration data " , " " , " Calibration files (*.cal) " , nullptr , QFileDialog : : DontUseNativeDialog ) ;
if ( filename . isEmpty ( ) ) {
// aborted selection
return false ;
}
}
2021-10-13 03:58:44 +08:00
// force correct file ending
if ( filename . toLower ( ) . endsWith ( " .cal " ) ) {
filename . chop ( 4 ) ;
filename + = " .cal " ;
}
2020-12-01 00:05:15 +08:00
qDebug ( ) < < " Attempting to open calibration from file " < < filename ;
2020-08-31 04:03:41 +08:00
2020-11-11 02:16:16 +08:00
// reset all data before loading new calibration
clearMeasurements ( ) ;
resetErrorTerms ( ) ;
2020-08-31 04:03:41 +08:00
// attempt to load associated calibration kit first (needs to be available when performing calibration)
auto calkit_file = filename ;
auto dotPos = calkit_file . lastIndexOf ( ' . ' ) ;
if ( dotPos > = 0 ) {
calkit_file . truncate ( dotPos ) ;
}
calkit_file . append ( " .calkit " ) ;
2020-11-12 02:13:53 +08:00
qDebug ( ) < < " Associated calibration kit expected in " < < calkit_file ;
2020-08-31 04:03:41 +08:00
try {
2020-11-11 02:16:16 +08:00
kit = Calkit : : fromFile ( calkit_file ) ;
2022-03-17 19:53:13 +08:00
} catch ( runtime_error & e ) {
2021-10-13 03:58:44 +08:00
InformationBox : : ShowError ( " Missing calibration kit " , " The calibration kit file associated with the selected calibration could not be parsed. The calibration might not be accurate. ( " + QString ( e . what ( ) ) + " ) " ) ;
2020-11-12 02:13:53 +08:00
qWarning ( ) < < " Parsing of calibration kit failed while opening calibration file: " < < e . what ( ) ;
2020-08-31 04:03:41 +08:00
}
ifstream file ;
2021-10-13 03:58:44 +08:00
2020-08-31 04:03:41 +08:00
file . open ( filename . toStdString ( ) ) ;
2021-10-13 03:58:44 +08:00
if ( ! file . good ( ) ) {
QString msg = " Unable to open file: " + filename ;
InformationBox : : ShowError ( " Error " , msg ) ;
qWarning ( ) < < msg ;
return false ;
}
2020-08-31 04:03:41 +08:00
try {
2021-12-02 06:21:13 +08:00
nlohmann : : json j ;
file > > j ;
fromJSON ( j ) ;
2022-03-17 19:53:13 +08:00
} catch ( exception & e ) {
2021-12-02 06:21:13 +08:00
// json parsing failed, probably using a legacy file format
try {
file . clear ( ) ;
file . seekg ( 0 ) ;
file > > * this ;
InformationBox : : ShowMessage ( " Loading calibration file " , " The file \" " + filename + " \" is stored in a deprecated "
" calibration format. Future versions of this application might not support "
2021-12-02 06:21:24 +08:00
" it anymore. Please save the calibration to update to the new format " ) ;
2022-03-17 19:53:13 +08:00
} catch ( exception & e ) {
2021-12-02 06:21:13 +08:00
InformationBox : : ShowError ( " File parsing error " , e . what ( ) ) ;
qWarning ( ) < < " Calibration file parsing failed: " < < e . what ( ) ;
return false ;
}
2020-08-31 04:03:41 +08:00
}
2020-12-01 05:27:38 +08:00
this - > currentCalFile = filename ; // if all ok, remember this
2020-08-31 04:03:41 +08:00
return true ;
}
bool Calibration : : saveToFile ( QString filename )
{
if ( filename . isEmpty ( ) ) {
2020-12-07 09:44:25 +08:00
QString fn = descriptiveCalName ( ) ;
2020-12-01 00:05:15 +08:00
filename = QFileDialog : : getSaveFileName ( nullptr , " Save calibration data " , fn , " Calibration files (*.cal) " , nullptr , QFileDialog : : DontUseNativeDialog ) ;
2020-08-31 04:03:41 +08:00
if ( filename . isEmpty ( ) ) {
// aborted selection
return false ;
}
}
2020-11-11 02:16:16 +08:00
2021-10-13 03:58:44 +08:00
if ( filename . toLower ( ) . endsWith ( " .cal " ) ) {
2020-11-11 02:16:16 +08:00
filename . chop ( 4 ) ;
2020-08-31 04:03:41 +08:00
}
2020-11-11 02:16:16 +08:00
auto calibration_file = filename + " .cal " ;
2020-08-31 04:03:41 +08:00
ofstream file ;
file . open ( calibration_file . toStdString ( ) ) ;
2021-12-02 06:21:13 +08:00
file < < setw ( 1 ) < < toJSON ( ) ;
2020-08-31 04:03:41 +08:00
2020-11-11 02:16:16 +08:00
auto calkit_file = filename + " .calkit " ;
2020-11-12 02:13:53 +08:00
qDebug ( ) < < " Saving associated calibration kit to file " < < calkit_file ;
2020-11-11 02:16:16 +08:00
kit . toFile ( calkit_file ) ;
2020-12-01 05:27:38 +08:00
this - > currentCalFile = calibration_file ; // if all ok, remember this
2020-08-31 04:03:41 +08:00
return true ;
}
2020-12-01 00:05:15 +08:00
/**
* @ brief Calibration : : hzToString
* @ param freqHz - input frequency in Hz
2020-12-07 09:44:25 +08:00
* @ return descriptive name ie . " SOLT 40M-700M 1000pt "
2020-12-01 00:05:15 +08:00
*/
2020-12-07 09:44:25 +08:00
QString Calibration : : descriptiveCalName ( ) {
int precision = 3 ;
2020-12-08 00:58:13 +08:00
QString lo = Unit : : ToString ( this - > minFreq , " " , " kMG " , precision ) ;
2020-12-07 09:44:25 +08:00
QString hi = Unit : : ToString ( this - > maxFreq , " " , " kMG " , precision ) ;
// due to rounding up 123.66M and 123.99M -> we get lo="124M" and hi="124M"
// so let's add some precision
if ( lo = = hi ) {
// Only in case of 123.66M and 123.69M we would need 5 digits, but that kind of narrow cal. is very unlikely.
precision = 4 ;
lo = Unit : : ToString ( this - > minFreq , " " , " kMG " , precision ) ;
hi = Unit : : ToString ( this - > maxFreq , " " , " kMG " , precision ) ;
}
2020-12-07 23:04:59 +08:00
QString tmp =
Calibration : : TypeToString ( this - > getType ( ) )
2020-12-07 09:44:25 +08:00
+ " "
+ lo + " - " + hi
+ " "
+ QString : : number ( this - > points . size ( ) ) + " pt " ;
return tmp ;
2020-12-01 00:05:15 +08:00
}
2021-12-03 05:41:51 +08:00
bool Calibration : : getThroughZeroLength ( ) const
{
return throughZeroLength ;
}
void Calibration : : setThroughZeroLength ( bool value )
{
throughZeroLength = value ;
}
2020-12-07 23:04:59 +08:00
double Calibration : : getMinFreq ( ) {
return this - > minFreq ;
}
double Calibration : : getMaxFreq ( ) {
return this - > maxFreq ;
}
int Calibration : : getNumPoints ( ) {
return this - > points . size ( ) ;
}
2021-12-02 06:21:13 +08:00
nlohmann : : json Calibration : : toJSON ( )
{
nlohmann : : json j ;
nlohmann : : json j_measurements ;
for ( auto m : measurements ) {
if ( m . second . datapoints . size ( ) > 0 ) {
nlohmann : : json j_measurement ;
j_measurement [ " name " ] = MeasurementToString ( m . first ) . toStdString ( ) ;
j_measurement [ " timestamp " ] = m . second . timestamp . toSecsSinceEpoch ( ) ;
nlohmann : : json j_points ;
for ( auto p : m . second . datapoints ) {
nlohmann : : json j_point ;
j_point [ " frequency " ] = p . frequency ;
2022-03-18 01:24:18 +08:00
j_point [ " S11_real " ] = p . S . m11 . real ( ) ;
j_point [ " S11_imag " ] = p . S . m11 . imag ( ) ;
j_point [ " S12_real " ] = p . S . m12 . real ( ) ;
j_point [ " S12_imag " ] = p . S . m12 . imag ( ) ;
j_point [ " S21_real " ] = p . S . m21 . real ( ) ;
j_point [ " S21_imag " ] = p . S . m21 . imag ( ) ;
j_point [ " S22_real " ] = p . S . m22 . real ( ) ;
j_point [ " S22_imag " ] = p . S . m22 . imag ( ) ;
2021-12-02 06:21:13 +08:00
j_points . push_back ( j_point ) ;
}
j_measurement [ " points " ] = j_points ;
j_measurements . push_back ( j_measurement ) ;
}
}
j [ " measurements " ] = j_measurements ;
j [ " type " ] = TypeToString ( getType ( ) ) . toStdString ( ) ;
j [ " port1StandardMale " ] = port1Standard = = PortStandard : : Male ;
j [ " port2StandardMale " ] = port2Standard = = PortStandard : : Male ;
2021-12-03 05:41:51 +08:00
j [ " throughZeroLength " ] = throughZeroLength ;
2021-12-02 06:21:13 +08:00
return j ;
}
void Calibration : : fromJSON ( nlohmann : : json j )
{
clearMeasurements ( ) ;
resetErrorTerms ( ) ;
port1Standard = j . value ( " port1StandardMale " , true ) ? PortStandard : : Male : PortStandard : : Female ;
port2Standard = j . value ( " port2StandardMale " , true ) ? PortStandard : : Male : PortStandard : : Female ;
2021-12-03 05:41:51 +08:00
throughZeroLength = j . value ( " throughZeroLength " , false ) ;
2021-12-02 06:21:13 +08:00
if ( j . contains ( " measurements " ) ) {
// grab measurements
for ( auto j_m : j [ " measurements " ] ) {
if ( ! j_m . contains ( " name " ) ) {
throw runtime_error ( " Measurement without name given " ) ;
}
auto m = MeasurementFromString ( QString : : fromStdString ( j_m [ " name " ] ) ) ;
if ( m = = Measurement : : Last ) {
throw runtime_error ( " Measurement name unknown: " + std : : string ( j_m [ " name " ] ) ) ;
}
// get timestamp
measurements [ m ] . timestamp = QDateTime : : fromSecsSinceEpoch ( j_m . value ( " timestamp " , 0 ) ) ;
// extract points
if ( ! j_m . contains ( " points " ) ) {
throw runtime_error ( " Measurement " + MeasurementToString ( m ) . toStdString ( ) + " does not contain any points " ) ;
}
int pointNum = 0 ;
for ( auto j_p : j_m [ " points " ] ) {
2022-03-18 01:24:18 +08:00
VNAData p ;
2021-12-02 06:21:13 +08:00
p . pointNum = pointNum + + ;
p . frequency = j_p . value ( " frequency " , 0.0 ) ;
2022-03-18 01:24:18 +08:00
p . S . m11 = complex < double > ( j_p . value ( " S11_real " , 0.0 ) , j_p . value ( " S11_imag " , 0.0 ) ) ;
p . S . m12 = complex < double > ( j_p . value ( " S12_real " , 0.0 ) , j_p . value ( " S12_imag " , 0.0 ) ) ;
p . S . m21 = complex < double > ( j_p . value ( " S21_real " , 0.0 ) , j_p . value ( " S21_imag " , 0.0 ) ) ;
p . S . m22 = complex < double > ( j_p . value ( " S22_real " , 0.0 ) , j_p . value ( " S22_imag " , 0.0 ) ) ;
2021-12-02 06:21:13 +08:00
measurements [ m ] . datapoints . push_back ( p ) ;
}
}
}
// got all measurements, construct calibration according to type
if ( j . contains ( " type " ) ) {
auto t = TypeFromString ( QString : : fromStdString ( j [ " type " ] ) ) ;
if ( t = = Type : : Last ) {
throw runtime_error ( " Calibration type unknown: " + std : : string ( j [ " type " ] ) ) ;
}
if ( calculationPossible ( t ) ) {
constructErrorTerms ( t ) ;
} else {
throw runtime_error ( " Incomplete calibration data, the requested calibration could not be performed. " ) ;
}
}
}
2020-12-01 01:16:33 +08:00
QString Calibration : : getCurrentCalibrationFile ( ) {
return this - > currentCalFile ;
}
2020-08-31 04:03:41 +08:00
istream & operator > > ( istream & in , Calibration & c )
{
2021-12-03 05:41:51 +08:00
// old file format did not contain port standard gender, set default
c . port1Standard = Calibration : : PortStandard : : Male ;
c . port2Standard = Calibration : : PortStandard : : Male ;
c . throughZeroLength = false ;
2020-08-31 04:03:41 +08:00
std : : string line ;
while ( getline ( in , line ) ) {
2020-11-01 06:03:34 +08:00
QString qLine = QString : : fromStdString ( line ) . simplified ( ) ;
2020-10-03 19:01:59 +08:00
for ( auto m : c . Measurements ( ) ) {
2020-11-01 06:03:34 +08:00
if ( Calibration : : MeasurementToString ( m ) = = qLine ) {
2020-08-31 04:03:41 +08:00
// this is the correct measurement
2020-11-12 02:13:53 +08:00
c . measurements [ m ] . datapoints . clear ( ) ;
2020-08-31 04:03:41 +08:00
uint timestamp ;
in > > timestamp ;
c . measurements [ m ] . timestamp = QDateTime : : fromSecsSinceEpoch ( timestamp ) ;
unsigned int points ;
in > > points ;
2020-11-12 02:13:53 +08:00
qDebug ( ) < < " Found measurement " < < Calibration : : MeasurementToString ( m ) < < " , containing " < < points < < " points " ;
2020-08-31 04:03:41 +08:00
for ( unsigned int i = 0 ; i < points ; i + + ) {
Protocol : : Datapoint p ;
in > > p . pointNum > > p . frequency ;
in > > p . imag_S11 > > p . real_S11 > > p . imag_S21 > > p . real_S21 > > p . imag_S12 > > p . real_S12 > > p . imag_S22 > > p . real_S22 ;
2022-03-18 01:24:18 +08:00
c . measurements [ m ] . datapoints . push_back ( VNAData ( p ) ) ;
2020-08-31 04:03:41 +08:00
if ( in . eof ( ) | | in . bad ( ) | | in . fail ( ) ) {
c . clearMeasurement ( m ) ;
throw runtime_error ( " Failed to parse measurement \" " + line + " \" , aborting calibration data import. " ) ;
}
}
break ;
}
}
for ( auto t : Calibration : : Types ( ) ) {
2020-11-01 06:03:34 +08:00
if ( Calibration : : TypeToString ( t ) = = qLine ) {
// try to apply this calibration type
2020-11-12 02:13:53 +08:00
qDebug ( ) < < " Specified calibration in file is " < < Calibration : : TypeToString ( t ) ;
2020-08-31 04:03:41 +08:00
if ( c . calculationPossible ( t ) ) {
c . constructErrorTerms ( t ) ;
} else {
throw runtime_error ( " Incomplete calibration data, the requested \" " + line + " \" -Calibration could not be performed. " ) ;
}
2020-09-16 05:22:08 +08:00
break ;
2020-08-31 04:03:41 +08:00
}
}
}
2020-11-12 02:13:53 +08:00
qDebug ( ) < < " Calibration file parsing complete " ;
2020-08-31 04:03:41 +08:00
return in ;
}
2020-10-03 19:01:59 +08:00
bool Calibration : : SanityCheckSamples ( const std : : vector < Calibration : : Measurement > & requiredMeasurements )
2020-08-31 04:03:41 +08:00
{
// 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 ;
for ( auto type : requiredMeasurements ) {
auto m = measurements [ type ] ;
if ( m . datapoints . size ( ) = = 0 ) {
// empty required measurement
return false ;
}
if ( freqs . size ( ) = = 0 ) {
// this is the first measurement, create frequency vector
for ( auto p : m . datapoints ) {
freqs . push_back ( p . frequency ) ;
}
} else {
// compare with already assembled frequency vector
if ( m . datapoints . size ( ) ! = freqs . size ( ) ) {
return false ;
}
for ( unsigned int i = 0 ; i < freqs . size ( ) ; i + + ) {
if ( m . datapoints [ i ] . frequency ! = freqs [ i ] ) {
return false ;
}
}
}
}
minFreq = freqs . front ( ) ;
maxFreq = freqs . back ( ) ;
return true ;
}
2022-03-18 01:24:18 +08:00
Calibration : : Point Calibration : : getCalibrationPoint ( VNAData & d )
2020-08-31 04:03:41 +08:00
{
if ( ! points . size ( ) ) {
throw runtime_error ( " No calibration points available " ) ;
}
if ( d . frequency < = points . front ( ) . frequency ) {
// use first point even for lower frequencies
return points . front ( ) ;
}
if ( d . frequency > = points . back ( ) . frequency ) {
// use last point even for higher frequencies
return points . back ( ) ;
}
auto p = lower_bound ( points . begin ( ) , points . end ( ) , d . frequency , [ ] ( Point p , uint64_t freq ) - > bool {
return p . frequency < freq ;
} ) ;
if ( p - > frequency = = d . frequency ) {
// Exact match, return point
return * p ;
}
// need to interpolate
auto high = p ;
p - - ;
auto low = p ;
double alpha = ( d . frequency - low - > frequency ) / ( high - > frequency - low - > frequency ) ;
Point ret ;
ret . frequency = d . frequency ;
ret . fe00 = low - > fe00 * ( 1 - alpha ) + high - > fe00 * alpha ;
ret . fe11 = low - > fe11 * ( 1 - alpha ) + high - > fe11 * alpha ;
ret . fe22 = low - > fe22 * ( 1 - alpha ) + high - > fe22 * alpha ;
ret . fe30 = low - > fe30 * ( 1 - alpha ) + high - > fe30 * alpha ;
2021-12-05 04:16:45 +08:00
ret . fex = low - > fex * ( 1 - alpha ) + high - > fex * alpha ;
2020-08-31 04:03:41 +08:00
ret . re03 = low - > re03 * ( 1 - alpha ) + high - > re03 * alpha ;
2021-12-05 04:16:45 +08:00
ret . rex = low - > rex * ( 1 - alpha ) + high - > rex * alpha ;
2020-08-31 04:03:41 +08:00
ret . re11 = low - > re11 * ( 1 - alpha ) + high - > re11 * alpha ;
ret . re22 = low - > re22 * ( 1 - alpha ) + high - > re22 * alpha ;
ret . re33 = low - > re33 * ( 1 - alpha ) + high - > re33 * alpha ;
ret . fe10e01 = low - > fe10e01 * ( 1 - alpha ) + high - > fe10e01 * alpha ;
ret . fe10e32 = low - > fe10e32 * ( 1 - alpha ) + high - > fe10e32 * alpha ;
ret . re23e01 = low - > re23e01 * ( 1 - alpha ) + high - > re23e01 * alpha ;
ret . re23e32 = low - > re23e32 * ( 1 - alpha ) + high - > re23e32 * alpha ;
return ret ;
}
void Calibration : : computeSOL ( std : : complex < double > s_m , std : : complex < double > o_m , std : : complex < double > l_m ,
std : : complex < double > & directivity , std : : complex < double > & match , std : : complex < double > & tracking ,
std : : complex < double > o_c , std : : complex < double > s_c , std : : complex < double > l_c )
{
// equations from page 13 of http://www2.electron.frba.utn.edu.ar/~jcecconi/Bibliografia/04%20-%20Param_S_y_VNA/Network_Analyzer_Error_Models_and_Calibration_Methods.pdf
// solved while taking non ideal o/s/l standards into account
auto denom = l_c * o_c * ( o_m - l_m ) + l_c * s_c * ( l_m - s_m ) + o_c * s_c * ( s_m - o_m ) ;
directivity = ( l_c * o_m * ( s_m * ( o_c - s_c ) + l_m * s_c ) - l_c * o_c * l_m * s_m + o_c * l_m * s_c * ( s_m - o_m ) ) / denom ;
match = ( l_c * ( o_m - s_m ) + o_c * ( s_m - l_m ) + s_c * ( l_m - o_m ) ) / denom ;
auto delta = ( l_c * l_m * ( o_m - s_m ) + o_c * o_m * ( s_m - l_m ) + s_c * s_m * ( l_m - o_m ) ) / denom ;
tracking = directivity * match - delta ;
}
2021-12-05 04:16:45 +08:00
void Calibration : : computeIsolation ( std : : complex < double > x0_m , std : : complex < double > x1_m , std : : complex < double > reverse_match , std : : complex < double > reverse_tracking , std : : complex < double > reverse_directivity , std : : complex < double > x0 , std : : complex < double > x1 , std : : complex < double > & internal_isolation , std : : complex < double > & external_isolation )
{
external_isolation = ( x1_m - x0_m ) * ( 1.0 - reverse_match * ( x1 - x0 ) + x1 * x0 * reverse_match * reverse_match ) / ( reverse_tracking * ( x1 - x0 ) ) ;
internal_isolation = x0_m - external_isolation * ( reverse_directivity + reverse_tracking * x0 / ( 1.0 - x0 * reverse_match ) ) ;
}
2020-08-31 04:03:41 +08:00
std : : complex < double > Calibration : : correctSOL ( std : : complex < double > measured , std : : complex < double > directivity , std : : complex < double > match , std : : complex < double > tracking )
{
return ( measured - directivity ) / ( measured * match - directivity * match + tracking ) ;
}
Calkit & Calibration : : getCalibrationKit ( )
{
return kit ;
}
void Calibration : : setCalibrationKit ( const Calkit & value )
{
kit = value ;
}
2021-12-02 06:21:13 +08:00
void Calibration : : setPortStandard ( int port , Calibration : : PortStandard standard )
{
if ( port = = 1 ) {
port1Standard = standard ;
} else if ( port = = 2 ) {
port2Standard = standard ;
}
}
Calibration : : PortStandard Calibration : : getPortStandard ( int port )
{
if ( port = = 1 ) {
return port1Standard ;
} else if ( port = = 2 ) {
return port2Standard ;
} else {
return PortStandard : : Male ;
}
}
2020-08-31 04:03:41 +08:00
Calibration : : Type Calibration : : getType ( ) const
{
return type ;
}