From 958bd1897d0cd925cc2e09a6a1deeca55409fc84 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Sat, 22 Oct 2022 04:33:26 +0200 Subject: [PATCH] Sim: Add potentiometer model --- eeschema/CMakeLists.txt | 1 + eeschema/dialogs/dialog_sim_model.cpp | 5 +- eeschema/sim/sim_model.cpp | 31 ++++++-- eeschema/sim/sim_model.h | 6 +- eeschema/sim/sim_model_behavioral.cpp | 3 +- eeschema/sim/sim_model_r_pot.cpp | 103 ++++++++++++++++++++++++++ eeschema/sim/sim_model_r_pot.h | 56 ++++++++++++++ eeschema/sim/sim_plot_frame.cpp | 3 +- 8 files changed, 194 insertions(+), 14 deletions(-) create mode 100644 eeschema/sim/sim_model_r_pot.cpp create mode 100644 eeschema/sim/sim_model_r_pot.h diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index 50afaeca60..8bc28bb8e3 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -291,6 +291,7 @@ set( EESCHEMA_SRCS sim/sim_model_kibis.cpp sim/sim_model_ngspice.cpp sim/sim_model_ngspice_data.cpp + sim/sim_model_r_pot.cpp sim/sim_model_raw_spice.cpp sim/sim_model_source.cpp sim/sim_model_spice.cpp diff --git a/eeschema/dialogs/dialog_sim_model.cpp b/eeschema/dialogs/dialog_sim_model.cpp index 07d7cda5f9..93ff8e4912 100644 --- a/eeschema/dialogs/dialog_sim_model.cpp +++ b/eeschema/dialogs/dialog_sim_model.cpp @@ -171,11 +171,12 @@ bool DIALOG_SIM_MODEL::TransferDataToWindow() else { // The model is sourced from the instance. - SIM_MODEL::TYPE type = SIM_MODEL::ReadTypeFromFields( m_fields ); + SIM_MODEL::TYPE type = SIM_MODEL::ReadTypeFromFields( m_fields, m_sortedSymbolPins.size() ); try { - m_models.at( static_cast( SIM_MODEL::ReadTypeFromFields( m_fields ) ) ) = + m_models.at( static_cast( SIM_MODEL::ReadTypeFromFields( m_fields, + m_sortedSymbolPins.size() ) ) ) = SIM_MODEL::Create( m_sortedSymbolPins.size(), m_fields ); } catch( const IO_ERROR& e ) diff --git a/eeschema/sim/sim_model.cpp b/eeschema/sim/sim_model.cpp index c8f701b1da..b799d1ecf3 100644 --- a/eeschema/sim/sim_model.cpp +++ b/eeschema/sim/sim_model.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,7 @@ SIM_MODEL::INFO SIM_MODEL::TypeInfo( TYPE aType ) case TYPE::NONE: return { DEVICE_TYPE_::NONE, "", "" }; case TYPE::R: return { DEVICE_TYPE_::R, "", "Ideal" }; + case TYPE::R_POT: return { DEVICE_TYPE_::R, "POT", "Potentiometer" }; case TYPE::R_BEHAVIORAL: return { DEVICE_TYPE_::R, "=", "Behavioral" }; case TYPE::C: return { DEVICE_TYPE_::C, "", "Ideal" }; @@ -254,6 +256,7 @@ SIM_MODEL::SPICE_INFO SIM_MODEL::SpiceInfo( TYPE aType ) switch( aType ) { case TYPE::R: return { "R", "" }; + case TYPE::R_POT: return { "A", "" }; case TYPE::R_BEHAVIORAL: return { "R", "", "", "0", false, true }; case TYPE::C: return { "C", "" }; @@ -389,11 +392,13 @@ SIM_MODEL::SPICE_INFO SIM_MODEL::SpiceInfo( TYPE aType ) } -template TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields ); -template TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields ); +template TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields, + int aSymbolPinCount ); +template TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields, + int aSymbolPinCount ); template -TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields ) +TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields, int aSymbolPinCount ) { std::string deviceTypeFieldValue = GetFieldValue( &aFields, DEVICE_TYPE_FIELD ); std::string typeFieldValue = GetFieldValue( &aFields, TYPE_FIELD ); @@ -422,7 +427,8 @@ TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields ) // Still no type information. // We try to infer the model from the mandatory fields in this case. return InferTypeFromRefAndValue( GetFieldValue( &aFields, REFERENCE_FIELD ), - GetFieldValue( &aFields, VALUE_FIELD ) ); + GetFieldValue( &aFields, VALUE_FIELD ), + aSymbolPinCount ); } @@ -446,7 +452,8 @@ DEVICE_TYPE SIM_MODEL::InferDeviceTypeFromRef( const std::string& aRef ) } -TYPE SIM_MODEL::InferTypeFromRefAndValue( const std::string& aRef, const std::string& aValue ) +TYPE SIM_MODEL::InferTypeFromRefAndValue( const std::string& aRef, const std::string& aValue, + int aSymbolPinCount ) { std::string typeString; @@ -470,6 +477,10 @@ TYPE SIM_MODEL::InferTypeFromRefAndValue( const std::string& aRef, const std::st DEVICE_TYPE deviceType = InferDeviceTypeFromRef( aRef ); + // Exception. Potentiometer model is determined from pin count. + if( deviceType == DEVICE_TYPE_::R && aSymbolPinCount == 3 ) + return TYPE::R_POT; + for( TYPE type : TYPE_ITERATOR() ) { if( TypeInfo( type ).deviceType == deviceType && TypeInfo( type ).fieldValue == typeString ) @@ -579,7 +590,7 @@ template std::unique_ptr SIM_MODEL::Create( const SIM_MODEL& aBaseModel, unsigned aSymbolPinCount, const std::vector& aFields ) { - TYPE type = ReadTypeFromFields( aFields ); + TYPE type = ReadTypeFromFields( aFields, aSymbolPinCount ); // If the model has a specified type, it takes priority over the type of its base class. if( type == TYPE::NONE ) @@ -605,7 +616,7 @@ template std::unique_ptr SIM_MODEL::Create( unsigned aSymbolPinCount, const std::vector& aFields ) { - TYPE type = ReadTypeFromFields( aFields ); + TYPE type = ReadTypeFromFields( aFields, aSymbolPinCount ); if( type == TYPE::NONE ) THROW_IO_ERROR( wxString::Format( _( "Failed to read simulation model from fields" ) ) ); @@ -876,6 +887,9 @@ std::unique_ptr SIM_MODEL::Create( TYPE aType ) case TYPE::C: case TYPE::L: return std::make_unique( aType ); + + case TYPE::R_POT: + return std::make_unique(); case TYPE::L_MUTUAL: return std::make_unique(); @@ -1176,7 +1190,8 @@ void SIM_MODEL::InferredReadDataFields( unsigned aSymbolPinCount, const std::vec // TODO: Don't call this multiple times. if( InferTypeFromRefAndValue( GetFieldValue( aFields, REFERENCE_FIELD ), - GetFieldValue( aFields, VALUE_FIELD ) ) != GetType() ) + GetFieldValue( aFields, VALUE_FIELD ), + aSymbolPinCount ) != GetType() ) { // Not an inferred model. Nothing to do here. return; diff --git a/eeschema/sim/sim_model.h b/eeschema/sim/sim_model.h index d3e8502f24..9633bd81ea 100644 --- a/eeschema/sim/sim_model.h +++ b/eeschema/sim/sim_model.h @@ -201,6 +201,7 @@ public: NONE, R, + R_POT, R_BEHAVIORAL, C, @@ -482,11 +483,12 @@ public: template - static TYPE ReadTypeFromFields( const std::vector& aFields ); + static TYPE ReadTypeFromFields( const std::vector& aFields, int aSymbolPinCount ); static DEVICE_TYPE_ InferDeviceTypeFromRef( const std::string& aRef ); - static TYPE InferTypeFromRefAndValue( const std::string& aRef, const std::string& aValue ); + static TYPE InferTypeFromRefAndValue( const std::string& aRef, const std::string& aValue, + int aSymbolPinCount ); template static TYPE InferTypeFromLegacyFields( const std::vector& aFields ); diff --git a/eeschema/sim/sim_model_behavioral.cpp b/eeschema/sim/sim_model_behavioral.cpp index 4f688ddc46..2a766e7375 100644 --- a/eeschema/sim/sim_model_behavioral.cpp +++ b/eeschema/sim/sim_model_behavioral.cpp @@ -151,7 +151,8 @@ void SIM_MODEL_BEHAVIORAL::inferredReadDataFields( unsigned aSymbolPinCount, ParsePinsField( aSymbolPinCount, GetFieldValue( aFields, PINS_FIELD ) ); if( ( InferTypeFromRefAndValue( GetFieldValue( aFields, REFERENCE_FIELD ), - GetFieldValue( aFields, VALUE_FIELD ) ) == GetType() + GetFieldValue( aFields, VALUE_FIELD ), + aSymbolPinCount ) == GetType() && parseValueField( GetFieldValue( aFields, VALUE_FIELD ) ) ) // If Value is device type, this is an empty model || GetFieldValue( aFields, VALUE_FIELD ) == DeviceTypeInfo( GetDeviceType() ).fieldValue ) diff --git a/eeschema/sim/sim_model_r_pot.cpp b/eeschema/sim/sim_model_r_pot.cpp new file mode 100644 index 0000000000..c662ad66ec --- /dev/null +++ b/eeschema/sim/sim_model_r_pot.cpp @@ -0,0 +1,103 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mikolaj Wielgus + * Copyright (C) 2022 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 +#include + + +std::string SPICE_GENERATOR_R_POT::ModelLine( const SPICE_ITEM& aItem ) const +{ + std::string r = m_model.FindParam( "r" )->value->ToSpiceString(); + std::string pos = m_model.FindParam( "pos" )->value->ToSpiceString(); + + if( pos != "" ) + return fmt::format( ".model {} potentiometer( r0={} position={} )\n", aItem.modelName, r, pos ); + else + return fmt::format( ".model {} potentiometer( r0={} )\n", aItem.modelName, r ); +} + + +SIM_MODEL_R_POT::SIM_MODEL_R_POT() : + SIM_MODEL( TYPE::R_POT, std::make_unique( *this ) ) +{ + static std::vector paramInfos = makeParamInfos(); + + for( const SIM_MODEL::PARAM::INFO& paramInfo : paramInfos ) + AddParam( paramInfo ); +} + + +void SIM_MODEL_R_POT::WriteDataSchFields( std::vector& aFields ) const +{ + SIM_MODEL::WriteDataSchFields( aFields ); + + if( IsInferred() ) + inferredWriteDataFields( aFields ); +} + + +void SIM_MODEL_R_POT::WriteDataLibFields( std::vector& aFields ) const +{ + SIM_MODEL::WriteDataLibFields( aFields ); + + if( IsInferred() ) + inferredWriteDataFields( aFields ); +} + + +template +void SIM_MODEL_R_POT::inferredWriteDataFields( std::vector& aFields ) const +{ + std::string value = GetFieldValue( &aFields, PARAMS_FIELD ); + + if( value == "" ) + value = GetDeviceTypeInfo().fieldValue; + + WriteInferredDataFields( aFields, value ); +} + + +const std::vector SIM_MODEL_R_POT::makeParamInfos() +{ + std::vector paramInfos; + PARAM::INFO paramInfo; + + paramInfo.name = "r"; + paramInfo.type = SIM_VALUE::TYPE_STRING; + paramInfo.unit = "Ω"; + paramInfo.category = PARAM::CATEGORY::PRINCIPAL; + paramInfo.defaultValue = ""; + paramInfo.description = "Resistance"; + paramInfos.push_back( paramInfo ); + + paramInfo.name = "pos"; + paramInfo.type = SIM_VALUE::TYPE_FLOAT; + paramInfo.unit = ""; + paramInfo.category = PARAM::CATEGORY::PRINCIPAL; + paramInfo.defaultValue = "0.5"; + paramInfo.description = "Wiper position"; + paramInfos.push_back( paramInfo ); + + return paramInfos; +} diff --git a/eeschema/sim/sim_model_r_pot.h b/eeschema/sim/sim_model_r_pot.h new file mode 100644 index 0000000000..a50b81177e --- /dev/null +++ b/eeschema/sim/sim_model_r_pot.h @@ -0,0 +1,56 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mikolaj Wielgus + * Copyright (C) 2022 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 + */ + +#ifndef SIM_MODEL_R_POT_H +#define SIM_MODEL_R_POT_H + +#include +#include + + +class SPICE_GENERATOR_R_POT : public SPICE_GENERATOR +{ +public: + using SPICE_GENERATOR::SPICE_GENERATOR; + + std::string ModelLine( const SPICE_ITEM& aItem ) const override; +}; + + +class SIM_MODEL_R_POT : public SIM_MODEL +{ +public: + SIM_MODEL_R_POT(); + + void WriteDataSchFields( std::vector& aFields ) const override; + void WriteDataLibFields( std::vector& aFields ) const override; + +private: + template + void inferredWriteDataFields( std::vector& aFields ) const; + + static const std::vector makeParamInfos(); +}; + +#endif // SIM_MODEL_POT_H diff --git a/eeschema/sim/sim_plot_frame.cpp b/eeschema/sim/sim_plot_frame.cpp index 010b81bfaf..cdf3b4bd19 100644 --- a/eeschema/sim/sim_plot_frame.cpp +++ b/eeschema/sim/sim_plot_frame.cpp @@ -549,7 +549,8 @@ void SIM_PLOT_FRAME::AddTuner( SCH_SYMBOL* aSymbol ) if( !plotPanel ) return; - SIM_MODEL::TYPE type = SIM_MODEL::ReadTypeFromFields( aSymbol->GetFields() ); + SIM_MODEL::TYPE type = SIM_MODEL::ReadTypeFromFields( aSymbol->GetFields(), + static_cast( aSymbol->GetLibPins().size() ) ); SIM_MODEL::DEVICE_TYPE_ deviceType = SIM_MODEL::TypeInfo( type ).deviceType; switch( deviceType )