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