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 <functional>
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
|
||||
// If BENCHMARK is defined, calculations will print their execution time to the STDERR
|
||||
// #define BENCHMARK
|
||||
@ -50,7 +51,10 @@ class SolutionCollector
|
||||
*/
|
||||
{
|
||||
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.
|
||||
@ -59,50 +63,82 @@ public:
|
||||
* @param aValueFunc transforms value from aResults into final value of the combination
|
||||
* @param aResultFunc transforms RESISTANCE instance from aResults into final instance
|
||||
*/
|
||||
void Add2RLookupResults( std::pair<RESISTANCE&, RESISTANCE&> aResults,
|
||||
std::function<double( double )> aValueFunc,
|
||||
void Add2RLookupResults( std::pair<RESISTANCE&, RESISTANCE&> aResults, std::function<double( double )> aValueFunc,
|
||||
std::function<RESISTANCE( RESISTANCE& )> aResultFunc )
|
||||
{
|
||||
addSolution( aValueFunc( aResults.first.value ), &aResults.first, aResultFunc );
|
||||
addSolution( aValueFunc( aResults.second.value ), &aResults.second, aResultFunc );
|
||||
considerSolution( aValueFunc( aResults.first.value ), aResults.first, 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()
|
||||
{
|
||||
if( !m_best_found_resistance )
|
||||
if( !m_best_solution )
|
||||
throw std::logic_error( "Empty solution collector" );
|
||||
|
||||
return m_best_result_func( *m_best_found_resistance );
|
||||
return *m_best_solution;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* 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 )
|
||||
void considerSolution( double aValue, RESISTANCE& aFound, std::function<RESISTANCE( RESISTANCE& )>& aResultFunc )
|
||||
{
|
||||
double deviation = std::abs( aValue - m_target );
|
||||
if( deviation < m_best_deviation )
|
||||
|
||||
if( deviation + epsilon < m_best_deviation )
|
||||
{
|
||||
m_best_deviation = deviation;
|
||||
m_best_found_resistance = aFound;
|
||||
m_best_result_func = aResultFunc;
|
||||
m_best_solution = aResultFunc( aFound );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
static int uniqueCount( const RESISTANCE& aRes )
|
||||
{
|
||||
std::vector<double> parts = aRes.parts;
|
||||
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;
|
||||
RESISTANCE* m_best_found_resistance = nullptr;
|
||||
std::function<RESISTANCE( RESISTANCE& )> m_best_result_func;
|
||||
std::optional<RESISTANCE> m_best_solution;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -152,25 +188,33 @@ static inline double parallelValue( double aR1, double aR2 )
|
||||
static inline RESISTANCE serialResistance( const RESISTANCE& aR1, const RESISTANCE& aR2 )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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).
|
||||
@ -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 )
|
||||
return { m_buffer_2R.front(), m_buffer_2R.back() };
|
||||
|
||||
auto it = std::lower_bound( m_buffer_2R.begin(), m_buffer_2R.end(), aTarget )
|
||||
- m_buffer_2R.begin();
|
||||
auto it = std::lower_bound( m_buffer_2R.begin(), m_buffer_2R.end(), aTarget ) - m_buffer_2R.begin();
|
||||
|
||||
if( it == 0 )
|
||||
return { m_buffer_2R[0], m_buffer_2R[0] };
|
||||
@ -380,8 +423,7 @@ RESISTANCE RES_EQUIV_CALC::calculate3RSolution()
|
||||
{
|
||||
return serialResistance( aFoundRes, r );
|
||||
};
|
||||
solution.Add2RLookupResults( findIn2RBuffer( m_target - r.value ), valueFunc,
|
||||
resultFunc );
|
||||
solution.Add2RLookupResults( findIn2RBuffer( m_target - r.value ), valueFunc, resultFunc );
|
||||
}
|
||||
|
||||
// try r | 2R combination
|
||||
@ -394,8 +436,7 @@ RESISTANCE RES_EQUIV_CALC::calculate3RSolution()
|
||||
{
|
||||
return parallelResistance( aFoundRes, r );
|
||||
};
|
||||
solution.Add2RLookupResults(
|
||||
findIn2RBuffer( m_target * r.value / ( r.value - m_target ) ), valueFunc,
|
||||
solution.Add2RLookupResults( findIn2RBuffer( m_target * r.value / ( r.value - m_target ) ), valueFunc,
|
||||
resultFunc );
|
||||
}
|
||||
}
|
||||
@ -419,8 +460,7 @@ RESISTANCE RES_EQUIV_CALC::calculate4RSolution()
|
||||
{
|
||||
return serialResistance( aFoundRes, rr );
|
||||
};
|
||||
solution.Add2RLookupResults( findIn2RBuffer( m_target - rr.value ), valueFunc,
|
||||
resultFunc );
|
||||
solution.Add2RLookupResults( findIn2RBuffer( m_target - rr.value ), valueFunc, resultFunc );
|
||||
}
|
||||
|
||||
// try 2R | 2R combination
|
||||
@ -433,8 +473,7 @@ RESISTANCE RES_EQUIV_CALC::calculate4RSolution()
|
||||
{
|
||||
return parallelResistance( aFoundRes, rr );
|
||||
};
|
||||
solution.Add2RLookupResults(
|
||||
findIn2RBuffer( m_target * rr.value / ( rr.value - m_target ) ), valueFunc,
|
||||
solution.Add2RLookupResults( findIn2RBuffer( m_target * rr.value / ( rr.value - m_target ) ), valueFunc,
|
||||
resultFunc );
|
||||
}
|
||||
}
|
||||
@ -453,8 +492,8 @@ RESISTANCE RES_EQUIV_CALC::calculate4RSolution()
|
||||
{
|
||||
return serialResistance( r1, parallelResistance( r2, aFoundRes ) );
|
||||
};
|
||||
solution.Add2RLookupResults( findIn2RBuffer( ( m_target - r1.value ) * r2.value
|
||||
/ ( r1.value + r2.value - m_target ) ),
|
||||
solution.Add2RLookupResults(
|
||||
findIn2RBuffer( ( m_target - r1.value ) * r2.value / ( r1.value + r2.value - m_target ) ),
|
||||
valueFunc, resultFunc );
|
||||
}
|
||||
|
||||
@ -468,8 +507,7 @@ RESISTANCE RES_EQUIV_CALC::calculate4RSolution()
|
||||
{
|
||||
return parallelResistance( r1, serialResistance( r2, aFoundRes ) );
|
||||
};
|
||||
solution.Add2RLookupResults(
|
||||
findIn2RBuffer( m_target * r1.value / ( r1.value - m_target ) - r2.value ),
|
||||
solution.Add2RLookupResults( findIn2RBuffer( m_target * r1.value / ( r1.value - m_target ) - r2.value ),
|
||||
valueFunc, resultFunc );
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,16 @@ struct RESISTANCE
|
||||
{
|
||||
double value;
|
||||
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.
|
||||
* Empty std::optional means that the exact value can be achieved using fewer resistors.
|
||||
*/
|
||||
const std::array<std::optional<RESISTANCE>, NUMBER_OF_LEVELS>& GetResults()
|
||||
{
|
||||
return m_results;
|
||||
}
|
||||
const std::array<std::optional<RESISTANCE>, NUMBER_OF_LEVELS>& GetResults() { return m_results; }
|
||||
|
||||
private:
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user