KiCad PCB EDA Suite
Loading...
Searching...
No Matches
common/transline_calculations/rectwaveguide.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2001 Gopal Narayanan <[email protected]>
3 * Copyright (C) 2005, 2006 Stefan Jahn <[email protected]>
4 * Modified for Kicad: 2015 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; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <cstdio>
24#include <cstring>
25
28
29
30namespace TC = TRANSLINE_CALCULATIONS;
32
33
34// Formulas throughout this file follow Pozar, "Microwave Engineering", 4th ed.,
35// Wiley 2012, §3.3 (rectangular waveguide, eq. 3.84 cutoff, Table 3.2 summary).
36// Ramo, Whinnery, Van Duzer, "Fields and Waves in Communication Electronics", 3rd
37// ed., Wiley 1994, chapter 8 gives equivalent derivations; Collin, "Foundations for
38// Microwave Engineering", 2nd ed., McGraw-Hill 1992, §3.3 tabulates the same
39// attenuation sums. Open-access cross-reference: Orfanidis, "Electromagnetic Waves
40// and Antennas", Ch. 9, eq. (9.8.1), freely available at
41// https://eceweb1.rutgers.edu/~orfanidi/ewa/.
42
44{
45 // Free-medium wavenumber squared, k^2 = omega^2*mu*eps (Pozar §3.3).
46 const double kval = 2.0 * M_PI * GetParameter( TCP::FREQUENCY )
47 * std::sqrt( GetParameter( TCP::MUR ) * GetParameter( TCP::EPSILONR ) ) / TC::C0;
48 return kval * kval;
49}
50
51
52double RECTWAVEGUIDE::KcSquare( const int aM, const int aN ) const
53{
54 // TE(m,n)/TM(m,n) cutoff wavenumber, kc^2 = (m*pi/a)^2 + (n*pi/b)^2 (Pozar eq. 3.84).
55 const double a = GetParameter( TCP::PHYS_WIDTH );
56 const double b = GetParameter( TCP::PHYS_S );
57 return std::pow( aM * M_PI / a, 2.0 ) + std::pow( aN * M_PI / b, 2.0 );
58}
59
60
61double RECTWAVEGUIDE::Fc( const int aM, const int aN ) const
62{
63 // Cutoff frequency, fc = kc / (2*pi*sqrt(mu*eps)) (Pozar eq. 3.84).
64 return std::sqrt( KcSquare( aM, aN ) / GetParameter( TCP::MUR ) / GetParameter( TCP::EPSILONR ) ) * TC::C0
65 / ( 2.0 * M_PI );
66}
67
68
70{
71 // Per-mode conductor attenuation from the surface-resistance integrals given in
72 // Pozar §3.3 (TE: eq. form of Pozar table 3.2; TM: analogous closed form). Ramo,
73 // Whinnery, Van Duzer §8.7 derives the same integrals from the tangential H field.
74 const double a = GetParameter( TCP::PHYS_WIDTH );
75 const double b = GetParameter( TCP::PHYS_S );
76 const double f = GetParameter( TCP::FREQUENCY );
77 const double murc = GetParameter( TCP::MURC );
78 const double sigma = GetParameter( TCP::SIGMA );
79
80 const double Rs = std::sqrt( M_PI * f * murc * TC::MU0 / sigma );
81 double ac = 0.0;
82
83 const int mmax = static_cast<int>( std::floor( f / Fc( 1, 0 ) ) );
84 const int nmax = mmax;
85
86 // TE(m, n) modes.
87 for( int n = 0; n <= nmax; ++n )
88 {
89 for( int m = 1; m <= mmax; ++m )
90 {
91 const double f_c = Fc( m, n );
92
93 if( f <= f_c )
94 continue;
95
96 if( n == 0 )
97 {
98 ac += ( Rs / ( b * TC::ZF0 * std::sqrt( 1.0 - std::pow( f_c / f, 2.0 ) ) ) )
99 * ( 1.0 + ( ( 2.0 * b / a ) * std::pow( f_c / f, 2.0 ) ) );
100 }
101 else
102 {
103 ac += ( ( 2.0 * Rs ) / ( b * TC::ZF0 * std::sqrt( 1.0 - std::pow( f_c / f, 2.0 ) ) ) )
104 * ( ( ( 1.0 + ( b / a ) ) * std::pow( f_c / f, 2.0 ) )
105 + ( ( 1.0 - std::pow( f_c / f, 2.0 ) )
106 * ( ( ( b / a ) * ( ( ( b / a ) * std::pow( m, 2.0 ) ) + std::pow( n, 2.0 ) ) )
107 / ( std::pow( b * m / a, 2.0 ) + std::pow( n, 2.0 ) ) ) ) );
108 }
109 }
110 }
111
112 // TM(m, n) modes.
113 for( int n = 1; n <= nmax; ++n )
114 {
115 for( int m = 1; m <= mmax; ++m )
116 {
117 const double f_c = Fc( m, n );
118
119 if( f <= f_c )
120 continue;
121
122 ac += ( ( 2.0 * Rs ) / ( b * TC::ZF0 * std::sqrt( 1.0 - std::pow( f_c / f, 2.0 ) ) ) )
123 * ( ( ( std::pow( m, 2.0 ) * std::pow( b / a, 3.0 ) ) + std::pow( n, 2.0 ) )
124 / ( std::pow( m * b / a, 2.0 ) + std::pow( n, 2.0 ) ) );
125 }
126 }
127
128 return ac * TC::LOG2DB;
129}
130
131
133{
134 double acc = std::sqrt( KcSquare( 1, 0 ) - KvalSquare() );
135 acc = TC::LOG2DB * acc;
136 return acc;
137}
138
139
141{
142 const double k_square = KvalSquare();
143 const double beta = std::sqrt( k_square - KcSquare( 1, 0 ) );
144
145 double ad = ( k_square * GetParameter( TCP::TAND ) ) / ( 2.0 * beta );
146 ad = ad * TC::LOG2DB;
147 return ad;
148}
149
150
152{
153 // Walks a bounded (m, n) grid and emits every mode whose cutoff lies below the current
154 // operating frequency. Buffer length caps the display string; excess modes collapse to
155 // an ellipsis. Format matches the legacy pcb_calculator output: H(m,n) for TE, E(m,n)
156 // for TM.
157 constexpr int MAX_INDEX = 6;
158 constexpr std::size_t MAX_STR = 128;
159 char buf[MAX_STR];
160 char token[32];
161
162 const double freq = GetParameter( TCP::FREQUENCY );
163
164 m_teModes.clear();
165 m_tmModes.clear();
166
167 if( freq >= Fc( 1, 0 ) )
168 {
169 buf[0] = '\0';
170 bool truncated = false;
171
172 for( int m = 0; m <= MAX_INDEX && !truncated; ++m )
173 {
174 for( int n = 0; n <= MAX_INDEX && !truncated; ++n )
175 {
176 if( m == 0 && n == 0 )
177 continue;
178
179 if( freq >= Fc( m, n ) )
180 {
181 std::snprintf( token, sizeof( token ), "H(%d,%d) ", m, n );
182
183 if( std::strlen( buf ) + std::strlen( token ) + 5 < MAX_STR )
184 {
185 std::strcat( buf, token );
186 }
187 else
188 {
189 std::strcat( buf, "..." );
190 truncated = true;
191 }
192 }
193 }
194 }
195
196 m_teModes = buf;
197 }
198
199 if( freq >= Fc( 1, 1 ) )
200 {
201 buf[0] = '\0';
202 bool truncated = false;
203
204 for( int m = 1; m <= MAX_INDEX && !truncated; ++m )
205 {
206 for( int n = 1; n <= MAX_INDEX && !truncated; ++n )
207 {
208 if( freq >= Fc( m, n ) )
209 {
210 std::snprintf( token, sizeof( token ), "E(%d,%d) ", m, n );
211
212 if( std::strlen( buf ) + std::strlen( token ) + 5 < MAX_STR )
213 {
214 std::strcat( buf, token );
215 }
216 else
217 {
218 std::strcat( buf, "..." );
219 truncated = true;
220 }
221 }
222 }
223 }
224
225 m_tmModes = buf;
226 }
227}
228
229
231{
233
234 const double freq = GetParameter( TCP::FREQUENCY );
235 const double rawEpsR = GetParameter( TCP::EPSILONR );
236 const double rawTanD = GetParameter( TCP::TAND );
237
238 // Overlay dispersed values so helpers reading EPSILONR / TAND via GetParameter
239 // pick them up. Raw inputs are restored before return.
242
245
246 const double len = GetParameter( TCP::PHYS_LEN );
247 const double k_square = KvalSquare();
248 const double kc10_square = KcSquare( 1, 0 );
249
250 if( kc10_square <= k_square )
251 {
252 // Propagating TE10 mode. Z0 uses the fictive-voltage / fictive-current definition,
253 // i.e. ZF0 * sqrt(mur/epsr) / sqrt(1 - (fc/f)^2). This is the definition the
254 // pcb_calculator UI has always reported as the analysis output.
255 const double fc10 = Fc( 1, 0 );
258 / std::sqrt( 1.0 - std::pow( fc10 / freq, 2.0 ) ) );
259
260 const double lambda_g = 2.0 * M_PI / std::sqrt( k_square - kc10_square );
261 SetParameter( TCP::ANG_L, 2.0 * M_PI * len / lambda_g );
262
265 SetParameter( TCP::EPSILON_EFF, 1.0 - std::pow( fc10 / freq, 2.0 ) );
266 }
267 else
268 {
269 // Evanescent region. No propagating energy; conductor "loss" is the bulk
270 // attenuation of the cutoff waveguide, dielectric loss is undefined.
271 SetParameter( TCP::Z0, 0.0 );
272 SetParameter( TCP::ANG_L, 0.0 );
276 }
277
279
280 SetParameter( TCP::EPSILONR, rawEpsR );
281 SetParameter( TCP::TAND, rawTanD );
282}
283
284
286{
287 // Closed-form inverse of the TE10 Z0 expression. The narrow dimension b does not
288 // appear in either Z0(a) or f_c10(a), so only a is solved for. Caller supplies b
289 // as a free input (typically a / 2 to push TE01 cutoff above the operating band).
291
292 const double mur = GetParameter( TCP::MUR );
293 const double freq = GetParameter( TCP::FREQUENCY );
294 const double Z0 = GetParameter( TCP::Z0 );
295 const double angL = GetParameter( TCP::ANG_L );
296 const double rawEpsR = GetParameter( TCP::EPSILONR );
297 const double rawTanD = GetParameter( TCP::TAND );
298 const double epsr = GetDispersedEpsilonR( freq );
299
302
303 const double eta = TC::ZF0 * std::sqrt( mur / epsr );
304
306 TC::C0 / ( std::sqrt( mur * epsr ) * 2.0 * freq * std::sqrt( 1.0 - std::pow( eta / Z0, 2.0 ) ) ) );
307
308 const double k_square = KvalSquare();
309 const double kc10_square = KcSquare( 1, 0 );
310 const double beta = std::sqrt( k_square - kc10_square );
311 const double lambda_g = 2.0 * M_PI / beta;
312
313 SetParameter( TCP::PHYS_LEN, ( angL * lambda_g ) / ( 2.0 * M_PI ) );
314
315 if( kc10_square <= k_square )
316 {
317 const double len = GetParameter( TCP::PHYS_LEN );
320 SetParameter( TCP::EPSILON_EFF, 1.0 - std::pow( Fc( 1, 0 ) / freq, 2.0 ) );
321 }
322 else
323 {
324 // Synthesized geometry is below cutoff: warn via zeroed outputs. The legacy UI
325 // did the same and marked Z0 / ANG_L with the warning status.
329 }
330
333
335
336 // Restore user-supplied Z0 and ANG_L targets so the result map reports what was asked
337 // for, matching the Synthesize contract used by the other migrated calculators.
339 SetParameter( TCP::ANG_L, angL );
340
341 SetParameter( TCP::EPSILONR, rawEpsR );
342 SetParameter( TCP::TAND, rawTanD );
343
344 return std::isfinite( GetParameter( TCP::PHYS_WIDTH ) ) && GetParameter( TCP::PHYS_WIDTH ) > 0.0;
345}
346
347
349{
355
356 const double Z0 = GetParameter( TCP::Z0 );
357 const double angL = GetParameter( TCP::ANG_L );
358 const double len = GetParameter( TCP::PHYS_LEN );
359 const double a = GetParameter( TCP::PHYS_WIDTH );
360 const double b = GetParameter( TCP::PHYS_S );
361
362 const bool Z0_invalid = !std::isfinite( Z0 ) || Z0 < 0;
363 const bool angL_invalid = !std::isfinite( angL ) || angL < 0;
364 const bool len_invalid = !std::isfinite( len ) || len < 0;
365 const bool a_invalid = !std::isfinite( a ) || a <= 0.0;
366 const bool b_invalid = !std::isfinite( b ) || b <= 0.0;
367
373}
374
375
377{
383
384 const double Z0 = GetParameter( TCP::Z0 );
385 const double angL = GetParameter( TCP::ANG_L );
386 const double len = GetParameter( TCP::PHYS_LEN );
387 const double a = GetParameter( TCP::PHYS_WIDTH );
388 const double b = GetParameter( TCP::PHYS_S );
389
390 const bool Z0_invalid = !std::isfinite( Z0 ) || Z0 < 0;
391 const bool angL_invalid = !std::isfinite( angL ) || angL < 0;
392 const bool len_invalid = !std::isfinite( len ) || len < 0;
393 const bool a_invalid = !std::isfinite( a ) || a <= 0.0;
394 const bool b_invalid = !std::isfinite( b ) || b <= 0.0;
395
399
403 SetSynthesisResult( TCP::PHYS_WIDTH, a, a_status );
405}
double AlphaC() const
Conductor loss summed over all propagating TE_mn and TM_mn modes, in dB/m.
void SetSynthesisResults() override
Sets the output values and status following synthesis.
std::string m_tmModes
Cached TM_mn propagating-modes string produced by UpdateModeStrings.
void UpdateModeStrings()
Populates m_teModes and m_tmModes with all propagating modes at the current frequency.
void SetAnalysisResults() override
Sets the output values and status following analysis.
bool Synthesize(SYNTHESIZE_OPTS aOpts) override
Synthesize the broad dimension a from a target Z0. Only PHYS_WIDTH is a valid target.
double Fc(int aM, int aN) const
Cutoff frequency for mode (m, n), in Hz.
double AlphaCCutoff() const
Evanescent attenuation below TE10 cutoff, in dB/m.
double KvalSquare() const
Square of the free-space wavenumber k = omega * sqrt(mu * eps)
void Analyse() override
Analyse waveguide geometry to produce Z0, electrical length, loss, and mode cutoffs.
double KcSquare(int aM, int aN) const
Square of the transverse cutoff wavenumber kc for mode (m, n)
double AlphaD() const
Dielectric loss of the dominant TE10 mode, in dB/m.
std::string m_teModes
Cached TE_mn propagating-modes string produced by UpdateModeStrings.
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.
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.
void SetAnalysisResult(TRANSLINE_PARAMETERS aParam, const double aValue, const TRANSLINE_STATUS aStatus=TRANSLINE_STATUS::OK)
Sets an analysis result.
TRANSLINE_PARAMETERS TCP
#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.