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