mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 02:03:12 +02:00
394 lines
15 KiB
C++
394 lines
15 KiB
C++
/*
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this package; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/*
|
|
* This implements the calculations described in:
|
|
*
|
|
* [1] S. B. Cohn, "Characteristic Impedance of the Shielded-Strip Transmission Line," in Transactions of the IRE
|
|
* Professional Group on Microwave Theory and Techniques, vol. 2, no. 2, pp. 52-57, July 1954
|
|
* [2] S. B. Cohn, "Shielded Coupled-Strip Transmission Line," in IRE Transactions on Microwave Theory and Techniques,
|
|
* vol. 3, no. 5, pp. 29-38, October 1955
|
|
*/
|
|
|
|
#include <transline_calculations/coupled_stripline.h>
|
|
#include <transline_calculations/units.h>
|
|
#include <transline_calculations/units_scales.h>
|
|
|
|
|
|
namespace TC = TRANSLINE_CALCULATIONS;
|
|
using TCP = TRANSLINE_PARAMETERS;
|
|
|
|
|
|
void COUPLED_STRIPLINE::Analyse()
|
|
{
|
|
// Calculate skin depth
|
|
SetParameter( TCP::SKIN_DEPTH, SkinDepth() );
|
|
|
|
// Get analysis parameters
|
|
double w = GetParameter( TCP::PHYS_WIDTH );
|
|
double t = GetParameter( TCP::T );
|
|
double s = GetParameter( TCP::PHYS_S );
|
|
double h = GetParameter( TCP::H );
|
|
const double er = GetParameter( TCP::EPSILONR );
|
|
|
|
calcZeroThicknessCoupledImpedances( h, w, s, er );
|
|
|
|
// We've got the impedances now for an infinitely thin line
|
|
if( t == 0.0 )
|
|
{
|
|
SetParameter( TCP::Z0_E, Z0_e_w_h_0_s_h );
|
|
SetParameter( TCP::Z0_O, Z0_o_w_h_0_s_h );
|
|
}
|
|
else
|
|
{
|
|
calcSingleStripImpedances();
|
|
calcFringeCapacitances( h, t, er );
|
|
calcZ0EvenMode();
|
|
calcZ0OddMode( t, s );
|
|
}
|
|
|
|
calcLosses();
|
|
calcDielectrics();
|
|
}
|
|
|
|
|
|
bool COUPLED_STRIPLINE::Synthesize( const SYNTHESIZE_OPTS aOpts )
|
|
{
|
|
if( aOpts == SYNTHESIZE_OPTS::FIX_WIDTH )
|
|
return MinimiseZ0Error1D( TCP::PHYS_S, TCP::Z0_O );
|
|
|
|
if( aOpts == SYNTHESIZE_OPTS::FIX_SPACING )
|
|
return MinimiseZ0Error1D( TCP::PHYS_WIDTH, TCP::Z0_O );
|
|
|
|
// This synthesis approach is modified from wcalc, which is released under GPL version 2
|
|
// Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006 Dan McMahill
|
|
// All rights reserved
|
|
|
|
double ze0 = 0;
|
|
double zo0 = 0;
|
|
|
|
const double h = GetParameter( TCP::H );
|
|
const double er = GetParameter( TCP::EPSILONR );
|
|
|
|
const double z0e_target = GetParameter( TCP::Z0_E );
|
|
const double z0o_target = GetParameter( TCP::Z0_O );
|
|
// Calculate Z0 and coupling, k
|
|
const double z0 = sqrt( z0e_target * z0o_target );
|
|
const double k = ( z0e_target - z0o_target ) / ( z0e_target + z0o_target );
|
|
|
|
int maxiters = 50;
|
|
|
|
// Initial guess at a solution. Note that this is an initial guess for coupled microstrip, not coupled stripline...
|
|
static constexpr double ai[] = { 1, -0.301, 3.209, -27.282, 56.609, -37.746 };
|
|
static constexpr double bi[] = { 0.020, -0.623, 17.192, -68.946, 104.740, -16.148 };
|
|
static constexpr double ci[] = { 0.002, -0.347, 7.171, -36.910, 76.132, -51.616 };
|
|
|
|
const double AW = exp( z0 * sqrt( er + 1.0 ) / 42.4 ) - 1.0;
|
|
const double F1 = 8.0 * sqrt( AW * ( 7.0 + 4.0 / er ) / 11.0 + ( 1.0 + 1.0 / er ) / 0.81 ) / AW;
|
|
|
|
double F2 = 0.0, F3 = 0.0;
|
|
;
|
|
|
|
for( int i = 0; i <= 5; i++ )
|
|
F2 = F2 + ai[i] * pow( k, i );
|
|
|
|
for( int i = 0; i <= 5; i++ )
|
|
F3 = F3 + ( bi[i] - ci[i] * ( 9.6 - er ) ) * pow( ( 0.6 - k ), static_cast<double>( i ) );
|
|
|
|
double w = h * fabs( F1 * F2 );
|
|
double s = h * fabs( F1 * F3 );
|
|
|
|
int iters = 0;
|
|
bool done = false;
|
|
double delta = 0.0;
|
|
|
|
delta = TC::UNIT_MIL * 1e-5;
|
|
|
|
const double cval = 1e-12 * z0e_target * z0o_target;
|
|
|
|
while( !done && iters < maxiters )
|
|
{
|
|
iters++;
|
|
|
|
// Compute impedances with initial solution guess
|
|
SetParameter( TCP::PHYS_WIDTH, w );
|
|
SetParameter( TCP::PHYS_S, s );
|
|
Analyse();
|
|
|
|
// Check for convergence
|
|
ze0 = GetParameter( TCP::Z0_E );
|
|
zo0 = GetParameter( TCP::Z0_O );
|
|
const double err = pow( ( ze0 - z0e_target ), 2.0 ) + pow( ( zo0 - z0o_target ), 2.0 );
|
|
|
|
if( err < cval )
|
|
{
|
|
done = true;
|
|
}
|
|
else
|
|
{
|
|
// Approximate the first Jacobian
|
|
SetParameter( TCP::PHYS_WIDTH, w + delta );
|
|
SetParameter( TCP::PHYS_S, s );
|
|
Analyse();
|
|
|
|
const double ze1 = GetParameter( TCP::Z0_E );
|
|
const double zo1 = GetParameter( TCP::Z0_O );
|
|
|
|
SetParameter( TCP::PHYS_WIDTH, w );
|
|
SetParameter( TCP::PHYS_S, s + delta );
|
|
Analyse();
|
|
|
|
const double ze2 = GetParameter( TCP::Z0_E );
|
|
const double zo2 = GetParameter( TCP::Z0_O );
|
|
|
|
const double dedw = ( ze1 - ze0 ) / delta;
|
|
const double dodw = ( zo1 - zo0 ) / delta;
|
|
const double deds = ( ze2 - ze0 ) / delta;
|
|
const double dods = ( zo2 - zo0 ) / delta;
|
|
|
|
// Find the determinate
|
|
const double d = dedw * dods - deds * dodw;
|
|
|
|
// Estimate the new solution, but don't change by more than 10% at a time to avoid convergence problems
|
|
double dw = -1.0 * ( ( ze0 - z0e_target ) * dods - ( zo0 - z0o_target ) * deds ) / d;
|
|
|
|
if( fabs( dw ) > 0.1 * w )
|
|
{
|
|
if( dw > 0.0 )
|
|
dw = 0.1 * w;
|
|
else
|
|
dw = -0.1 * w;
|
|
}
|
|
|
|
w = fabs( w + dw );
|
|
|
|
double ds = ( ( ze0 - z0e_target ) * dodw - ( zo0 - z0o_target ) * dedw ) / d;
|
|
|
|
if( fabs( ds ) > 0.1 * s )
|
|
{
|
|
if( ds > 0.0 )
|
|
ds = 0.1 * s;
|
|
else
|
|
ds = -0.1 * s;
|
|
}
|
|
|
|
s = fabs( s + ds );
|
|
}
|
|
}
|
|
|
|
if( !done )
|
|
return false;
|
|
|
|
// Recompute with the final parameters
|
|
SetParameter( TCP::PHYS_WIDTH, w );
|
|
SetParameter( TCP::PHYS_S, s );
|
|
Analyse();
|
|
|
|
// Reset the impedances
|
|
SetParameter( TCP::Z0_E, z0e_target );
|
|
SetParameter( TCP::Z0_O, z0o_target );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void COUPLED_STRIPLINE::SetAnalysisResults()
|
|
{
|
|
SetAnalysisResult( TCP::EPSILON_EFF_EVEN, e_eff_e );
|
|
SetAnalysisResult( TCP::EPSILON_EFF_ODD, e_eff_o );
|
|
SetAnalysisResult( TCP::UNIT_PROP_DELAY_EVEN, unit_prop_delay_e );
|
|
SetAnalysisResult( TCP::UNIT_PROP_DELAY_ODD, unit_prop_delay_o );
|
|
SetAnalysisResult( TCP::SKIN_DEPTH, GetParameter( TCP::SKIN_DEPTH ) );
|
|
|
|
const double Z0_E = GetParameter( TCP::Z0_E );
|
|
const double Z0_O = GetParameter( TCP::Z0_O );
|
|
const double Z_DIFF = GetParameter( TCP::Z_DIFF );
|
|
const double W = GetParameter( TCP::PHYS_WIDTH );
|
|
const double L = GetParameter( TCP::PHYS_LEN );
|
|
const double S = GetParameter( TCP::PHYS_S );
|
|
|
|
const bool Z0_E_invalid = !std::isfinite( Z0_E ) || Z0_E <= 0;
|
|
const bool Z0_O_invalid = !std::isfinite( Z0_O ) || Z0_O <= 0;
|
|
const bool Z_DIFF_invalid = !std::isfinite( Z_DIFF ) || Z_DIFF <= 0;
|
|
const bool ANG_L_invalid = !std::isfinite( ang_l ) || ang_l < 0;
|
|
const bool W_invalid = !std::isfinite( W ) || W <= 0;
|
|
const bool L_invalid = !std::isfinite( L ) || L < 0;
|
|
const bool S_invalid = !std::isfinite( S ) || S <= 0;
|
|
|
|
SetAnalysisResult( TCP::Z0_E, Z0_E, Z0_E_invalid ? TRANSLINE_STATUS::ERROR : TRANSLINE_STATUS::OK );
|
|
SetAnalysisResult( TCP::Z0_O, Z0_O, Z0_O_invalid ? TRANSLINE_STATUS::ERROR : TRANSLINE_STATUS::OK );
|
|
SetAnalysisResult( TCP::Z_DIFF, Z_DIFF, Z_DIFF_invalid ? TRANSLINE_STATUS::ERROR : TRANSLINE_STATUS::OK );
|
|
SetAnalysisResult( TCP::ANG_L, ang_l, ANG_L_invalid ? TRANSLINE_STATUS::ERROR : TRANSLINE_STATUS::OK );
|
|
SetAnalysisResult( TCP::PHYS_WIDTH, W, W_invalid ? TRANSLINE_STATUS::WARNING : TRANSLINE_STATUS::OK );
|
|
SetAnalysisResult( TCP::PHYS_LEN, L, L_invalid ? TRANSLINE_STATUS::WARNING : TRANSLINE_STATUS::OK );
|
|
SetAnalysisResult( TCP::PHYS_S, S, S_invalid ? TRANSLINE_STATUS::WARNING : TRANSLINE_STATUS::OK );
|
|
}
|
|
|
|
|
|
void COUPLED_STRIPLINE::SetSynthesisResults()
|
|
{
|
|
SetSynthesisResult( TCP::EPSILON_EFF_EVEN, e_eff_e );
|
|
SetSynthesisResult( TCP::EPSILON_EFF_ODD, e_eff_o );
|
|
SetSynthesisResult( TCP::UNIT_PROP_DELAY_EVEN, unit_prop_delay_e );
|
|
SetSynthesisResult( TCP::UNIT_PROP_DELAY_ODD, unit_prop_delay_o );
|
|
SetSynthesisResult( TCP::SKIN_DEPTH, GetParameter( TCP::SKIN_DEPTH ) );
|
|
|
|
const double Z0_E = GetParameter( TCP::Z0_E );
|
|
const double Z0_O = GetParameter( TCP::Z0_O );
|
|
const double Z_DIFF = GetParameter( TCP::Z_DIFF );
|
|
const double W = GetParameter( TCP::PHYS_WIDTH );
|
|
const double L = GetParameter( TCP::PHYS_LEN );
|
|
const double S = GetParameter( TCP::PHYS_S );
|
|
|
|
const bool Z0_E_invalid = !std::isfinite( Z0_E ) || Z0_E <= 0;
|
|
const bool Z0_O_invalid = !std::isfinite( Z0_O ) || Z0_O <= 0;
|
|
const bool Z_DIFF_invalid = !std::isfinite( Z_DIFF ) || Z_DIFF <= 0;
|
|
const bool ANG_L_invalid = !std::isfinite( ang_l ) || ang_l < 0;
|
|
const bool W_invalid = !std::isfinite( W ) || W <= 0;
|
|
const bool L_invalid = !std::isfinite( L ) || L < 0;
|
|
const bool S_invalid = !std::isfinite( S ) || S <= 0;
|
|
|
|
SetSynthesisResult( TCP::Z0_E, Z0_E, Z0_E_invalid ? TRANSLINE_STATUS::WARNING : TRANSLINE_STATUS::OK );
|
|
SetSynthesisResult( TCP::Z0_O, Z0_O, Z0_O_invalid ? TRANSLINE_STATUS::WARNING : TRANSLINE_STATUS::OK );
|
|
SetSynthesisResult( TCP::Z_DIFF, Z_DIFF, Z_DIFF_invalid ? TRANSLINE_STATUS::WARNING : TRANSLINE_STATUS::OK );
|
|
SetSynthesisResult( TCP::ANG_L, ang_l, ANG_L_invalid ? TRANSLINE_STATUS::WARNING : TRANSLINE_STATUS::OK );
|
|
SetSynthesisResult( TCP::PHYS_WIDTH, W, W_invalid ? TRANSLINE_STATUS::ERROR : TRANSLINE_STATUS::OK );
|
|
SetSynthesisResult( TCP::PHYS_LEN, L, L_invalid ? TRANSLINE_STATUS::ERROR : TRANSLINE_STATUS::OK );
|
|
SetSynthesisResult( TCP::PHYS_S, S, S_invalid ? TRANSLINE_STATUS::ERROR : TRANSLINE_STATUS::OK );
|
|
}
|
|
|
|
|
|
void COUPLED_STRIPLINE::calcFringeCapacitances( const double h, const double t, const double er )
|
|
{
|
|
// Reference [1], Eq. 2
|
|
C_f_t_h = ( TC::E0 * er / M_PI )
|
|
* ( ( 2.0 / ( 1.0 - t / h ) ) * log( ( 1.0 / ( 1.0 - t / h ) ) + 1.0 )
|
|
- ( 1.0 / ( 1.0 - t / h ) - 1.0 ) * log( ( 1.0 / pow( 1.0 - t / h, 2.0 ) ) - 1.0 ) );
|
|
|
|
// Reference [2], Eq. 13
|
|
C_f_0 = ( TC::E0 * er / M_PI ) * 2.0 * log( 2.0 );
|
|
}
|
|
|
|
|
|
void COUPLED_STRIPLINE::calcZeroThicknessCoupledImpedances( const double h, const double w, const double s,
|
|
const double er )
|
|
{
|
|
// Reference [2], Eqs. 2 - 7
|
|
const double k_e = tanh( M_PI * w / ( 2.0 * h ) ) * tanh( M_PI * ( w + s ) / ( 2.0 * h ) );
|
|
const double k_o = tanh( M_PI * w / ( 2.0 * h ) ) * coth( M_PI * ( w + s ) / ( 2.0 * h ) );
|
|
const double k_e_p = std::sqrt( 1 - std::pow( k_e, 2 ) );
|
|
const double k_o_p = std::sqrt( 1 - std::pow( k_o, 2 ) );
|
|
Z0_e_w_h_0_s_h = ( TC::ZF0 / ( 4.0 * std::sqrt( er ) ) )
|
|
* ( EllipticIntegral( k_e_p ).first / EllipticIntegral( k_e ).first );
|
|
Z0_o_w_h_0_s_h = ( TC::ZF0 / ( 4.0 * std::sqrt( er ) ) )
|
|
* ( EllipticIntegral( k_o_p ).first / EllipticIntegral( k_o ).first );
|
|
}
|
|
|
|
|
|
void COUPLED_STRIPLINE::calcSingleStripImpedances()
|
|
{
|
|
const double er = GetParameter( TCP::EPSILONR );
|
|
const double h = GetParameter( TCP::H );
|
|
const double w = GetParameter( TCP::PHYS_WIDTH );
|
|
|
|
// Finite-thickness single strip impedance
|
|
Z0_w_h_t_h = calcZ0SymmetricStripline();
|
|
|
|
// Zero-thickness single strip impedance
|
|
// Reference [1], Eqs. 5 - 6 (corrected for sqrt(e_r))
|
|
const double k = sech( M_PI * w / ( 2.0 * h ) );
|
|
const double k_p = tanh( M_PI * w / ( 2.0 * h ) );
|
|
Z0_w_h_0 =
|
|
( TC::ZF0 / ( 4.0 * std::sqrt( er ) ) ) * ( EllipticIntegral( k ).first / EllipticIntegral( k_p ).first );
|
|
}
|
|
|
|
|
|
void COUPLED_STRIPLINE::calcZ0EvenMode()
|
|
{
|
|
// Reference [2], Eq. 18
|
|
const double Z_e =
|
|
1.0 / ( ( 1.0 / Z0_w_h_t_h ) - ( C_f_t_h / C_f_0 ) * ( ( 1.0 / Z0_w_h_0 ) - ( 1.0 / Z0_e_w_h_0_s_h ) ) );
|
|
SetParameter( TCP::Z0_E, Z_e );
|
|
}
|
|
|
|
|
|
void COUPLED_STRIPLINE::calcZ0OddMode( const double t, const double s )
|
|
{
|
|
// Reference [2], Eq. 20
|
|
const double Z_o_1 =
|
|
1.0 / ( ( 1.0 / Z0_w_h_t_h ) + ( C_f_t_h / C_f_0 ) * ( ( 1.0 / Z0_o_w_h_0_s_h ) - ( 1.0 / Z0_w_h_0 ) ) );
|
|
|
|
// Reference [2], Eq. 22
|
|
const double Z_o_2 =
|
|
1.0
|
|
/ ( ( 1.0 / Z0_o_w_h_0_s_h ) + ( ( 1.0 / Z0_w_h_t_h ) - ( 1.0 / Z0_w_h_0 ) )
|
|
- ( 2.0 / TC::ZF0 ) * ( C_f_t_h / TC::E0 - C_f_0 / TC::E0 ) + ( 2.0 * t ) / ( TC::ZF0 * s ) );
|
|
|
|
const double Z_o = s / t >= 5.0 ? Z_o_1 : Z_o_2;
|
|
|
|
SetParameter( TCP::Z0_O, Z_o );
|
|
SetParameter( TCP::Z_DIFF, 2.0 * Z_o );
|
|
}
|
|
|
|
|
|
void COUPLED_STRIPLINE::calcLosses()
|
|
{
|
|
}
|
|
|
|
|
|
void COUPLED_STRIPLINE::calcDielectrics()
|
|
{
|
|
// We assume here that the dielectric is homogenous surrounding the strips - in this case, the odd or even modes
|
|
// don't change the effective dielectric constant of the transmission mode. This would not be the case if the
|
|
// dielectric were inhomogenous, as there is more electric field permeating the dielectric between the traces in
|
|
// the odd mode compared to the even mode.
|
|
const double e_r = GetParameter( TCP::EPSILONR );
|
|
e_eff_e = e_r;
|
|
e_eff_o = e_r;
|
|
|
|
// Both modes have the same propagation delay
|
|
const double unitPropDelay = UnitPropagationDelay( e_r );
|
|
unit_prop_delay_e = unitPropDelay;
|
|
unit_prop_delay_o = unitPropDelay;
|
|
|
|
// Electrical length (in radians)
|
|
const double v = TC::C0 / sqrt( e_r );
|
|
const double lambda_g = v / GetParameter( TCP::FREQUENCY );
|
|
ang_l = 2.0 * M_PI * GetParameter( TCP::PHYS_LEN ) / lambda_g;
|
|
}
|
|
|
|
|
|
double COUPLED_STRIPLINE::calcZ0SymmetricStripline()
|
|
{
|
|
m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::EPSILONR, GetParameter( TCP::EPSILONR ) );
|
|
m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::T, GetParameter( TCP::T ) );
|
|
m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::STRIPLINE_A, GetParameter( TCP::H ) / 2.0 );
|
|
m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::H, GetParameter( TCP::H ) );
|
|
m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::PHYS_LEN, GetParameter( TCP::PHYS_LEN ) );
|
|
m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::FREQUENCY, GetParameter( TCP::FREQUENCY ) );
|
|
m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::TAND, 0.0 );
|
|
m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::PHYS_WIDTH, GetParameter( TCP::PHYS_WIDTH ) );
|
|
m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::ANG_L, 0 );
|
|
m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::SIGMA, GetParameter( TCP::SIGMA ) );
|
|
m_striplineCalc.SetParameter( TRANSLINE_PARAMETERS::MURC, GetParameter( TCP::MURC ) );
|
|
m_striplineCalc.Analyse();
|
|
|
|
return m_striplineCalc.GetParameter( TCP::Z0 );
|
|
}
|