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. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <cstdio>
22#include <cstring>
23
26
27
28namespace TC = TRANSLINE_CALCULATIONS;
30
31
32// Formulas throughout this file follow Pozar, "Microwave Engineering", 4th ed.,
33// Wiley 2012, §3.3 (rectangular waveguide, eq. 3.84 cutoff, Table 3.2 summary).
34// Ramo, Whinnery, Van Duzer, "Fields and Waves in Communication Electronics", 3rd
35// ed., Wiley 1994, chapter 8 gives equivalent derivations; Collin, "Foundations for
36// Microwave Engineering", 2nd ed., McGraw-Hill 1992, §3.3 tabulates the same
37// attenuation sums. Open-access cross-reference: Orfanidis, "Electromagnetic Waves
38// and Antennas", Ch. 9, eq. (9.8.1), freely available at
39// https://eceweb1.rutgers.edu/~orfanidi/ewa/.
40
42{
43 // Free-medium wavenumber squared, k^2 = omega^2*mu*eps (Pozar §3.3).
44 const double kval = 2.0 * M_PI * GetParameter( TCP::FREQUENCY )
45 * std::sqrt( GetParameter( TCP::MUR ) * GetParameter( TCP::EPSILONR ) ) / TC::C0;
46 return kval * kval;
47}
48
49
50double RECTWAVEGUIDE::KcSquare( const int aM, const int aN ) const
51{
52 // TE(m,n)/TM(m,n) cutoff wavenumber, kc^2 = (m*pi/a)^2 + (n*pi/b)^2 (Pozar eq. 3.84).
53 const double a = GetParameter( TCP::PHYS_WIDTH );
54 const double b = GetParameter( TCP::PHYS_S );
55 return std::pow( aM * M_PI / a, 2.0 ) + std::pow( aN * M_PI / b, 2.0 );
56}
57
58
59double RECTWAVEGUIDE::Fc( const int aM, const int aN ) const
60{
61 // Cutoff frequency, fc = kc / (2*pi*sqrt(mu*eps)) (Pozar eq. 3.84).
62 return std::sqrt( KcSquare( aM, aN ) / GetParameter( TCP::MUR ) / GetParameter( TCP::EPSILONR ) ) * TC::C0
63 / ( 2.0 * M_PI );
64}
65
66
68{
69 // Per-mode conductor attenuation from the surface-resistance integrals given in
70 // Pozar §3.3 (TE: eq. form of Pozar table 3.2; TM: analogous closed form). Ramo,
71 // Whinnery, Van Duzer §8.7 derives the same integrals from the tangential H field.
72 const double a = GetParameter( TCP::PHYS_WIDTH );
73 const double b = GetParameter( TCP::PHYS_S );
74 const double f = GetParameter( TCP::FREQUENCY );
75 const double murc = GetParameter( TCP::MURC );
76 const double sigma = GetParameter( TCP::SIGMA );
77
78 const double Rs = std::sqrt( M_PI * f * murc * TC::MU0 / sigma );
79 double ac = 0.0;
80
81 const int mmax = static_cast<int>( std::floor( f / Fc( 1, 0 ) ) );
82 const int nmax = mmax;
83
84 // TE(m, n) modes.
85 for( int n = 0; n <= nmax; ++n )
86 {
87 for( int m = 1; m <= mmax; ++m )
88 {
89 const double f_c = Fc( m, n );
90
91 if( f <= f_c )
92 continue;
93
94 if( n == 0 )
95 {
96 ac += ( Rs / ( b * TC::ZF0 * std::sqrt( 1.0 - std::pow( f_c / f, 2.0 ) ) ) )
97 * ( 1.0 + ( ( 2.0 * b / a ) * std::pow( f_c / f, 2.0 ) ) );
98 }
99 else
100 {
101 ac += ( ( 2.0 * Rs ) / ( b * TC::ZF0 * std::sqrt( 1.0 - std::pow( f_c / f, 2.0 ) ) ) )
102 * ( ( ( 1.0 + ( b / a ) ) * std::pow( f_c / f, 2.0 ) )
103 + ( ( 1.0 - std::pow( f_c / f, 2.0 ) )
104 * ( ( ( b / a ) * ( ( ( b / a ) * std::pow( m, 2.0 ) ) + std::pow( n, 2.0 ) ) )
105 / ( std::pow( b * m / a, 2.0 ) + std::pow( n, 2.0 ) ) ) ) );
106 }
107 }
108 }
109
110 // TM(m, n) modes.
111 for( int n = 1; n <= nmax; ++n )
112 {
113 for( int m = 1; m <= mmax; ++m )
114 {
115 const double f_c = Fc( m, n );
116
117 if( f <= f_c )
118 continue;
119
120 ac += ( ( 2.0 * Rs ) / ( b * TC::ZF0 * std::sqrt( 1.0 - std::pow( f_c / f, 2.0 ) ) ) )
121 * ( ( ( std::pow( m, 2.0 ) * std::pow( b / a, 3.0 ) ) + std::pow( n, 2.0 ) )
122 / ( std::pow( m * b / a, 2.0 ) + std::pow( n, 2.0 ) ) );
123 }
124 }
125
126 return ac * TC::LOG2DB;
127}
128
129
131{
132 double acc = std::sqrt( KcSquare( 1, 0 ) - KvalSquare() );
133 acc = TC::LOG2DB * acc;
134 return acc;
135}
136
137
139{
140 const double k_square = KvalSquare();
141 const double beta = std::sqrt( k_square - KcSquare( 1, 0 ) );
142
143 double ad = ( k_square * GetParameter( TCP::TAND ) ) / ( 2.0 * beta );
144 ad = ad * TC::LOG2DB;
145 return ad;
146}
147
148
150{
151 // Walks a bounded (m, n) grid and emits every mode whose cutoff lies below the current
152 // operating frequency. Buffer length caps the display string; excess modes collapse to
153 // an ellipsis. Format matches the legacy pcb_calculator output: H(m,n) for TE, E(m,n)
154 // for TM.
155 constexpr int MAX_INDEX = 6;
156 constexpr std::size_t MAX_STR = 128;
157 char buf[MAX_STR];
158 char token[32];
159
160 const double freq = GetParameter( TCP::FREQUENCY );
161
162 m_teModes.clear();
163 m_tmModes.clear();
164
165 if( freq >= Fc( 1, 0 ) )
166 {
167 buf[0] = '\0';
168 bool truncated = false;
169
170 for( int m = 0; m <= MAX_INDEX && !truncated; ++m )
171 {
172 for( int n = 0; n <= MAX_INDEX && !truncated; ++n )
173 {
174 if( m == 0 && n == 0 )
175 continue;
176
177 if( freq >= Fc( m, n ) )
178 {
179 std::snprintf( token, sizeof( token ), "H(%d,%d) ", m, n );
180
181 if( std::strlen( buf ) + std::strlen( token ) + 5 < MAX_STR )
182 {
183 std::strcat( buf, token );
184 }
185 else
186 {
187 std::strcat( buf, "..." );
188 truncated = true;
189 }
190 }
191 }
192 }
193
194 m_teModes = buf;
195 }
196
197 if( freq >= Fc( 1, 1 ) )
198 {
199 buf[0] = '\0';
200 bool truncated = false;
201
202 for( int m = 1; m <= MAX_INDEX && !truncated; ++m )
203 {
204 for( int n = 1; n <= MAX_INDEX && !truncated; ++n )
205 {
206 if( freq >= Fc( m, n ) )
207 {
208 std::snprintf( token, sizeof( token ), "E(%d,%d) ", m, n );
209
210 if( std::strlen( buf ) + std::strlen( token ) + 5 < MAX_STR )
211 {
212 std::strcat( buf, token );
213 }
214 else
215 {
216 std::strcat( buf, "..." );
217 truncated = true;
218 }
219 }
220 }
221 }
222
223 m_tmModes = buf;
224 }
225}
226
227
229{
231
232 const double freq = GetParameter( TCP::FREQUENCY );
233 const double rawEpsR = GetParameter( TCP::EPSILONR );
234 const double rawTanD = GetParameter( TCP::TAND );
235
236 // Overlay dispersed values so helpers reading EPSILONR / TAND via GetParameter
237 // pick them up. Raw inputs are restored before return.
240
243
244 const double len = GetParameter( TCP::PHYS_LEN );
245 const double k_square = KvalSquare();
246 const double kc10_square = KcSquare( 1, 0 );
247
248 if( kc10_square <= k_square )
249 {
250 // Propagating TE10 mode. Z0 uses the fictive-voltage / fictive-current definition,
251 // i.e. ZF0 * sqrt(mur/epsr) / sqrt(1 - (fc/f)^2). This is the definition the
252 // pcb_calculator UI has always reported as the analysis output.
253 const double fc10 = Fc( 1, 0 );
256 / std::sqrt( 1.0 - std::pow( fc10 / freq, 2.0 ) ) );
257
258 const double lambda_g = 2.0 * M_PI / std::sqrt( k_square - kc10_square );
259 SetParameter( TCP::ANG_L, 2.0 * M_PI * len / lambda_g );
260
263 SetParameter( TCP::EPSILON_EFF, 1.0 - std::pow( fc10 / freq, 2.0 ) );
264 }
265 else
266 {
267 // Evanescent region. No propagating energy; conductor "loss" is the bulk
268 // attenuation of the cutoff waveguide, dielectric loss is undefined.
269 SetParameter( TCP::Z0, 0.0 );
270 SetParameter( TCP::ANG_L, 0.0 );
274 }
275
277
278 SetParameter( TCP::EPSILONR, rawEpsR );
279 SetParameter( TCP::TAND, rawTanD );
280}
281
282
284{
285 // Closed-form inverse of the TE10 Z0 expression. The narrow dimension b does not
286 // appear in either Z0(a) or f_c10(a), so only a is solved for. Caller supplies b
287 // as a free input (typically a / 2 to push TE01 cutoff above the operating band).
289
290 const double mur = GetParameter( TCP::MUR );
291 const double freq = GetParameter( TCP::FREQUENCY );
292 const double Z0 = GetParameter( TCP::Z0 );
293 const double angL = GetParameter( TCP::ANG_L );
294 const double rawEpsR = GetParameter( TCP::EPSILONR );
295 const double rawTanD = GetParameter( TCP::TAND );
296 const double epsr = GetDispersedEpsilonR( freq );
297
300
301 const double eta = TC::ZF0 * std::sqrt( mur / epsr );
302
304 TC::C0 / ( std::sqrt( mur * epsr ) * 2.0 * freq * std::sqrt( 1.0 - std::pow( eta / Z0, 2.0 ) ) ) );
305
306 const double k_square = KvalSquare();
307 const double kc10_square = KcSquare( 1, 0 );
308 const double beta = std::sqrt( k_square - kc10_square );
309 const double lambda_g = 2.0 * M_PI / beta;
310
311 SetParameter( TCP::PHYS_LEN, ( angL * lambda_g ) / ( 2.0 * M_PI ) );
312
313 if( kc10_square <= k_square )
314 {
315 const double len = GetParameter( TCP::PHYS_LEN );
318 SetParameter( TCP::EPSILON_EFF, 1.0 - std::pow( Fc( 1, 0 ) / freq, 2.0 ) );
319 }
320 else
321 {
322 // Synthesized geometry is below cutoff: warn via zeroed outputs. The legacy UI
323 // did the same and marked Z0 / ANG_L with the warning status.
327 }
328
331
333
334 // Restore user-supplied Z0 and ANG_L targets so the result map reports what was asked
335 // for, matching the Synthesize contract used by the other migrated calculators.
337 SetParameter( TCP::ANG_L, angL );
338
339 SetParameter( TCP::EPSILONR, rawEpsR );
340 SetParameter( TCP::TAND, rawTanD );
341
342 return std::isfinite( GetParameter( TCP::PHYS_WIDTH ) ) && GetParameter( TCP::PHYS_WIDTH ) > 0.0;
343}
344
345
347{
353
354 const double Z0 = GetParameter( TCP::Z0 );
355 const double angL = GetParameter( TCP::ANG_L );
356 const double len = GetParameter( TCP::PHYS_LEN );
357 const double a = GetParameter( TCP::PHYS_WIDTH );
358 const double b = GetParameter( TCP::PHYS_S );
359
360 const bool Z0_invalid = !std::isfinite( Z0 ) || Z0 < 0;
361 const bool angL_invalid = !std::isfinite( angL ) || angL < 0;
362 const bool len_invalid = !std::isfinite( len ) || len < 0;
363 const bool a_invalid = !std::isfinite( a ) || a <= 0.0;
364 const bool b_invalid = !std::isfinite( b ) || b <= 0.0;
365
371}
372
373
375{
381
382 const double Z0 = GetParameter( TCP::Z0 );
383 const double angL = GetParameter( TCP::ANG_L );
384 const double len = GetParameter( TCP::PHYS_LEN );
385 const double a = GetParameter( TCP::PHYS_WIDTH );
386 const double b = GetParameter( TCP::PHYS_S );
387
388 const bool Z0_invalid = !std::isfinite( Z0 ) || Z0 < 0;
389 const bool angL_invalid = !std::isfinite( angL ) || angL < 0;
390 const bool len_invalid = !std::isfinite( len ) || len < 0;
391 const bool a_invalid = !std::isfinite( a ) || a <= 0.0;
392 const bool b_invalid = !std::isfinite( b ) || b <= 0.0;
393
397
401 SetSynthesisResult( TCP::PHYS_WIDTH, a, a_status );
403}
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.