KiCad PCB EDA Suite
Loading...
Searching...
No Matches
common/transline_calculations/microstrip.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2001 Gopal Narayanan <[email protected]>
3 * Copyright (C) 2002 Claudio Girardi <[email protected]>
4 * Copyright (C) 2005, 2006 Stefan Jahn <[email protected]>
5 * Modified for Kicad: 2018 Jean-Pierre Charras <jp.charras at wanadoo.fr>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this package; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include <algorithm>
25
28
29
30namespace TC = TRANSLINE_CALCULATIONS;
32
33
35{
37
38 const double f = GetParameter( TCP::FREQUENCY );
39 const double rawEpsR = GetParameter( TCP::EPSILONR );
40 const double rawTanD = GetParameter( TCP::TAND );
41
42 // Overlay dispersed values so helpers reading EPSILONR / TAND via GetParameter
43 // pick them up. Raw inputs are restored before return.
46
47 // Effective permeability
48 mur_eff_ms();
49
50 // Static impedance
52
53 // Apply the soldermask cover correction to the static er_eff and Z0 before dispersion
54 // and losses consume them. Uses the Wan-Hoorfar 2000 / Svacina 1992 three-layer
55 // filling factor with Bahl-Stuchly 1980 air-replacement decomposition. When the mask
56 // is disabled or the thickness is zero this is a bit-identical no-op path. Z0 scales
57 // as 1/sqrt(eps_eff) for a given homogeneous equivalent, so rescaling by
58 // sqrt(uncoated/coated) keeps the physics consistent.
59 const double erEffUncoated = er_eff_0;
60 const double dispersedEpsR = GetParameter( TCP::EPSILONR );
61 const double dispersedTanD = GetParameter( TCP::TAND );
62 const double uOverH = GetParameter( TCP::PHYS_WIDTH ) / GetParameter( TCP::H );
63
64 const auto [ erEffCoated, tanDCoated ] =
65 ApplySoldermaskCorrection( erEffUncoated, dispersedTanD, dispersedEpsR, uOverH, f );
66
67 if( erEffCoated != erEffUncoated )
68 {
69 er_eff_0 = erEffCoated;
70 Z0_0 *= std::sqrt( erEffUncoated / erEffCoated );
72 }
73
74 // Calculate freq dependence of er and Z0
75 dispersion();
76
77 // Calculate electrical lengths
78 line_angle();
79
80 // Swap the mask-blended tan delta in so dielectric_losses picks it up through the
81 // parameter map; restore the dispersed substrate value immediately after attenuation.
82 SetParameter( TCP::TAND, tanDCoated );
83
84 // Calculate losses
86
87 SetParameter( TCP::EPSILONR, rawEpsR );
88 SetParameter( TCP::TAND, rawTanD );
89}
90
91
93{
94 const double z0_dest = GetParameter( TCP::Z0 );
95 const double angl_dest = GetParameter( TCP::ANG_L );
96
97 // Calculate width and use for initial value in Newton's method
99
100 // Optimise Z0, varying width
102 return false;
103
104 // Re-calculate with required output parameters
105 SetParameter( TCP::Z0, z0_dest );
106 SetParameter( TCP::ANG_L, angl_dest );
107 double const er_eff = GetParameter( TCP::EPSILON_EFF );
109 * GetParameter( TCP::ANG_L ) / 2.0 / M_PI ); /* in m */
110 Analyse();
111
112 // Set the output parameters
113 SetParameter( TCP::Z0, z0_dest );
114 SetParameter( TCP::ANG_L, angl_dest );
116 * GetParameter( TCP::ANG_L ) / 2.0 / M_PI ); /* in m */
117
118 return true;
119}
120
121
123{
129
130 const double Z0 = GetParameter( TCP::Z0 );
131 const double ANG_L = GetParameter( TCP::ANG_L );
132 const double L = GetParameter( TCP::PHYS_LEN );
133 const double W = GetParameter( TCP::PHYS_WIDTH );
134
135 const bool Z0_invalid = !std::isfinite( Z0 ) || Z0 < 0;
136 const bool ANG_L_invalid = !std::isfinite( ANG_L ) || ANG_L < 0;
137 const bool L_invalid = !std::isfinite( L ) || L < 0;
138 const bool W_invalid = !std::isfinite( W ) || W <= 0;
139
144}
145
146
148{
154
155 const double Z0 = GetParameter( TCP::Z0 );
156 const double ANG_L = GetParameter( TCP::ANG_L );
157 const double L = GetParameter( TCP::PHYS_LEN );
158 const double W = GetParameter( TCP::PHYS_WIDTH );
159
160 const bool Z0_invalid = !std::isfinite( Z0 ) || Z0 < 0;
161 const bool ANG_L_invalid = !std::isfinite( ANG_L ) || ANG_L < 0;
162 const bool L_invalid = !std::isfinite( L ) || L < 0;
163 const bool W_invalid = !std::isfinite( W ) || W <= 0;
164
169}
170
171
173{
174 const double e_r = GetParameter( TCP::EPSILONR );
175 const double a = ( ( GetParameter( TCP::Z0 ) / TC::ZF0 / 2 / M_PI ) * sqrt( ( e_r + 1 ) / 2. ) )
176 + ( ( e_r - 1 ) / ( e_r + 1 ) * ( 0.23 + ( 0.11 / e_r ) ) );
177 const double b = TC::ZF0 / 2 * M_PI / ( GetParameter( TCP::Z0 ) * sqrt( e_r ) );
178
179 double w_h;
180
181 if( a > 1.52 )
182 {
183 w_h = 8 * exp( a ) / ( exp( 2. * a ) - 2 );
184 }
185 else
186 {
187 w_h = ( 2. / M_PI )
188 * ( b - 1. - log( ( 2 * b ) - 1. )
189 + ( ( e_r - 1 ) / ( 2 * e_r ) ) * ( log( b - 1. ) + 0.39 - 0.61 / e_r ) );
190 }
191
192 double width;
193
194 if( GetParameter( TCP::H ) > 0.0 )
195 width = w_h * GetParameter( TCP::H );
196 else
197 width = 0;
198
199 return width;
200}
201
202
203double MICROSTRIP::Z0_dispersion( double u, double e_r, double e_r_eff_0, double e_r_eff_f, double f_n )
204{
205 const double R_1 = 0.03891 * pow( e_r, 1.4 );
206 const double R_2 = 0.267 * pow( u, 7.0 );
207 const double R_3 = 4.766 * exp( -3.228 * pow( u, 0.641 ) );
208 const double R_4 = 0.016 + pow( 0.0514 * e_r, 4.524 );
209 const double R_5 = pow( f_n / 28.843, 12.0 );
210 const double R_6 = 22.2 * pow( u, 1.92 );
211 const double R_7 = 1.206 - 0.3144 * exp( -R_1 ) * ( 1.0 - exp( -R_2 ) );
212 const double R_8 = 1.0 + 1.275 * ( 1.0 - exp( -0.004625 * R_3 * pow( e_r, 1.674 ) * pow( f_n / 18.365, 2.745 ) ) );
213 double tmpf = pow( e_r - 1.0, 6.0 );
214 const double R_9 = 5.086 * R_4 * ( R_5 / ( 0.3838 + 0.386 * R_4 ) ) * ( exp( -R_6 ) / ( 1.0 + 1.2992 * R_5 ) )
215 * ( tmpf / ( 1.0 + 10.0 * tmpf ) );
216 const double R_10 = 0.00044 * pow( e_r, 2.136 ) + 0.0184;
217 tmpf = pow( f_n / 19.47, 6.0 );
218 const double R_11 = tmpf / ( 1.0 + 0.0962 * tmpf );
219 const double R_12 = 1.0 / ( 1.0 + 0.00245 * u * u );
220 const double R_13 = 0.9408 * pow( e_r_eff_f, R_8 ) - 0.9603;
221 const double R_14 = ( 0.9408 - R_9 ) * pow( e_r_eff_0, R_8 ) - 0.9603;
222 const double R_15 = 0.707 * R_10 * pow( f_n / 12.3, 1.097 );
223 const double R_16 = 1.0 + 0.0503 * e_r * e_r * R_11 * ( 1.0 - exp( -pow( u / 15.0, 6.0 ) ) );
224 const double R_17 = R_7 * ( 1.0 - 1.1241 * ( R_12 / R_16 ) * exp( -0.026 * pow( f_n, 1.15656 ) - R_15 ) );
225
226 return pow( R_13 / R_14, R_17 );
227}
228
229
231{
232 const double freq = 6.0 + ( 2.0 * M_PI - 6.0 ) * exp( -pow( 30.666 / u, 0.7528 ) );
233 return ( TC::ZF0 / ( 2.0 * M_PI ) ) * log( freq / u + sqrt( 1.0 + 4.0 / ( u * u ) ) );
234}
235
236
237double MICROSTRIP::delta_Z0_cover( double u, double h2h )
238{
239 // March, "Empirical Formulas for the Impedance and Effective Dielectric Constant of
240 // Covered Microstrip", 11th European Microwave Conf., 1981, pp. 671-672, Eq. (1) with
241 // corrected P and Q. Reproduced as Garg-Bahl-Bozzi, "Microstrip Lines and Slotlines",
242 // 4th ed., Artech House 2024, Sec. 2.4 Eqs. (2.128a)-(2.128b). P alone applies for
243 // W/h <= 1 and P * Q for W/h >= 1; the sqrt(u - 1) factor in Q collapses to zero at
244 // u = 1 so a single expression handles both cases. Validity 0.05 <= W/h <= 20,
245 // h'/h > 1.
246 //
247 // The original P/Q in Bahl, "Use Exact Methods for Microstrip Design", Microwaves
248 // Dec. 1978, p. 61, contains a typo that March (p. 671) calls out explicitly, noting
249 // "the equation for Q should be of the form 1 - tanh^-1(x) in lieu of 1 - tanh(1 - x),
250 // as published". Wan, Int. J. MMCAE 5(6), 1995, Fig. 6(a) confirms Bahl's published
251 // Z0 deviates from rigorous numerical data while March's corrected form tracks it.
252 const double h2hp1 = 1.0 + h2h;
253 const double P = 270.0 * ( 1.0 - tanh( 0.28 + 1.2 * sqrt( h2h ) ) );
254
255 // Outside GBB's validity range the Q numerator saturates atanh (wide trace, low
256 // cover); fall back to the P-only branch so Z0 stays finite.
257 const double qArg = ( 0.48 * sqrt( std::max( 0.0, u - 1.0 ) ) ) / ( h2hp1 * h2hp1 );
258
259 if( qArg >= 1.0 )
260 return P;
261
262 const double Q = 1.0 - atanh( qArg );
263 return P * Q;
264}
265
266
267double MICROSTRIP::filling_factor( double u, double e_r )
268{
269 const double u2 = u * u;
270 const double u3 = u2 * u;
271 const double u4 = u3 * u;
272 const double a = 1.0 + log( ( u4 + u2 / 2704 ) / ( u4 + 0.432 ) ) / 49.0 + log( 1.0 + u3 / 5929.741 ) / 18.7;
273 const double b = 0.564 * pow( ( e_r - 0.9 ) / ( e_r + 3.0 ), 0.053 );
274 return pow( 1.0 + 10.0 / u, -a * b );
275}
276
277
278double MICROSTRIP::delta_q_cover( double h2h )
279{
280 // March, "Empirical Formulas for the Impedance and Effective Dielectric Constant of
281 // Covered Microstrip", 11th European Microwave Conf., 1981, p. 672, Eq. (7). March
282 // derived this q_c by curve-fitting the Bryant-Weiss numerical data as a replacement
283 // for the original tanh form in Bahl, "Use Exact Methods for Microstrip Design",
284 // Microwaves Dec. 1978, p. 61, which March (p. 672) calls "highly inaccurate for H2/H
285 // ratios from 2 to 5". q_c collapses to unity as the cover retreats.
286 //
287 // Wan, Int. J. MMCAE 5(6), 1995, Fig. 6(b) reports that for eps_eff at e_r = 9.8 and
288 // h'/h = 2.0 Bahl's original q_c tracks numerical data more closely than March's. We
289 // retain March's form for consistency with the matching Z0 correction in
290 // delta_Z0_cover, which is also from March 1981 and was fitted against the same
291 // Bryant-Weiss reference set.
292 return tanh( 1.043 + 0.121 * h2h - 1.164 / h2h );
293}
294
295
296double MICROSTRIP::delta_q_thickness( double u, double t_h )
297{
298 return ( 2.0 * log( 2.0 ) / M_PI ) * ( t_h / sqrt( u ) );
299}
300
301
302double MICROSTRIP::e_r_effective( double e_r, double q )
303{
304 return 0.5 * ( e_r + 1.0 ) + 0.5 * q * ( e_r - 1.0 );
305}
306
307
308double MICROSTRIP::delta_u_thickness( double u, double t_h, double e_r )
309{
310 double delta_u;
311
312 if( t_h > 0.0 )
313 {
314 /* correction for thickness for a homogeneous microstrip */
315 delta_u = ( t_h / M_PI ) * log( 1.0 + ( 4.0 * M_E ) * pow( tanh( sqrt( 6.517 * u ) ), 2.0 ) / t_h );
316 /* correction for strip on a substrate with relative permettivity e_r */
317 delta_u = 0.5 * delta_u * ( 1.0 + 1.0 / cosh( sqrt( e_r - 1.0 ) ) );
318 }
319 else
320 {
321 delta_u = 0.0;
322 }
323 return delta_u;
324}
325
326
327double MICROSTRIP::e_r_dispersion( double u, double e_r, double f_n )
328{
329 const double P_1 =
330 0.27488 + u * ( 0.6315 + 0.525 / pow( 1.0 + 0.0157 * f_n, 20.0 ) ) - 0.065683 * exp( -8.7513 * u );
331 const double P_2 = 0.33622 * ( 1.0 - exp( -0.03442 * e_r ) );
332 const double P_3 = 0.0363 * exp( -4.6 * u ) * ( 1.0 - exp( -pow( f_n / 38.7, 4.97 ) ) );
333 const double P_4 = 1.0 + 2.751 * ( 1.0 - exp( -pow( e_r / 15.916, 8.0 ) ) );
334
335 return P_1 * P_2 * pow( ( P_3 * P_4 + 0.1844 ) * f_n, 1.5763 );
336}
337
338
340{
341 double alpha_c;
342 const double e_r_eff_0 = er_eff_0;
343 const double delta = GetParameter( TCP::SKIN_DEPTH );
344
345 if( GetParameter( TCP::FREQUENCY ) > 0.0 )
346 {
347 /* current distribution factor */
348 const double K = exp( -1.2 * pow( Z0_h_1 / TC::ZF0, 0.7 ) );
349 /* skin resistance */
350 double R_s = 1.0 / ( GetParameter( TCP::SIGMA ) * delta );
351
352 /* correction for surface roughness */
353 R_s *= 1.0 + ( ( 2.0 / M_PI ) * atan( 1.40 * pow( ( GetParameter( TCP::ROUGH ) / delta ), 2.0 ) ) );
354 /* strip inductive quality factor */
355 const double Q_c = ( M_PI * Z0_h_1 * GetParameter( TCP::PHYS_WIDTH ) * GetParameter( TCP::FREQUENCY ) )
356 / ( R_s * TC::C0 * K );
357 alpha_c = ( 20.0 * M_PI / log( 10.0 ) ) * GetParameter( TCP::FREQUENCY ) * sqrt( e_r_eff_0 ) / ( TC::C0 * Q_c );
358 }
359 else
360 {
361 alpha_c = 0.0;
362 }
363
364 return alpha_c;
365}
366
367
369{
370 const double e_r = GetParameter( TCP::EPSILONR );
371 const double e_r_eff_0 = er_eff_0;
372
373 return ( 20.0 * M_PI / log( 10.0 ) ) * ( GetParameter( TCP::FREQUENCY ) / TC::C0 ) * ( e_r / sqrt( e_r_eff_0 ) )
374 * ( ( e_r_eff_0 - 1.0 ) / ( e_r - 1.0 ) ) * GetParameter( TCP::TAND );
375}
376
377
379{
380 const double e_r = GetParameter( TCP::EPSILONR );
381 const double h2 = GetParameter( TCP::H_T );
382 const double h2h = h2 / GetParameter( TCP::H );
384 const double t_h = GetParameter( TCP::T ) / GetParameter( TCP::H );
385
386 /* compute normalized width correction for e_r = 1.0 */
387 const double delta_u_1 = delta_u_thickness( u, t_h, 1.0 );
388 /* compute homogeneous stripline impedance */
389 Z0_h_1 = Z0_homogeneous( u + delta_u_1 );
390 /* compute normalized width correction */
391 const double delta_u_r = delta_u_thickness( u, t_h, e_r );
392 u += delta_u_r;
393 /* compute homogeneous stripline impedance */
394 const double Z0_h_r = Z0_homogeneous( u );
395
396 /* filling factor, with width corrected for thickness */
397 const double q_inf = filling_factor( u, e_r );
398 /* cover effect */
399 const double q_c = delta_q_cover( h2h );
400 /* thickness effect */
401 const double q_t = delta_q_thickness( u, t_h );
402 /* resultant filling factor */
403 const double q = ( q_inf - q_t ) * q_c;
404
405 /* e_r corrected for thickness and non-homogeneous material */
406 const double e_r_eff_t = e_r_effective( e_r, q );
407
408 /* effective dielectric constant */
409 const double e_r_eff = e_r_eff_t * pow( Z0_h_1 / Z0_h_r, 2.0 );
410
411 /* characteristic impedance, corrected for thickness, cover */
412 /* and non-homogeneous material */
413 SetParameter( TCP::Z0, ( Z0_h_r - delta_Z0_cover( u, h2h ) ) / sqrt( e_r_eff_t ) );
414
415 w_eff = u * GetParameter( TCP::H );
416 er_eff_0 = e_r_eff;
418}
419
420
422{
423 const double e_r = GetParameter( TCP::EPSILONR );
424 const double e_r_eff_0 = er_eff_0;
425 const double u = GetParameter( TCP::PHYS_WIDTH ) / GetParameter( TCP::H );
426
427 /* normalized frequency [GHz * mm] */
428 const double f_n = GetParameter( TCP::FREQUENCY ) * GetParameter( TCP::H ) / 1e06;
429
430 const double P = e_r_dispersion( u, e_r, f_n );
431 /* effective dielectric constant corrected for dispersion */
432 const double e_r_eff_f = e_r - ( e_r - e_r_eff_0 ) / ( 1.0 + P );
433
434 const double D = Z0_dispersion( u, e_r, e_r_eff_0, e_r_eff_f, f_n );
435 const double Z0_f = Z0_0 * D;
436
438 SetParameter( TCP::EPSILON_EFF, e_r_eff_f );
439 SetParameter( TCP::Z0, Z0_f );
440}
441
442
449
450
452{
453 const double mur = GetParameter( TCP::MUR );
454 const double h = GetParameter( TCP::H );
455 const double w = GetParameter( TCP::PHYS_WIDTH );
456 mur_eff = ( 2.0 * mur ) / ( ( 1.0 + mur ) + ( ( 1.0 - mur ) * pow( ( 1.0 + ( 10.0 * h / w ) ), -0.5 ) ) );
457}
458
459
461{
462 double e_r_eff = GetParameter( TCP::EPSILON_EFF );
463
464 // Velocity
465 double v = TC::C0 / sqrt( e_r_eff * mur_eff );
466 // Wavelength
467 double lambda_g = v / GetParameter( TCP::FREQUENCY );
468 // Electrical angles (rad)
469 SetParameter( TCP::ANG_L, 2.0 * M_PI * GetParameter( TCP::PHYS_LEN ) / lambda_g );
470}
double w_eff
Effective width of line.
void microstrip_Z0()
Calculates the microstrip static impedance.
static double e_r_effective(double, double)
Calculates effective dielectric constant from material e_r and filling factor.
bool Synthesize(SYNTHESIZE_OPTS aOpts) override
Synthesis track geometry parameters to match given Z0.
double Z0_0
static characteristic impedance
void line_angle()
Calculates microstrip length in radians.
void Analyse() override
Analyse track geometry parameters to output Z0 and Ang_L.
static double Z0_dispersion(double, double, double, double, double)
Calculates the dispersion correction factor for the characteristic impedance static.
void dispersion()
Calculates frequency dependent parameters of the microstrip.
static double filling_factor(double, double)
Calculates the filling factor for a microstrip without cover and zero conductor thickness.
double SynthesizeWidth() const
Calculates the width with the current set of parameters.
static double e_r_dispersion(double, double, double)
Calculates the dispersion correction factor for the effective permeability.
static double delta_u_thickness(double, double, double)
Calculates the thickness effect on normalized width.
static double delta_q_thickness(double, double)
Calculates the thickness effect on filling factor.
static double delta_q_cover(double)
Calculates the cover effect on filling factor.
double mur_eff
Effective mag. permeability.
double er_eff_0
Static effective dielectric constant.
void mur_eff_ms()
Calculates the effective magnetic permeability.
void SetSynthesisResults() override
Sets the output values and status following synthesis.
void SetAnalysisResults() override
Sets the output values and status following analysis.
static double Z0_homogeneous(double)
Calculates the impedance for a stripline in a homogeneous medium, without cover effects.
double dielectric_losses() const
Calculates the microstrip dielectric losses per unit.
double Z0_h_1
homogeneous stripline impedance
void attenuation()
Calculates the attenuation of the microstrip.
static double delta_Z0_cover(double, double)
Calculates the cover effect on impedance for a stripline in a homogeneous medium.
double conductor_losses() const
Calculate the microstrip conductor losses per unit.
double GetDispersedEpsilonR(double aF) const
Dispersed permittivity at aF. Returns raw EPSILONR when the model is inactive.
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.
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.
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
double atanh(double x)
#define D(x)
Definition ptree.cpp:41
int delta
#define M_PI
SYNTHESIZE_OPTS
Options for specifying synthesis inputs, targets, or strategies.
TRANSLINE_PARAMETERS
All possible parameters used (as inputs or outputs) by the transmission line calculations.