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. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <algorithm>
23
26
27
28namespace TC = TRANSLINE_CALCULATIONS;
30
31
33{
35
36 const double f = GetParameter( TCP::FREQUENCY );
37 const double rawEpsR = GetParameter( TCP::EPSILONR );
38 const double rawTanD = GetParameter( TCP::TAND );
39
40 // Overlay dispersed values so helpers reading EPSILONR / TAND via GetParameter
41 // pick them up. Raw inputs are restored before return.
44
45 // Effective permeability
46 mur_eff_ms();
47
48 // Static impedance
50
51 // Apply the soldermask cover correction to the static er_eff and Z0 before dispersion
52 // and losses consume them. Uses the Wan-Hoorfar 2000 / Svacina 1992 three-layer
53 // filling factor with Bahl-Stuchly 1980 air-replacement decomposition. When the mask
54 // is disabled or the thickness is zero this is a bit-identical no-op path. Z0 scales
55 // as 1/sqrt(eps_eff) for a given homogeneous equivalent, so rescaling by
56 // sqrt(uncoated/coated) keeps the physics consistent.
57 const double erEffUncoated = er_eff_0;
58 const double dispersedEpsR = GetParameter( TCP::EPSILONR );
59 const double dispersedTanD = GetParameter( TCP::TAND );
60 const double uOverH = GetParameter( TCP::PHYS_WIDTH ) / GetParameter( TCP::H );
61
62 const auto [ erEffCoated, tanDCoated ] =
63 ApplySoldermaskCorrection( erEffUncoated, dispersedTanD, dispersedEpsR, uOverH, f );
64
65 if( erEffCoated != erEffUncoated )
66 {
67 er_eff_0 = erEffCoated;
68 Z0_0 *= std::sqrt( erEffUncoated / erEffCoated );
70 }
71
72 // Calculate freq dependence of er and Z0
73 dispersion();
74
75 // Calculate electrical lengths
76 line_angle();
77
78 // Swap the mask-blended tan delta in so dielectric_losses picks it up through the
79 // parameter map; restore the dispersed substrate value immediately after attenuation.
80 SetParameter( TCP::TAND, tanDCoated );
81
82 // Calculate losses
84
85 SetParameter( TCP::EPSILONR, rawEpsR );
86 SetParameter( TCP::TAND, rawTanD );
87}
88
89
91{
92 const double z0_dest = GetParameter( TCP::Z0 );
93 const double angl_dest = GetParameter( TCP::ANG_L );
94
95 // Calculate width and use for initial value in Newton's method
97
98 // Optimise Z0, varying width
100 return false;
101
102 // Re-calculate with required output parameters
103 SetParameter( TCP::Z0, z0_dest );
104 SetParameter( TCP::ANG_L, angl_dest );
105 double const er_eff = GetParameter( TCP::EPSILON_EFF );
107 * GetParameter( TCP::ANG_L ) / 2.0 / M_PI ); /* in m */
108 Analyse();
109
110 // Set the output parameters
111 SetParameter( TCP::Z0, z0_dest );
112 SetParameter( TCP::ANG_L, angl_dest );
114 * GetParameter( TCP::ANG_L ) / 2.0 / M_PI ); /* in m */
115
116 return true;
117}
118
119
121{
127
128 const double Z0 = GetParameter( TCP::Z0 );
129 const double ANG_L = GetParameter( TCP::ANG_L );
130 const double L = GetParameter( TCP::PHYS_LEN );
131 const double W = GetParameter( TCP::PHYS_WIDTH );
132
133 const bool Z0_invalid = !std::isfinite( Z0 ) || Z0 < 0;
134 const bool ANG_L_invalid = !std::isfinite( ANG_L ) || ANG_L < 0;
135 const bool L_invalid = !std::isfinite( L ) || L < 0;
136 const bool W_invalid = !std::isfinite( W ) || W <= 0;
137
142}
143
144
146{
152
153 const double Z0 = GetParameter( TCP::Z0 );
154 const double ANG_L = GetParameter( TCP::ANG_L );
155 const double L = GetParameter( TCP::PHYS_LEN );
156 const double W = GetParameter( TCP::PHYS_WIDTH );
157
158 const bool Z0_invalid = !std::isfinite( Z0 ) || Z0 < 0;
159 const bool ANG_L_invalid = !std::isfinite( ANG_L ) || ANG_L < 0;
160 const bool L_invalid = !std::isfinite( L ) || L < 0;
161 const bool W_invalid = !std::isfinite( W ) || W <= 0;
162
167}
168
169
171{
172 const double e_r = GetParameter( TCP::EPSILONR );
173 const double a = ( ( GetParameter( TCP::Z0 ) / TC::ZF0 / 2 / M_PI ) * sqrt( ( e_r + 1 ) / 2. ) )
174 + ( ( e_r - 1 ) / ( e_r + 1 ) * ( 0.23 + ( 0.11 / e_r ) ) );
175 const double b = TC::ZF0 / 2 * M_PI / ( GetParameter( TCP::Z0 ) * sqrt( e_r ) );
176
177 double w_h;
178
179 if( a > 1.52 )
180 {
181 w_h = 8 * exp( a ) / ( exp( 2. * a ) - 2 );
182 }
183 else
184 {
185 w_h = ( 2. / M_PI )
186 * ( b - 1. - log( ( 2 * b ) - 1. )
187 + ( ( e_r - 1 ) / ( 2 * e_r ) ) * ( log( b - 1. ) + 0.39 - 0.61 / e_r ) );
188 }
189
190 double width;
191
192 if( GetParameter( TCP::H ) > 0.0 )
193 width = w_h * GetParameter( TCP::H );
194 else
195 width = 0;
196
197 return width;
198}
199
200
201double MICROSTRIP::Z0_dispersion( double u, double e_r, double e_r_eff_0, double e_r_eff_f, double f_n )
202{
203 const double R_1 = 0.03891 * pow( e_r, 1.4 );
204 const double R_2 = 0.267 * pow( u, 7.0 );
205 const double R_3 = 4.766 * exp( -3.228 * pow( u, 0.641 ) );
206 const double R_4 = 0.016 + pow( 0.0514 * e_r, 4.524 );
207 const double R_5 = pow( f_n / 28.843, 12.0 );
208 const double R_6 = 22.2 * pow( u, 1.92 );
209 const double R_7 = 1.206 - 0.3144 * exp( -R_1 ) * ( 1.0 - exp( -R_2 ) );
210 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 ) ) );
211 double tmpf = pow( e_r - 1.0, 6.0 );
212 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 ) )
213 * ( tmpf / ( 1.0 + 10.0 * tmpf ) );
214 const double R_10 = 0.00044 * pow( e_r, 2.136 ) + 0.0184;
215 tmpf = pow( f_n / 19.47, 6.0 );
216 const double R_11 = tmpf / ( 1.0 + 0.0962 * tmpf );
217 const double R_12 = 1.0 / ( 1.0 + 0.00245 * u * u );
218 const double R_13 = 0.9408 * pow( e_r_eff_f, R_8 ) - 0.9603;
219 const double R_14 = ( 0.9408 - R_9 ) * pow( e_r_eff_0, R_8 ) - 0.9603;
220 const double R_15 = 0.707 * R_10 * pow( f_n / 12.3, 1.097 );
221 const double R_16 = 1.0 + 0.0503 * e_r * e_r * R_11 * ( 1.0 - exp( -pow( u / 15.0, 6.0 ) ) );
222 const double R_17 = R_7 * ( 1.0 - 1.1241 * ( R_12 / R_16 ) * exp( -0.026 * pow( f_n, 1.15656 ) - R_15 ) );
223
224 return pow( R_13 / R_14, R_17 );
225}
226
227
229{
230 const double freq = 6.0 + ( 2.0 * M_PI - 6.0 ) * exp( -pow( 30.666 / u, 0.7528 ) );
231 return ( TC::ZF0 / ( 2.0 * M_PI ) ) * log( freq / u + sqrt( 1.0 + 4.0 / ( u * u ) ) );
232}
233
234
235double MICROSTRIP::delta_Z0_cover( double u, double h2h )
236{
237 // March, "Empirical Formulas for the Impedance and Effective Dielectric Constant of
238 // Covered Microstrip", 11th European Microwave Conf., 1981, pp. 671-672, Eq. (1) with
239 // corrected P and Q. Reproduced as Garg-Bahl-Bozzi, "Microstrip Lines and Slotlines",
240 // 4th ed., Artech House 2024, Sec. 2.4 Eqs. (2.128a)-(2.128b). P alone applies for
241 // W/h <= 1 and P * Q for W/h >= 1; the sqrt(u - 1) factor in Q collapses to zero at
242 // u = 1 so a single expression handles both cases. Validity 0.05 <= W/h <= 20,
243 // h'/h > 1.
244 //
245 // The original P/Q in Bahl, "Use Exact Methods for Microstrip Design", Microwaves
246 // Dec. 1978, p. 61, contains a typo that March (p. 671) calls out explicitly, noting
247 // "the equation for Q should be of the form 1 - tanh^-1(x) in lieu of 1 - tanh(1 - x),
248 // as published". Wan, Int. J. MMCAE 5(6), 1995, Fig. 6(a) confirms Bahl's published
249 // Z0 deviates from rigorous numerical data while March's corrected form tracks it.
250 const double h2hp1 = 1.0 + h2h;
251 const double P = 270.0 * ( 1.0 - tanh( 0.28 + 1.2 * sqrt( h2h ) ) );
252
253 // Outside GBB's validity range the Q numerator saturates atanh (wide trace, low
254 // cover); fall back to the P-only branch so Z0 stays finite.
255 const double qArg = ( 0.48 * sqrt( std::max( 0.0, u - 1.0 ) ) ) / ( h2hp1 * h2hp1 );
256
257 if( qArg >= 1.0 )
258 return P;
259
260 const double Q = 1.0 - atanh( qArg );
261 return P * Q;
262}
263
264
265double MICROSTRIP::filling_factor( double u, double e_r )
266{
267 const double u2 = u * u;
268 const double u3 = u2 * u;
269 const double u4 = u3 * u;
270 const double a = 1.0 + log( ( u4 + u2 / 2704 ) / ( u4 + 0.432 ) ) / 49.0 + log( 1.0 + u3 / 5929.741 ) / 18.7;
271 const double b = 0.564 * pow( ( e_r - 0.9 ) / ( e_r + 3.0 ), 0.053 );
272 return pow( 1.0 + 10.0 / u, -a * b );
273}
274
275
276double MICROSTRIP::delta_q_cover( double h2h )
277{
278 // March, "Empirical Formulas for the Impedance and Effective Dielectric Constant of
279 // Covered Microstrip", 11th European Microwave Conf., 1981, p. 672, Eq. (7). March
280 // derived this q_c by curve-fitting the Bryant-Weiss numerical data as a replacement
281 // for the original tanh form in Bahl, "Use Exact Methods for Microstrip Design",
282 // Microwaves Dec. 1978, p. 61, which March (p. 672) calls "highly inaccurate for H2/H
283 // ratios from 2 to 5". q_c collapses to unity as the cover retreats.
284 //
285 // Wan, Int. J. MMCAE 5(6), 1995, Fig. 6(b) reports that for eps_eff at e_r = 9.8 and
286 // h'/h = 2.0 Bahl's original q_c tracks numerical data more closely than March's. We
287 // retain March's form for consistency with the matching Z0 correction in
288 // delta_Z0_cover, which is also from March 1981 and was fitted against the same
289 // Bryant-Weiss reference set.
290 return tanh( 1.043 + 0.121 * h2h - 1.164 / h2h );
291}
292
293
294double MICROSTRIP::delta_q_thickness( double u, double t_h )
295{
296 return ( 2.0 * log( 2.0 ) / M_PI ) * ( t_h / sqrt( u ) );
297}
298
299
300double MICROSTRIP::e_r_effective( double e_r, double q )
301{
302 return 0.5 * ( e_r + 1.0 ) + 0.5 * q * ( e_r - 1.0 );
303}
304
305
306double MICROSTRIP::delta_u_thickness( double u, double t_h, double e_r )
307{
308 double delta_u;
309
310 if( t_h > 0.0 )
311 {
312 /* correction for thickness for a homogeneous microstrip */
313 delta_u = ( t_h / M_PI ) * log( 1.0 + ( 4.0 * M_E ) * pow( tanh( sqrt( 6.517 * u ) ), 2.0 ) / t_h );
314 /* correction for strip on a substrate with relative permettivity e_r */
315 delta_u = 0.5 * delta_u * ( 1.0 + 1.0 / cosh( sqrt( e_r - 1.0 ) ) );
316 }
317 else
318 {
319 delta_u = 0.0;
320 }
321 return delta_u;
322}
323
324
325double MICROSTRIP::e_r_dispersion( double u, double e_r, double f_n )
326{
327 const double P_1 =
328 0.27488 + u * ( 0.6315 + 0.525 / pow( 1.0 + 0.0157 * f_n, 20.0 ) ) - 0.065683 * exp( -8.7513 * u );
329 const double P_2 = 0.33622 * ( 1.0 - exp( -0.03442 * e_r ) );
330 const double P_3 = 0.0363 * exp( -4.6 * u ) * ( 1.0 - exp( -pow( f_n / 38.7, 4.97 ) ) );
331 const double P_4 = 1.0 + 2.751 * ( 1.0 - exp( -pow( e_r / 15.916, 8.0 ) ) );
332
333 return P_1 * P_2 * pow( ( P_3 * P_4 + 0.1844 ) * f_n, 1.5763 );
334}
335
336
338{
339 double alpha_c;
340 const double e_r_eff_0 = er_eff_0;
341 const double delta = GetParameter( TCP::SKIN_DEPTH );
342
343 if( GetParameter( TCP::FREQUENCY ) > 0.0 )
344 {
345 /* current distribution factor */
346 const double K = exp( -1.2 * pow( Z0_h_1 / TC::ZF0, 0.7 ) );
347 /* skin resistance */
348 double R_s = 1.0 / ( GetParameter( TCP::SIGMA ) * delta );
349
350 /* correction for surface roughness */
351 R_s *= 1.0 + ( ( 2.0 / M_PI ) * atan( 1.40 * pow( ( GetParameter( TCP::ROUGH ) / delta ), 2.0 ) ) );
352 /* strip inductive quality factor */
353 const double Q_c = ( M_PI * Z0_h_1 * GetParameter( TCP::PHYS_WIDTH ) * GetParameter( TCP::FREQUENCY ) )
354 / ( R_s * TC::C0 * K );
355 alpha_c = ( 20.0 * M_PI / log( 10.0 ) ) * GetParameter( TCP::FREQUENCY ) * sqrt( e_r_eff_0 ) / ( TC::C0 * Q_c );
356 }
357 else
358 {
359 alpha_c = 0.0;
360 }
361
362 return alpha_c;
363}
364
365
367{
368 const double e_r = GetParameter( TCP::EPSILONR );
369 const double e_r_eff_0 = er_eff_0;
370
371 return ( 20.0 * M_PI / log( 10.0 ) ) * ( GetParameter( TCP::FREQUENCY ) / TC::C0 ) * ( e_r / sqrt( e_r_eff_0 ) )
372 * ( ( e_r_eff_0 - 1.0 ) / ( e_r - 1.0 ) ) * GetParameter( TCP::TAND );
373}
374
375
377{
378 const double e_r = GetParameter( TCP::EPSILONR );
379 const double h2 = GetParameter( TCP::H_T );
380 const double h2h = h2 / GetParameter( TCP::H );
382 const double t_h = GetParameter( TCP::T ) / GetParameter( TCP::H );
383
384 /* compute normalized width correction for e_r = 1.0 */
385 const double delta_u_1 = delta_u_thickness( u, t_h, 1.0 );
386 /* compute homogeneous stripline impedance */
387 Z0_h_1 = Z0_homogeneous( u + delta_u_1 );
388 /* compute normalized width correction */
389 const double delta_u_r = delta_u_thickness( u, t_h, e_r );
390 u += delta_u_r;
391 /* compute homogeneous stripline impedance */
392 const double Z0_h_r = Z0_homogeneous( u );
393
394 /* filling factor, with width corrected for thickness */
395 const double q_inf = filling_factor( u, e_r );
396 /* cover effect */
397 const double q_c = delta_q_cover( h2h );
398 /* thickness effect */
399 const double q_t = delta_q_thickness( u, t_h );
400 /* resultant filling factor */
401 const double q = ( q_inf - q_t ) * q_c;
402
403 /* e_r corrected for thickness and non-homogeneous material */
404 const double e_r_eff_t = e_r_effective( e_r, q );
405
406 /* effective dielectric constant */
407 const double e_r_eff = e_r_eff_t * pow( Z0_h_1 / Z0_h_r, 2.0 );
408
409 /* characteristic impedance, corrected for thickness, cover */
410 /* and non-homogeneous material */
411 SetParameter( TCP::Z0, ( Z0_h_r - delta_Z0_cover( u, h2h ) ) / sqrt( e_r_eff_t ) );
412
413 w_eff = u * GetParameter( TCP::H );
414 er_eff_0 = e_r_eff;
416}
417
418
420{
421 const double e_r = GetParameter( TCP::EPSILONR );
422 const double e_r_eff_0 = er_eff_0;
423 const double u = GetParameter( TCP::PHYS_WIDTH ) / GetParameter( TCP::H );
424
425 /* normalized frequency [GHz * mm] */
426 const double f_n = GetParameter( TCP::FREQUENCY ) * GetParameter( TCP::H ) / 1e06;
427
428 const double P = e_r_dispersion( u, e_r, f_n );
429 /* effective dielectric constant corrected for dispersion */
430 const double e_r_eff_f = e_r - ( e_r - e_r_eff_0 ) / ( 1.0 + P );
431
432 const double D = Z0_dispersion( u, e_r, e_r_eff_0, e_r_eff_f, f_n );
433 const double Z0_f = Z0_0 * D;
434
436 SetParameter( TCP::EPSILON_EFF, e_r_eff_f );
437 SetParameter( TCP::Z0, Z0_f );
438}
439
440
447
448
450{
451 const double mur = GetParameter( TCP::MUR );
452 const double h = GetParameter( TCP::H );
453 const double w = GetParameter( TCP::PHYS_WIDTH );
454 mur_eff = ( 2.0 * mur ) / ( ( 1.0 + mur ) + ( ( 1.0 - mur ) * pow( ( 1.0 + ( 10.0 * h / w ) ), -0.5 ) ) );
455}
456
457
459{
460 double e_r_eff = GetParameter( TCP::EPSILON_EFF );
461
462 // Velocity
463 double v = TC::C0 / sqrt( e_r_eff * mur_eff );
464 // Wavelength
465 double lambda_g = v / GetParameter( TCP::FREQUENCY );
466 // Electrical angles (rad)
467 SetParameter( TCP::ANG_L, 2.0 * M_PI * GetParameter( TCP::PHYS_LEN ) / lambda_g );
468}
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:37
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.