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