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