Sim: Fix tuners
This commit is contained in:
parent
78b193af5f
commit
9b6cc6c505
|
@ -252,21 +252,30 @@ void NETLIST_EXPORTER_SPICE::ReplaceForbiddenChars( std::string& aNetName )
|
||||||
|
|
||||||
|
|
||||||
std::string NETLIST_EXPORTER_SPICE::GetItemName( const std::string& aRefName ) const
|
std::string NETLIST_EXPORTER_SPICE::GetItemName( const std::string& aRefName ) const
|
||||||
|
{
|
||||||
|
const SPICE_ITEM* item = FindItem( aRefName );
|
||||||
|
|
||||||
|
if( !item )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return item->model->SpiceGenerator().ItemName( *item );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const SPICE_ITEM* NETLIST_EXPORTER_SPICE::FindItem( const std::string& aRefName ) const
|
||||||
{
|
{
|
||||||
const std::list<SPICE_ITEM>& spiceItems = GetItems();
|
const std::list<SPICE_ITEM>& spiceItems = GetItems();
|
||||||
|
|
||||||
auto it = std::find_if( spiceItems.begin(), spiceItems.end(),
|
auto it = std::find_if( spiceItems.begin(), spiceItems.end(),
|
||||||
[aRefName]( const SPICE_ITEM& aItem )
|
[aRefName]( const SPICE_ITEM& item )
|
||||||
{
|
{
|
||||||
return aItem.refName == aRefName;
|
return item.refName == aRefName;
|
||||||
} );
|
} );
|
||||||
|
|
||||||
if( it == spiceItems.end() )
|
if( it != spiceItems.end() )
|
||||||
return "";
|
return &*it;
|
||||||
|
|
||||||
SPICE_ITEM item;
|
return nullptr;
|
||||||
item.refName = aRefName;
|
|
||||||
return it->model->SpiceGenerator().ItemName( item );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -98,24 +98,28 @@ public:
|
||||||
* Return the list of nets.
|
* Return the list of nets.
|
||||||
*/
|
*/
|
||||||
std::set<std::string> GetNets() const { return m_nets; }
|
std::set<std::string> GetNets() const { return m_nets; }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return name of Spice device corresponding to a schematic symbol.
|
* Return name of Spice device corresponding to a schematic symbol.
|
||||||
*
|
*
|
||||||
* @param aRefName is the component reference.
|
* @param aRefName is the component reference.
|
||||||
* @return Spice device name or empty string if there is no such symbol in the netlist.
|
* @return Spice device name or empty string if there is no such symbol in the netlist.
|
||||||
* Normally the name is either plain reference if the first character of reference corresponds
|
* Normally the name is either a plain reference if the first character of reference
|
||||||
* to the assigned device model type or it is the reference prefixed with a character defining
|
* corresponds to the assigned device model type or a reference prefixed with a character
|
||||||
* the device model type.
|
* defining the device model type.
|
||||||
*/
|
*/
|
||||||
std::string GetItemName( const std::string& aRefName ) const;
|
std::string GetItemName( const std::string& aRefName ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the list of items representing schematic components in the Spice world.
|
* Return the list of items representing schematic symbols in the Spice world.
|
||||||
*/
|
*/
|
||||||
const std::list<SPICE_ITEM>& GetItems() const { return m_items; }
|
const std::list<SPICE_ITEM>& GetItems() const { return m_items; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find and return the item corresponding to \a aRefName.
|
||||||
|
*/
|
||||||
|
const SPICE_ITEM* FindItem( const std::string& aRefName ) const;
|
||||||
|
|
||||||
const std::vector<std::string>& GetDirectives() { return m_directives; }
|
const std::vector<std::string>& GetDirectives() { return m_directives; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <sim/sim_model_ideal.h>
|
#include <sim/sim_model_ideal.h>
|
||||||
#include <pegtl.hpp>
|
#include <pegtl.hpp>
|
||||||
#include <pegtl/contrib/parse_tree.hpp>
|
#include <pegtl/contrib/parse_tree.hpp>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
|
||||||
std::string SPICE_GENERATOR_IDEAL::ModelLine( const SPICE_ITEM& aItem ) const
|
std::string SPICE_GENERATOR_IDEAL::ModelLine( const SPICE_ITEM& aItem ) const
|
||||||
|
@ -45,6 +46,15 @@ std::string SPICE_GENERATOR_IDEAL::ItemLine( const SPICE_ITEM& aItem ) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string SPICE_GENERATOR_IDEAL::TunerCommand( const SPICE_ITEM& aItem,
|
||||||
|
const SIM_VALUE_FLOAT& aValue ) const
|
||||||
|
{
|
||||||
|
return fmt::format( "alter @{}={}",
|
||||||
|
aItem.model->SpiceGenerator().ItemName( aItem ),
|
||||||
|
aValue.ToSpiceString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SIM_MODEL_IDEAL::SIM_MODEL_IDEAL( TYPE aType ) :
|
SIM_MODEL_IDEAL::SIM_MODEL_IDEAL( TYPE aType ) :
|
||||||
SIM_MODEL( aType, std::make_unique<SPICE_GENERATOR_IDEAL>( *this ) )
|
SIM_MODEL( aType, std::make_unique<SPICE_GENERATOR_IDEAL>( *this ) )
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,8 @@ public:
|
||||||
|
|
||||||
std::string ModelLine( const SPICE_ITEM& aItem ) const override;
|
std::string ModelLine( const SPICE_ITEM& aItem ) const override;
|
||||||
std::string ItemLine( const SPICE_ITEM& aItem ) const override;
|
std::string ItemLine( const SPICE_ITEM& aItem ) const override;
|
||||||
|
|
||||||
|
std::string TunerCommand( const SPICE_ITEM& aItem, const SIM_VALUE_FLOAT& aValue ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,15 @@ std::string SPICE_GENERATOR_R_POT::ModelLine( const SPICE_ITEM& aItem ) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string SPICE_GENERATOR_R_POT::TunerCommand( const SPICE_ITEM& aItem,
|
||||||
|
const SIM_VALUE_FLOAT& aValue ) const
|
||||||
|
{
|
||||||
|
return fmt::format( "altermod @{}[position]={}",
|
||||||
|
aItem.model->SpiceGenerator().ItemName( aItem ),
|
||||||
|
aValue.ToSpiceString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
SIM_MODEL_R_POT::SIM_MODEL_R_POT() :
|
SIM_MODEL_R_POT::SIM_MODEL_R_POT() :
|
||||||
SIM_MODEL( TYPE::R_POT, std::make_unique<SPICE_GENERATOR_R_POT>( *this ) )
|
SIM_MODEL( TYPE::R_POT, std::make_unique<SPICE_GENERATOR_R_POT>( *this ) )
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,6 +35,7 @@ public:
|
||||||
using SPICE_GENERATOR::SPICE_GENERATOR;
|
using SPICE_GENERATOR::SPICE_GENERATOR;
|
||||||
|
|
||||||
std::string ModelLine( const SPICE_ITEM& aItem ) const override;
|
std::string ModelLine( const SPICE_ITEM& aItem ) const override;
|
||||||
|
std::string TunerCommand( const SPICE_ITEM& aItem, const SIM_VALUE_FLOAT& aValue ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -549,33 +549,24 @@ void SIM_PLOT_FRAME::AddTuner( SCH_SYMBOL* aSymbol )
|
||||||
if( !plotPanel )
|
if( !plotPanel )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SIM_MODEL::TYPE type = SIM_MODEL::ReadTypeFromFields( aSymbol->GetFields(),
|
wxString ref = aSymbol->GetField( REFERENCE_FIELD )->GetShownText();
|
||||||
static_cast<int>( aSymbol->GetLibPins().size() ) );
|
|
||||||
SIM_MODEL::DEVICE_TYPE_ deviceType = SIM_MODEL::TypeInfo( type ).deviceType;
|
|
||||||
|
|
||||||
switch( deviceType )
|
// Do not add multiple instances for the same component.
|
||||||
{
|
auto tunerIt = std::find_if( m_tuners.begin(), m_tuners.end(),
|
||||||
case SIM_MODEL::DEVICE_TYPE_::R:
|
[ref]( const TUNER_SLIDER* tuner )
|
||||||
case SIM_MODEL::DEVICE_TYPE_::C:
|
{
|
||||||
case SIM_MODEL::DEVICE_TYPE_::L:
|
return tuner->GetComponentName() == ref;
|
||||||
case SIM_MODEL::DEVICE_TYPE_::XSPICE:
|
} );
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wxString componentName = aSymbol->GetField( REFERENCE_FIELD )->GetText();
|
|
||||||
|
|
||||||
// Do not add multiple instances for the same component
|
|
||||||
auto tunerIt = std::find_if( m_tuners.begin(), m_tuners.end(), [&]( const TUNER_SLIDER* t )
|
|
||||||
{
|
|
||||||
return t->GetComponentName() == componentName;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if( tunerIt != m_tuners.end() )
|
if( tunerIt != m_tuners.end() )
|
||||||
return; // We already have it
|
return; // We already have it.
|
||||||
|
|
||||||
|
const SPICE_ITEM* item = GetExporter()->FindItem( std::string( ref.ToUTF8() ) );
|
||||||
|
const SIM_MODEL::PARAM* tunerParam = item->model->GetTunerParam();
|
||||||
|
|
||||||
|
// Do nothing if the symbol is not tunable.
|
||||||
|
if( !tunerParam )
|
||||||
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -586,30 +577,31 @@ void SIM_PLOT_FRAME::AddTuner( SCH_SYMBOL* aSymbol )
|
||||||
}
|
}
|
||||||
catch( const KI_PARAM_ERROR& e )
|
catch( const KI_PARAM_ERROR& e )
|
||||||
{
|
{
|
||||||
// Sorry, no bonus
|
|
||||||
DisplayErrorMessage( nullptr, e.What() );
|
DisplayErrorMessage( nullptr, e.What() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SIM_PLOT_FRAME::UpdateTunerValue( SCH_SYMBOL* aSymbol, int aId, const wxString& aValue )
|
void SIM_PLOT_FRAME::UpdateTunerValue( SCH_SYMBOL* aSymbol, const wxString& aValue )
|
||||||
{
|
{
|
||||||
for( auto& item : m_schematicFrame->GetScreen()->Items().OfType( SCH_SYMBOL_T ) )
|
for( auto& item : m_schematicFrame->GetScreen()->Items().OfType( SCH_SYMBOL_T ) )
|
||||||
{
|
{
|
||||||
if( item == aSymbol )
|
if( item == aSymbol )
|
||||||
{
|
{
|
||||||
SCH_FIELD* field = aSymbol->GetFieldById( aId );
|
SIM_LIB_MGR mgr( Prj() );
|
||||||
|
SIM_MODEL& model = mgr.CreateModel( *aSymbol ).model;
|
||||||
|
|
||||||
if( !field )
|
const SIM_MODEL::PARAM* tunerParam = model.GetTunerParam();
|
||||||
break;
|
|
||||||
|
|
||||||
field->SetText( aValue );
|
if( !tunerParam )
|
||||||
|
return;
|
||||||
|
|
||||||
|
model.SetParamValue( tunerParam->info.name, std::string( aValue.ToUTF8() ) );
|
||||||
|
model.WriteFields( aSymbol->GetFields() );
|
||||||
|
|
||||||
m_schematicFrame->UpdateItem( aSymbol, false, true );
|
m_schematicFrame->UpdateItem( aSymbol, false, true );
|
||||||
m_schematicFrame->OnModify();
|
m_schematicFrame->OnModify();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -923,17 +915,7 @@ void SIM_PLOT_FRAME::updateTuners()
|
||||||
void SIM_PLOT_FRAME::applyTuners()
|
void SIM_PLOT_FRAME::applyTuners()
|
||||||
{
|
{
|
||||||
for( auto& tuner : m_tuners )
|
for( auto& tuner : m_tuners )
|
||||||
{
|
m_simulator->Command( tuner->GetTunerCommand() );
|
||||||
std::pair<wxString, bool> command = tuner->GetSpiceTuningCommand();
|
|
||||||
const SPICE_VALUE& value = tuner->GetValue();
|
|
||||||
|
|
||||||
// 0 < value < 1 for model parameter to avoid division by zero, etc.
|
|
||||||
command.first += command.second
|
|
||||||
? wxString::FromCDouble( Clamp( 1e-9, value.ToDouble() / 100.0, 1-1e-9 ), 9 )
|
|
||||||
: value.ToSpiceString();
|
|
||||||
|
|
||||||
m_simulator->Command( command.first.ToStdString() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ public:
|
||||||
* @param aId id of the symbol field
|
* @param aId id of the symbol field
|
||||||
* @param aValue new value of the symbol field
|
* @param aValue new value of the symbol field
|
||||||
*/
|
*/
|
||||||
void UpdateTunerValue( SCH_SYMBOL* aSymbol, int aId, const wxString& aValue );
|
void UpdateTunerValue( SCH_SYMBOL* aSymbol, const wxString& aValue );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the currently opened plot panel (or NULL if there is none).
|
* Return the currently opened plot panel (or NULL if there is none).
|
||||||
|
|
|
@ -156,9 +156,10 @@ std::string SPICE_GENERATOR::ItemParams() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string SPICE_GENERATOR::TuningLine( const SPICE_ITEM& aItem ) const
|
std::string SPICE_GENERATOR::TunerCommand( const SPICE_ITEM& aItem,
|
||||||
|
const SIM_VALUE_FLOAT& aValue ) const
|
||||||
{
|
{
|
||||||
// TODO.
|
// No tuning available by default.
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ public:
|
||||||
virtual std::string ItemModelName( const SPICE_ITEM& aItem ) const;
|
virtual std::string ItemModelName( const SPICE_ITEM& aItem ) const;
|
||||||
virtual std::string ItemParams() const;
|
virtual std::string ItemParams() const;
|
||||||
|
|
||||||
virtual std::string TuningLine( const SPICE_ITEM& aItem ) const;
|
virtual std::string TunerCommand( const SPICE_ITEM& aItem, const SIM_VALUE_FLOAT& aValue ) const;
|
||||||
|
|
||||||
virtual std::vector<std::string> CurrentNames( const SPICE_ITEM& aItem ) const;
|
virtual std::vector<std::string> CurrentNames( const SPICE_ITEM& aItem ) const;
|
||||||
|
|
||||||
|
|
|
@ -35,190 +35,49 @@
|
||||||
#include <complex> // norm
|
#include <complex> // norm
|
||||||
|
|
||||||
|
|
||||||
// characteristic curves (default: 0B Lin)
|
|
||||||
static const struct { double m_law, m_mid; } CURVES[] =
|
|
||||||
{
|
|
||||||
// same order as choices in m_curve
|
|
||||||
{ 1.00, 0.10 }, // 10A Log
|
|
||||||
{ 1.00, 0.15 }, // 15A Log
|
|
||||||
{ 0.90, 0.13 }, // 15A Log S
|
|
||||||
{ 0.00, 0.10 }, // 10C Rev Log
|
|
||||||
{ 0.00, 0.15 }, // 15C Rev Log
|
|
||||||
{ 0.10, 0.13 }, // 15C Rev Log S
|
|
||||||
{ 0.50, 0.50 }, // 0B Lin
|
|
||||||
{ 0.50, 0.15 }, // 4B S-Curve
|
|
||||||
{ 0.50, 0.10 }, // 5B S-Curve
|
|
||||||
{ 0.50, 0.00 } // Switch
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, int S>
|
|
||||||
static inline int arraysize( const T ( &v )[S] ) { return S; }
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform ratio according to linear, logarithmic or reverse logarithmic laws
|
|
||||||
* (characteristic curve or taper) of rotary or slide potentiometer (pot or fader).
|
|
||||||
*
|
|
||||||
* Parameters corresponding to *IEC 60393-1:2008* and *JIS C 5260-1* code letters:
|
|
||||||
*
|
|
||||||
* | @p aLaw | @p aMid | Code | Resistance Law (Taper)
|
|
||||||
* | ------: | ------: | ---: | :---------------------
|
|
||||||
* | 1.00 | 0.10 | 10A | CW logarithmic
|
|
||||||
* | ^ | 0.15 | 15A | CW logarithmic (audio)
|
|
||||||
* | 0.90 | 0.13 | ^ | CW logarithmic (high-end audio)
|
|
||||||
* | 0.00 | 0.10 | 10C | CCW/reverse logarithmic
|
|
||||||
* | ^ | 0.15 | 15C | CCW/reverse logarithmic (reverse audio)
|
|
||||||
* | 0.10 | 0.13 | ^ | CCW/reverse logarithmic (high-end reverse audio)
|
|
||||||
* | 0.50 | 0.50 | 0B | (ideal) linear
|
|
||||||
* | ^ | 0.15 | 4B | symmetric (audio S-curve)
|
|
||||||
* | ^ | 0.10 | 5B | symmetric (S-curve)
|
|
||||||
* | ^ | 0.00 | - | switch
|
|
||||||
*
|
|
||||||
* Standards code letters cross-reference:
|
|
||||||
*
|
|
||||||
* | IEC 60393-1:2008 | IEC 60393-1:1989 | MIL-R-94 | Resistance Law
|
|
||||||
* | ----------------: | :--------------: | :------: | :-------------
|
|
||||||
* | 0B | A | A | linear
|
|
||||||
* | 10A | B | C | logarithmic
|
|
||||||
* | 15A | ^ | - | ^
|
|
||||||
* | 10C | C | F | reverse logarithmic
|
|
||||||
* | 15C | ^ | - | ^
|
|
||||||
*
|
|
||||||
* **Logarithmic Law** is for *levels* (logarithmic units) and is actually an exponential curve.
|
|
||||||
* **Reverse** refers to a reverse-mounted resistive element or shaft on a potentiometer
|
|
||||||
* (resulting in a reflected curve). An **S-curve** is a curve joined to its (scaled) reflection,
|
|
||||||
* and *may* be symmetric or linear. **Inverse** refers to the mathematical inverse of a function.
|
|
||||||
*
|
|
||||||
* @tparam F is a floating point type.
|
|
||||||
* @param aRatio is the input (travel) ratio or moving contact (wiper) position, from
|
|
||||||
* 0%/CCW/left (0) through 50%/mid-travel/center (1/2) to 100%/CW/right (1).
|
|
||||||
* @param aMid is the logarithmic laws' output ratio at 50%/mid-travel/center (1/2)
|
|
||||||
* input ratio.
|
|
||||||
* @param aLaw is the (resistance) law, interpolating from *reverse logarithmic* (0)
|
|
||||||
* through *symmetric/linear* (1/2) to *logarithmic* (1).
|
|
||||||
* @param aInverse swaps input and output ratios (inverse function, where possible),
|
|
||||||
* if @c true.
|
|
||||||
* @return the output (resistance or voltage) ratio in [0, 1].
|
|
||||||
*/
|
|
||||||
template <typename F>
|
|
||||||
static F taper( F aRatio, F aMid = 0.5, F aLaw = 1.0, bool aInverse = false )
|
|
||||||
{
|
|
||||||
static_assert( std::is_floating_point<F>::value, "F must be floating point" );
|
|
||||||
|
|
||||||
// clamp to [0, 1] and short-cut
|
|
||||||
if( aRatio <= 0 )
|
|
||||||
return 0;
|
|
||||||
if( aRatio >= 1 )
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
// short-cut for ideal linear or at S-curve inflection point
|
|
||||||
if( aMid == 0.5 || aRatio == aLaw )
|
|
||||||
return aRatio;
|
|
||||||
|
|
||||||
F t = aRatio;
|
|
||||||
|
|
||||||
// clamp to [0, 1] and short-cut at (non-invertible) limits
|
|
||||||
if( aMid <= 0 )
|
|
||||||
{
|
|
||||||
t = aInverse ? 1 : 0;
|
|
||||||
}
|
|
||||||
else if( aMid >= 1 )
|
|
||||||
{
|
|
||||||
t = aInverse ? 0 : 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// clamp, and reflect and/or scale for reverse...symmetric...normal laws
|
|
||||||
if( aLaw >= 1 )
|
|
||||||
{
|
|
||||||
// Do nothing, leave t = t
|
|
||||||
}
|
|
||||||
else if( aLaw <= 0 )
|
|
||||||
{
|
|
||||||
t = 1 - t;
|
|
||||||
}
|
|
||||||
else if( aRatio <= aLaw )
|
|
||||||
{
|
|
||||||
t = t / aLaw;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
t = ( 1 - t ) / ( 1 - aLaw );
|
|
||||||
}
|
|
||||||
|
|
||||||
// scaling factors for domain and range in [0, 1]
|
|
||||||
F a = std::norm( 1 - 1 / aMid );
|
|
||||||
F b = std::log( a );
|
|
||||||
F c = a - 1;
|
|
||||||
|
|
||||||
// scaling: a = (1 - 1/m)^2
|
|
||||||
// log law: (a^t - 1) / (a - 1)
|
|
||||||
// inverse: log_a(1 + t (a - 1))
|
|
||||||
t = aInverse ? ( std::log1p( t * c ) / b ) : ( std::expm1( t * b ) / c );
|
|
||||||
}
|
|
||||||
|
|
||||||
// clamp, and scale and/or reflect for reverse...symmetric...normal laws
|
|
||||||
if( aLaw >= 1 )
|
|
||||||
{
|
|
||||||
// Do nothing, leave t = t
|
|
||||||
}
|
|
||||||
else if( aLaw <= 0 )
|
|
||||||
{
|
|
||||||
t = 1 - t;
|
|
||||||
}
|
|
||||||
else if( aRatio <= aLaw )
|
|
||||||
{
|
|
||||||
t = t * aLaw;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
t = 1 - t * ( 1-aLaw );
|
|
||||||
}
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TUNER_SLIDER::TUNER_SLIDER( SIM_PLOT_FRAME* aFrame, wxWindow* aParent, SCH_SYMBOL* aSymbol ) :
|
TUNER_SLIDER::TUNER_SLIDER( SIM_PLOT_FRAME* aFrame, wxWindow* aParent, SCH_SYMBOL* aSymbol ) :
|
||||||
TUNER_SLIDER_BASE( aParent ),
|
TUNER_SLIDER_BASE( aParent ),
|
||||||
m_symbol( aSymbol ),
|
m_symbol( aSymbol ),
|
||||||
m_fieldId( MANDATORY_FIELD_T::VALUE_FIELD ),
|
|
||||||
m_min( 0.0 ),
|
m_min( 0.0 ),
|
||||||
m_max( 0.0 ),
|
m_max( 0.0 ),
|
||||||
m_value( 0.0 ),
|
m_value( 0.0 ),
|
||||||
m_changed( false ),
|
m_changed( false ),
|
||||||
m_frame ( aFrame )
|
m_frame ( aFrame )
|
||||||
{
|
{
|
||||||
// TODO.
|
wxString ref = aSymbol->GetField( REFERENCE_FIELD )->GetShownText();
|
||||||
|
m_item = aFrame->GetExporter()->FindItem( std::string( ref.ToUTF8() ) );
|
||||||
|
|
||||||
/*const wxString compName = aSymbol->GetField( REFERENCE_FIELD )->GetText();
|
if( !m_item )
|
||||||
m_name->SetLabel( compName );
|
throw KI_PARAM_ERROR( wxString::Format( _( "Could not find Spice item with reference '%s'" ),
|
||||||
m_spiceTuningCommand = aFrame->GetExporter()->GetSpiceTuningCommand( compName );
|
ref ) );
|
||||||
|
|
||||||
if( m_spiceTuningCommand.second )
|
m_name->SetLabel( ref );
|
||||||
|
|
||||||
|
|
||||||
|
const SIM_MODEL::PARAM* tunerParam = m_item->model->GetTunerParam();
|
||||||
|
|
||||||
|
if( !tunerParam )
|
||||||
|
throw KI_PARAM_ERROR( wxString::Format(
|
||||||
|
_( "Symbol '%s' has simulation model of type '%s %s', which cannot be tuned" ),
|
||||||
|
ref,
|
||||||
|
m_item->model->GetDeviceTypeInfo().fieldValue,
|
||||||
|
m_item->model->GetTypeInfo().fieldValue ) );
|
||||||
|
|
||||||
|
|
||||||
|
m_value = SPICE_VALUE( m_item->model->GetTunerParam()->value->ToSpiceString() );
|
||||||
|
|
||||||
|
// Special case for potentiometers because we don't have value ranges implemented yet.
|
||||||
|
if( m_item->model->GetType() == SIM_MODEL::TYPE::R_POT )
|
||||||
{
|
{
|
||||||
// model parameter, with fixed %-range and unknown initial value
|
m_min = SPICE_VALUE( 0 );
|
||||||
m_min = 0;
|
m_max = SPICE_VALUE( 1 );
|
||||||
m_max = 100;
|
|
||||||
m_value = ( m_max - m_min ) / 2.0; // midpoint
|
|
||||||
m_minText->Disable();
|
|
||||||
m_maxText->Disable();
|
|
||||||
m_saveBtn->Disable(); // not an instance parameter that could be updated (invalid m_fieldId)
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// instance parameter
|
m_min = SPICE_VALUE( 0.5 ) * m_value;
|
||||||
if( aSymbol->FindField( netlist_exporter_spice::GetSpiceFieldName( SF_MODEL ) ) )
|
m_max = SPICE_VALUE( 2.0 ) * m_value;
|
||||||
m_fieldId = aSymbol->FindField( netlist_exporter_spice::GetSpiceFieldName( SF_MODEL ) )->GetId();
|
|
||||||
else
|
|
||||||
m_fieldId = aSymbol->GetField( VALUE_FIELD )->GetId();
|
|
||||||
|
|
||||||
m_value = SPICE_VALUE( aSymbol->GetFieldById( m_fieldId )->GetText() );
|
|
||||||
m_min = SPICE_VALUE( 0.5 ) * m_value;
|
|
||||||
m_max = SPICE_VALUE( 2.0 ) * m_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call Set*() methods to update fields and slider
|
|
||||||
m_minText->SetValue( m_min.ToOrigString() );
|
m_minText->SetValue( m_min.ToOrigString() );
|
||||||
m_maxText->SetValue( m_max.ToOrigString() );
|
m_maxText->SetValue( m_max.ToOrigString() );
|
||||||
|
|
||||||
|
@ -226,7 +85,14 @@ TUNER_SLIDER::TUNER_SLIDER( SIM_PLOT_FRAME* aFrame, wxWindow* aParent, SCH_SYMBO
|
||||||
updateSlider();
|
updateSlider();
|
||||||
|
|
||||||
m_simTimer.SetOwner( this );
|
m_simTimer.SetOwner( this );
|
||||||
Connect( wxEVT_TIMER, wxTimerEventHandler( TUNER_SLIDER::onSimTimer ), nullptr, this );*/
|
Connect( wxEVT_TIMER, wxTimerEventHandler( TUNER_SLIDER::onSimTimer ), nullptr, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string TUNER_SLIDER::GetTunerCommand() const
|
||||||
|
{
|
||||||
|
return m_item->model->SpiceGenerator().TunerCommand( *m_item,
|
||||||
|
SIM_VALUE_FLOAT( m_value.ToDouble() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -292,13 +158,7 @@ void TUNER_SLIDER::updateComponentValue()
|
||||||
void TUNER_SLIDER::updateSlider()
|
void TUNER_SLIDER::updateSlider()
|
||||||
{
|
{
|
||||||
wxASSERT( m_max >= m_value && m_value >= m_min );
|
wxASSERT( m_max >= m_value && m_value >= m_min );
|
||||||
|
m_slider->SetValue( ( ( m_value - m_min ) / ( m_max - m_min ) ).ToDouble() * 100 );
|
||||||
int choice = m_curve->GetSelection();
|
|
||||||
wxCHECK( choice >= 0 && choice < arraysize( CURVES ), /*void*/ );
|
|
||||||
|
|
||||||
double ratio = ( ( m_value - m_min ) / ( m_max - m_min ) ).ToDouble();
|
|
||||||
double travel = taper( ratio, CURVES[choice].m_mid, CURVES[choice].m_law, true );
|
|
||||||
m_slider->SetValue( KiROUND( travel * 100.0 ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -363,18 +223,13 @@ void TUNER_SLIDER::onClose( wxCommandEvent& event )
|
||||||
|
|
||||||
void TUNER_SLIDER::onSave( wxCommandEvent& event )
|
void TUNER_SLIDER::onSave( wxCommandEvent& event )
|
||||||
{
|
{
|
||||||
m_frame->UpdateTunerValue( m_symbol, m_fieldId, m_value.ToOrigString() );
|
m_frame->UpdateTunerValue( m_symbol, m_value.ToOrigString() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TUNER_SLIDER::onSliderChanged( wxScrollEvent& event )
|
void TUNER_SLIDER::onSliderChanged( wxScrollEvent& event )
|
||||||
{
|
{
|
||||||
int choice = m_curve->GetSelection();
|
m_value = m_min + ( m_max - m_min ) * SPICE_VALUE( m_slider->GetValue() / 100.0 );
|
||||||
wxCHECK( choice >= 0 && choice < arraysize( CURVES ), /*void*/ );
|
|
||||||
|
|
||||||
double travel = m_slider->GetValue() / 100.0;
|
|
||||||
double ratio = taper( travel, CURVES[choice].m_mid, CURVES[choice].m_law, false );
|
|
||||||
m_value = m_min + ( m_max - m_min ) * SPICE_VALUE( ratio );
|
|
||||||
updateValueText();
|
updateValueText();
|
||||||
updateComponentValue();
|
updateComponentValue();
|
||||||
m_changed = true;
|
m_changed = true;
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "tuner_slider_base.h"
|
#include "tuner_slider_base.h"
|
||||||
|
|
||||||
#include <sim/spice_value.h>
|
#include <sim/spice_value.h>
|
||||||
|
#include <sim/spice_generator.h>
|
||||||
|
|
||||||
#include <wx/timer.h>
|
#include <wx/timer.h>
|
||||||
|
|
||||||
|
@ -49,16 +50,6 @@ public:
|
||||||
return m_name->GetLabel();
|
return m_name->GetLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::pair<wxString, bool>& GetSpiceTuningCommand() const
|
|
||||||
{
|
|
||||||
return m_spiceTuningCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SPICE_VALUE& GetValue() const
|
|
||||||
{
|
|
||||||
return m_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SPICE_VALUE& GetMin() const
|
const SPICE_VALUE& GetMin() const
|
||||||
{
|
{
|
||||||
return m_min;
|
return m_min;
|
||||||
|
@ -69,6 +60,8 @@ public:
|
||||||
return m_max;
|
return m_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GetTunerCommand() const;
|
||||||
|
|
||||||
bool SetValue( const SPICE_VALUE& aVal );
|
bool SetValue( const SPICE_VALUE& aVal );
|
||||||
bool SetMin( const SPICE_VALUE& aVal );
|
bool SetMin( const SPICE_VALUE& aVal );
|
||||||
bool SetMax( const SPICE_VALUE& aVal );
|
bool SetMax( const SPICE_VALUE& aVal );
|
||||||
|
@ -97,14 +90,12 @@ private:
|
||||||
|
|
||||||
void onSimTimer( wxTimerEvent& event );
|
void onSimTimer( wxTimerEvent& event );
|
||||||
|
|
||||||
std::pair<wxString, bool> m_spiceTuningCommand;
|
|
||||||
|
|
||||||
///< Timer that restarts the simulation after the slider value has changed
|
///< Timer that restarts the simulation after the slider value has changed
|
||||||
wxTimer m_simTimer;
|
wxTimer m_simTimer;
|
||||||
|
|
||||||
SCH_SYMBOL* m_symbol;
|
SCH_SYMBOL* m_symbol;
|
||||||
|
const SPICE_ITEM* m_item;
|
||||||
|
|
||||||
int m_fieldId;
|
|
||||||
SPICE_VALUE m_min, m_max, m_value;
|
SPICE_VALUE m_min, m_max, m_value;
|
||||||
bool m_changed;
|
bool m_changed;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue