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
39
40void SPICE_VALUE_FORMAT::FromString( const wxString& aString )
41{
42 long val;
43 aString.Left( 1 ).ToLong( &val );
44 Precision = (int) val;
45 Range = aString.Right( aString.Length() - 1 );
46}
47
48
50{
51 return wxString::Format( wxS( "%d%s" ), alg::clamp( 0, Precision, 9 ), Range );
52}
53
54
55void SPICE_VALUE_FORMAT::UpdateUnits( const wxString& aUnits )
56{
57 if( Range.GetChar( 0 ) == '~' )
58 Range = Range.Left( 1 ) + aUnits;
59 else if( SPICE_VALUE::ParseSIPrefix( Range.GetChar( 0 ) ) != SPICE_VALUE::PFX_NONE )
60 Range = Range.Left( 1 ) + aUnits;
61 else
62 Range = aUnits;
63}
64
65
66SPICE_VALUE::SPICE_VALUE( const wxString& aString ) :
67 m_base( 0.0 ),
68 m_prefix( PFX_NONE ),
69 m_spiceStr( false )
70{
71 if( aString.IsEmpty() )
72 return;
73
74 char units[8] = { 0, };
75 LOCALE_IO dummy; // Numeric values must be in "C" locale ('.' decimal separator)
76
77 sscanf( (const char*) aString.c_str(), "%lf%7s", &m_base, units );
78
79 if( *units == 0 )
80 {
81 Normalize();
82 return;
83 }
84
85 m_spiceStr = true;
86
87 for( char* bufPtr = units; *bufPtr; ++bufPtr )
88 *bufPtr = tolower( *bufPtr );
89
90 if( strcmp( units, "meg" ) == 0 )
91 {
93 }
94 else
95 {
96 switch( units[0] )
97 {
98 case 'f': m_prefix = PFX_FEMTO; break;
99 case 'p': m_prefix = PFX_PICO; break;
100 case 'n': m_prefix = PFX_NANO; break;
101 case 'u': m_prefix = PFX_MICRO; break;
102 case 'm': m_prefix = PFX_MILI; break;
103 case 'k': m_prefix = PFX_KILO; break;
104 case 'g': m_prefix = PFX_GIGA; break;
105 case 't': m_prefix = PFX_TERA; break;
106 default: m_prefix = PFX_NONE; break;
107 }
108 }
109
110 Normalize();
111}
112
113
115{
116 while( std::fabs( m_base ) >= 1000.0 )
117 {
118 if( m_prefix == PFX_TERA ) // this is the biggest unit available
119 break;
120
121 m_base *= 0.001;
122 m_prefix = (UNIT_PREFIX)( m_prefix + 3 );
123 }
124
125 while( m_base != 0.0 && std::fabs( m_base ) < 1.000 )
126 {
127 if( m_prefix == PFX_FEMTO ) // this is the smallest unit available
128 break;
129
130 m_base *= 1000.0;
131 m_prefix = (UNIT_PREFIX)( m_prefix - 3 );
132 }
133}
134
135
137{
138 switch( aPrefix )
139 {
140 case SPICE_VALUE::PFX_FEMTO: return wxT( "f" );
141 case SPICE_VALUE::PFX_PICO: return wxT( "p" );
142 case SPICE_VALUE::PFX_NANO: return wxT( "n" );
143 case SPICE_VALUE::PFX_MICRO: return wxT( "u" );
144 case SPICE_VALUE::PFX_MILI: return wxT( "m" );
145 case SPICE_VALUE::PFX_NONE: return wxEmptyString;
146 case SPICE_VALUE::PFX_KILO: return wxT( "k" );
147 case SPICE_VALUE::PFX_MEGA: return wxT( "Meg" );
148 case SPICE_VALUE::PFX_GIGA: return wxT( "G" );
149 case SPICE_VALUE::PFX_TERA: return wxT( "T" );
150 }
151
152 return wxEmptyString;
153}
154
155
157{
158 switch( aPrefix )
159 {
160 case SPICE_VALUE::PFX_FEMTO: return wxT( "f" );
161 case SPICE_VALUE::PFX_PICO: return wxT( "p" );
162 case SPICE_VALUE::PFX_NANO: return wxT( "n" );
163 case SPICE_VALUE::PFX_MICRO: return wxT( "u" );
164 case SPICE_VALUE::PFX_MILI: return wxT( "m" );
165 case SPICE_VALUE::PFX_NONE: return wxEmptyString;
166 case SPICE_VALUE::PFX_KILO: return wxT( "K" );
167 case SPICE_VALUE::PFX_MEGA: return wxT( "M" );
168 case SPICE_VALUE::PFX_GIGA: return wxT( "G" );
169 case SPICE_VALUE::PFX_TERA: return wxT( "T" );
170 }
171
172 return wxEmptyString;
173}
174
175
177{
178 switch( c )
179 {
180 case 'f': return SPICE_VALUE::PFX_FEMTO;
181 case 'p': return SPICE_VALUE::PFX_PICO;
182 case 'n': return SPICE_VALUE::PFX_NANO;
183 case 'u': return SPICE_VALUE::PFX_MICRO;
184 case 'm': return SPICE_VALUE::PFX_MILI;
185 case 'K': return SPICE_VALUE::PFX_KILO;
186 case 'M': return SPICE_VALUE::PFX_MEGA;
187 case 'G': return SPICE_VALUE::PFX_GIGA;
188 case 'T': return SPICE_VALUE::PFX_TERA;
189 default: return SPICE_VALUE::PFX_NONE;
190 }
191}
192
193
194double SPICE_VALUE::ToNormalizedDouble( wxString* aPrefix )
195{
196 Normalize();
197
198 *aPrefix = spice_prefix( m_prefix );
199 return m_base;
200}
201
202
204{
205 double res = m_base;
206
207 if( m_prefix != PFX_NONE )
208 res *= std::pow( 10, (int) m_prefix );
209
210 return res;
211}
212
213wxString SPICE_VALUE::ToString() const
214{
215 wxString res( wxString::Format( "%.3f", ToDouble() ) );
216 StripZeros( res );
217
218 return res;
219}
220
221
223{
224 wxString range( aFormat.Range );
225
226 if( range.StartsWith( wxS( "~" ) ) )
227 {
228 Normalize();
229 range = si_prefix( m_prefix ) + range.Right( range.Length() - 1 );
230 }
231 else
232 {
233 SPICE_VALUE::UNIT_PREFIX rangePrefix = ParseSIPrefix( range[0] );
234 m_base = m_base * std::pow( 10, m_prefix - rangePrefix );
235 m_prefix = rangePrefix;
236 }
237
238 double mantissa = m_base;
239 int scale = 0;
240
241 while( std::fabs( mantissa ) >= 10.0 )
242 {
243 mantissa *= 0.1;
244 scale += 1;
245 }
246
247 while( mantissa != 0.0 && std::fabs( mantissa ) < 1.0 )
248 {
249 mantissa *= 10;
250 scale -= 1;
251 }
252
253 mantissa = KiROUND( mantissa * std::pow( 10, aFormat.Precision - 1 ) );
254 mantissa *= std::pow( 10, scale - aFormat.Precision + 1 );
255
256 wxString res = wxString::FromCDouble( mantissa, std::max( 0, aFormat.Precision - scale - 1 ) );
257 return res + range;
258}
259
260
262{
263 wxString res = wxString::FromCDouble( m_base );
264 StripZeros( res );
266
267 return res;
268}
269
270
272{
273 int prefixDiff = m_prefix - aOther.m_prefix;
275 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
276
277 // Convert both numbers to a common prefix
278 if( prefixDiff > 0 )
279 {
280 // Switch to the aOther prefix
281 res.m_base = ( m_base * std::pow( 10, prefixDiff ) ) + aOther.m_base;
282 res.m_prefix = aOther.m_prefix;
283 }
284 else if( prefixDiff < 0 )
285 {
286 // Use the current prefix
287 res.m_base = m_base + ( aOther.m_base * std::pow( 10, -prefixDiff ) );
288 res.m_prefix = m_prefix;
289 }
290 else
291 {
292 res.m_base = m_base + aOther.m_base;
293 res.m_prefix = m_prefix; // == aOther.m_prefix
294 }
295
296 res.Normalize();
297
298 return res;
299}
300
301
303{
304 int prefixDiff = m_prefix - aOther.m_prefix;
306 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
307
308 // Convert both numbers to a common prefix
309 if( prefixDiff > 0 )
310 {
311 // Switch to the aOther prefix
312 res.m_base = m_base * std::pow( 10, prefixDiff ) - aOther.m_base;
313 res.m_prefix = aOther.m_prefix;
314 }
315 else if( prefixDiff < 0 )
316 {
317 // Use the current prefix
318 res.m_base = m_base - aOther.m_base * std::pow( 10, -prefixDiff );
319 res.m_prefix = m_prefix;
320 }
321 else
322 {
323 res.m_base = m_base - aOther.m_base;
324 res.m_prefix = m_prefix; // == aOther.m_prefix
325 }
326
327 res.Normalize();
328
329 return res;
330}
331
332
334{
335 SPICE_VALUE res( m_base * aOther.m_base, (UNIT_PREFIX)( m_prefix + aOther.m_prefix ) );
336 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
337 res.Normalize();
338
339 return res;
340}
341
342
344{
345 SPICE_VALUE res( m_base / aOther.m_base, (UNIT_PREFIX)( m_prefix - aOther.m_prefix ) );
346 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
347 res.Normalize();
348
349 return res;
350}
351
352
353void SPICE_VALUE::StripZeros( wxString& aString )
354{
355 if ( aString.Find( ',' ) >= 0 || aString.Find( '.' ) >= 0 )
356 {
357 while( aString.EndsWith( '0' ) )
358 aString.RemoveLast();
359
360 if( aString.EndsWith( '.' ) || aString.EndsWith( ',' ) )
361 aString.RemoveLast();
362 }
363}
364
365
366bool SPICE_VALIDATOR::Validate( wxWindow* aParent )
367{
368 wxTextEntry* const text = GetTextEntry();
369
370 if( !text )
371 return false;
372
373 if( text->IsEmpty() )
374 {
375 if( m_emptyAllowed )
376 return true;
377
378 DisplayError( aParent, wxString::Format( _( "Please, fill required fields" ) ) );
379 return false;
380 }
381
382 wxString svalue = text->GetValue();
383
384 // In countries where the decimal separator is not a point, if the user
385 // has not used a point, replace the decimal separator by the point, as needed
386 // by spice simulator which uses the "C" decimal separator
387 svalue.Replace(",", "." );
388
389 try
390 {
391 // If SPICE_VALUE can be constructed, then it is a valid Spice value
392 SPICE_VALUE val( svalue );
393 }
394 catch( ... )
395 {
396 DisplayError( aParent, wxString::Format( _( "'%s' is not a valid Spice value." ),
397 text->GetValue() ) );
398
399 return false;
400 }
401
402 if( svalue != text->GetValue() )
403 text->SetValue( svalue );
404
405 return true;
406}
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:280
This file is part of the common library.
#define _(s)
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:49
void UpdateUnits(const wxString &aUnits)
Definition: spice_value.cpp:55
void FromString(const wxString &aString)
Definition: spice_value.cpp:40
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:85