KiCad PCB EDA Suite
Loading...
Searching...
No Matches
spice_value.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 (C) 2016 CERN
5 * Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * https://www.gnu.org/licenses/gpl-3.0.html
21 * or you may search the http://www.gnu.org website for the version 3 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include "spice_value.h"
27#include <math/util.h>
28#include <core/kicad_algo.h>
29
30#include <stdexcept>
31#include <cmath>
32
33#include <wx/textentry.h>
34#include <wx/numformatter.h>
35#include <confirm.h>
36#include <common.h>
37#include <locale_io.h>
38#include <geometry/eda_angle.h>
39
40
41void SPICE_VALUE_FORMAT::FromString( const wxString& aString )
42{
43 if( aString.IsEmpty() )
44 {
45 Precision = 3;
46 Range = wxS( "~V" );
47 }
48 else
49 {
50 long val;
51 aString.Left( 1 ).ToLong( &val );
52 Precision = (int) val;
53 Range = aString.Right( aString.Length() - 1 );
54 }
55}
56
57
59{
60 return wxString::Format( wxS( "%d%s" ), alg::clamp( 0, Precision, 9 ), Range );
61}
62
63
64void SPICE_VALUE_FORMAT::UpdateUnits( const wxString& aUnits )
65{
66 if( Range.GetChar( 0 ) == '~' )
67 Range = Range.Left( 1 ) + aUnits;
68 else if( SPICE_VALUE::ParseSIPrefix( Range.GetChar( 0 ) ) != SPICE_VALUE::PFX_NONE )
69 Range = Range.Left( 1 ) + aUnits;
70 else
71 Range = aUnits;
72}
73
74
75SPICE_VALUE::SPICE_VALUE( const wxString& aString ) :
76 m_base( 0.0 ),
77 m_prefix( PFX_NONE ),
78 m_spiceStr( false )
79{
80 if( aString.IsEmpty() )
81 return;
82
83 char units[8] = { 0, };
84 LOCALE_IO dummy; // Numeric values must be in "C" locale ('.' decimal separator)
85
86 sscanf( (const char*) aString.c_str(), "%lf%7s", &m_base, units );
87
88 if( *units == 0 )
89 {
90 Normalize();
91 return;
92 }
93
94 m_spiceStr = true;
95
96 for( char* bufPtr = units; *bufPtr; ++bufPtr )
97 *bufPtr = tolower( *bufPtr );
98
99 if( strcmp( units, "meg" ) == 0 )
100 {
102 }
103 else
104 {
105 switch( units[0] )
106 {
107 case 'f': m_prefix = PFX_FEMTO; break;
108 case 'p': m_prefix = PFX_PICO; break;
109 case 'n': m_prefix = PFX_NANO; break;
110 case 'u': m_prefix = PFX_MICRO; break;
111 case 'm': m_prefix = PFX_MILI; break;
112 case 'k': m_prefix = PFX_KILO; break;
113 case 'g': m_prefix = PFX_GIGA; break;
114 case 't': m_prefix = PFX_TERA; break;
115 default: m_prefix = PFX_NONE; break;
116 }
117 }
118
119 Normalize();
120}
121
122
124{
125 while( std::fabs( m_base ) >= 1000.0 )
126 {
127 if( m_prefix == PFX_TERA ) // this is the biggest unit available
128 break;
129
130 m_base *= 0.001;
131 m_prefix = (UNIT_PREFIX)( m_prefix + 3 );
132 }
133
134 while( m_base != 0.0 && std::fabs( m_base ) < 1.000 )
135 {
136 if( m_prefix == PFX_FEMTO ) // this is the smallest unit available
137 break;
138
139 m_base *= 1000.0;
140 m_prefix = (UNIT_PREFIX)( m_prefix - 3 );
141 }
142}
143
144
146{
147 switch( aPrefix )
148 {
149 case SPICE_VALUE::PFX_FEMTO: return wxT( "f" );
150 case SPICE_VALUE::PFX_PICO: return wxT( "p" );
151 case SPICE_VALUE::PFX_NANO: return wxT( "n" );
152 case SPICE_VALUE::PFX_MICRO: return wxT( "u" );
153 case SPICE_VALUE::PFX_MILI: return wxT( "m" );
154 case SPICE_VALUE::PFX_NONE: return wxEmptyString;
155 case SPICE_VALUE::PFX_KILO: return wxT( "k" );
156 case SPICE_VALUE::PFX_MEGA: return wxT( "Meg" );
157 case SPICE_VALUE::PFX_GIGA: return wxT( "G" );
158 case SPICE_VALUE::PFX_TERA: return wxT( "T" );
159 }
160
161 return wxEmptyString;
162}
163
164
166{
167 switch( aPrefix )
168 {
169 case SPICE_VALUE::PFX_FEMTO: return wxT( "f" );
170 case SPICE_VALUE::PFX_PICO: return wxT( "p" );
171 case SPICE_VALUE::PFX_NANO: return wxT( "n" );
172 case SPICE_VALUE::PFX_MICRO: return wxT( "u" );
173 case SPICE_VALUE::PFX_MILI: return wxT( "m" );
174 case SPICE_VALUE::PFX_NONE: return wxEmptyString;
175 case SPICE_VALUE::PFX_KILO: return wxT( "K" );
176 case SPICE_VALUE::PFX_MEGA: return wxT( "M" );
177 case SPICE_VALUE::PFX_GIGA: return wxT( "G" );
178 case SPICE_VALUE::PFX_TERA: return wxT( "T" );
179 }
180
181 return wxEmptyString;
182}
183
184
186{
187 switch( c )
188 {
189 case 'f': return SPICE_VALUE::PFX_FEMTO;
190 case 'p': return SPICE_VALUE::PFX_PICO;
191 case 'n': return SPICE_VALUE::PFX_NANO;
192 case 'u': return SPICE_VALUE::PFX_MICRO;
193 case 'm': return SPICE_VALUE::PFX_MILI;
194 case 'K': return SPICE_VALUE::PFX_KILO;
195 case 'M': return SPICE_VALUE::PFX_MEGA;
196 case 'G': return SPICE_VALUE::PFX_GIGA;
197 case 'T': return SPICE_VALUE::PFX_TERA;
198 default: return SPICE_VALUE::PFX_NONE;
199 }
200}
201
202
203double SPICE_VALUE::ToNormalizedDouble( wxString* aPrefix )
204{
205 Normalize();
206
207 *aPrefix = spice_prefix( m_prefix );
208 return m_base;
209}
210
211
213{
214 double res = m_base;
215
216 if( m_prefix != PFX_NONE )
217 res *= std::pow( 10, (int) m_prefix );
218
219 return res;
220}
221
222wxString SPICE_VALUE::ToString() const
223{
224 wxString res( wxString::Format( "%.3f", ToDouble() ) );
225 StripZeros( res );
226
227 return res;
228}
229
230
232{
233 wxString range( aFormat.Range );
234
235 if( range.EndsWith( wxS( "°" ) ) )
236 {
237 EDA_ANGLE angle( m_base * std::pow( 10, (int) m_prefix ), DEGREES_T );
238 angle.Normalize180();
239 return wxString::FromCDouble( angle.AsDegrees(), aFormat.Precision ) + wxS( "°" );
240 }
241
242 if( range.StartsWith( wxS( "~" ) ) )
243 {
244 Normalize();
245 range = si_prefix( m_prefix ) + range.Right( range.Length() - 1 );
246 }
247 else
248 {
249 SPICE_VALUE::UNIT_PREFIX rangePrefix = ParseSIPrefix( range[0] );
250 m_base = m_base * std::pow( 10, m_prefix - rangePrefix );
251 m_prefix = rangePrefix;
252 }
253
254 double mantissa = m_base;
255 int scale = 0;
256
257 while( std::fabs( mantissa ) >= 10.0 )
258 {
259 mantissa *= 0.1;
260 scale += 1;
261 }
262
263 while( mantissa != 0.0 && std::fabs( mantissa ) < 1.0 )
264 {
265 mantissa *= 10;
266 scale -= 1;
267 }
268
269 mantissa = KiROUND( mantissa * std::pow( 10, aFormat.Precision - 1 ) );
270 mantissa *= std::pow( 10, scale - aFormat.Precision + 1 );
271
272 wxString res = wxString::FromCDouble( mantissa, std::max( 0, aFormat.Precision - scale - 1 ) );
273
274 // If we have an excessively long number, switch to scientific notation
275 if( ssize_t( res.length() ) > aFormat.Precision + scale + 1 )
276 res = wxString::FromCDouble( mantissa );
277
278 return res + range;
279}
280
281
283{
284 wxString res = wxString::FromCDouble( m_base );
285 StripZeros( res );
287
288 return res;
289}
290
291
293{
294 int prefixDiff = m_prefix - aOther.m_prefix;
296 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
297
298 // Convert both numbers to a common prefix
299 if( prefixDiff > 0 )
300 {
301 // Switch to the aOther prefix
302 res.m_base = ( m_base * std::pow( 10, prefixDiff ) ) + aOther.m_base;
303 res.m_prefix = aOther.m_prefix;
304 }
305 else if( prefixDiff < 0 )
306 {
307 // Use the current prefix
308 res.m_base = m_base + ( aOther.m_base * std::pow( 10, -prefixDiff ) );
309 res.m_prefix = m_prefix;
310 }
311 else
312 {
313 res.m_base = m_base + aOther.m_base;
314 res.m_prefix = m_prefix; // == aOther.m_prefix
315 }
316
317 res.Normalize();
318
319 return res;
320}
321
322
324{
325 int prefixDiff = m_prefix - aOther.m_prefix;
327 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
328
329 // Convert both numbers to a common prefix
330 if( prefixDiff > 0 )
331 {
332 // Switch to the aOther prefix
333 res.m_base = m_base * std::pow( 10, prefixDiff ) - aOther.m_base;
334 res.m_prefix = aOther.m_prefix;
335 }
336 else if( prefixDiff < 0 )
337 {
338 // Use the current prefix
339 res.m_base = m_base - aOther.m_base * std::pow( 10, -prefixDiff );
340 res.m_prefix = m_prefix;
341 }
342 else
343 {
344 res.m_base = m_base - aOther.m_base;
345 res.m_prefix = m_prefix; // == aOther.m_prefix
346 }
347
348 res.Normalize();
349
350 return res;
351}
352
353
355{
356 SPICE_VALUE res( m_base * aOther.m_base, (UNIT_PREFIX)( m_prefix + aOther.m_prefix ) );
357 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
358 res.Normalize();
359
360 return res;
361}
362
363
365{
366 SPICE_VALUE res( m_base / aOther.m_base, (UNIT_PREFIX)( m_prefix - aOther.m_prefix ) );
367 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
368 res.Normalize();
369
370 return res;
371}
372
373
374void SPICE_VALUE::StripZeros( wxString& aString )
375{
376 if ( aString.Find( ',' ) >= 0 || aString.Find( '.' ) >= 0 )
377 {
378 while( aString.EndsWith( '0' ) )
379 aString.RemoveLast();
380
381 if( aString.EndsWith( '.' ) || aString.EndsWith( ',' ) )
382 aString.RemoveLast();
383 }
384}
385
386
387bool SPICE_VALIDATOR::Validate( wxWindow* aParent )
388{
389 wxTextEntry* const text = GetTextEntry();
390
391 if( !text )
392 return false;
393
394 if( text->IsEmpty() )
395 {
396 if( m_emptyAllowed )
397 return true;
398
399 DisplayError( aParent, wxString::Format( _( "Please, fill required fields" ) ) );
400 return false;
401 }
402
403 wxString svalue = text->GetValue();
404
405 // In countries where the decimal separator is not a point, if the user
406 // has not used a point, replace the decimal separator by the point, as needed
407 // by spice simulator which uses the "C" decimal separator
408 svalue.Replace(",", "." );
409
410 try
411 {
412 // If SPICE_VALUE can be constructed, then it is a valid Spice value
413 SPICE_VALUE val( svalue );
414 }
415 catch( ... )
416 {
417 DisplayError( aParent, wxString::Format( _( "'%s' is not a valid SPICE value." ),
418 text->GetValue() ) );
419
420 return false;
421 }
422
423 if( svalue != text->GetValue() )
424 text->SetValue( svalue );
425
426 return true;
427}
double AsDegrees() const
Definition: eda_angle.h:113
EDA_ANGLE Normalize180()
Definition: eda_angle.h:260
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:49
bool m_emptyAllowed
< Is it valid to get an empty value?
Definition: spice_value.h:197
bool Validate(wxWindow *aParent) override
Helper class to recognize Spice formatted values.
Definition: spice_value.h:56
SPICE_VALUE operator+(const SPICE_VALUE &aOther) const
double m_base
Definition: spice_value.h:171
SPICE_VALUE operator-(const SPICE_VALUE &aOther) const
UNIT_PREFIX m_prefix
Was the value defined using the Spice notation?
Definition: spice_value.h:172
static UNIT_PREFIX ParseSIPrefix(wxChar c)
void Normalize()
Normalize the value.
double ToNormalizedDouble(wxString *aPrefix)
wxString ToString() const
Return string value as when converting double to string (e.g.
SPICE_VALUE operator/(const SPICE_VALUE &aOther) const
Remove redundant zeros from the end of a string.
wxString ToSpiceString() const
Return string value in Spice format (e.g.
SPICE_VALUE operator*(const SPICE_VALUE &aOther) const
SPICE_VALUE()
Parses the string to create a Spice value (e.g. 100n)
Definition: spice_value.h:72
double ToDouble() const
static void StripZeros(wxString &aString)
bool m_spiceStr
Definition: spice_value.h:175
VECTOR3< T > Normalize()
Compute the normalized vector.
Definition: vector3.h:154
The common library.
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:170
This file is part of the common library.
#define _(s)
@ DEGREES_T
Definition: eda_angle.h:31
T clamp(T min, T value, T max)
Definition: kicad_algo.h:205
wxString si_prefix(SPICE_VALUE::UNIT_PREFIX aPrefix)
wxString spice_prefix(SPICE_VALUE::UNIT_PREFIX aPrefix)
const int scale
std::vector< FAB_LAYER_COLOR > dummy
A SPICE_VALUE_FORMAT holds precision and range info for formatting values.Helper class to handle Spic...
Definition: spice_value.h:43
wxString ToString() const
Definition: spice_value.cpp:58
void UpdateUnits(const wxString &aUnits)
Definition: spice_value.cpp:64
void FromString(const wxString &aString)
Definition: spice_value.cpp:41
VECTOR3I res
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:121