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
27#include <stdexcept>
28#include <cmath>
29
30#include <wx/textentry.h>
31#include <wx/numformatter.h>
32#include <confirm.h>
33#include <common.h>
34#include <ki_exception.h>
35#include <locale_io.h>
36
37SPICE_VALUE::SPICE_VALUE( const wxString& aString )
38{
39 char buf[8] = { 0, };
40
41 if( aString.IsEmpty() )
42 throw KI_PARAM_ERROR( _( "Spice value cannot be empty" ) );
43
44 LOCALE_IO dummy; // All numeric values should be in "C" locale(decimal separator = .)
45
46 if( sscanf( (const char*) aString.c_str(), "%lf%7s", &m_base, buf ) == 0 )
47 throw KI_PARAM_ERROR( _( "Invalid Spice value string" ) );
48
49 if( *buf == 0 )
50 {
52 m_spiceStr = false;
53 Normalize();
54 return;
55 }
56
57 m_spiceStr = true;
58
59 for( char* bufPtr = buf; *bufPtr; ++bufPtr )
60 *bufPtr = tolower( *bufPtr );
61
62 if( !strcmp( buf, "meg" ) )
63 {
65 }
66 else
67 {
68 switch( buf[0] )
69 {
70 case 'f': m_prefix = PFX_FEMTO; break;
71 case 'p': m_prefix = PFX_PICO; break;
72 case 'n': m_prefix = PFX_NANO; break;
73 case 'u': m_prefix = PFX_MICRO; break;
74 case 'm': m_prefix = PFX_MILI; break;
75 case 'k': m_prefix = PFX_KILO; break;
76 case 'g': m_prefix = PFX_GIGA; break;
77 case 't': m_prefix = PFX_TERA; break;
78
79 default:
80 throw KI_PARAM_ERROR( _( "Invalid unit prefix" ) );
81 }
82 }
83
84 Normalize();
85}
86
87
89{
90 while( std::fabs( m_base ) >= 1000.0 )
91 {
92 m_base *= 0.001;
93 m_prefix = (UNIT_PREFIX)( m_prefix + 3 );
94
95 if( m_prefix == PFX_TERA ) // this is the biggest unit available
96 break;
97 }
98
99 while( m_base != 0.0 && std::fabs( m_base ) < 1.000 )
100 {
101 m_base *= 1000.0;
102 m_prefix = (UNIT_PREFIX)( m_prefix - 3 );
103
104 if( m_prefix == PFX_FEMTO ) // this is the smallest unit available
105 break;
106 }
107}
108
109
111{
112 double res = m_base;
113
114 if( m_prefix != PFX_NONE )
115 res *= std::pow( 10, (int) m_prefix );
116
117 return res;
118}
119
120wxString SPICE_VALUE::ToString() const
121{
122 wxString res( wxString::Format( "%.3f", ToDouble() ) );
123 stripZeros( res );
124
125 return res;
126}
127
128
130{
131 wxString res = wxString::FromCDouble( m_base );
132 stripZeros( res );
133
134 switch( m_prefix )
135 {
136 case PFX_FEMTO: res += "f"; break;
137 case PFX_PICO: res += "p"; break;
138 case PFX_NANO: res += "n"; break;
139 case PFX_MICRO: res += "u"; break;
140 case PFX_MILI: res += "m"; break;
141 case PFX_NONE: break;
142 case PFX_KILO: res += "k"; break;
143 case PFX_MEGA: res += "Meg"; break;
144 case PFX_GIGA: res += "G"; break;
145 case PFX_TERA: res += "T"; break;
146 }
147
148 return res;
149}
150
151
153{
154 int prefixDiff = m_prefix - aOther.m_prefix;
155 SPICE_VALUE res;
156 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
157
158 // Convert both numbers to a common prefix
159 if( prefixDiff > 0 )
160 {
161 // Switch to the aOther prefix
162 res.m_base = ( m_base * std::pow( 10, prefixDiff ) ) + aOther.m_base;
163 res.m_prefix = aOther.m_prefix;
164 }
165 else if( prefixDiff < 0 )
166 {
167 // Use the current prefix
168 res.m_base = m_base + ( aOther.m_base * std::pow( 10, -prefixDiff ) );
169 res.m_prefix = m_prefix;
170 }
171 else
172 {
173 res.m_base = m_base + aOther.m_base;
174 res.m_prefix = m_prefix; // == aOther.m_prefix
175 }
176
177 res.Normalize();
178
179 return res;
180}
181
182
184{
185 int prefixDiff = m_prefix - aOther.m_prefix;
186 SPICE_VALUE res;
187 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
188
189 // Convert both numbers to a common prefix
190 if( prefixDiff > 0 )
191 {
192 // Switch to the aOther prefix
193 res.m_base = m_base * std::pow( 10, prefixDiff ) - aOther.m_base;
194 res.m_prefix = aOther.m_prefix;
195 }
196 else if( prefixDiff < 0 )
197 {
198 // Use the current prefix
199 res.m_base = m_base - aOther.m_base * std::pow( 10, -prefixDiff );
200 res.m_prefix = m_prefix;
201 }
202 else
203 {
204 res.m_base = m_base - aOther.m_base;
205 res.m_prefix = m_prefix; // == aOther.m_prefix
206 }
207
208 res.Normalize();
209
210 return res;
211}
212
213
215{
216 SPICE_VALUE res( m_base * aOther.m_base, (UNIT_PREFIX)( m_prefix + aOther.m_prefix ) );
217 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
218 res.Normalize();
219
220 return res;
221}
222
223
225{
226 SPICE_VALUE res( m_base / aOther.m_base, (UNIT_PREFIX)( m_prefix - aOther.m_prefix ) );
227 res.m_spiceStr = m_spiceStr || aOther.m_spiceStr;
228 res.Normalize();
229
230 return res;
231}
232
233
234void SPICE_VALUE::stripZeros( wxString& aString )
235{
236 if ( aString.Find( ',' ) >= 0 || aString.Find( '.' ) >= 0 )
237 {
238 while( aString.EndsWith( '0' ) )
239 aString.RemoveLast();
240
241 if( aString.EndsWith( '.' ) || aString.EndsWith( ',' ) )
242 aString.RemoveLast();
243 }
244}
245
246
247bool SPICE_VALIDATOR::Validate( wxWindow* aParent )
248{
249 wxTextEntry* const text = GetTextEntry();
250
251 if( !text )
252 return false;
253
254 if( text->IsEmpty() )
255 {
256 if( m_emptyAllowed )
257 return true;
258
259 DisplayError( aParent, wxString::Format( _( "Please, fill required fields" ) ) );
260 return false;
261 }
262
263 wxString svalue = text->GetValue();
264
265 // In countries where the decimal separator is not a point, if the user
266 // has not used a point, replace the decimal separator by the point, as needed
267 // by spice simulator which uses the "C" decimal separator
268 svalue.Replace(",", "." );
269
270 try
271 {
272 // If SPICE_VALUE can be constructed, then it is a valid Spice value
273 SPICE_VALUE val( svalue );
274 }
275 catch( ... )
276 {
277 DisplayError( aParent, wxString::Format( _( "'%s' is not a valid Spice value." ),
278 text->GetValue() ) );
279
280 return false;
281 }
282
283 if( svalue != text->GetValue() )
284 text->SetValue( svalue );
285
286 return true;
287}
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:165
bool Validate(wxWindow *aParent) override
< Helper class to handle Spice way of expressing values (e.g. 10.5 Meg) Helper class to recognize Spi...
Definition: spice_value.h:35
SPICE_VALUE operator+(const SPICE_VALUE &aOther) const
double m_base
Definition: spice_value.h:139
SPICE_VALUE operator-(const SPICE_VALUE &aOther) const
UNIT_PREFIX m_prefix
Was the value defined using the Spice notation?
Definition: spice_value.h:140
void Normalize()
Normalize the value.
Definition: spice_value.cpp:88
wxString ToString() const
Return string value as when converting double to string (e.g.
SPICE_VALUE operator/(const SPICE_VALUE &aOther) const
static void stripZeros(wxString &aString)
< 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:51
double ToDouble() const
bool m_spiceStr
Definition: spice_value.h:143
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)
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
std::vector< FAB_LAYER_COLOR > dummy