KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_smith_chart_math.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
21#include <sim/smith_math.h>
22
23BOOST_AUTO_TEST_SUITE( SmithChartMath )
24
25
26BOOST_AUTO_TEST_CASE( GammaScreenRoundTrip )
27{
28 SMITH_VIEW view;
29 view.center = wxPoint( 400, 300 );
30 view.radius = 250.0;
31 view.zoom = 1.0;
32 view.pan = wxRealPoint( 0.0, 0.0 );
33
34 // one pixel of rounding corresponds to 1/radius in gamma
35 double tol = 1.0 / view.radius;
36
37 for( double re : { -1.0, -0.5, 0.0, 0.3, 1.0 } )
38 {
39 for( double im : { -1.0, -0.25, 0.0, 0.6, 1.0 } )
40 {
41 wxRealPoint back = view.ToGamma( view.ToScreen( re, im ) );
42
43 BOOST_CHECK_SMALL( back.x - re, tol );
44 BOOST_CHECK_SMALL( back.y - im, tol );
45 }
46 }
47
48 // positive Im (inductive) plots above the center
49 BOOST_CHECK_LT( view.ToScreen( 0.0, 0.5 ).y, view.center.y );
50 BOOST_CHECK_GT( view.ToScreen( 0.0, -0.5 ).y, view.center.y );
51 BOOST_CHECK_GT( view.ToScreen( 0.5, 0.0 ).x, view.center.x );
52
53 view.zoom = 8.0;
54 view.radius = 250.0 * view.zoom;
55 view.pan = wxRealPoint( 0.4, -0.2 );
56
57 tol = 1.0 / view.radius;
58
59 wxRealPoint back = view.ToGamma( view.ToScreen( 0.45, -0.15 ) );
60
61 BOOST_CHECK_SMALL( back.x - 0.45, tol );
62 BOOST_CHECK_SMALL( back.y - -0.15, tol );
63
64 // the pan point lands on the window center
65 BOOST_CHECK_EQUAL( view.ToScreen( 0.4, -0.2 ).x, view.center.x );
66 BOOST_CHECK_EQUAL( view.ToScreen( 0.4, -0.2 ).y, view.center.y );
67}
68
69
70BOOST_AUTO_TEST_CASE( ZoomAboutPointKeepsGamma )
71{
72 // the gamma under the cursor must stay fixed while zooming
73 double baseRadius = 250.0;
74 double oldZoom = 2.0;
75 double newZoom = 5.0;
76 wxPoint center( 400, 300 );
77 wxPoint pos( 520, 180 );
78
79 SMITH_VIEW view;
80 view.center = center;
81 view.zoom = oldZoom;
82 view.radius = baseRadius * oldZoom;
83 view.pan = wxRealPoint( 0.1, 0.3 );
84
85 wxRealPoint gamma = view.ToGamma( pos );
86
87 SMITH_VIEW zoomed;
88 zoomed.center = center;
89 zoomed.zoom = newZoom;
90 zoomed.radius = baseRadius * newZoom;
91 zoomed.pan = SMITH_MATH::ZoomAboutPoint( view, pos, newZoom );
92
93 wxRealPoint after = zoomed.ToGamma( pos );
94
95 BOOST_CHECK_CLOSE( after.x, gamma.x, 1e-9 );
96 BOOST_CHECK_CLOSE( after.y, gamma.y, 1e-9 );
97
98 // zooming back out returns the original pan
99 SMITH_VIEW restored;
100 restored.center = center;
101 restored.zoom = oldZoom;
102 restored.radius = baseRadius * oldZoom;
103 restored.pan = SMITH_MATH::ZoomAboutPoint( zoomed, pos, oldZoom );
104
105 BOOST_CHECK_CLOSE( restored.pan.x, view.pan.x, 1e-9 );
106 BOOST_CHECK_CLOSE( restored.pan.y, view.pan.y, 1e-9 );
107}
108
109
110BOOST_AUTO_TEST_CASE( ToScreenOverflowSafety )
111{
112 SMITH_VIEW view;
113 view.center = wxPoint( 400, 300 );
114 view.radius = 25000.0; // 50x zoom on a large window
115 view.zoom = 50.0;
116 view.pan = wxRealPoint( 0.0, 0.0 );
117
118 // a huge gamma from an active circuit must clamp, not overflow the int math
119 wxPoint far = view.ToScreen( 1e12, -1e12 );
120
121 BOOST_CHECK( far.x > view.center.x );
122 BOOST_CHECK( far.y > view.center.y );
123
124 // non-finite samples land on a defined point instead of feeding NaN to KiROUND
125 double nan = std::nan( "" );
126 double inf = std::numeric_limits<double>::infinity();
127
128 BOOST_CHECK_EQUAL( view.ToScreen( nan, 0.0 ).x, view.center.x );
129 BOOST_CHECK_EQUAL( view.ToScreen( 0.0, nan ).y, view.center.y );
130 BOOST_CHECK_EQUAL( view.ToScreen( inf, inf ).x, view.center.x );
131
132 // a NaN gamma cannot produce an impedance
133 double r, x;
134 BOOST_CHECK( !SMITH_MATH::GammaToImpedance( nan, 0.0, 50.0, r, x ) );
135 BOOST_CHECK( !SMITH_MATH::GammaToImpedance( 0.0, nan, 50.0, r, x ) );
136}
137
138
139BOOST_AUTO_TEST_CASE( ImpedanceFromGamma )
140{
141 double r, x;
142
143 // matched, gamma = 0 -> z = z0
144 BOOST_REQUIRE( SMITH_MATH::GammaToImpedance( 0.0, 0.0, 50.0, r, x ) );
145 BOOST_CHECK_CLOSE( r, 50.0, 1e-9 );
146 BOOST_CHECK_SMALL( x, 1e-9 );
147
148 // gamma = 1/3 on the real axis -> z = 2 z0
149 BOOST_REQUIRE( SMITH_MATH::GammaToImpedance( 1.0 / 3.0, 0.0, 50.0, r, x ) );
150 BOOST_CHECK_CLOSE( r, 100.0, 1e-9 );
151 BOOST_CHECK_SMALL( x, 1e-9 );
152
153 // gamma = -1/3 -> z = z0 / 2
154 BOOST_REQUIRE( SMITH_MATH::GammaToImpedance( -1.0 / 3.0, 0.0, 50.0, r, x ) );
155 BOOST_CHECK_CLOSE( r, 25.0, 1e-9 );
156
157 // gamma = j0.5 -> z = 0.6 + j0.8 normalized
158 BOOST_REQUIRE( SMITH_MATH::GammaToImpedance( 0.0, 0.5, 50.0, r, x ) );
159 BOOST_CHECK_CLOSE( r, 30.0, 1e-9 );
160 BOOST_CHECK_CLOSE( x, 40.0, 1e-9 );
161
162 BOOST_REQUIRE( SMITH_MATH::GammaToImpedance( 0.0, -0.5, 50.0, r, x ) );
163 BOOST_CHECK_CLOSE( x, -40.0, 1e-9 );
164
165 // 75 ohm reference scales linearly
166 BOOST_REQUIRE( SMITH_MATH::GammaToImpedance( 1.0 / 3.0, 0.0, 75.0, r, x ) );
167 BOOST_CHECK_CLOSE( r, 150.0, 1e-9 );
168
169 // open circuit singularity
170 BOOST_CHECK( !SMITH_MATH::GammaToImpedance( 1.0, 0.0, 50.0, r, x ) );
171}
172
173
174BOOST_AUTO_TEST_CASE( SeriesEquivalents )
175{
176 BOOST_CHECK_CLOSE( SMITH_MATH::SeriesInductance( 72.652, 13.56e6 ), 852.72e-9, 0.1 );
177 BOOST_CHECK_CLOSE( SMITH_MATH::SeriesInductance( 3259.6, 33.470438e6 ), 15.5e-6, 0.1 );
178
179 BOOST_CHECK_CLOSE( SMITH_MATH::SeriesCapacitance( -100.0, 1e6 ), 1.5915e-9, 0.1 );
180}
181
182
183BOOST_AUTO_TEST_CASE( MatchMetrics )
184{
185 BOOST_CHECK_CLOSE( SMITH_MATH::VSWR( 0.0 ), 1.0, 1e-9 );
186 BOOST_CHECK_CLOSE( SMITH_MATH::VSWR( 1.0 / 3.0 ), 2.0, 1e-9 );
187 BOOST_CHECK_CLOSE( SMITH_MATH::VSWR( 0.5 ), 3.0, 1e-9 );
188 BOOST_CHECK( std::isinf( SMITH_MATH::VSWR( 1.0 ) ) );
189
190 BOOST_CHECK_SMALL( SMITH_MATH::ReturnLoss( 1.0 ), 1e-9 );
191 BOOST_CHECK_CLOSE( SMITH_MATH::ReturnLoss( 0.1 ), 20.0, 1e-9 );
192 BOOST_CHECK( std::isinf( SMITH_MATH::ReturnLoss( 0.0 ) ) );
193
194 // the two dialects always agree
195 for( double gm : { 0.05, 0.2, 0.45, 0.7, 0.95 } )
196 {
197 double rl = SMITH_MATH::ReturnLoss( gm );
198 double lin = std::pow( 10.0, rl / 20.0 );
199
200 BOOST_CHECK_CLOSE( SMITH_MATH::VSWR( gm ), ( lin + 1.0 ) / ( lin - 1.0 ), 1e-6 );
201 }
202}
203
204
205BOOST_AUTO_TEST_CASE( SParamPortParsing )
206{
207 long response, drive;
208
209 BOOST_REQUIRE( SMITH_MATH::ParseSParamPorts( wxS( "S_1_1" ), &response, &drive ) );
210 BOOST_CHECK_EQUAL( response, 1 );
211 BOOST_CHECK_EQUAL( drive, 1 );
212
213 BOOST_REQUIRE( SMITH_MATH::ParseSParamPorts( wxS( "S_2_1" ), &response, &drive ) );
214 BOOST_CHECK_EQUAL( response, 2 );
215 BOOST_CHECK_EQUAL( drive, 1 );
216
217 BOOST_REQUIRE( SMITH_MATH::ParseSParamPorts( wxS( "S_12_3" ), &response, &drive ) );
218 BOOST_CHECK_EQUAL( response, 12 );
219 BOOST_CHECK_EQUAL( drive, 3 );
220
221 // user-defined or unrelated names are not S-parameter vectors
222 BOOST_CHECK( !SMITH_MATH::ParseSParamPorts( wxS( "S_out" ), &response, &drive ) );
223 BOOST_CHECK( !SMITH_MATH::ParseSParamPorts( wxS( "V(out)" ), &response, &drive ) );
224 BOOST_CHECK( !SMITH_MATH::ParseSParamPorts( wxS( "S_1_" ), &response, &drive ) );
225 BOOST_CHECK( !SMITH_MATH::ParseSParamPorts( wxS( "S__1" ), &response, &drive ) );
226 BOOST_CHECK( !SMITH_MATH::ParseSParamPorts( wxS( "user0" ), &response, &drive ) );
227}
228
229
double SeriesCapacitance(double aReactance, double aFreq)
Pan that keeps the gamma point under aPos fixed when the view zooms to aNewZoom.
Definition smith_math.h:101
double SeriesInductance(double aReactance, double aFreq)
Definition smith_math.h:96
double VSWR(double aGammaMag)
Definition smith_math.h:78
wxRealPoint ZoomAboutPoint(const SMITH_VIEW &aView, const wxPoint &aPos, double aNewZoom)
S-parameter vectors are named S_<responsePort>_<drivePort>.
Definition smith_math.h:107
bool ParseSParamPorts(const wxString &aVectorName, long *aResponsePort, long *aDrivePort)
Definition smith_math.h:117
bool GammaToImpedance(double aRe, double aIm, double aZ0, double &aResistance, double &aReactance)
< Impedance of a reflection coefficient, z = z0 ( 1 + gamma ) / ( 1 - gamma ), false at the gamma = 1...
Definition smith_math.h:65
double ReturnLoss(double aGammaMag)
Series equivalent element of a reactance at one frequency, henries for aReactance > 0,...
Definition smith_math.h:86
< Smith chart placement plus pan/zoom, maps a gamma point to a screen pixel and back.
Definition smith_math.h:32
wxRealPoint pan
Definition smith_math.h:36
double zoom
Definition smith_math.h:35
wxRealPoint ToGamma(const wxPoint &aPt) const
Definition smith_math.h:53
wxPoint center
Definition smith_math.h:33
wxPoint ToScreen(double aRe, double aIm) const
Definition smith_math.h:39
double radius
Definition smith_math.h:34
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
VECTOR2I center
BOOST_AUTO_TEST_CASE(GammaScreenRoundTrip)
BOOST_CHECK_EQUAL(result, "25.4")