KiCad PCB EDA Suite
Loading...
Searching...
No Matches
coupled_stripline.cpp
Go to the documentation of this file.
1/*
2 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this package; see the file COPYING. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20/*
21 * This implements the calculations described in:
22 *
23 * [1] S. B. Cohn, "Characteristic Impedance of the Shielded-Strip Transmission Line," in Transactions of the IRE
24 * Professional Group on Microwave Theory and Techniques, vol. 2, no. 2, pp. 52-57, July 1954
25 * [2] S. B. Cohn, "Shielded Coupled-Strip Transmission Line," in IRE Transactions on Microwave Theory and Techniques,
26 * vol. 3, no. 5, pp. 29-38, October 1955
27 * [3] B. C. Wadell, "Transmission Line Design Handbook," Artech House, Norwood, MA, 1991. Sec. 3.6.3
28 * "Off-Center Stripline" (Eqs. 3.6.3.21 - 3.6.3.23) gives the image-method plus three-term
29 * correction for a single strip offset between two ground planes; applied here per mode to the
30 * Reference [2] coupled-stripline solver.
31 */
32
36
37#include <limits>
38#include <utility>
39
40
41namespace TC = TRANSLINE_CALCULATIONS;
43
44
46{
48
49 const double f = GetParameter( TCP::FREQUENCY );
50 const double rawEpsR = GetParameter( TCP::EPSILONR );
51 const double rawTanD = GetParameter( TCP::TAND );
52
53 // Overlay dispersed values so helpers reading EPSILONR / TAND via GetParameter
54 // pick them up. Raw inputs are restored before return.
57
58 // Calculate skin depth
60
61 // Get analysis parameters
62 double w = GetParameter( TCP::PHYS_WIDTH );
63 double t = GetParameter( TCP::T );
64 double s = GetParameter( TCP::PHYS_S );
65 double h = GetParameter( TCP::H );
66 double a = GetParameter( TCP::STRIPLINE_A );
67 const double er = GetParameter( TCP::EPSILONR );
68
69 // A non-positive value means STRIPLINE_A was never written by the UI; default to the centred
70 // case so existing callers keep the exact pre-offset behaviour.
71 if( a <= 0.0 )
72 a = h / 2.0;
73
75
76 // We've got the impedances now for an infinitely thin line
77 if( t == 0.0 )
78 {
81 }
82 else if( IsCenteredOffset( a, h ) )
83 {
85 calcFringeCapacitances( h, t, er );
87 calcZ0OddMode( t, s );
88 }
89 else if( !isOffsetWithinFiniteThicknessLimits( a, h, t ) )
90 {
91 // The Reference [1] finite-thickness fringe correction (Eq. 2) requires t < h on each
92 // virtual plate spacing. After the Reference [3] Eq. 3.6.3.22 image split the spacings
93 // are 2a and 2(h - a), so the safe range is t/2 < a < h - t/2. Outside it the fringe
94 // formula divides by zero or takes log of a non-positive, producing NaN. Surface the
95 // failure as TS_ERROR via the NaN-driven status path rather than returning silently
96 // bogus impedances.
97 const double nan = std::numeric_limits<double>::quiet_NaN();
98 SetParameter( TCP::Z0_E, nan );
99 SetParameter( TCP::Z0_O, nan );
101 }
102 else
103 {
104 // Reference [3] Eq. 3.6.3.22 image-method: combine two virtual centred finite-thickness
105 // striplines at plate spacings 2a and 2(h - a) via parallel admittance. Each branch runs
106 // the centred-style fringe-capacitance correction internally. Reference [3] Eq. 3.6.3.23
107 // then applies the three-term correction that recovers ~2 percent agreement with rigorous
108 // numerical solutions; we apply it per mode. H is swapped around each inner pass because
109 // the helpers read TCP::H directly.
110 const auto [z0e_1, z0o_1] = calcOffsetVirtualBranch( 2.0 * a, w, s, t, er );
111 const auto [z0e_2, z0o_2] = calcOffsetVirtualBranch( 2.0 * ( h - a ), w, s, t, er );
112
113 SetParameter( TCP::H, h );
114
115 const double z0e_image = 2.0 / ( 1.0 / z0e_1 + 1.0 / z0e_2 );
116 const double z0o_image = 2.0 / ( 1.0 / z0o_1 + 1.0 / z0o_2 );
117
118 const double z0e = applyOffsetCorrection( z0e_image, a, h, w, t, er );
119 const double z0o = applyOffsetCorrection( z0o_image, a, h, w, t, er );
120
121 SetParameter( TCP::Z0_E, z0e );
122 SetParameter( TCP::Z0_O, z0o );
123 SetParameter( TCP::Z_DIFF, 2.0 * z0o );
124
125 // Restore the offset zero-thickness impedances in the member variables so downstream
126 // consumers (e.g. diagnostics) see the corrected values rather than the last virtual
127 // branch's output.
128 calcOffsetZeroThicknessCoupledImpedances( h, a, w, s, t, er );
129 }
130
131 calcLosses();
133
134 // Expose the two derived coupled-line figures-of-merit. Z_comm = Z0e / 2 follows from
135 // Pozar "Microwave Engineering" 4th ed. Sec. 7.6 Eqs. 7.68-7.69 (two Z0e lines in parallel
136 // at equal voltage); k_c = (Z0e - Z0o) / (Z0e + Z0o) is Pozar Eq. 7.81.
137 const double z0e = GetParameter( TCP::Z0_E );
138 const double z0o = GetParameter( TCP::Z0_O );
139
140 SetParameter( TCP::Z_COMM, 0.5 * z0e );
141
142 if( ( z0e + z0o ) > 0.0 )
143 SetParameter( TCP::COUPLING_K, ( z0e - z0o ) / ( z0e + z0o ) );
144 else
146
147 SetParameter( TCP::EPSILONR, rawEpsR );
148 SetParameter( TCP::TAND, rawTanD );
149}
150
151
153{
154 // Opt-in (Z_diff, Z_comm) target translation. The 1-D fix-W / fix-S paths only optimise
155 // Z0_O so they cannot honour Z_comm; reject the option there. When selected, translate
156 // via Z_diff = 2 * Z0_O and Z_comm = Z0_E / 2 (Pozar "Microwave Engineering" 4th ed.
157 // Sec. 7.6 Eqs. 7.68-7.71) and fall through to the joint 2-D solver below.
159 {
160 const double zDiffTarget = GetParameter( TCP::Z_DIFF );
161 const double zCommTarget = GetParameter( TCP::Z_COMM );
162
163 if( !( zDiffTarget > 0.0 ) || !( zCommTarget > 0.0 ) )
164 return false;
165
166 SetParameter( TCP::Z0_O, 0.5 * zDiffTarget );
167 SetParameter( TCP::Z0_E, 2.0 * zCommTarget );
168 }
169
170 if( aOpts == SYNTHESIZE_OPTS::FIX_WIDTH )
171 return MinimiseZ0Error1D( TCP::PHYS_S, TCP::Z0_O, false );
172
173 if( aOpts == SYNTHESIZE_OPTS::FIX_SPACING )
175
176 // This synthesis approach is modified from wcalc, which is released under GPL version 2
177 // Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006 Dan McMahill
178 // All rights reserved
179
180 double ze0 = 0;
181 double zo0 = 0;
182
183 const double h = GetParameter( TCP::H );
184 const double er = GetParameter( TCP::EPSILONR );
185
186 const double z0e_target = GetParameter( TCP::Z0_E );
187 const double z0o_target = GetParameter( TCP::Z0_O );
188 // Calculate Z0 and coupling, k
189 const double z0 = sqrt( z0e_target * z0o_target );
190 const double k = ( z0e_target - z0o_target ) / ( z0e_target + z0o_target );
191
192 int maxiters = 50;
193
194 // Initial guess at a solution. Note that this is an initial guess for coupled microstrip, not coupled stripline...
195 static constexpr double ai[] = { 1, -0.301, 3.209, -27.282, 56.609, -37.746 };
196 static constexpr double bi[] = { 0.020, -0.623, 17.192, -68.946, 104.740, -16.148 };
197 static constexpr double ci[] = { 0.002, -0.347, 7.171, -36.910, 76.132, -51.616 };
198
199 const double AW = exp( z0 * sqrt( er + 1.0 ) / 42.4 ) - 1.0;
200 const double F1 = 8.0 * sqrt( AW * ( 7.0 + 4.0 / er ) / 11.0 + ( 1.0 + 1.0 / er ) / 0.81 ) / AW;
201
202 double F2 = 0.0, F3 = 0.0;
203 ;
204
205 for( int i = 0; i <= 5; i++ )
206 F2 = F2 + ai[i] * pow( k, i );
207
208 for( int i = 0; i <= 5; i++ )
209 F3 = F3 + ( bi[i] - ci[i] * ( 9.6 - er ) ) * pow( ( 0.6 - k ), static_cast<double>( i ) );
210
211 double w = h * fabs( F1 * F2 );
212 double s = h * fabs( F1 * F3 );
213
214 int iters = 0;
215 bool done = false;
216 double delta = 0.0;
217
218 delta = TC::UNIT_MIL * 1e-5;
219
220 const double cval = 1e-12 * z0e_target * z0o_target;
221
222 while( !done && iters < maxiters )
223 {
224 iters++;
225
226 // Compute impedances with initial solution guess
229 Analyse();
230
231 // Check for convergence
232 ze0 = GetParameter( TCP::Z0_E );
233 zo0 = GetParameter( TCP::Z0_O );
234 const double err = pow( ( ze0 - z0e_target ), 2.0 ) + pow( ( zo0 - z0o_target ), 2.0 );
235
236 if( err < cval )
237 {
238 done = true;
239 }
240 else
241 {
242 // Approximate the first Jacobian
245 Analyse();
246
247 const double ze1 = GetParameter( TCP::Z0_E );
248 const double zo1 = GetParameter( TCP::Z0_O );
249
252 Analyse();
253
254 const double ze2 = GetParameter( TCP::Z0_E );
255 const double zo2 = GetParameter( TCP::Z0_O );
256
257 const double dedw = ( ze1 - ze0 ) / delta;
258 const double dodw = ( zo1 - zo0 ) / delta;
259 const double deds = ( ze2 - ze0 ) / delta;
260 const double dods = ( zo2 - zo0 ) / delta;
261
262 // Find the determinate
263 const double d = dedw * dods - deds * dodw;
264
265 // Estimate the new solution, but don't change by more than 10% at a time to avoid convergence problems
266 double dw = -1.0 * ( ( ze0 - z0e_target ) * dods - ( zo0 - z0o_target ) * deds ) / d;
267
268 if( fabs( dw ) > 0.1 * w )
269 {
270 if( dw > 0.0 )
271 dw = 0.1 * w;
272 else
273 dw = -0.1 * w;
274 }
275
276 w = fabs( w + dw );
277
278 double ds = ( ( ze0 - z0e_target ) * dodw - ( zo0 - z0o_target ) * dedw ) / d;
279
280 if( fabs( ds ) > 0.1 * s )
281 {
282 if( ds > 0.0 )
283 ds = 0.1 * s;
284 else
285 ds = -0.1 * s;
286 }
287
288 s = fabs( s + ds );
289 }
290 }
291
292 if( !done )
293 return false;
294
295 // Recompute with the final parameters
298 Analyse();
299
300 // Reset the impedances
301 SetParameter( TCP::Z0_E, z0e_target );
302 SetParameter( TCP::Z0_O, z0o_target );
303
304 return true;
305}
306
307
308void COUPLED_STRIPLINE::publishResults( const ResultSink aSink, const TRANSLINE_STATUS aImpedanceFailure,
309 const TRANSLINE_STATUS aGeometryFailure )
310{
311 auto write = [this, aSink]( const TRANSLINE_PARAMETERS aParam, const double aValue,
312 const TRANSLINE_STATUS aStatus )
313 {
314 if( aSink == ResultSink::ANALYSIS )
315 SetAnalysisResult( aParam, aValue, aStatus );
316 else
317 SetSynthesisResult( aParam, aValue, aStatus );
318 };
319
320 auto writeOk = [&write]( const TRANSLINE_PARAMETERS aParam, const double aValue )
321 {
322 write( aParam, aValue, TRANSLINE_STATUS::OK );
323 };
324
325 auto writeChecked = [&write]( const TRANSLINE_PARAMETERS aParam, const double aValue, const bool aPositive,
326 const TRANSLINE_STATUS aFailureStatus )
327 {
328 const bool invalid = !std::isfinite( aValue ) || ( aPositive ? aValue <= 0.0 : aValue < 0.0 );
329 write( aParam, aValue, invalid ? aFailureStatus : TRANSLINE_STATUS::OK );
330 };
331
332 writeOk( TCP::EPSILON_EFF_EVEN, e_eff_e );
333 writeOk( TCP::EPSILON_EFF_ODD, e_eff_o );
341
342 writeChecked( TCP::Z0_E, GetParameter( TCP::Z0_E ), true, aImpedanceFailure );
343 writeChecked( TCP::Z0_O, GetParameter( TCP::Z0_O ), true, aImpedanceFailure );
344 writeChecked( TCP::Z_DIFF, GetParameter( TCP::Z_DIFF ), true, aImpedanceFailure );
345 writeOk( TCP::Z_COMM, GetParameter( TCP::Z_COMM ) );
347 writeChecked( TCP::ANG_L, ang_l, false, aImpedanceFailure );
348 writeChecked( TCP::PHYS_WIDTH, GetParameter( TCP::PHYS_WIDTH ), true, aGeometryFailure );
349 writeChecked( TCP::PHYS_LEN, GetParameter( TCP::PHYS_LEN ), false, aGeometryFailure );
350 writeChecked( TCP::PHYS_S, GetParameter( TCP::PHYS_S ), true, aGeometryFailure );
351}
352
353
355{
356 // Analysis treats invalid mode impedances as hard errors and invalid geometry as a warning,
357 // since the user supplied geometry directly and the math layer simply could not produce a
358 // physically meaningful Z out of it.
360}
361
362
364{
365 // Synthesis flips the severity: geometry is the solver output so a non-finite W / L / S is
366 // a hard error, while the user-supplied target impedances are demoted to warnings.
368}
369
370
371void COUPLED_STRIPLINE::calcFringeCapacitances( const double h, const double t, const double er )
372{
373 // Reference [1], Eq. 2
374 C_f_t_h = ( TC::E0 * er / M_PI )
375 * ( ( 2.0 / ( 1.0 - t / h ) ) * log( ( 1.0 / ( 1.0 - t / h ) ) + 1.0 )
376 - ( 1.0 / ( 1.0 - t / h ) - 1.0 ) * log( ( 1.0 / pow( 1.0 - t / h, 2.0 ) ) - 1.0 ) );
377
378 // Reference [2], Eq. 13
379 C_f_0 = ( TC::E0 * er / M_PI ) * 2.0 * log( 2.0 );
380}
381
382
383void COUPLED_STRIPLINE::calcZeroThicknessCoupledImpedances( const double h, const double w, const double s,
384 const double er )
385{
386 // Reference [2], Eqs. 2 - 7
387 const double k_e = tanh( M_PI * w / ( 2.0 * h ) ) * tanh( M_PI * ( w + s ) / ( 2.0 * h ) );
388 const double k_o = tanh( M_PI * w / ( 2.0 * h ) ) * coth( M_PI * ( w + s ) / ( 2.0 * h ) );
389 const double k_e_p = std::sqrt( 1 - std::pow( k_e, 2 ) );
390 const double k_o_p = std::sqrt( 1 - std::pow( k_o, 2 ) );
391 Z0_e_w_h_0_s_h = ( TC::ZF0 / ( 4.0 * std::sqrt( er ) ) )
392 * ( EllipticIntegral( k_e_p ).first / EllipticIntegral( k_e ).first );
393 Z0_o_w_h_0_s_h = ( TC::ZF0 / ( 4.0 * std::sqrt( er ) ) )
394 * ( EllipticIntegral( k_o_p ).first / EllipticIntegral( k_o ).first );
395}
396
397
398bool COUPLED_STRIPLINE::IsCenteredOffset( const double a, const double h )
399{
400 // Tight relative tolerance so the centred fast path is taken only when the UI genuinely meant
401 // "centred" and any physically meaningful offset routes through the image-method branch.
402 return std::fabs( a - h / 2.0 ) <= h * 1e-9;
403}
404
405
406bool COUPLED_STRIPLINE::isOffsetWithinFiniteThicknessLimits( const double a, const double h, const double t )
407{
408 // Reference [3] splits the asymmetric stripline into two virtual symmetric striplines with
409 // plate spacings 2a and 2(h - a). The Reference [1] finite-thickness fringe formula
410 // (Eq. 2) needs t < h on each, so the strip plane must sit strictly inside (t/2, h - t/2).
411 // Reject zero and negative t-clearances rather than letting the fringe formula produce NaN.
412 return ( a > 0.5 * t ) && ( a < h - 0.5 * t );
413}
414
415
416std::pair<double, double> COUPLED_STRIPLINE::calcOffsetVirtualBranch( const double aVirtualH, const double w,
417 const double s, const double t,
418 const double er )
419{
420 SetParameter( TCP::H, aVirtualH );
421 calcZeroThicknessCoupledImpedances( aVirtualH, w, s, er );
423 calcFringeCapacitances( aVirtualH, t, er );
425 calcZ0OddMode( t, s );
426
428}
429
430
431void COUPLED_STRIPLINE::calcOffsetZeroThicknessCoupledImpedances( const double h, const double a, const double w,
432 const double s, const double t, const double er )
433{
434 // Centred short-circuit so the default-input path is bit-identical to the pre-offset code.
435 if( IsCenteredOffset( a, h ) )
436 {
438 return;
439 }
440
441 // Reference [3] Eq. 3.6.3.22 image-method: represent the offset-strip geometry as the parallel
442 // combination of two virtual centred striplines at plate spacings 2a and 2(h - a). Each
443 // virtual stripline carries half the total capacitance-to-ground per unit length, so the
444 // combined Y = (Y1 + Y2) / 2 collapses to Y_centred at a = h/2 and writes the correct 1/Z
445 // offset elsewhere.
446 calcZeroThicknessCoupledImpedances( 2.0 * a, w, s, er );
447 const double z0e_1 = Z0_e_w_h_0_s_h;
448 const double z0o_1 = Z0_o_w_h_0_s_h;
449
450 calcZeroThicknessCoupledImpedances( 2.0 * ( h - a ), w, s, er );
451 const double z0e_2 = Z0_e_w_h_0_s_h;
452 const double z0o_2 = Z0_o_w_h_0_s_h;
453
454 const double z0e_image = 2.0 / ( 1.0 / z0e_1 + 1.0 / z0e_2 );
455 const double z0o_image = 2.0 / ( 1.0 / z0o_1 + 1.0 / z0o_2 );
456
457 // Reference [3] Eq. 3.6.3.23 three-term correction. Zero thickness (t = 0) still carries the
458 // (t + w)^2.9 width factor so the correction is driven purely by strip width when no
459 // finite-thickness branch is active. Reduces to zero at a = h/2 so the centred call above
460 // stays bit-exact.
461 Z0_e_w_h_0_s_h = applyOffsetCorrection( z0e_image, a, h, w, t, er );
462 Z0_o_w_h_0_s_h = applyOffsetCorrection( z0o_image, a, h, w, t, er );
463}
464
465
466double COUPLED_STRIPLINE::applyOffsetCorrection( const double aZImage, const double aOffset, const double aPlateSpacing,
467 const double aWidth, const double aThickness, const double aEr )
468{
469 // Reference [3] Eq. 3.6.3.23:
470 // delta_Z_air = (0.26 * pi / 8) * Z_air^2 * |0.5 - h1 / h|^2.2 * ((t + w) / h)^2.9
471 // Z = (1 / sqrt(er)) * (Z_air - delta_Z_air)
472 //
473 // Wadell's derivation is in air; express in dielectric space via Z_air = sqrt(er) * Z.
474 // Substituting and factoring out Z_image:
475 // Z_corrected = Z_image * (1 - (0.26 * pi / 8) * sqrt(er) * Z_image * |0.5 - a/h|^2.2
476 // * ((t + w) / h)^2.9)
477 //
478 // The position factor uses a/h (strip centreline as a fraction of total stack), so |0.5 - a/h|
479 // is the offset fraction from the centre. Absolute value handles strips above or below centre
480 // symmetrically; the 2.2 exponent ensures a smooth zero at a = h/2.
481 const double position = std::fabs( 0.5 - aOffset / aPlateSpacing );
482 const double positionFactor = std::pow( position, 2.2 );
483 const double widthFactor = std::pow( ( aThickness + aWidth ) / aPlateSpacing, 2.9 );
484 const double correction = ( 0.26 * M_PI / 8.0 ) * std::sqrt( aEr ) * aZImage * positionFactor * widthFactor;
485
486 return aZImage * ( 1.0 - correction );
487}
488
489
491{
492 const double er = GetParameter( TCP::EPSILONR );
493 const double h = GetParameter( TCP::H );
494 const double w = GetParameter( TCP::PHYS_WIDTH );
495
496 // Finite-thickness single strip impedance
498
499 // Zero-thickness single strip impedance
500 // Reference [1], Eqs. 5 - 6 (corrected for sqrt(e_r))
501 const double k = sech( M_PI * w / ( 2.0 * h ) );
502 const double k_p = tanh( M_PI * w / ( 2.0 * h ) );
503 Z0_w_h_0 =
504 ( TC::ZF0 / ( 4.0 * std::sqrt( er ) ) ) * ( EllipticIntegral( k ).first / EllipticIntegral( k_p ).first );
505}
506
507
509{
510 // Reference [2], Eq. 18
511 const double Z_e =
512 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 ) ) );
513 SetParameter( TCP::Z0_E, Z_e );
514}
515
516
517void COUPLED_STRIPLINE::calcZ0OddMode( const double t, const double s )
518{
519 // Reference [2], Eq. 20
520 const double Z_o_1 =
521 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 ) ) );
522
523 // Reference [2], Eq. 22
524 const double Z_o_2 =
525 1.0
526 / ( ( 1.0 / Z0_o_w_h_0_s_h ) + ( ( 1.0 / Z0_w_h_t_h ) - ( 1.0 / Z0_w_h_0 ) )
527 - ( 2.0 / TC::ZF0 ) * ( C_f_t_h / TC::E0 - C_f_0 / TC::E0 ) + ( 2.0 * t ) / ( TC::ZF0 * s ) );
528
529 const double Z_o = s / t >= 5.0 ? Z_o_1 : Z_o_2;
530
531 SetParameter( TCP::Z0_O, Z_o );
532 SetParameter( TCP::Z_DIFF, 2.0 * Z_o );
533}
534
535
537{
538 const double length = GetParameter( TCP::PHYS_LEN );
539 const double freq = GetParameter( TCP::FREQUENCY );
540 const double er = GetParameter( TCP::EPSILONR );
541 const double tand = GetParameter( TCP::TAND );
542
543 // Dielectric loss for a homogeneous TEM stripline. The same ฮฑ_d applies to both even and odd
544 // modes because the dielectric fills the whole cross-section, so ฮตr_eff does not change with
545 // mode excitation. ฮฑ_d = ฯ€ ยท f ยท โˆšฮตr ยท tan ฮด / c (Np/m). Convert to dB via LOG2DB.
546 // Reference Pozar, "Microwave Engineering" 4th ed., ยง3.1 homogeneous-TEM loss.
547 const double alpha_d_dB_per_m = TC::LOG2DB * ( M_PI / TC::C0 ) * freq * std::sqrt( er ) * tand;
548 const double atten_d = alpha_d_dB_per_m * length;
549
552
553 // Conductor loss via the single-stripline incremental-inductance formulation. STRIPLINE::Analyse
554 // calls lineImpedance() twice (strip to top plane, strip to bottom plane) and sums per-unit-length
555 // attenuations into LOSS_CONDUCTOR (already in dB after multiplication by PHYS_LEN). Reuse it
556 // rather than duplicate the incremental-inductance algebra.
557 // Reference Wheeler, "Formulas for the Skin Effect", Proc. IRE 30(9):412-424, Sept. 1942
558 // (incremental-inductance rule).
559 m_striplineCalc.SetParameter( TCP::EPSILONR, er );
560 m_striplineCalc.SetParameter( TCP::T, GetParameter( TCP::T ) );
561 m_striplineCalc.SetParameter( TCP::STRIPLINE_A, GetParameter( TCP::H ) / 2.0 );
562 m_striplineCalc.SetParameter( TCP::H, GetParameter( TCP::H ) );
563 m_striplineCalc.SetParameter( TCP::PHYS_LEN, length );
564 m_striplineCalc.SetParameter( TCP::FREQUENCY, freq );
565 m_striplineCalc.SetParameter( TCP::TAND, 0.0 );
567 m_striplineCalc.SetParameter( TCP::ANG_L, 0.0 );
570 m_striplineCalc.Analyse();
571
572 const double loss_single = m_striplineCalc.GetParameter( TCP::LOSS_CONDUCTOR );
573 const double z0_single = m_striplineCalc.GetParameter( TCP::Z0 );
574 const double z0_e = GetParameter( TCP::Z0_E );
575 const double z0_o = GetParameter( TCP::Z0_O );
576
577 // Scale single-strip conductor attenuation by Z0_single / Z0_mode. The per-unit-length conductor
578 // attenuation in the incremental-inductance model is inversely proportional to Z0 of the mode,
579 // so a line of the same geometry but driven at Z0_mode dissipates loss_single ยท Z0_single / Z0_mode.
580 double atten_c_e = 0.0;
581 double atten_c_o = 0.0;
582
583 if( z0_e > 0.0 && std::isfinite( z0_e ) )
584 atten_c_e = loss_single * ( z0_single / z0_e );
585
586 if( z0_o > 0.0 && std::isfinite( z0_o ) )
587 atten_c_o = loss_single * ( z0_single / z0_o );
588
590 SetParameter( TCP::ATTEN_COND_ODD, atten_c_o );
591}
592
593
595{
596 // We assume here that the dielectric is homogenous surrounding the strips - in this case, the odd or even modes
597 // don't change the effective dielectric constant of the transmission mode. This would not be the case if the
598 // dielectric were inhomogenous, as there is more electric field permeating the dielectric between the traces in
599 // the odd mode compared to the even mode.
600 const double e_r = GetParameter( TCP::EPSILONR );
601 e_eff_e = e_r;
602 e_eff_o = e_r;
603
604 // Both modes have the same propagation delay
605 const double unitPropDelay = UnitPropagationDelay( e_r );
606 unit_prop_delay_e = unitPropDelay;
607 unit_prop_delay_o = unitPropDelay;
608
609 // Electrical length (in radians)
610 const double v = TC::C0 / sqrt( e_r );
611 const double lambda_g = v / GetParameter( TCP::FREQUENCY );
612 ang_l = 2.0 * M_PI * GetParameter( TCP::PHYS_LEN ) / lambda_g;
613}
614
615
void Analyse() override
Analyse track geometry parameters to output Z0 and Ang_L.
double C_f_t_h
Fringing capacitance of single strip of finite width.
void calcLosses()
Calculates conductor and dielectric losses.
void SetSynthesisResults() override
Sets the output values and status following synthesis.
STRIPLINE m_striplineCalc
Calculator used to determine single stripline values.
double unit_prop_delay_o
Odd mode unit propagation delay (ps/cm)
bool Synthesize(SYNTHESIZE_OPTS aOpts) override
Synthesis track geometry parameters to match given Z0.
void calcFringeCapacitances(double h, double t, double er)
Calculate the coupling fringe capacitances.
double Z0_o_w_h_0_s_h
Odd mode impedance of coupled zero thickness strips.
double Z0_w_h_t_h
Impedance of single strip of finite thickness.
void calcZ0EvenMode()
Calculates even mode Z0.
void calcDielectrics()
Calculate dialectric and propagation parameters.
static bool isOffsetWithinFiniteThicknessLimits(double a, double h, double t)
Returns true when the offset a is far enough from each ground plane that the Reference [1] finite-thi...
static bool IsCenteredOffset(double a, double h)
Returns true when the strip plane offset a is effectively at the centre (a = h/2 within numerical tol...
double Z0_w_h_0
Impedance of single strip of zero thickness.
void calcSingleStripImpedances()
Calculates impedances of finite- and zero-thickness single strips.
std::pair< double, double > calcOffsetVirtualBranch(double aVirtualH, double w, double s, double t, double er)
Runs the centred finite-thickness pipeline for a single Reference [3] virtual stripline of plate spac...
static double applyOffsetCorrection(double aZImage, double aOffset, double aPlateSpacing, double aWidth, double aThickness, double aEr)
Applies the Reference [3] Eq.
double e_eff_e
Even mode effective dielectric constant.
void calcZ0OddMode(double t, double s)
Calculates odd mode Z0.
void SetAnalysisResults() override
Sets the output values and status following analysis.
double C_f_0
Fringing capacitance from one edge to ground of zero thickness strip.
double e_eff_o
Odd mode effective dielectric constant.
double Z0_e_w_h_0_s_h
Even mode impedance of coupled zero thickness strips.
double calcZ0SymmetricStripline()
Calculates the impedance of a finite-width single strip.
double ang_l
Angular length (rad)
void calcZeroThicknessCoupledImpedances(double h, double w, double s, double er)
Calculates zero-thickness coupled strip impedances.
void calcOffsetZeroThicknessCoupledImpedances(double h, double a, double w, double s, double t, double er)
Offset-aware wrapper around calcZeroThicknessCoupledImpedances built on Reference [3] Sec.
ResultSink
Identifies which result map publishResults() should populate.
void publishResults(ResultSink aSink, TRANSLINE_STATUS aImpedanceFailure, TRANSLINE_STATUS aGeometryFailure)
Shared body of SetAnalysisResults / SetSynthesisResults.
double unit_prop_delay_e
Even mode unit propagation delay (ps/cm)
static double coth(const double x)
Calculates cosh of the given argument.
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.
static double sech(const double x)
Calculates sech of the given argument.
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.
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
constexpr double correction
int delta
#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.