mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 10:13:19 +02:00
Resistor calculator should minimize parts
If we can get a equivalent value match using the same parts, prefer that. Fixes https://gitlab.com/kicad/code/kicad/-/issues/7100
This commit is contained in:
parent
77b99968ed
commit
aa4bba3c94
@ -23,6 +23,7 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
// If BENCHMARK is defined, calculations will print their execution time to the STDERR
|
// If BENCHMARK is defined, calculations will print their execution time to the STDERR
|
||||||
// #define BENCHMARK
|
// #define BENCHMARK
|
||||||
@ -50,7 +51,10 @@ class SolutionCollector
|
|||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SolutionCollector( double aTarget ) : m_target( aTarget ) {}
|
SolutionCollector( double aTarget ) :
|
||||||
|
m_target( aTarget )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add two solutions, based on single 2R buffer lookup, to the collector.
|
* Add two solutions, based on single 2R buffer lookup, to the collector.
|
||||||
@ -59,50 +63,82 @@ public:
|
|||||||
* @param aValueFunc transforms value from aResults into final value of the combination
|
* @param aValueFunc transforms value from aResults into final value of the combination
|
||||||
* @param aResultFunc transforms RESISTANCE instance from aResults into final instance
|
* @param aResultFunc transforms RESISTANCE instance from aResults into final instance
|
||||||
*/
|
*/
|
||||||
void Add2RLookupResults( std::pair<RESISTANCE&, RESISTANCE&> aResults,
|
void Add2RLookupResults( std::pair<RESISTANCE&, RESISTANCE&> aResults, std::function<double( double )> aValueFunc,
|
||||||
std::function<double( double )> aValueFunc,
|
|
||||||
std::function<RESISTANCE( RESISTANCE& )> aResultFunc )
|
std::function<RESISTANCE( RESISTANCE& )> aResultFunc )
|
||||||
{
|
{
|
||||||
addSolution( aValueFunc( aResults.first.value ), &aResults.first, aResultFunc );
|
considerSolution( aValueFunc( aResults.first.value ), aResults.first, aResultFunc );
|
||||||
addSolution( aValueFunc( aResults.second.value ), &aResults.second, aResultFunc );
|
considerSolution( aValueFunc( aResults.second.value ), aResults.second, aResultFunc );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the best collected combination, running the corresponding result_func.
|
* Return the best collected combination.
|
||||||
*/
|
*/
|
||||||
RESISTANCE GetBest()
|
RESISTANCE GetBest()
|
||||||
{
|
{
|
||||||
if( !m_best_found_resistance )
|
if( !m_best_solution )
|
||||||
throw std::logic_error( "Empty solution collector" );
|
throw std::logic_error( "Empty solution collector" );
|
||||||
|
|
||||||
return m_best_result_func( *m_best_found_resistance );
|
return *m_best_solution;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
void considerSolution( double aValue, RESISTANCE& aFound, std::function<RESISTANCE( RESISTANCE& )>& aResultFunc )
|
||||||
* Add single solution to the collector.
|
|
||||||
*
|
|
||||||
* @param aValue is a value of the combination in Ohms
|
|
||||||
* @param aFound is the corresponding RESISTANCE found in 2R buffer
|
|
||||||
* @param aResultFunc is a function calculating final result (RESISTANCE instance)
|
|
||||||
* for this combination
|
|
||||||
*/
|
|
||||||
void addSolution( double aValue, RESISTANCE* aFound,
|
|
||||||
std::function<RESISTANCE( RESISTANCE& )>& aResultFunc )
|
|
||||||
{
|
{
|
||||||
double deviation = std::abs( aValue - m_target );
|
double deviation = std::abs( aValue - m_target );
|
||||||
if( deviation < m_best_deviation )
|
|
||||||
|
if( deviation + epsilon < m_best_deviation )
|
||||||
{
|
{
|
||||||
m_best_deviation = deviation;
|
m_best_deviation = deviation;
|
||||||
m_best_found_resistance = aFound;
|
m_best_solution = aResultFunc( aFound );
|
||||||
m_best_result_func = aResultFunc;
|
}
|
||||||
|
else if( std::abs( deviation - m_best_deviation ) < epsilon )
|
||||||
|
{
|
||||||
|
RESISTANCE candidate = aResultFunc( aFound );
|
||||||
|
|
||||||
|
if( !m_best_solution || betterCandidate( candidate, *m_best_solution ) )
|
||||||
|
m_best_solution = std::move( candidate );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double m_target;
|
static int uniqueCount( const RESISTANCE& aRes )
|
||||||
double m_best_deviation = INFINITY;
|
{
|
||||||
RESISTANCE* m_best_found_resistance = nullptr;
|
std::vector<double> parts = aRes.parts;
|
||||||
std::function<RESISTANCE( RESISTANCE& )> m_best_result_func;
|
std::sort( parts.begin(), parts.end() );
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
double last = 0.0;
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
for( double v : parts )
|
||||||
|
{
|
||||||
|
if( first || std::abs( v - last ) > epsilon )
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
last = v;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool betterCandidate( const RESISTANCE& aCand, const RESISTANCE& aBest )
|
||||||
|
{
|
||||||
|
int candUnique = uniqueCount( aCand );
|
||||||
|
int bestUnique = uniqueCount( aBest );
|
||||||
|
|
||||||
|
if( candUnique != bestUnique )
|
||||||
|
return candUnique < bestUnique;
|
||||||
|
|
||||||
|
if( aCand.parts.size() != aBest.parts.size() )
|
||||||
|
return aCand.parts.size() < aBest.parts.size();
|
||||||
|
|
||||||
|
return aCand.name < aBest.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
double m_target;
|
||||||
|
double m_best_deviation = INFINITY;
|
||||||
|
std::optional<RESISTANCE> m_best_solution;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,26 +187,34 @@ static inline double parallelValue( double aR1, double aR2 )
|
|||||||
|
|
||||||
static inline RESISTANCE serialResistance( const RESISTANCE& aR1, const RESISTANCE& aR2 )
|
static inline RESISTANCE serialResistance( const RESISTANCE& aR1, const RESISTANCE& aR2 )
|
||||||
{
|
{
|
||||||
std::string name = maybeEmbrace( aR1.name, '|' ) + " + " + maybeEmbrace( aR2.name, '|' );
|
std::string name = maybeEmbrace( aR1.name, '|' ) + " + " + maybeEmbrace( aR2.name, '|' );
|
||||||
return RESISTANCE( serialValue( aR1.value, aR2.value ), name );
|
std::vector<double> parts = aR1.parts;
|
||||||
|
parts.insert( parts.end(), aR2.parts.begin(), aR2.parts.end() );
|
||||||
|
return RESISTANCE( serialValue( aR1.value, aR2.value ), name, parts );
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline RESISTANCE parallelResistance( const RESISTANCE& aR1, const RESISTANCE& aR2 )
|
static inline RESISTANCE parallelResistance( const RESISTANCE& aR1, const RESISTANCE& aR2 )
|
||||||
{
|
{
|
||||||
std::string name = maybeEmbrace( aR1.name, '+' ) + " | " + maybeEmbrace( aR2.name, '+' );
|
std::string name = maybeEmbrace( aR1.name, '+' ) + " | " + maybeEmbrace( aR2.name, '+' );
|
||||||
return RESISTANCE( parallelValue( aR1.value, aR2.value ), name );
|
std::vector<double> parts = aR1.parts;
|
||||||
|
parts.insert( parts.end(), aR2.parts.begin(), aR2.parts.end() );
|
||||||
|
return RESISTANCE( parallelValue( aR1.value, aR2.value ), name, parts );
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline RESISTANCE serialResistanceSimple( const RESISTANCE& aR1, const RESISTANCE& aR2 )
|
static inline RESISTANCE serialResistanceSimple( const RESISTANCE& aR1, const RESISTANCE& aR2 )
|
||||||
{
|
{
|
||||||
std::string name = aR1.name + " + " + aR2.name;
|
std::string name = aR1.name + " + " + aR2.name;
|
||||||
return RESISTANCE( serialValue( aR1.value, aR2.value ), name );
|
std::vector<double> parts = aR1.parts;
|
||||||
|
parts.insert( parts.end(), aR2.parts.begin(), aR2.parts.end() );
|
||||||
|
return RESISTANCE( serialValue( aR1.value, aR2.value ), name, parts );
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline RESISTANCE parallelResistanceSimple( const RESISTANCE& aR1, const RESISTANCE& aR2 )
|
static inline RESISTANCE parallelResistanceSimple( const RESISTANCE& aR1, const RESISTANCE& aR2 )
|
||||||
{
|
{
|
||||||
std::string name = aR1.name + " | " + aR2.name;
|
std::string name = aR1.name + " | " + aR2.name;
|
||||||
return RESISTANCE( parallelValue( aR1.value, aR2.value ), name );
|
std::vector<double> parts = aR1.parts;
|
||||||
|
parts.insert( parts.end(), aR2.parts.begin(), aR2.parts.end() );
|
||||||
|
return RESISTANCE( parallelValue( aR1.value, aR2.value ), name, parts );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a string from aValue (aValue is expected in ohms).
|
// Return a string from aValue (aValue is expected in ohms).
|
||||||
@ -247,7 +291,7 @@ void RES_EQUIV_CALC::Exclude( double aValue )
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
std::vector<RESISTANCE>& series = m_e_series[m_series];
|
std::vector<RESISTANCE>& series = m_e_series[m_series];
|
||||||
auto it = std::lower_bound( series.begin(), series.end(), aValue - epsilon );
|
auto it = std::lower_bound( series.begin(), series.end(), aValue - epsilon );
|
||||||
|
|
||||||
if( it != series.end() && std::abs( it->value - aValue ) < epsilon )
|
if( it != series.end() && std::abs( it->value - aValue ) < epsilon )
|
||||||
m_exclude_mask[it - series.begin()] = true;
|
m_exclude_mask[it - series.begin()] = true;
|
||||||
@ -336,8 +380,7 @@ std::pair<RESISTANCE&, RESISTANCE&> RES_EQUIV_CALC::findIn2RBuffer( double aTarg
|
|||||||
if( aTarget <= m_buffer_2R.front().value || aTarget >= m_buffer_2R.back().value )
|
if( aTarget <= m_buffer_2R.front().value || aTarget >= m_buffer_2R.back().value )
|
||||||
return { m_buffer_2R.front(), m_buffer_2R.back() };
|
return { m_buffer_2R.front(), m_buffer_2R.back() };
|
||||||
|
|
||||||
auto it = std::lower_bound( m_buffer_2R.begin(), m_buffer_2R.end(), aTarget )
|
auto it = std::lower_bound( m_buffer_2R.begin(), m_buffer_2R.end(), aTarget ) - m_buffer_2R.begin();
|
||||||
- m_buffer_2R.begin();
|
|
||||||
|
|
||||||
if( it == 0 )
|
if( it == 0 )
|
||||||
return { m_buffer_2R[0], m_buffer_2R[0] };
|
return { m_buffer_2R[0], m_buffer_2R[0] };
|
||||||
@ -380,8 +423,7 @@ RESISTANCE RES_EQUIV_CALC::calculate3RSolution()
|
|||||||
{
|
{
|
||||||
return serialResistance( aFoundRes, r );
|
return serialResistance( aFoundRes, r );
|
||||||
};
|
};
|
||||||
solution.Add2RLookupResults( findIn2RBuffer( m_target - r.value ), valueFunc,
|
solution.Add2RLookupResults( findIn2RBuffer( m_target - r.value ), valueFunc, resultFunc );
|
||||||
resultFunc );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// try r | 2R combination
|
// try r | 2R combination
|
||||||
@ -394,9 +436,8 @@ RESISTANCE RES_EQUIV_CALC::calculate3RSolution()
|
|||||||
{
|
{
|
||||||
return parallelResistance( aFoundRes, r );
|
return parallelResistance( aFoundRes, r );
|
||||||
};
|
};
|
||||||
solution.Add2RLookupResults(
|
solution.Add2RLookupResults( findIn2RBuffer( m_target * r.value / ( r.value - m_target ) ), valueFunc,
|
||||||
findIn2RBuffer( m_target * r.value / ( r.value - m_target ) ), valueFunc,
|
resultFunc );
|
||||||
resultFunc );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,8 +460,7 @@ RESISTANCE RES_EQUIV_CALC::calculate4RSolution()
|
|||||||
{
|
{
|
||||||
return serialResistance( aFoundRes, rr );
|
return serialResistance( aFoundRes, rr );
|
||||||
};
|
};
|
||||||
solution.Add2RLookupResults( findIn2RBuffer( m_target - rr.value ), valueFunc,
|
solution.Add2RLookupResults( findIn2RBuffer( m_target - rr.value ), valueFunc, resultFunc );
|
||||||
resultFunc );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// try 2R | 2R combination
|
// try 2R | 2R combination
|
||||||
@ -433,9 +473,8 @@ RESISTANCE RES_EQUIV_CALC::calculate4RSolution()
|
|||||||
{
|
{
|
||||||
return parallelResistance( aFoundRes, rr );
|
return parallelResistance( aFoundRes, rr );
|
||||||
};
|
};
|
||||||
solution.Add2RLookupResults(
|
solution.Add2RLookupResults( findIn2RBuffer( m_target * rr.value / ( rr.value - m_target ) ), valueFunc,
|
||||||
findIn2RBuffer( m_target * rr.value / ( rr.value - m_target ) ), valueFunc,
|
resultFunc );
|
||||||
resultFunc );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,9 +492,9 @@ RESISTANCE RES_EQUIV_CALC::calculate4RSolution()
|
|||||||
{
|
{
|
||||||
return serialResistance( r1, parallelResistance( r2, aFoundRes ) );
|
return serialResistance( r1, parallelResistance( r2, aFoundRes ) );
|
||||||
};
|
};
|
||||||
solution.Add2RLookupResults( findIn2RBuffer( ( m_target - r1.value ) * r2.value
|
solution.Add2RLookupResults(
|
||||||
/ ( r1.value + r2.value - m_target ) ),
|
findIn2RBuffer( ( m_target - r1.value ) * r2.value / ( r1.value + r2.value - m_target ) ),
|
||||||
valueFunc, resultFunc );
|
valueFunc, resultFunc );
|
||||||
}
|
}
|
||||||
|
|
||||||
// try r1 | (r2 + 2R)
|
// try r1 | (r2 + 2R)
|
||||||
@ -468,9 +507,8 @@ RESISTANCE RES_EQUIV_CALC::calculate4RSolution()
|
|||||||
{
|
{
|
||||||
return parallelResistance( r1, serialResistance( r2, aFoundRes ) );
|
return parallelResistance( r1, serialResistance( r2, aFoundRes ) );
|
||||||
};
|
};
|
||||||
solution.Add2RLookupResults(
|
solution.Add2RLookupResults( findIn2RBuffer( m_target * r1.value / ( r1.value - m_target ) - r2.value ),
|
||||||
findIn2RBuffer( m_target * r1.value / ( r1.value - m_target ) - r2.value ),
|
valueFunc, resultFunc );
|
||||||
valueFunc, resultFunc );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,10 +44,18 @@ const double epsilon = 1e-12; // machine epsilon for floating-point equality tes
|
|||||||
// Struct representing resistance value together with its composition, e.g. {20.0, "10R + 10R"}
|
// Struct representing resistance value together with its composition, e.g. {20.0, "10R + 10R"}
|
||||||
struct RESISTANCE
|
struct RESISTANCE
|
||||||
{
|
{
|
||||||
double value;
|
double value;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::vector<double> parts;
|
||||||
|
|
||||||
RESISTANCE( double aValue, const std::string& aName ) : value( aValue ), name( aName ) {}
|
RESISTANCE( double aValue, const std::string& aName, std::vector<double> aParts = {} ) :
|
||||||
|
value( aValue ),
|
||||||
|
name( aName ),
|
||||||
|
parts( std::move( aParts ) )
|
||||||
|
{
|
||||||
|
if( parts.empty() )
|
||||||
|
parts.push_back( aValue );
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -109,10 +117,7 @@ public:
|
|||||||
* Accessor to calculation results.
|
* Accessor to calculation results.
|
||||||
* Empty std::optional means that the exact value can be achieved using fewer resistors.
|
* Empty std::optional means that the exact value can be achieved using fewer resistors.
|
||||||
*/
|
*/
|
||||||
const std::array<std::optional<RESISTANCE>, NUMBER_OF_LEVELS>& GetResults()
|
const std::array<std::optional<RESISTANCE>, NUMBER_OF_LEVELS>& GetResults() { return m_results; }
|
||||||
{
|
|
||||||
return m_results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user