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 <maciej.suminski@cern.ch>
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 
37 SPICE_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 
110 double SPICE_VALUE::ToDouble() const
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 
120 wxString 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 
234 void 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 
247 bool 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,
278  wxString::Format( _( "\"%s\" is not a valid Spice value" ), text->GetValue() ) );
279 
280  return false;
281  }
282 
283  if( svalue != text->GetValue() )
284  text->SetValue( svalue );
285 
286  return true;
287 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:252
SPICE_VALUE operator-(const SPICE_VALUE &aOther) const
wxString ToSpiceString() const
Return string value in Spice format (e.g.
bool Validate(wxWindow *aParent) override
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
This file is part of the common library.
double m_base
Definition: spice_value.h:139
bool m_emptyAllowed
< Is it valid to get an empty value?
Definition: spice_value.h:165
double ToDouble() const
void Normalize()
Normalize the value.
Definition: spice_value.cpp:88
static LIB_PART * dummy()
Used to draw a dummy shape when a LIB_PART is not found in library.
Definition: sch_symbol.cpp:69
wxString ToString() const
Return string value as when converting double to string (e.g.
UNIT_PREFIX m_prefix
Was the value defined using the Spice notation?
Definition: spice_value.h:140
static void stripZeros(wxString &aString)
< Remove redundant zeros from the end of a string.
< Helper class to handle Spice way of expressing values (e.g. 10.5 Meg) Helper class to recognize Spi...
Definition: spice_value.h:34
SPICE_VALUE operator *(const SPICE_VALUE &aOther) const
SPICE_VALUE operator+(const SPICE_VALUE &aOther) const
SPICE_VALUE operator/(const SPICE_VALUE &aOther) const
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
bool m_spiceStr
Definition: spice_value.h:143
SPICE_VALUE()
Parses the string to create a Spice value (e.g. 100n)
Definition: spice_value.h:51
#define _(s)
Definition: 3d_actions.cpp:33
The common library.
Hold a translatable error message and may be used when throwing exceptions containing a translated er...
Definition: ki_exception.h:44