You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

287 lines
7.3 KiB

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