KiCad PCB EDA Suite
Loading...
Searching...
No Matches
common/transline_calculations/coplanar.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2008 Michael Margraf <[email protected]>
3 * Copyright (C) 2005, 2006 Stefan Jahn <[email protected]>
4 * Modified for Kicad: 2011 jean-pierre.charras
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this package; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <algorithm>
24
27
28
29namespace TC = TRANSLINE_CALCULATIONS;
31
32
33double COPLANAR::GetSoldermaskDeltaQ( double aWOverH, double aCOverH ) const
34{
35 if( aWOverH <= 0.0 || aCOverH <= 0.0 )
36 return 0.0;
37
38 // Start from the microstrip incremental filling factor (Wan-Hoorfar 2000 improvement
39 // on Svacina 1992). CPW shares the above-substrate geometry with microstrip but
40 // only half of the total field lies in the upper half-space in the symmetric case
41 // with no back metal, so the capturable fraction is half that of microstrip. This
42 // is a first-order empirical adaptation: a rigorous treatment would extend
43 // Ghione-Naldi conformal mapping to include the overlay, which is not attempted
44 // here.
45 const double q2Coated = WanHoorfarQ2( aWOverH, 1.0 + aCOverH );
46 const double q2Base = WanHoorfarQ2( aWOverH, 1.0 );
47 const double microstripDelta = std::max( 0.0, q2Coated - q2Base );
48
49 const bool fillsGaps = GetParameter( TCP::SOLDERMASK_FILLS_GAPS ) >= 0.5;
50 const bool backMetal = hasBackMetal();
51
52 // Upper half-space factor. A symmetric CPW stores roughly half its capacitance in
53 // the upper half-space above the substrate; a back-metal-backed CPW shifts more of
54 // the capacitance to the substrate side so the upper-half share is smaller.
55 const double halfSpace = backMetal ? 0.25 : 0.5;
56
57 // Slot-coverage factor. With the slots mask-filled the whole upper-half fringe sees
58 // mask; with the slots air-filled the mask covers only the conductor strips and
59 // captures a smaller share of the fringe energy. 0.4 is a coarse estimate of the
60 // conductor-to-structure area ratio for typical 50 ohm CPW geometries and matches
61 // the ordering in Bogatin 2018 Ch 7 that gaps-filled produces a larger Z0 drop than
62 // gaps-air on identical geometry.
63 const double slotCoverage = fillsGaps ? 1.0 : 0.4;
64
65 return halfSpace * slotCoverage * microstripDelta;
66}
67
68
70{
72
74
75 const double W = GetParameter( TCP::PHYS_WIDTH );
76 const double S = GetParameter( TCP::PHYS_S );
77 const double H = GetParameter( TCP::H );
78 const double T = GetParameter( TCP::T );
79 const double freq = GetParameter( TCP::FREQUENCY );
80 const double epsr = GetDispersedEpsilonR( freq );
81 const double len = GetParameter( TCP::PHYS_LEN );
82 const double tand_substrate = GetDispersedTanDelta( freq );
83 const double sigma = GetParameter( TCP::SIGMA );
84
85 const bool backMetal = hasBackMetal();
86
87 // Quasi-static conformal-mapping approximation. K() is the complete elliptic integral of the
88 // first kind; k1 is the ratio used for the coplanar half-plane solution. The air-backed
89 // expressions follow Ghione & Naldi, "Analytical Formulas for Coplanar Lines in Hybrid and
90 // Monolithic MICs", Electronics Letters 20(4):179-181, Feb. 1984, eq. (1) and (3a); the
91 // metal-backed k3 branch follows Ghione & Naldi, "Parameters of Coplanar Waveguides with
92 // Lower Common Planes", Electronics Letters 19(18):734-735, Sept. 1983, eq. (8). The
93 // original CPW geometry is from Wen, "Coplanar Waveguide: A Surface Strip Transmission Line
94 // Suitable for Nonreciprocal Gyromagnetic Device Applications", IEEE Trans. MTT
95 // 17(12):1087-1090, Dec. 1969.
96 const double k1 = W / ( W + S + S );
97 const double kk1 = EllipticIntegral( k1 ).first;
98 const double kpk1 = EllipticIntegral( std::sqrt( 1.0 - k1 * k1 ) ).first;
99 const double q1 = kk1 / kpk1;
100
101 double q3 = 0.0;
102 double qz = 0.0;
103 double er0 = 0.0;
104 double zl_factor = 0.0;
105
106 if( backMetal )
107 {
108 const double k3 = std::tanh( ( M_PI / 4.0 ) * ( W / H ) )
109 / std::tanh( ( M_PI / 4.0 ) * ( W + S + S ) / H );
110 q3 = EllipticIntegral( k3 ).first / EllipticIntegral( std::sqrt( 1.0 - k3 * k3 ) ).first;
111 qz = 1.0 / ( q1 + q3 );
112 er0 = 1.0 + q3 * qz * ( epsr - 1.0 );
113 zl_factor = TC::ZF0 / 2.0 * qz;
114 }
115 else
116 {
117 const double k2 = std::sinh( ( M_PI / 4.0 ) * ( W / H ) )
118 / std::sinh( ( M_PI / 4.0 ) * ( W + S + S ) / H );
119 const double q2 = EllipticIntegral( k2 ).first / EllipticIntegral( std::sqrt( 1.0 - k2 * k2 ) ).first;
120 er0 = 1.0 + ( epsr - 1.0 ) / 2.0 * q2 / q1;
121 zl_factor = TC::ZF0 / 4.0 / q1;
122 }
123
124 // Finite-thickness correction. Widens the effective centre conductor and narrows the slots
125 // per Gupta, Garg, Bahl, Bhartia, "Microstrip Lines and Slotlines", 2nd ed., Artech House
126 // 1996, eq. (7.98)-(7.100) (strip-thickness adjustment d = (T*1.25/pi)*(1 + ln(4*pi*W/T))).
127 if( T > 0.0 )
128 {
129 const double d = ( T * 1.25 / M_PI ) * ( 1.0 + std::log( 4.0 * M_PI * W / T ) );
130 const double se = S - d;
131 const double We = W + d;
132 const double ke = We / ( We + se + se );
133 const double qe = EllipticIntegral( ke ).first / EllipticIntegral( std::sqrt( 1.0 - ke * ke ) ).first;
134
135 if( backMetal )
136 {
137 qz = 1.0 / ( qe + q3 );
138 er0 = 1.0 + q3 * qz * ( epsr - 1.0 );
139 zl_factor = TC::ZF0 / 2.0 * qz;
140 }
141 else
142 {
143 zl_factor = TC::ZF0 / 4.0 / qe;
144 }
145
146 er0 = er0 - ( 0.7 * ( er0 - 1.0 ) * T / S ) / ( q1 + ( 0.7 * T / S ) );
147 }
148
149 // Apply the soldermask cover correction to the static er0 and scale the Z0
150 // pre-factor so Z0 = zl_factor / sr_er_f remains self-consistent after dispersion.
151 // SOLDERMASK_PRESENT == 0, zero thickness, and zero filling-factor each take the
152 // no-op path out of ApplySoldermaskCorrection so the un-coated results are unchanged.
153 double tand_eff = tand_substrate;
154 const double uOverH = ( H > 0.0 ) ? ( W / H ) : 0.0;
155 const auto [ er0_coated, tand_coated ] =
156 ApplySoldermaskCorrection( er0, tand_substrate, epsr, uOverH, freq );
157
158 if( er0_coated != er0 )
159 {
160 zl_factor *= std::sqrt( er0 / er0_coated );
161 er0 = er0_coated;
162 tand_eff = tand_coated;
163 }
164
165 const double sr_er = std::sqrt( epsr );
166 const double sr_er0 = std::sqrt( er0 );
167
168 // TE0-mode cutoff frequency and the dispersion factor G, used by the ad-hoc quadratic
169 // correction that lifts er0 toward er at high frequency.
170 const double fte = ( TC::C0 / 4.0 ) / ( H * std::sqrt( epsr - 1.0 ) );
171
172 const double p = std::log( W / H );
173 const double u = 0.54 - ( 0.64 - 0.015 * p ) * p;
174 const double v = 0.43 - ( 0.86 - 0.54 * p ) * p;
175 const double G = std::exp( u * std::log( W / S ) + v );
176
177 // Skin-effect conductor loss from Wheeler's incremental-inductance rule (Wheeler,
178 // "Formulas for the Skin Effect", Proc. IRE 30(9):412-424, Sept. 1942), first applied
179 // to CPW by Owyang & Wu, "The Approximate Parameters of Slot Lines and Their Complement",
180 // IRE Trans. Antennas and Propagation AP-6:49-55, Jan. 1958, extended to asymmetric
181 // geometry in Ghione, "A CAD-Oriented Analytical Model for the Losses of General
182 // Asymmetric Coplanar Lines...", IEEE Trans. MTT 41(9):1499-1510, Sept. 1993, and
183 // corrected/generalised to multiconductor form in Ghione-Goano-Naldi, "A CAD-oriented
184 // model for the ohmic losses of multiconductor coplanar lines in hybrid and monolithic
185 // MIC's", GAAS'96 Symposium, Paris, 5-7 June 1996, paper 8A2, eq. (13) and (14).
186 // Valid only for finite strip thickness T > 0.
187 double ac = 0.0;
188
189 if( T > 0.0 )
190 {
191 const double n = ( 1.0 - k1 ) * 8.0 * M_PI / ( T * ( 1.0 + k1 ) );
192 const double a = W / 2.0;
193 const double b = a + S;
194 ac = ( M_PI + std::log( n * a ) ) / a + ( M_PI + std::log( n * b ) ) / b;
195 }
196
197 const double ac_factor = ac / ( 4.0 * TC::ZF0 * kk1 * kpk1 * ( 1.0 - k1 * k1 ) );
198 const double ad_factor = ( epsr / ( epsr - 1.0 ) ) * tand_eff * M_PI / TC::C0;
199
200 // Effective-permittivity dispersion from Gevorgian, Martinsson, Deleniv, Kollberg, Vendik,
201 // "Simple and accurate dispersion expression for the effective dielectric constant of
202 // coplanar waveguides", IEE Proc. Microwaves, Antennas and Propagation 144(2):145-148,
203 // Apr. 1997, DOI 10.1049/ip-map:19970843. The u/v coefficients and the (f/fte)^-1.8
204 // scaling come from that paper's dispersion factor G.
205 double sr_er_f = sr_er0;
206 sr_er_f += ( sr_er - sr_er0 ) / ( 1.0 + G * std::pow( freq / fte, -1.8 ) );
207
208 // Strip losses only (no radiation losses yet). Neper-to-dB conversion via LOG2DB.
210 TC::LOG2DB * len * ac_factor * sr_er0 * std::sqrt( M_PI * TC::MU0 * freq / sigma ) );
212 TC::LOG2DB * len * ad_factor * freq * ( sr_er_f * sr_er_f - 1.0 ) / sr_er_f );
213
214 SetParameter( TCP::ANG_L, 2.0 * M_PI * len * sr_er_f * freq / TC::C0 );
215 SetParameter( TCP::EPSILON_EFF, sr_er_f * sr_er_f );
216 SetParameter( TCP::Z0, zl_factor / sr_er_f );
218}
219
220
221bool COPLANAR::Synthesize( const SYNTHESIZE_OPTS /* aOpts */ )
222{
223 const TRANSLINE_PARAMETERS target =
225
226 // Recompute PHYS_LEN from the requested ANG_L to preserve the legacy round-trip contract.
227 return MinimiseZ0Error1D( target, TCP::Z0, true );
228}
229
230
232{
238
239 const double Z0 = GetParameter( TCP::Z0 );
240 const double angL = GetParameter( TCP::ANG_L );
241 const double len = GetParameter( TCP::PHYS_LEN );
242 const double W = GetParameter( TCP::PHYS_WIDTH );
243 const double S = GetParameter( TCP::PHYS_S );
244
245 const bool Z0_invalid = !std::isfinite( Z0 ) || Z0 < 0;
246 const bool angL_invalid = !std::isfinite( angL ) || angL < 0;
247 const bool len_invalid = !std::isfinite( len ) || len < 0;
248 const bool W_invalid = !std::isfinite( W ) || W <= 0.0;
249 const bool S_invalid = !std::isfinite( S ) || S <= 0.0;
250
256}
257
258
260{
266
267 const double Z0 = GetParameter( TCP::Z0 );
268 const double angL = GetParameter( TCP::ANG_L );
269 const double len = GetParameter( TCP::PHYS_LEN );
270 const double W = GetParameter( TCP::PHYS_WIDTH );
271 const double S = GetParameter( TCP::PHYS_S );
272
273 const bool Z0_invalid = !std::isfinite( Z0 ) || Z0 < 0;
274 const bool angL_invalid = !std::isfinite( angL ) || angL < 0;
275 const bool len_invalid = !std::isfinite( len ) || len < 0;
276 const bool W_invalid = !std::isfinite( W ) || W <= 0.0;
277 const bool S_invalid = !std::isfinite( S ) || S <= 0.0;
278
279 const TRANSLINE_STATUS W_status =
283 const TRANSLINE_STATUS S_status =
287
291 SetSynthesisResult( TCP::PHYS_WIDTH, W, W_status );
292 SetSynthesisResult( TCP::PHYS_S, S, S_status );
293}
void SetAnalysisResults() override
Sets the output values and status following analysis.
void Analyse() override
Analyse trace geometry to produce Z0, electrical length, effective permittivity, and losses.
bool Synthesize(SYNTHESIZE_OPTS aOpts) override
Synthesize the unknown geometry parameter to match the Z0 target.
bool hasBackMetal() const
True when CPW_BACKMETAL reads as grounded (CBCPW)
void SetSynthesisResults() override
Sets the output values and status following synthesis.
double GetSoldermaskDeltaQ(double aWOverH, double aCOverH) const override
Coplanar waveguide soldermask filling factor.
double GetDispersedEpsilonR(double aF) const
Dispersed permittivity at aF. Returns raw EPSILONR when the model is inactive.
TRANSLINE_PARAMETERS m_synthesizeTarget
Which geometry parameter is the unknown during synthesis (set by the UI)
double GetDispersedTanDelta(double aF) const
Dispersed loss tangent at aF. Returns raw TAND when the model is inactive.
double GetParameter(const TRANSLINE_PARAMETERS aParam) const
Gets the given calculation property.
static std::pair< double, double > EllipticIntegral(double arg)
Computes the complete elliptic integral of first kind K() and the second kind E() using the arithmeti...
void SetParameter(const TRANSLINE_PARAMETERS aParam, const double aValue)
Sets the given calculation property.
void SetSynthesisResult(TRANSLINE_PARAMETERS aParam, const double aValue, const TRANSLINE_STATUS aStatus=TRANSLINE_STATUS::OK)
Sets a synthesis result.
void UpdateDielectricModel()
Refit the Djordjevic-Sarkar model from the current parameter map.
std::pair< double, double > ApplySoldermaskCorrection(double aEpsEffUncoated, double aTanDeltaSubstrate, double aEpsRSubstrate, double aWOverH, double aF) const
Apply a three-layer (substrate / soldermask / air) correction to an un-coated (eps_eff,...
double SkinDepth() const
Calculate skin depth.
static double WanHoorfarQ2(double aU, double aHBarTop)
Wan-Hoorfar 2000 eq.
bool MinimiseZ0Error1D(TRANSLINE_PARAMETERS aOptimise, TRANSLINE_PARAMETERS aMeasure, bool aRecalculateLength=false)
minimizeZ0Error1D
void SetAnalysisResult(TRANSLINE_PARAMETERS aParam, const double aValue, const TRANSLINE_STATUS aStatus=TRANSLINE_STATUS::OK)
Sets an analysis result.
static double UnitPropagationDelay(double aEpsilonEff)
Calculates the unit propagation delay (ps/cm) for the given effective permittivity.
TRANSLINE_PARAMETERS TCP
@ S
Solder (HASL/SMOBC)
#define G(x, y, z)
Definition md5_hash.cpp:16
#define H(x, y, z)
Definition md5_hash.cpp:17
#define M_PI
SYNTHESIZE_OPTS
Options for specifying synthesis inputs, targets, or strategies.
TRANSLINE_STATUS
Parameter status values.
TRANSLINE_PARAMETERS
All possible parameters used (as inputs or outputs) by the transmission line calculations.