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