kicad/eeschema/sim/sim_value.cpp

467 lines
16 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mikolaj Wielgus
* Copyright (C) 2022-2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* https://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <sim/sim_value.h>
#include <wx/translation.h>
#include <ki_exception.h>
#include <locale_io.h>
#include <pegtl/contrib/parse_tree.hpp>
#include <fmt/core.h>
#include <math/util.h>
#define CALL_INSTANCE( ValueType, Notation, func, ... ) \
switch( ValueType ) \
{ \
case SIM_VALUE::TYPE_INT: \
switch( Notation ) \
{ \
case NOTATION::SI: \
func<SIM_VALUE::TYPE_INT, NOTATION::SI>( __VA_ARGS__ ); \
break; \
\
case NOTATION::SPICE: \
func<SIM_VALUE::TYPE_INT, NOTATION::SPICE>( __VA_ARGS__ ); \
break; \
} \
break; \
\
case SIM_VALUE::TYPE_FLOAT: \
switch( Notation ) \
{ \
case NOTATION::SI: \
func<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>( __VA_ARGS__ ); \
break; \
\
case NOTATION::SPICE: \
func<SIM_VALUE::TYPE_FLOAT, NOTATION::SPICE>( __VA_ARGS__ ); \
break; \
} \
break; \
\
case SIM_VALUE::TYPE_BOOL: \
case SIM_VALUE::TYPE_COMPLEX: \
case SIM_VALUE::TYPE_STRING: \
case SIM_VALUE::TYPE_BOOL_VECTOR: \
case SIM_VALUE::TYPE_INT_VECTOR: \
case SIM_VALUE::TYPE_FLOAT_VECTOR: \
case SIM_VALUE::TYPE_COMPLEX_VECTOR: \
wxFAIL_MSG( "Unhandled SIM_VALUE type" ); \
break; \
}
namespace SIM_VALUE_PARSER
{
using namespace SIM_VALUE_GRAMMAR;
template <typename Rule>
struct numberSelector : std::false_type {};
// TODO: Reorder. NOTATION should be before TYPE.
template <> struct numberSelector<SIM_VALUE_GRAMMAR::significand<SIM_VALUE::TYPE_INT>>
: std::true_type {};
template <> struct numberSelector<SIM_VALUE_GRAMMAR::significand<SIM_VALUE::TYPE_FLOAT>>
: std::true_type {};
template <> struct numberSelector<intPart> : std::true_type {};
template <> struct numberSelector<fracPart> : std::true_type {};
template <> struct numberSelector<exponent> : std::true_type {};
template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_INT, NOTATION::SI>>
: std::true_type {};
template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_INT, NOTATION::SPICE>>
: std::true_type {};
template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>>
: std::true_type {};
template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_FLOAT, NOTATION::SPICE>>
: std::true_type {};
struct PARSE_RESULT
{
bool isOk = true;
bool isEmpty = true;
std::string significand;
std::optional<int64_t> intPart;
std::optional<int64_t> fracPart;
std::optional<int> exponent;
std::optional<int> unitPrefixExponent;
};
PARSE_RESULT Parse( const std::string& aString,
NOTATION aNotation = NOTATION::SI,
SIM_VALUE::TYPE aValueType = SIM_VALUE::TYPE_FLOAT );
int UnitPrefixToExponent( std::string aPrefix, NOTATION aNotation = NOTATION::SI );
std::string ExponentToUnitPrefix( double aExponent, int& aExponentReduction,
NOTATION aNotation = NOTATION::SI );
}
template <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
static inline void doIsValid( tao::pegtl::string_input<>& aIn )
{
tao::pegtl::parse<SIM_VALUE_PARSER::numberGrammar<ValueType, Notation>>( aIn );
}
bool SIM_VALUE_GRAMMAR::IsValid( const std::string& aString, SIM_VALUE::TYPE aValueType,
NOTATION aNotation )
{
tao::pegtl::string_input<> in( aString, "from_content" );
try
{
CALL_INSTANCE( aValueType, aNotation, doIsValid, in );
}
catch( const tao::pegtl::parse_error& )
{
return false;
}
return true;
}
template <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
static inline std::unique_ptr<tao::pegtl::parse_tree::node> doParse(
tao::pegtl::string_input<>& aIn )
{
return tao::pegtl::parse_tree::parse<SIM_VALUE_PARSER::numberGrammar<ValueType, Notation>,
SIM_VALUE_PARSER::numberSelector>
( aIn );
}
template <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
static inline void handleNodeForParse( tao::pegtl::parse_tree::node& aNode,
SIM_VALUE_PARSER::PARSE_RESULT& aParseResult )
{
if( aNode.is_type<SIM_VALUE_PARSER::significand<ValueType>>() )
{
aParseResult.significand = aNode.string();
aParseResult.isEmpty = false;
for( const auto& subnode : aNode.children )
{
try
{
if( subnode->is_type<SIM_VALUE_PARSER::intPart>() )
aParseResult.intPart = std::stoll( subnode->string() );
else if( subnode->is_type<SIM_VALUE_PARSER::fracPart>() )
aParseResult.fracPart = std::stoll( subnode->string() );
}
catch( const std::exception& )
{
aParseResult.isOk = false;
}
}
}
else if( aNode.is_type<SIM_VALUE_PARSER::exponent>() )
{
aParseResult.exponent = std::stoi( aNode.string() );
aParseResult.isEmpty = false;
}
else if( aNode.is_type<SIM_VALUE_PARSER::unitPrefix<ValueType, Notation>>() )
{
aParseResult.unitPrefixExponent = SIM_VALUE_PARSER::UnitPrefixToExponent( aNode.string(),
Notation );
aParseResult.isEmpty = false;
}
else
wxFAIL_MSG( "Unhandled parse tree node" );
}
SIM_VALUE_PARSER::PARSE_RESULT SIM_VALUE_PARSER::Parse( const std::string& aString,
NOTATION aNotation,
SIM_VALUE::TYPE aValueType )
{
LOCALE_IO toggle;
tao::pegtl::string_input<> in( aString, "from_content" );
std::unique_ptr<tao::pegtl::parse_tree::node> root;
PARSE_RESULT result;
try
{
CALL_INSTANCE( aValueType, aNotation, root = doParse, in );
}
catch( tao::pegtl::parse_error& )
{
result.isOk = false;
return result;
}
wxASSERT( root );
try
{
for( const auto& node : root->children )
{
CALL_INSTANCE( aValueType, aNotation, handleNodeForParse, *node, result );
}
}
catch( const std::invalid_argument& e )
{
wxFAIL_MSG( fmt::format( "Parsing simulator value failed: {:s}", e.what() ) );
result.isOk = false;
}
return result;
}
int SIM_VALUE_PARSER::UnitPrefixToExponent( std::string aPrefix, NOTATION aNotation )
{
switch( aNotation )
{
case NOTATION::SI:
if( aPrefix.empty() )
return 0;
switch( aPrefix[0] )
{
case 'a': return -18;
case 'f': return -15;
case 'p': return -12;
case 'n': return -9;
case 'u': return -6;
case 'm': return -3;
case 'k':
case 'K': return 3;
case 'M': return 6;
case 'G': return 9;
case 'T': return 12;
case 'P': return 15;
case 'E': return 18;
}
break;
case NOTATION::SPICE:
std::transform( aPrefix.begin(), aPrefix.end(), aPrefix.begin(),
::tolower );
if( aPrefix == "f" )
return -15;
else if( aPrefix == "p" )
return -12;
else if( aPrefix == "n" )
return -9;
else if( aPrefix == "u" )
return -6;
else if( aPrefix == "m" )
return -3;
else if( aPrefix == "" )
return 0;
else if( aPrefix == "k" )
return 3;
else if( aPrefix == "meg" )
return 6;
else if( aPrefix == "g" )
return 9;
else if( aPrefix == "t" )
return 12;
break;
}
wxFAIL_MSG( fmt::format( "Unknown simulator value suffix: '{:s}'", aPrefix ) );
return 0;
}
std::string SIM_VALUE_PARSER::ExponentToUnitPrefix( double aExponent, int& aExponentReduction,
NOTATION aNotation )
{
if( aNotation == NOTATION::SI && aExponent >= -18 && aExponent <= -15 )
{
aExponentReduction = -18;
return "a";
}
else if( aExponent >= -15 && aExponent < -12 )
{
aExponentReduction = -15;
return "f";
}
else if( aExponent >= -12 && aExponent < -9 )
{
aExponentReduction = -12;
return "p";
}
else if( aExponent >= -9 && aExponent < -6 )
{
aExponentReduction = -9;
return "n";
}
else if( aExponent >= -6 && aExponent < -3 )
{
aExponentReduction = -6;
return "u";
}
else if( aExponent >= -3 && aExponent < 0 )
{
aExponentReduction = -3;
return "m";
}
else if( aExponent >= 0 && aExponent < 3 )
{
aExponentReduction = 0;
return "";
}
else if( aExponent >= 3 && aExponent < 6 )
{
aExponentReduction = 3;
return "k";
}
else if( aExponent >= 6 && aExponent < 9 )
{
aExponentReduction = 6;
return ( aNotation == NOTATION::SI ) ? "M" : "Meg";
}
else if( aExponent >= 9 && aExponent < 12 )
{
aExponentReduction = 9;
return "G";
}
else if( aExponent >= 12 && aExponent < 15 )
{
aExponentReduction = 12;
return "T";
}
else if( aNotation == NOTATION::SI && aExponent >= 15 && aExponent < 18 )
{
aExponentReduction = 15;
return "P";
}
else if( aNotation == NOTATION::SI && aExponent >= 18 && aExponent <= 21 )
{
aExponentReduction = 18;
return "E";
}
aExponentReduction = 0;
return "";
}
std::string SIM_VALUE::ConvertNotation( const std::string& aString, NOTATION aFromNotation,
NOTATION aToNotation )
{
wxString buf( aString );
buf.Replace( ',', '.' );
SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( buf.ToStdString(),
aFromNotation );
if( parseResult.isOk && !parseResult.isEmpty && !parseResult.significand.empty() )
{
int exponent = parseResult.exponent ? *parseResult.exponent : 0;
exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
try
{
LOCALE_IO toggle;
int expReduction = 0;
std::string prefix = SIM_VALUE_PARSER::ExponentToUnitPrefix( exponent, expReduction,
aToNotation );
exponent -= expReduction;
return fmt::format( "{:g}{}",
std::stod( parseResult.significand ) * std::pow( 10, exponent ),
prefix );
}
catch( const std::invalid_argument& )
{
// best efforts
}
}
return aString;
}
std::string SIM_VALUE::Normalize( double aValue )
{
double exponent = std::log10( std::abs( aValue ) );
int expReduction = 0;
std::string prefix = SIM_VALUE_PARSER::ExponentToUnitPrefix( exponent, expReduction,
NOTATION::SI );
double reducedValue = aValue / std::pow( 10, expReduction );
return fmt::format( "{:g}{}", reducedValue, prefix );
}
double SIM_VALUE::ToDouble( const std::string& aString, double aDefault )
{
SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, NOTATION::SI );
if( parseResult.isOk && !parseResult.isEmpty && !parseResult.significand.empty() )
{
try
{
LOCALE_IO toggle;
int exponent = parseResult.exponent ? *parseResult.exponent : 0;
exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
return std::stod( parseResult.significand ) * std::pow( 10, exponent );
}
catch( const std::invalid_argument& )
{
// best efforts
}
}
return aDefault;
}
int SIM_VALUE::ToInt( const std::string& aString, int aDefault )
{
SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, NOTATION::SI );
if( parseResult.isOk
&& !parseResult.isEmpty
&& parseResult.intPart
&& ( !parseResult.fracPart || *parseResult.fracPart == 0 ) )
{
int exponent = parseResult.exponent ? *parseResult.exponent : 0;
exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
if( exponent >= 0 )
return (int) *parseResult.intPart * (int) std::pow( 10, exponent );
}
return aDefault;
}
bool SIM_VALUE::Equal( double aLH, const std::string& aRH )
{
return std::abs( aLH - ToDouble( aRH ) ) <= std::numeric_limits<double>::epsilon();
}