diff --git a/common/eseries.cpp b/common/eseries.cpp index 20abc5b840..4959e5ad65 100644 --- a/common/eseries.cpp +++ b/common/eseries.cpp @@ -2,8 +2,7 @@ * This program source code file * is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2020 - * Copyright (C) 2021-2023 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 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 @@ -19,369 +18,85 @@ * with this program. If not, see . */ -#include -#include -#include +#include #include "eseries.h" -/* - * If BENCHMARK is defined, any 4R E12 calculations will print its execution time to console - * My Hasswell Enthusiast reports 225 mSec what are reproducible within plusminus 2 percent - */ -//#define BENCHMARK - -#ifdef BENCHMARK -#include -#endif - - -// Return a string from aValue (aValue is expected in ohms) -// If aValue < 1000 the returned string is aValue with unit = R -// If aValue >= 1000 the returned string is aValue/1000 with unit = K -// with notation similar to 2K2 -// If aValue >= 1e6 the returned string is aValue/1e6 with unit = M -// with notation = 1M -static std::string strValue( double aValue ) +namespace ESERIES { - std::string result; - if( aValue < 1000.0 ) +const std::vector ESERIES_VALUES::s_e24table = { + 100, 110, 120, 130, 150, 160, 180, 200, 220, 240, 270, 300, + 330, 360, 390, 430, 470, 510, 560, 620, 680, 750, 820, 910 +}; + +const std::vector ESERIES_VALUES::s_e192table = { + 100, 101, 102, 104, 105, 106, 107, 109, 110, 111, 113, 114, 115, 117, 118, 120, 121, 123, + 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 142, 143, 145, 147, 149, 150, 152, + 154, 156, 158, 160, 162, 164, 165, 167, 169, 172, 174, 176, 178, 180, 182, 184, 187, 189, + 191, 193, 196, 198, 200, 203, 205, 208, 210, 213, 215, 218, 221, 223, 226, 229, 232, 234, + 237, 240, 243, 246, 249, 252, 255, 258, 261, 264, 267, 271, 274, 277, 280, 284, 287, 291, + 294, 298, 301, 305, 309, 312, 316, 320, 324, 328, 332, 336, 340, 344, 348, 352, 357, 361, + 365, 370, 374, 379, 383, 388, 392, 397, 402, 407, 412, 417, 422, 427, 432, 437, 442, 448, + 453, 459, 464, 470, 475, 481, 487, 493, 499, 505, 511, 517, 523, 530, 536, 542, 549, 556, + 562, 569, 576, 583, 590, 597, 604, 612, 619, 626, 634, 642, 649, 657, 665, 673, 681, 690, + 698, 706, 715, 723, 732, 741, 750, 759, 768, 777, 787, 796, 806, 816, 825, 835, 845, 856, + 866, 876, 887, 898, 909, 920, 931, 942, 953, 965, 976, 988 +}; + + +ESERIES_VALUES::ESERIES_VALUES( int aESeries ) +{ + const std::vector* baseSeries = &s_e24table; + unsigned int baseSeriesSkipValue = 24; + + if( ESERIES::E1 == aESeries || ESERIES::E3 == aESeries || ESERIES::E6 == aESeries + || ESERIES::E12 == aESeries || ESERIES::E24 == aESeries ) { - result = std::to_string( static_cast( aValue ) ); - result += 'R'; + // The below table depends on the values and order of entries + // in the E1,E3, etc. enum in eseries.h + const unsigned int skipTableE124[] = { 24, 8, 4, 2, 1 }; + + baseSeries = &ESERIES_VALUES::s_e24table; + baseSeriesSkipValue = skipTableE124[aESeries]; } - else + else if( ESERIES::E48 == aESeries || ESERIES::E96 == aESeries || ESERIES::E192 == aESeries ) { - double div = 1e3; - char unit = 'K'; + baseSeries = &ESERIES_VALUES::s_e192table; - if( aValue >= 1e6 ) - { - div = 1e6; - unit = 'M'; - } - - aValue /= div; - - int integer = static_cast( aValue ); - result = std::to_string(integer); - result += unit; - - // Add mantissa: 1 digit, suitable for series up to E24 - double mantissa = aValue - integer; - - if( mantissa > 0 ) - result += std::to_string( static_cast( (mantissa*10)+0.5 ) ); + // The below calculation depends on the values and order of entries + // in the E1,E3, etc. enum in eseries.h + baseSeriesSkipValue = 1 << ( ESERIES::E192 - aESeries ); } - return result; -} + unsigned int decadeBaseLen = baseSeries->size(); + reserve( decadeBaseLen / baseSeriesSkipValue ); -E_SERIES::E_SERIES() -{ - // Build the list of available resistor values in each En serie - double listValuesE1[] = { E1_VALUES }; - double listValuesE3[] = { E3_VALUES }; - double listValuesE6[] = { E6_VALUES }; - double listValuesE12[] = { E12_VALUES }; - double listValuesE24[] = { E24_VALUES }; - // buildSeriesData must be called in the order of En series, because - // the list of series is expected indexed by En for the serie En - buildSeriesData( listValuesE1 ); - buildSeriesData( listValuesE3 ); - buildSeriesData( listValuesE6 ); - buildSeriesData( listValuesE12 ); - int count = buildSeriesData( listValuesE24 ); - - // Reserve a buffer for intermediate calculations: - // the buffer size is 2*count*count to store all combinaisons of 2 values - // there are 2*count*count = 29282 combinations for E24 - int bufsize = 2*count*count; - m_combined_table.reserve( bufsize ); - - // Store predefined R_DATA items. - for( int ii = 0; ii < bufsize; ii++ ) - m_combined_table.emplace_back( "", 0.0 ); -} - - -int E_SERIES::buildSeriesData( const double aList[] ) -{ - double curr_decade = FIRST_VALUE; - int count = 0; - - std::vector curr_list; - - for( ; ; ) + for( unsigned int idx = 0; idx < decadeBaseLen; idx += baseSeriesSkipValue ) { - double curr_r = LAST_VALUE; - - for( int ii = 0; ; ii++ ) - { - if( aList[ii] == 0.0 ) // End of list - break; - - curr_r = curr_decade * aList[ii]; - curr_list.emplace_back( strValue( curr_r ), curr_r ); - count++; - - if( curr_r >= LAST_VALUE ) - break; - } - - if( curr_r >= LAST_VALUE ) - break; - - curr_decade *= 10; + emplace_back( ( *baseSeries )[idx] ); } - m_tables.push_back( std::move( curr_list ) ); - - return count; + shrink_to_fit(); } -void E_SERIES::Exclude( double aValue ) +ESERIES_IN_DECADE::ESERIES_IN_DECADE( int aESeries, int aDecadeExponent ) { - if( aValue != 0.0 ) // if there is a value to exclude other than a wire jumper + ESERIES::ESERIES_VALUES seriesValues( aESeries ); + + uint16_t decadeBase = seriesValues[0]; + unsigned int decadeBaseLen = seriesValues.size(); + double decadeMultiplier = std::pow( 10, aDecadeExponent ); + + reserve( decadeBaseLen ); + + for( const uint16_t seriesValue : seriesValues ) { - for( R_DATA& i : m_tables[m_series] ) // then search it in the selected E-Series table - { - if( i.e_value == aValue ) // if the value to exclude is found - i.e_use = false; // disable its use - } - } -} - - -void E_SERIES::simple_solution( uint32_t aSize ) -{ - uint32_t i; - - m_results.at( S2R ).e_value = std::numeric_limits::max(); // assume no 2R solution or max deviation - - for( i = 0; i < aSize; i++ ) - { - if( std::abs( m_combined_table.at( i ).e_value - m_required_value ) < std::abs( m_results.at( S2R ).e_value ) ) - { - m_results[S2R].e_value = m_combined_table[ i ].e_value - m_required_value; // save signed deviation in Ohms - m_results[S2R].e_name = m_combined_table[ i ].e_name; // save combination text - m_results[S2R].e_use = true; // this is a possible solution - } - } -} - - -void E_SERIES::combine4( uint32_t aSize ) -{ - uint32_t i,j; - double tmp; - - m_results[S4R].e_use = false; // disable 4R solution, until - m_results[S4R].e_value = m_results[S3R].e_value; // 4R becomes better than 3R solution - -#ifdef BENCHMARK - PROF_TIMER timer; // start timer to count execution time -#endif - - for( i = 0; i < aSize; i++ ) // 4R search outer loop - { // scan valid intermediate 2R solutions - for( j = 0; j < aSize; j++ ) // inner loop combines all with itself - { - tmp = m_combined_table[i].e_value + m_combined_table[j].e_value; // calculate 2R+2R serial - tmp -= m_required_value; // calculate 4R deviation - - if( std::abs( tmp ) < std::abs( m_results.at(S4R).e_value ) ) // if new 4R is better - { - m_results[S4R].e_value = tmp; // save amount of benefit - std::string s = "( "; - s.append( m_combined_table[i].e_name ); // mention 1st 2 component - s.append( " ) + ( " ); // in series - s.append( m_combined_table[j].e_name ); // with 2nd 2 components - s.append( " )" ); - m_results[S4R].e_name = s; // save the result and - m_results[S4R].e_use = true; // enable for later use - } - - tmp = ( m_combined_table[i].e_value * m_combined_table[j].e_value ) / - ( m_combined_table[i].e_value + m_combined_table[j].e_value ); // calculate 2R|2R parallel - tmp -= m_required_value; // calculate 4R deviation - - if( std::abs( tmp ) < std::abs( m_results[S4R].e_value ) ) // if new 4R is better - { - m_results[S4R].e_value = tmp; // save amount of benefit - std::string s = "( "; - s.append( m_combined_table[i].e_name ); // mention 1st 2 component - s.append( " ) | ( " ); // in parallel - s.append( m_combined_table[j].e_name ); // with 2nd 2 components - s.append( " )" ); - m_results[S4R].e_name = s; // save the result - m_results[S4R].e_use = true; // enable later use - } - } + emplace_back( decadeMultiplier * seriesValue / decadeBase ); } -#ifdef BENCHMARK - printf( "Calculation time = %d mS", timer.msecs() ); - fflush( 0 ); -#endif + shrink_to_fit(); } - -void E_SERIES::NewCalc() -{ - for( R_DATA& i : m_combined_table ) - i.e_use = false; // before any calculation is done, assume that - - for( R_DATA& i : m_results ) - i.e_use = false; // no combinations and no results are available - - for( R_DATA& i : m_tables[m_series]) - i.e_use = true; // all selected E-values available -} - - -uint32_t E_SERIES::combine2() -{ - uint32_t combi2R = 0; // target index counts calculated 2R combinations - std::string s; - - for( const R_DATA& i : m_tables[m_series] ) // outer loop to sweep selected source lookup table - { - if( i.e_use ) - { - for( const R_DATA& j : m_tables[m_series] ) // inner loop to combine values with itself - { - if( j.e_use ) - { - m_combined_table[combi2R].e_use = true; - m_combined_table[combi2R].e_value = i.e_value + j.e_value; // calculate 2R serial - s = i.e_name; - s.append( " + " ); - m_combined_table[combi2R].e_name = s.append( j.e_name); - combi2R++; // next destination - m_combined_table[combi2R].e_use = true; // calculate 2R parallel - m_combined_table[combi2R].e_value = i.e_value * j.e_value / ( i.e_value + j.e_value ); - s = i.e_name; - s.append( " | " ); - m_combined_table[combi2R].e_name = s.append( j.e_name ); - combi2R++; // next destination - } - } - } - } - return combi2R; -} - - -void E_SERIES::combine3( uint32_t aSize ) -{ - uint32_t j = 0; - double tmp = 0; // avoid warning for being uninitialized - std::string s; - - m_results[S3R].e_use = false; // disable 3R solution, until 3R - m_results[S3R].e_value = m_results[S2R].e_value; // becomes better than 2R solution - - for( const R_DATA& i : m_tables[m_series] ) // 3R Outer loop to selected primary E series table - { - if( i.e_use ) // skip all excluded values - { - for( j = 0; j < aSize; j++ ) // inner loop combines with all 2R intermediate - { // results R+2R serial combi - tmp = m_combined_table[j].e_value + i.e_value; - tmp -= m_required_value; // calculate deviation - - if( std::abs( tmp ) < std::abs( m_results[S3R].e_value ) ) // compare if better - { // then take it - s = i.e_name; // mention 3rd component - s.append( " + ( " ); // in series - s.append( m_combined_table[j].e_name ); // with 2R combination - s.append( " )" ); - m_results[S3R].e_name = s; // save S3R result - m_results[S3R].e_value = tmp; // save amount of benefit - m_results[S3R].e_use = true; // enable later use - } - - tmp = i.e_value * m_combined_table[j].e_value / - ( i.e_value + m_combined_table[j].e_value ); // calculate R + 2R parallel - tmp -= m_required_value; // calculate deviation - - if( std::abs( tmp ) < std::abs( m_results[S3R].e_value ) ) // compare if better - { // then take it - s = i.e_name; // mention 3rd component - s.append( " | ( " ); // in parallel - s.append( m_combined_table[j].e_name ); // with 2R combination - s.append( " )" ); - m_results[S3R].e_name = s; - m_results[S3R].e_value = tmp; // save amount of benefit - m_results[S3R].e_use = true; // enable later use - } - } - } - } - - // If there is a 3R result with remaining deviation consider to search a possibly better - // 4R solution - // calculate 4R for small series always - if( m_results[S3R].e_use && tmp ) - combine4( aSize ); -} - - -void E_SERIES::Calculate() -{ - uint32_t no_of_2Rcombi = 0; - - no_of_2Rcombi = combine2(); // combine all 2R combinations for selected E serie - simple_solution( no_of_2Rcombi ); // search for simple 2 component solution - - if( m_results[S2R].e_value ) // if simple 2R result is not exact - combine3( no_of_2Rcombi ); // continiue searching for a possibly better solution - - strip3(); - strip4(); -} - - -void E_SERIES::strip3() -{ - std::string s; - - if( m_results[S3R].e_use ) // if there is a 3 term result available - { // what is connected either by two "|" or by 3 plus - s = m_results[S3R].e_name; - - if( ( std::count( s.begin(), s.end(), '+' ) == 2 ) - || ( std::count( s.begin(), s.end(), '|' ) == 2 ) ) - { // then strip one pair of braces - s.erase( s.find( "( " ), 2 ); // it is known sure, this is available - s.erase( s.find( " )" ), 2 ); // in any unstripped 3R result term - m_results[S3R].e_name = s; // use stripped result - } - } -} - - -void E_SERIES::strip4() -{ - std::string s; - - if( m_results[S4R].e_use ) // if there is a 4 term result available - { // what are connected either by 3 "+" or by 3 "|" - s = m_results[S4R].e_name; - - if( ( std::count( s.begin(), s.end(), '+' ) == 3 ) - || ( std::count( s.begin(), s.end(), '|' ) == 3 ) ) - { // then strip two pair of braces - s.erase( s.find( "( " ), 2 ); // it is known sure, they are available - s.erase( s.find( " )" ), 2 ); // in any unstripped 4R result term - s.erase( s.find( "( " ), 2 ); - s.erase( s.find( " )" ), 2 ); - m_results[S4R].e_name = s; // use stripped result - } - } -} - - +} // namespace ESERIES diff --git a/include/eseries.h b/include/eseries.h index 61ce0cc5ec..d0307a4ab5 100644 --- a/include/eseries.h +++ b/include/eseries.h @@ -18,6 +18,8 @@ * with this program. If not, see . */ +#pragma once + #include #include #include @@ -27,174 +29,149 @@ * E-Values derived from a geometric sequence formula by Charles Renard were already * accepted and widely used before the ISO recommendation no. 3 has been published. * For this historical reason, rounding rules of some values are sometimes irregular. + * The current list of values is recorded in IEC 60063:2015. + * Previously it was in IEC publication 63. * Although all E-Values could be calculated at runtime, we initialize them in a lookup table * what seems the most easy way to consider any inconvenient irregular rules. Same table is - * also used to lookup non calculable but readable BOM value strings. Supported E-series are: + * also used to lookup non calculable but readable BOM value strings. */ -// List of normalized values between 1 and 10 -// The terminal 0.0 value is a end of list value -// Note also due to calculation time the E24 serie is the biggest usable. -#define E24_VALUES 1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.7, 3.0,\ - 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1, 0.0 +// The resistor calculator cannot operate on series larger than E24 due to calculation time -#define E12_VALUES 1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2, 0.0 +// Values are stored in the 100-999 decade. This is so that all values are integers +// and can be stored precisely. If the values are real values with a fraction part then +// the fractional part is typically imprecisely stores. In the 100 decade the values +// can be stored as precise values in integers. If used in floating point types +// they are as precise as before -#define E6_VALUES 1.0, 1.5, 2.2, 3.3, 4.7, 6.8, 0.0 +// If you want the values in the first decade then simply divide every value in +// the list by the first value in the list. -#define E3_VALUES 1.0, 2.2, 4.7, 0.0 +// E96 is a proper subset of E192. It is every 2nd value. E48 is every 4th value of E192. +// That is, all the series with 3 significant figures are subsets of the same series, E192. -#define E1_VALUES 1.0, 0.0 +// E24 is not a subset of E48 or E192. All series below E48 have only 2 significant figures +// and are differently from the series with 3 significant figures. +// E12, E6 and E3 are proper subsets of E24. Specifically they are evenly spaced +// values selected from E24. E12 is every 2nd value, E6 every 4th, E3 every 8th. -// First value of resistor in ohm -#define FIRST_VALUE 10 +// E1 is not in the IEC standard. -// last value of resistor in ohm -#define LAST_VALUE 1e6 +// The value 0 is not present in any series. It does not fit in any decade. +// It must be special cased in any calcuation or method of selection of +// values. -/** - * List of handled E series values: - * Note: series bigger than E24 have no interest because - * - probably the user will fing the needed value inside these series - * - the calculation time can be *very high* for series > E24 - */ -enum { E1, E3, E6, E12, E24 }; - -/** - * This calculator suggests solutions for 2R, 3R and 4R replacement combinations - */ -enum { S2R, S3R, S4R }; - -// R_DATA handles a resistor: string value, value and allowed to use -struct R_DATA +namespace ESERIES { - R_DATA() : - e_use( true ), - e_value( 0.0 ) - {} - R_DATA( const std::string& aName, double aValue ) - { - e_use = true; - e_name = aName; - e_value = aValue; - } - - bool e_use; - std::string e_name; - double e_value; +enum +{ + E1, + E3, + E6, + E12, + E24, + E48, + E96, + E192 }; -class E_SERIES +/* \brief Creates a vector of integers of E series values + * + */ +class ESERIES_VALUES : public std::vector { public: - E_SERIES(); - - /** - * If any value of the selected E-series not available, it can be entered as an exclude value. - * - * @param aValue is the value to exclude from calculation - * Values to exclude are set to false in the selected E-series source lookup table - */ - void Exclude( double aValue ); - - /** - * initialize next calculation and erase results from previous calculation - */ - void NewCalc(); - - /** - * called on calculate button to execute all the 2R, 3R and 4R calculations - */ - void Calculate(); - - /** - * Interface for CheckBox, RadioButton, RequriedResistor and calculated Results - */ - void SetSeries( uint32_t aSeries ) { m_series = aSeries; } - void SetRequiredValue( double aValue ) { m_required_value = aValue; } - - // Accessor: - const std::array& GetResults() { return m_results; } + ESERIES_VALUES( int aESeries ); private: - /** - * Add values from aList to m_tables. Covers all decades between FIRST_VALUE and LAST_VALUE. - * @return the count of items added to m_tables. - */ - int buildSeriesData( const double aList[] ); - - /** - * Build all 2R combinations from the selected E-series values - * - * Pre-calculated value combinations are saved in intermediate look up table m_combined_table - * @return is the number of found combinations what also depends from exclude values - */ - uint32_t combine2(); - - /** - * Search for closest two component solution - * - * @param aSize is the number of valid 2R combinations in m_combined_table on where to search - * The 2R result with smallest deviation will be saved in results - */ - void simple_solution( uint32_t aSize ); - - /** - * Check if there is a better 3 R solution than previous one using only two components. - * - * @param aSize gives the number of available combinations to be checked inside - * m_combined_table. Therefore m_combined_table is combined with the primary - * E-series look up table. The 3R result with smallest deviation will be saved - * in results if better than 2R - */ - void combine3( uint32_t aSize ); - - /** - * Check if there is a better four component solution. - * - * @param aSsize gives the number of 2R combinations to be checked inside m_combined_table - * Occupied calculation time depends from number of available E-series values with the power - * of 4 why execution for E12 is conditional with 4R check box for the case the previously - * found 3R solution is already exact - */ - void combine4( uint32_t aSize ); - - /* - * Strip redundant braces from three component result - * - * Example: R1+(R2+R3) become R1+R2+R3 - * and R1|(R2|R3) become R1|R2|R3 - * while R1+(R2|R3) or (R1+R2)|R3) remains untouched - */ - void strip3(); - - /* - * Strip redundant braces from four component result - * - * Example: (R1+R2)+(R3+R4) become R1+R2+R3+R4 - * and (R1|R2)|(R2|R3) become R1|R2|R3|R4 - * while (R1+R2)|(R3+R4) remains untouched - */ - void strip4(); - -private: - std::vector> m_tables; - - /* Note: intermediate calculations use m_combined_table - * if the biggest list is En, reserved array size should be 2*En*En of std::vector primary list. - * 2 component combinations including redundant swappable terms are for the moment - * ( using values between 10 ohms and 1Mohm ) - * 72 combinations for E1 - * 512 combinations for E3 - * 1922 combinations for E6 - * 7442 combinations for E12 - * 29282 combinations for E24 - */ - std::vector m_combined_table; // intermediate 2R combinations - - std::array m_results; // 2R, 3R and 4R results - uint32_t m_series = E6; // Radio Button State - double m_required_value = 0.0; // required Resistor + static const std::vector s_e24table; + static const std::vector s_e192table; }; + +/*! \brief Creates a vector of integers of the E1 series values. + * + */ +class E1_VALUES : public ESERIES_VALUES +{ +public: + E1_VALUES() : ESERIES_VALUES( E1 ) {} +}; + +/*! \brief Creates a vector of integers of the E3 series values. + * + */ +class E3_VALUES : public ESERIES_VALUES +{ +public: + E3_VALUES() : ESERIES_VALUES( E3 ) {} +}; + +/*! \brief Creates a vector of integers of the E6 series values. + * + */ +class E6_VALUES : public ESERIES_VALUES +{ +public: + E6_VALUES() : ESERIES_VALUES( E6 ) {} +}; + +/*! \brief Creates a vector of integers of the E12 series values. + * + */ +class E12_VALUES : public ESERIES_VALUES +{ +public: + E12_VALUES() : ESERIES_VALUES( E12 ) {} +}; + +/*! \brief Creates a vector of integers of the E24 series values. + * + */ +class E24_VALUES : public ESERIES_VALUES +{ +public: + E24_VALUES() : ESERIES_VALUES( E24 ) {} +}; + +/*! \brief Creates a vector of integers of the E48 series values. + * + */ +class E48_VALUES : public ESERIES_VALUES +{ +public: + E48_VALUES() : ESERIES_VALUES( E48 ) {} +}; + +/*! \brief Creates a vector of integers of the E96 series values. + * + */ +class E96_VALUES : public ESERIES_VALUES +{ +public: + E96_VALUES() : ESERIES_VALUES( E96 ) {} +}; + +/*! \brief Creates a vector of integers of the E192 series values. + * + */ +class E192_VALUES : public ESERIES_VALUES +{ +public: + E192_VALUES() : ESERIES_VALUES( E192 ) {} +}; + +/*! \brief Creates a vector of doubles of the values in the requested eseries and decade. + * + * The eSeries to select is a specified using the enumeration values above. + * The decade is specified as an integer exponent to the base 10. + * To receive vales between 100 and 1000 specify 2 as the exponent as each value will + * be normalized to betwen 1.0 and 10.0 and then multiplied by 10^2. + */ +class ESERIES_IN_DECADE : public std::vector +{ +public: + ESERIES_IN_DECADE( int eSeries, int decadeExponent ); +}; +} // namespace ESERIES diff --git a/pcb_calculator/CMakeLists.txt b/pcb_calculator/CMakeLists.txt index 1e91f8c8cc..84cf1c10a6 100644 --- a/pcb_calculator/CMakeLists.txt +++ b/pcb_calculator/CMakeLists.txt @@ -22,6 +22,8 @@ set( PCB_CALCULATOR_SRCS calculator_panels/panel_cable_size_base.cpp calculator_panels/panel_color_code.cpp calculator_panels/panel_color_code_base.cpp + calculator_panels/panel_eseries_display.cpp + calculator_panels/panel_eseries_display_base.cpp calculator_panels/panel_galvanic_corrosion.cpp calculator_panels/panel_galvanic_corrosion_base.cpp calculator_panels/panel_electrical_spacing.cpp @@ -31,8 +33,8 @@ set( PCB_CALCULATOR_SRCS calculator_panels/panel_electrical_spacing_iec60664_base.cpp calculator_panels/panel_electrical_spacing_ipc2221.cpp calculator_panels/panel_electrical_spacing_ipc2221_base.cpp - calculator_panels/panel_eseries.cpp - calculator_panels/panel_eseries_base.cpp + calculator_panels/panel_r_calculator.cpp + calculator_panels/panel_r_calculator_base.cpp calculator_panels/panel_fusing_current.cpp calculator_panels/panel_fusing_current_base.cpp calculator_panels/panel_regulator.cpp @@ -60,6 +62,7 @@ set( PCB_CALCULATOR_SRCS dialogs/dialog_regulator_form_base.cpp dialogs/dialog_regulator_form.cpp pcb_calculator_utils.cpp + resistor_substitution_utils.cpp ../common/env_vars.cpp # needed on MSW to avoid a link issue (a symbol not found) ) @@ -223,7 +226,8 @@ endfunction() md_doc2h( ${CMAKE_CURRENT_SOURCE_DIR}/attenuators/pi_formula ) md_doc2h( ${CMAKE_CURRENT_SOURCE_DIR}/attenuators/tee_formula ) md_doc2h( ${CMAKE_CURRENT_SOURCE_DIR}/tracks_width_versus_current_formula ) - md_doc2h( ${CMAKE_CURRENT_SOURCE_DIR}/eseries_help ) + md_doc2h( ${CMAKE_CURRENT_SOURCE_DIR}/eseries_display_help ) + md_doc2h( ${CMAKE_CURRENT_SOURCE_DIR}/r_calculator_help ) md_doc2h( ${CMAKE_CURRENT_SOURCE_DIR}/fusing_current_help ) md_doc2h( ${CMAKE_CURRENT_SOURCE_DIR}/galvanic_corrosion_help ) md_doc2h( ${CMAKE_CURRENT_SOURCE_DIR}/iec60664_help ) @@ -234,7 +238,8 @@ set( DOCS_LIST ${CMAKE_CURRENT_SOURCE_DIR}/attenuators/bridget_tee_formula.h ${CMAKE_CURRENT_SOURCE_DIR}/attenuators/splitter_formula.h ${CMAKE_CURRENT_SOURCE_DIR}/tracks_width_versus_current_formula.h - ${CMAKE_CURRENT_SOURCE_DIR}/eseries_help.h + ${CMAKE_CURRENT_SOURCE_DIR}/eseries_display_help.h + ${CMAKE_CURRENT_SOURCE_DIR}/r_calculator_help.h ${CMAKE_CURRENT_SOURCE_DIR}/fusing_current_help.h ${CMAKE_CURRENT_SOURCE_DIR}/galvanic_corrosion_help.h ${CMAKE_CURRENT_SOURCE_DIR}/iec60664_help.h diff --git a/pcb_calculator/calculator_panels/panel_eseries_display.cpp b/pcb_calculator/calculator_panels/panel_eseries_display.cpp new file mode 100644 index 0000000000..6575dac2f1 --- /dev/null +++ b/pcb_calculator/calculator_panels/panel_eseries_display.cpp @@ -0,0 +1,443 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 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, see . + */ + +#include + +#include +#include +#include +#include +#include // for IsDark +#include + +#include // For _HKI definition in eseries_display_help.h +wxString eseries_display_help = +#include "eseries_display_help.h" + + +PANEL_ESERIES_DISPLAY::PANEL_ESERIES_DISPLAY( wxWindow * parent, wxWindowID id, + const wxPoint& pos, const wxSize& size, + long style, const wxString& name ) : + PANEL_ESERIES_DISPLAY_BASE( parent, id, pos, size, style, name ) +{ + // show markdown stuff in lower help panel + wxString msg; + + ConvertMarkdown2Html( wxGetTranslation( eseries_display_help ), msg ); + m_panelESeriesHelp->SetPage( msg ); + + // Calculate E1,E3,E6,E12,E24,E48,E96 column background colours + // Get appearances. Note that I believe these panels will not be + // re-constructed on a mode change so this only works if you open + // the window *after* you set your dark mode. + const wxSystemAppearance appearances = wxSystemSettings::GetAppearance(); + const bool selectDark = appearances.IsDark(); + + recalculateColumnColours( selectDark ); + + // Lay out the smaller tree containing E1,E3,E6, and E12 + populateE112Tree(); + + // Set colours of smaller tree. + recolourE112Tree(); + + // Lay out the larger tree containing E24, R48 and E96 + populateE2496Tree(); + + // Set colours of larger tree. + recolourE2496Tree(); + + // Needed on wxWidgets 3.0 to ensure sizers are correctly set + GetSizer()->SetSizeHints( this ); + + // Make the grid lines disappear into the window background + // making the value boxes appear to be separated from each other. + wxColour gridLineColour = parent->GetBackgroundColour(); + + m_GridEseries112->SetGridLineColour( gridLineColour ); + m_GridEseries112->EnableGridLines( true ); + + m_GridEseries112->SetColLabelSize( wxGRID_AUTOSIZE ); + m_GridEseries112->AutoSize(); + + m_GridEseries2496->SetGridLineColour( gridLineColour ); + m_GridEseries2496->EnableGridLines( true ); + + m_GridEseries2496->SetColLabelSize( wxGRID_AUTOSIZE ); + m_GridEseries2496->AutoSize(); + + Layout(); +} + + +PANEL_ESERIES_DISPLAY::~PANEL_ESERIES_DISPLAY() +{ +} + + +void PANEL_ESERIES_DISPLAY::ThemeChanged() +{ + // Calculate E1,E3,E6,E12,E24,E48,E96 column background colours + // Get appearances. + const wxSystemAppearance appearances = wxSystemSettings::GetAppearance(); + const bool selectDark = appearances.IsDark(); + + recalculateColumnColours( selectDark ); + + // Set colours of smaller tree. + recolourE112Tree(); + + // Set colours of larger tree. + recolourE2496Tree(); +} + + +void PANEL_ESERIES_DISPLAY::SaveSettings( PCB_CALCULATOR_SETTINGS* aCfg ) +{ +} + + +void PANEL_ESERIES_DISPLAY::LoadSettings( PCB_CALCULATOR_SETTINGS* aCfg ) +{ +} + + +void PANEL_ESERIES_DISPLAY::recalculateColumnColours( bool aDarkModeOn ) +{ + // if in a dark mode then darken colors significantly so that + // the white numerals on top stand out better + const int colourAdjust = aDarkModeOn ? s_darkAdjustValue : 100; + + m_colourE1Column = wxColour( s_cE1BGR ).ChangeLightness( colourAdjust ); + const wxColour colourE3Column( wxColour( s_cE3BGR ).ChangeLightness( colourAdjust ) ); + const wxColour colourE6Column( wxColour( s_cE6BGR ).ChangeLightness( colourAdjust ) ); + const wxColour colourE12Column( wxColour( s_cE12BGR ).ChangeLightness( colourAdjust ) ); + const wxColour colourE24Column( wxColour( s_cE24BGR ).ChangeLightness( colourAdjust ) ); + const wxColour colourE48Column( wxColour( s_cE48BGR ).ChangeLightness( colourAdjust ) ); + const wxColour colourE96Column( wxColour( s_cE96BGR ).ChangeLightness( colourAdjust ) ); + + m_colourE3Pair[0] = colourE3Column; + m_colourE3Pair[1] = colourE3Column.ChangeLightness( s_altAdjustValue ); + m_colourE6Pair[0] = colourE6Column; + m_colourE6Pair[1] = colourE6Column.ChangeLightness( s_altAdjustValue ); + m_colourE12Pair[0] = colourE12Column; + m_colourE12Pair[1] = colourE12Column.ChangeLightness( s_altAdjustValue ); + m_colourE24Pair[0] = colourE24Column; + m_colourE24Pair[1] = colourE24Column.ChangeLightness( s_altAdjustValue ); + m_colourE48Pair[0] = colourE48Column; + m_colourE48Pair[1] = colourE48Column.ChangeLightness( s_altAdjustValue ); + m_colourE96Pair[0] = colourE96Column; + m_colourE96Pair[1] = colourE96Column.ChangeLightness( s_altAdjustValue ); + + s_colourMatching = m_GridEseries2496->GetLabelBackgroundColour(); +} + + +void PANEL_ESERIES_DISPLAY::populateE112Tree() +{ + // Lay out E1, R3, E6, E12 tree. + ESERIES::ESERIES_VALUES eSeries12 = ESERIES::ESERIES_VALUES( ESERIES::E12 ); + + int row = 0; + wxString value; + + m_GridEseries112->BeginBatch(); + + m_GridEseries112->DeleteCols( 0, m_GridEseries112->GetNumberCols() ); + m_GridEseries112->DeleteRows( 0, m_GridEseries112->GetNumberRows() ); + m_GridEseries112->AppendCols( 4 ); // E1, E3, E6, E12 + m_GridEseries112->AppendRows( 12 ); // Sufficient rows for all of E12 + + m_GridEseries112->SetColLabelValue( 0, "E1" ); + m_GridEseries112->SetColLabelValue( 1, "E3" ); + m_GridEseries112->SetColLabelValue( 2, "E6" ); + m_GridEseries112->SetColLabelValue( 3, "E12" ); + + value = wxString( "" ) << eSeries12[0]; + + m_GridEseries112->SetCellValue( 0, 0, value ); + m_GridEseries112->SetCellSize( 0, 0, 12, 1 ); + + for( const uint16_t& seriesEntry : eSeries12 ) + { + value = wxString( "" ) << seriesEntry; + + if( 0 == row % 4 ) + { + // Set E3 column + m_GridEseries112->SetCellValue( row, 1, value ); + m_GridEseries112->SetCellSize( row, 1, 4, 1 ); + } + + if( 0 == row % 2 ) + { + // Set E6 column + m_GridEseries112->SetCellValue( row, 2, value ); + m_GridEseries112->SetCellSize( row, 2, 2, 1 ); + } + + // Set E12 column + m_GridEseries112->SetCellValue( row, 3, value ); + + ++row; + } + + for( int i = 0; i < 3; ++i ) + { + // Resize data columns to fit data and set as minimum size + m_GridEseries112->AutoSizeColumn( i, true ); + } + + m_GridEseries112->EndBatch(); +} + + +void PANEL_ESERIES_DISPLAY::populateE2496Tree() +{ + // Lay out E24, E48, E96 tree. + ESERIES::ESERIES_VALUES eSeries24 = ESERIES::ESERIES_VALUES( ESERIES::E24 ); + ESERIES::ESERIES_VALUES eSeries96 = ESERIES::ESERIES_VALUES( ESERIES::E96 ); + + // This is the number of times the table is wrapped around into another + // column. Like in a newspaper. The term column is not used since + // the grid already has columns. The 96 in the second calculation + // is the number of entries in the largest series displayed (E96) + constexpr unsigned int numStripes = 4; + constexpr unsigned int stripeHeight = 96 / numStripes; + + int idx = 0; + int stripe = 0; + wxString value; + + m_GridEseries2496->BeginBatch(); + + m_GridEseries2496->DeleteCols( 0, m_GridEseries2496->GetNumberCols() ); + m_GridEseries2496->DeleteRows( 0, m_GridEseries2496->GetNumberRows() ); + + // Column headers are E24, E48, E96, - + // repeated numStripes times over. And the last - is omitted. + m_GridEseries2496->AppendCols( numStripes * 4 - 1 ); + m_GridEseries2496->AppendRows( stripeHeight ); + + m_GridEseries2496->SetColLabelValue( 0 + 0, "E24" ); + m_GridEseries2496->SetColLabelValue( 1 + 0, "E48" ); + m_GridEseries2496->SetColLabelValue( 2 + 0, "E96" ); + m_GridEseries2496->SetColLabelValue( 3 + 0, "-" ); + + m_GridEseries2496->SetColLabelValue( 0 + 4, "E24" ); + m_GridEseries2496->SetColLabelValue( 1 + 4, "E48" ); + m_GridEseries2496->SetColLabelValue( 2 + 4, "E96" ); + m_GridEseries2496->SetColLabelValue( 3 + 4, "-" ); + + m_GridEseries2496->SetColLabelValue( 0 + 8, "E24" ); + m_GridEseries2496->SetColLabelValue( 1 + 8, "E48" ); + m_GridEseries2496->SetColLabelValue( 2 + 8, "E96" ); + m_GridEseries2496->SetColLabelValue( 3 + 8, "-" ); + + m_GridEseries2496->SetColLabelValue( 0 + 12, "E24" ); + m_GridEseries2496->SetColLabelValue( 1 + 12, "E48" ); + m_GridEseries2496->SetColLabelValue( 2 + 12, "E96" ); + + for( const uint16_t& seriesEntry : eSeries24 ) + { + value = wxString( "" ) << seriesEntry; + + int row = ( idx * 4 ) % stripeHeight; + int colOffset = ( ( idx * 4 ) / stripeHeight ) * 4; + + m_GridEseries2496->SetCellValue( row, colOffset, value ); + m_GridEseries2496->SetCellSize( row, colOffset, 4, 1 ); + + ++idx; + } + + idx = 0; + + for( const uint16_t& seriesEntry : eSeries96 ) + { + value = wxString( "" ) << seriesEntry; + + int row = idx % stripeHeight; + int colOffset = ( idx / stripeHeight ) * 4; + + if( 0 == row % 2 ) + { + // Set E48 column + m_GridEseries2496->SetCellValue( row, colOffset + 1, value ); + m_GridEseries2496->SetCellSize( row, colOffset + 1, 2, 1 ); + } + + // Set E96 column + m_GridEseries2496->SetCellValue( row, colOffset + 2, value ); + ++idx; + } + + // cause the areas between stripes to appear more empty + // by making them a single cell the height of the stripe + // this causes the horizontal row lines rows to disappear + for( unsigned int stripeGap = 1; stripeGap < numStripes; ++stripeGap ) + { + const int stripeColumn = -1 + stripeGap * 4; + + m_GridEseries2496->SetCellSize( 0, stripeColumn, stripeHeight, 1 ); + } + + for( int i = 0; i < numStripes * 4 - 1; ++i ) + { + // Resize the column to fit data and set as minimum size + m_GridEseries2496->AutoSizeColumn( i, true ); + } + + m_GridEseries2496->EndBatch(); +} + + +void PANEL_ESERIES_DISPLAY::recolourE112Tree() +{ + const unsigned int numRows = m_GridEseries112->GetNumberRows(); + + m_GridEseries112->BeginBatch(); + + // Colouring E1 is easy. There is only one item. + m_GridEseries112->SetCellBackgroundColour( 0, 0, m_colourE1Column ); + + bool alternateColour = false; + int cellWidth = 0, cellHeight = 0; + + // Colour E3 column + for( int row = 0; row < numRows; row += cellHeight ) + { + m_GridEseries112->SetCellBackgroundColour( row, 1, m_colourE3Pair[alternateColour] ); + + // get height for iteration above + m_GridEseries112->GetCellSize( row, 1, &cellHeight, &cellWidth ); + alternateColour = !alternateColour; + + if( cellHeight < 1 ) + cellHeight = 1; // Do not loop forever, always make progress. + } + + // Colour E6 column + alternateColour = false; + + for( int row = 0; row < numRows; row += cellHeight ) + { + m_GridEseries112->SetCellBackgroundColour( row, 2, m_colourE6Pair[alternateColour] ); + + // get height for iteration above + m_GridEseries112->GetCellSize( row, 2, &cellHeight, &cellWidth ); + alternateColour = !alternateColour; + + if( cellHeight < 1 ) + cellHeight = 1; // Do not loop forever, always make progress. + } + + // Colour E12 column + alternateColour = false; + + for( int row = 0; row < numRows; row += cellHeight ) + { + m_GridEseries112->SetCellBackgroundColour( row, 3, m_colourE12Pair[alternateColour] ); + + // get height for iteration above + m_GridEseries112->GetCellSize( row, 3, &cellHeight, &cellWidth ); + alternateColour = !alternateColour; + + if( cellHeight < 1 ) + cellHeight = 1; // Do not loop forever, always make progress. + } + + m_GridEseries112->EndBatch(); +} + + +void PANEL_ESERIES_DISPLAY::recolourE2496Tree() +{ + const unsigned int numRows = m_GridEseries2496->GetNumberRows(); + constexpr unsigned int numStripes = 4; + constexpr unsigned int stripeHeight = 96 / numStripes; + + m_GridEseries2496->BeginBatch(); + + bool alternateColour = false; + int cellWidth = 0, cellHeight = 0; + + // Colour E24 columns + for( int row = 0; row < numRows; row += cellHeight ) + { + for( int stripe = 0; stripe < numStripes; ++stripe ) + { + m_GridEseries2496->SetCellBackgroundColour( row, 0 + 4 * stripe, + m_colourE24Pair[alternateColour] ); + } + // get height for iteration above + m_GridEseries2496->GetCellSize( row, 0, &cellHeight, &cellWidth ); + alternateColour = !alternateColour; + + if( cellHeight < 1 ) + cellHeight = 1; // Do not loop forever, always make progress. + } + + // Colour E48 columns + alternateColour = false; + + for( int row = 0; row < numRows; row += cellHeight ) + { + for( int stripe = 0; stripe < numStripes; ++stripe ) + { + m_GridEseries2496->SetCellBackgroundColour( row, 1 + 4 * stripe, + m_colourE48Pair[alternateColour] ); + } + // get height for iteration above + m_GridEseries2496->GetCellSize( row, 1, &cellHeight, &cellWidth ); + alternateColour = !alternateColour; + + if( cellHeight < 1 ) + cellHeight = 1; // Do not loop forever, always make progress. + } + + // Colour E96 columns + alternateColour = false; + + for( int row = 0; row < numRows; row += cellHeight ) + { + for( int stripe = 0; stripe < numStripes; ++stripe ) + { + m_GridEseries2496->SetCellBackgroundColour( row, 2 + 4 * stripe, + m_colourE96Pair[alternateColour] ); + } + // get height for iteration above + m_GridEseries2496->GetCellSize( row, 2, &cellHeight, &cellWidth ); + alternateColour = !alternateColour; + + if( cellHeight < 1 ) + cellHeight = 1; // Do not loop forever, always make progress. + } + + // recolour empty areas between columns to match label background + // to make them less eye-catching + for( unsigned int stripeGap = 1; stripeGap < numStripes; ++stripeGap ) + { + const int stripeColumn = -1 + stripeGap * 4; + + m_GridEseries2496->SetCellBackgroundColour( 0, stripeColumn, s_colourMatching ); + } + + m_GridEseries2496->EndBatch(); +} diff --git a/pcb_calculator/calculator_panels/panel_eseries_display.h b/pcb_calculator/calculator_panels/panel_eseries_display.h new file mode 100644 index 0000000000..b58ad2d671 --- /dev/null +++ b/pcb_calculator/calculator_panels/panel_eseries_display.h @@ -0,0 +1,156 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 1992-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, see . + */ + +#ifndef PANEL_ESERIES_DISPLAY_H +#define PANEL_ESERIES_DISPLAY_H + +#include +#include "panel_eseries_display_base.h" + +class PCB_CALCULATOR_SETTINGS; + + +class PANEL_ESERIES_DISPLAY : public PANEL_ESERIES_DISPLAY_BASE +{ +public: + PANEL_ESERIES_DISPLAY( wxWindow* parent, wxWindowID id = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL, + const wxString& name = wxEmptyString ); + ~PANEL_ESERIES_DISPLAY(); + + // Methods from CALCULATOR_PANEL that must be overridden + void LoadSettings( PCB_CALCULATOR_SETTINGS* aCfg ) override; + void SaveSettings( PCB_CALCULATOR_SETTINGS* aCfg ) override; + void ThemeChanged() override; + +private: + /*! \brief Recalculate colours used to highlight the E-series columns. + * + * Each colour is a pair of colours which are used in alternate rows + * to make the table easier to follow. + */ + void recalculateColumnColours( bool aDarkModeOn ); + + /*! \brief Fill small E-series tree with values. + * + * Contains values from E1, E3, E6 and E12 series. + */ + void populateE112Tree(); + + /*! \brief Fill larger E-series tree with values. + * + * Contains values from E24, E48, E96 series. + */ + void populateE2496Tree(); + + /*! \brief Colour small E-series tree according to current theme. + * + * Contains values from E1, E3, E6 and E12 series. + */ + void recolourE112Tree(); + + /*! \brief Colour large E-series tree according to current theme. + * + * Contains values from E24, E48, E96 series. + */ + void recolourE2496Tree(); + + /*! \brief Adjustment factor to create alternating R-series table entry colours. + * + * This is to make the table easier to read. + * This value is passed to wxColour::ChangeLightness(). + */ + constexpr static int s_altAdjustValue = 125; + + /*! \brief Adjustment factor to create darker grid cells in dark theme. + * + * Without this the light numbers on the grid backgrounds are difficult to read. + * This value is passed to wxColour::ChangeLightness(). + */ + constexpr static int s_darkAdjustValue = 78; + + /*! \brief Colour for E1 column in light theme. Passed to wxColour constructor. + * + * HTML honeydew + */ + constexpr static uint32_t s_cE1BGR = 0xf0fff0; + + /*! \brief Colour for E3 column in light theme. Passed to wxColour constructor. + * + * HTML palegreen + */ + constexpr static uint32_t s_cE3BGR = 0x98fb98; + + /*! \brief Colour for E6 column in light theme. Passed to wxColour constructor. + * + * HTML cornflowerblue + */ + constexpr static uint32_t s_cE6BGR = 0xed9564; + + /*! \brief Colour for E12 column in light theme. Passed to wxColour constructor. + * + * HTML plum + */ + constexpr static uint32_t s_cE12BGR = 0xdda0dd; + + /*! \brief Colour for E24 column in light theme. Passed to wxColour constructor. + * + * HTML skyblue + */ + constexpr static uint32_t s_cE24BGR = 0xebce87; + + /*! \brief Colour for E48 column in light theme. Passed to wxColour constructor. + * + * HTML olivedrab + */ + constexpr static uint32_t s_cE48BGR = 0x23e86b; + + /*! \brief Colour for E96 column in light theme. Passed to wxColour constructor. + * + * HTML lightsalmon + */ + constexpr static uint32_t s_cE96BGR = 0x7aa0ff; + + /*! \brief Calculated colour for E1 column in current (light,dark) theme. */ + wxColour m_colourE1Column; + + /*! \brief Calculated colours for E3 column in current (light,dark) theme. */ + wxColour m_colourE3Pair[2]; + + /*! \brief Calculated colours for E6 column in current (light,dark) theme. */ + wxColour m_colourE6Pair[2]; + + /*! \brief Calculated colours for E12 column in current (light,dark) theme. */ + wxColour m_colourE12Pair[2]; + + /*! \brief Calculated colours for E24 column in current (light,dark) theme. */ + wxColour m_colourE24Pair[2]; + + /*! \brief Calculated colours for E48 column in current (light,dark) theme. */ + wxColour m_colourE48Pair[2]; + + /*! \brief Calculated colours for E96 column in current (light,dark) theme. */ + wxColour m_colourE96Pair[2]; + + /*! \brief Calculated matching colour for empty columns. Same as background of labels */ + wxColour s_colourMatching; +}; + +#endif diff --git a/pcb_calculator/calculator_panels/panel_eseries_display_base.cpp b/pcb_calculator/calculator_panels/panel_eseries_display_base.cpp new file mode 100644 index 0000000000..dcdf7d6b50 --- /dev/null +++ b/pcb_calculator/calculator_panels/panel_eseries_display_base.cpp @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version 3.10.1-282-g1fa54006) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "panel_eseries_display_base.h" + +/////////////////////////////////////////////////////////////////////////// + +PANEL_ESERIES_DISPLAY_BASE::PANEL_ESERIES_DISPLAY_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : CALCULATOR_PANEL( parent, id, pos, size, style, name ) +{ + wxBoxSizer* bSizerESeries; + bSizerESeries = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bTablesSizerESeries; + bTablesSizerESeries = new wxBoxSizer( wxHORIZONTAL ); + + wxStaticBoxSizer* sbLowerSizerEseries2496; + sbLowerSizerEseries2496 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("E24,E48,E96") ), wxVERTICAL ); + + m_GridEseries2496 = new wxGrid( sbLowerSizerEseries2496->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + + // Grid + m_GridEseries2496->CreateGrid( 1, 1 ); + m_GridEseries2496->EnableEditing( false ); + m_GridEseries2496->EnableGridLines( false ); + m_GridEseries2496->EnableDragGridSize( false ); + m_GridEseries2496->SetMargins( 0, 0 ); + + // Columns + m_GridEseries2496->EnableDragColMove( false ); + m_GridEseries2496->EnableDragColSize( false ); + m_GridEseries2496->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); + + // Rows + m_GridEseries2496->EnableDragRowSize( false ); + m_GridEseries2496->SetRowLabelSize( 0 ); + m_GridEseries2496->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); + + // Label Appearance + + // Cell Defaults + m_GridEseries2496->SetDefaultCellAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); + sbLowerSizerEseries2496->Add( m_GridEseries2496, 0, wxALL, 5 ); + + + bTablesSizerESeries->Add( sbLowerSizerEseries2496, 0, 0, 5 ); + + wxStaticBoxSizer* sbLowerSizerEseries112; + sbLowerSizerEseries112 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("E1,E3,E6,E12") ), wxVERTICAL ); + + m_GridEseries112 = new wxGrid( sbLowerSizerEseries112->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + + // Grid + m_GridEseries112->CreateGrid( 1, 1 ); + m_GridEseries112->EnableEditing( false ); + m_GridEseries112->EnableGridLines( false ); + m_GridEseries112->EnableDragGridSize( false ); + m_GridEseries112->SetMargins( 0, 0 ); + + // Columns + m_GridEseries112->EnableDragColMove( false ); + m_GridEseries112->EnableDragColSize( false ); + m_GridEseries112->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); + + // Rows + m_GridEseries112->EnableDragRowSize( false ); + m_GridEseries112->SetRowLabelSize( 0 ); + m_GridEseries112->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); + + // Label Appearance + + // Cell Defaults + m_GridEseries112->SetDefaultCellAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); + sbLowerSizerEseries112->Add( m_GridEseries112, 0, wxALL, 5 ); + + + bTablesSizerESeries->Add( sbLowerSizerEseries112, 1, 0, 5 ); + + + bSizerESeries->Add( bTablesSizerESeries, 0, wxTOP, 5 ); + + m_panelESeriesHelp = new HTML_WINDOW( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO ); + m_panelESeriesHelp->SetMinSize( wxSize( -1,100 ) ); + + bSizerESeries->Add( m_panelESeriesHelp, 1, wxBOTTOM|wxLEFT|wxRIGHT|wxEXPAND, 5 ); + + + this->SetSizer( bSizerESeries ); + this->Layout(); + bSizerESeries->Fit( this ); +} + +PANEL_ESERIES_DISPLAY_BASE::~PANEL_ESERIES_DISPLAY_BASE() +{ +} diff --git a/pcb_calculator/calculator_panels/panel_eseries_display_base.fbp b/pcb_calculator/calculator_panels/panel_eseries_display_base.fbp new file mode 100644 index 0000000000..2877e273c2 --- /dev/null +++ b/pcb_calculator/calculator_panels/panel_eseries_display_base.fbp @@ -0,0 +1,340 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + panel_eseries_display_base + 1000 + none + + + 1 + panel_eseries_display_base + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + + 1 + 0 + 1 + impl_virtual + + + 0 + wxID_ANY + + + PANEL_ESERIES_DISPLAY_BASE + + -1,-1 + CALCULATOR_PANEL; calculator_panels/calculator_panel.h; + + 0 + + + + + + bSizerESeries + wxVERTICAL + none + + 5 + wxTOP + 0 + + + bTablesSizerESeries + wxHORIZONTAL + none + + 5 + + 0 + + wxID_ANY + E24,E48,E96 + + sbLowerSizerEseries2496 + wxVERTICAL + 1 + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + 0 + 0 + + + + 1 + + + wxALIGN_CENTER + + wxALIGN_CENTER + 0 + 1 + wxALIGN_CENTER + + + wxALIGN_CENTER + 1 + + + 1 + 0 + Dock + 0 + Left + 0 + 0 + 0 + 0 + 0 + 0 + 1 + + 1 + + + 0 + 0 + 0 + wxID_ANY + + + + 0 + 0 + + 0 + + + 0 + + 1 + m_GridEseries2496 + 1 + + + protected + 1 + + Resizable + wxALIGN_CENTER + 0 + + wxALIGN_CENTER + + 1 + 1 + + ; ; forward_declare + 0 + + + + + + + + + + 5 + + 1 + + wxID_ANY + E1,E3,E6,E12 + + sbLowerSizerEseries112 + wxVERTICAL + 1 + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + 0 + 0 + + + + 1 + + + wxALIGN_CENTER + + wxALIGN_CENTER + 0 + 1 + wxALIGN_CENTER + + + wxALIGN_CENTER + 1 + + + 1 + 0 + Dock + 0 + Left + 0 + 0 + 0 + 0 + 0 + 0 + 1 + + 1 + + + 0 + 0 + 0 + wxID_ANY + + + + 0 + 0 + + 0 + + + 0 + + 1 + m_GridEseries112 + 1 + + + protected + 1 + + Resizable + wxALIGN_CENTER + 0 + + wxALIGN_CENTER + + 1 + 1 + + ; ; forward_declare + 0 + + + + + + + + + + + + 5 + wxBOTTOM|wxLEFT|wxRIGHT|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + -1,100 + 1 + m_panelESeriesHelp + 1 + + + protected + 1 + + Resizable + 1 + + wxHW_SCROLLBAR_AUTO + HTML_WINDOW; html_window.h; + 0 + + + + + + + + + + diff --git a/pcb_calculator/calculator_panels/panel_eseries_display_base.h b/pcb_calculator/calculator_panels/panel_eseries_display_base.h new file mode 100644 index 0000000000..3d59825fb1 --- /dev/null +++ b/pcb_calculator/calculator_panels/panel_eseries_display_base.h @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version 3.10.1-282-g1fa54006) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include "html_window.h" +#include "calculator_panels/calculator_panel.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class PANEL_ESERIES_DISPLAY_BASE +/////////////////////////////////////////////////////////////////////////////// +class PANEL_ESERIES_DISPLAY_BASE : public CALCULATOR_PANEL +{ + private: + + protected: + wxGrid* m_GridEseries2496; + wxGrid* m_GridEseries112; + HTML_WINDOW* m_panelESeriesHelp; + + public: + + PANEL_ESERIES_DISPLAY_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = 0, const wxString& name = wxEmptyString ); + + ~PANEL_ESERIES_DISPLAY_BASE(); + +}; + diff --git a/pcb_calculator/calculator_panels/panel_eseries.cpp b/pcb_calculator/calculator_panels/panel_r_calculator.cpp similarity index 63% rename from pcb_calculator/calculator_panels/panel_eseries.cpp rename to pcb_calculator/calculator_panels/panel_r_calculator.cpp index 71ecd51797..0de6253672 100644 --- a/pcb_calculator/calculator_panels/panel_eseries.cpp +++ b/pcb_calculator/calculator_panels/panel_r_calculator.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2011 jean-pierre.charras - * Copyright (C) 1992-2022 Kicad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-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 @@ -24,23 +24,23 @@ * for more info */ -#include +#include #include #include #include -#include +#include +#include "resistor_substitution_utils.h" - -#include // For _HKI definition in eseries_help.h -wxString eseries_help = -#include "eseries_help.h" +#include // For _HKI definition in r_calculator_help.h +wxString r_calculator_help = +#include "r_calculator_help.h" extern double DoubleFromString( const wxString& TextValue ); -PANEL_E_SERIES::PANEL_E_SERIES( wxWindow* parent, wxWindowID id, const wxPoint& pos, - const wxSize& size, long style, const wxString& name ) : - PANEL_E_SERIES_BASE( parent, id, pos, size, style, name ) +PANEL_R_CALCULATOR::PANEL_R_CALCULATOR( wxWindow* parent, wxWindowID id, const wxPoint& pos, + const wxSize& size, long style, const wxString& name ) : + PANEL_R_CALCULATOR_BASE( parent, id, pos, size, style, name ) { m_reqResUnits->SetLabel( wxT( "kΩ" ) ); m_exclude1Units->SetLabel( wxT( "kΩ" ) ); @@ -62,7 +62,7 @@ PANEL_E_SERIES::PANEL_E_SERIES( wxWindow* parent, wxWindowID id, const wxPoint& // show markdown formula explanation in lower help panel wxString msg; - ConvertMarkdown2Html( wxGetTranslation( eseries_help ), msg ); + ConvertMarkdown2Html( wxGetTranslation( r_calculator_help ), msg ); m_panelESeriesHelp->SetPage( msg ); // Needed on wxWidgets 3.0 to ensure sizers are correctly set @@ -70,54 +70,54 @@ PANEL_E_SERIES::PANEL_E_SERIES( wxWindow* parent, wxWindowID id, const wxPoint& } -PANEL_E_SERIES::~PANEL_E_SERIES() +PANEL_R_CALCULATOR::~PANEL_R_CALCULATOR() { } -void PANEL_E_SERIES::ThemeChanged() +void PANEL_R_CALCULATOR::ThemeChanged() { // Update the HTML window with the help text m_panelESeriesHelp->ThemeChanged(); } -void PANEL_E_SERIES::SaveSettings( PCB_CALCULATOR_SETTINGS* aCfg ) +void PANEL_R_CALCULATOR::SaveSettings( PCB_CALCULATOR_SETTINGS* aCfg ) { } -void PANEL_E_SERIES::LoadSettings( PCB_CALCULATOR_SETTINGS* aCfg ) +void PANEL_R_CALCULATOR::LoadSettings( PCB_CALCULATOR_SETTINGS* aCfg ) { } -void PANEL_E_SERIES::OnCalculateESeries( wxCommandEvent& event ) +void PANEL_R_CALCULATOR::OnCalculateESeries( wxCommandEvent& event ) { - double reqr; // required resistor stored in local copy + double reqr; // required resistor stored in local copy double error, err3 = 0; - wxString es, fs; // error and formula strings + wxString es, fs; // error and formula strings wxBusyCursor dummy; reqr = ( 1000 * DoubleFromString( m_ResRequired->GetValue() ) ); m_eSeries.SetRequiredValue( reqr ); // keep a local copy of required resistor value - m_eSeries.NewCalc(); // assume all values available + m_eSeries.NewCalc(); // assume all values available /* * Exclude itself. For the case, a value from the available series is found as required value, * the calculator assumes this value needs a replacement for the reason of being not available. * Two further exclude values can be entered to exclude and are skipped as not being available. * All values entered in KiloOhms are converted to Ohm for internal calculation */ - m_eSeries.Exclude( 1000 * DoubleFromString( m_ResRequired->GetValue())); - m_eSeries.Exclude( 1000 * DoubleFromString( m_ResExclude1->GetValue())); - m_eSeries.Exclude( 1000 * DoubleFromString( m_ResExclude2->GetValue())); + m_eSeries.Exclude( 1000 * DoubleFromString( m_ResRequired->GetValue() ) ); + m_eSeries.Exclude( 1000 * DoubleFromString( m_ResExclude1->GetValue() ) ); + m_eSeries.Exclude( 1000 * DoubleFromString( m_ResExclude2->GetValue() ) ); try { m_eSeries.Calculate(); } - catch (std::out_of_range const& exc) + catch( std::out_of_range const& exc ) { wxString msg; msg << "Internal error: " << exc.what(); @@ -126,17 +126,18 @@ void PANEL_E_SERIES::OnCalculateESeries( wxCommandEvent& event ) return; } - fs = m_eSeries.GetResults()[S2R].e_name; // show 2R solution formula string + fs = m_eSeries.GetResults()[RES_EQUIV_CALC::S2R].e_name; // show 2R solution formula string m_ESeries_Sol2R->SetValue( fs ); - error = reqr + m_eSeries.GetResults()[S2R].e_value; // absolute value of solution - error = ( reqr / error - 1 ) * 100; // error in percent + error = reqr + + m_eSeries.GetResults()[RES_EQUIV_CALC::S2R].e_value; // absolute value of solution + error = ( reqr / error - 1 ) * 100; // error in percent if( error ) { if( std::abs( error ) < 0.01 ) es.Printf( "<%.2f", 0.01 ); else - es.Printf( "%+.2f",error); + es.Printf( "%+.2f", error ); } else { @@ -145,9 +146,9 @@ void PANEL_E_SERIES::OnCalculateESeries( wxCommandEvent& event ) m_ESeriesError2R->SetValue( es ); // anyway show 2R error string - if( m_eSeries.GetResults()[S3R].e_use ) // if 3R solution available + if( m_eSeries.GetResults()[RES_EQUIV_CALC::S3R].e_use ) // if 3R solution available { - err3 = reqr + m_eSeries.GetResults()[S3R].e_value; // calculate the 3R + err3 = reqr + m_eSeries.GetResults()[RES_EQUIV_CALC::S3R].e_value; // calculate the 3R err3 = ( reqr / err3 - 1 ) * 100; // error in percent if( err3 ) @@ -155,18 +156,18 @@ void PANEL_E_SERIES::OnCalculateESeries( wxCommandEvent& event ) if( std::abs( err3 ) < 0.01 ) es.Printf( "<%.2f", 0.01 ); else - es.Printf( "%+.2f",err3); + es.Printf( "%+.2f", err3 ); } else { es = _( "Exact" ); } - m_ESeriesError3R->SetValue( es ); // show 3R error string - fs = m_eSeries.GetResults()[S3R].e_name; - m_ESeries_Sol3R->SetValue( fs ); // show 3R formula string + m_ESeriesError3R->SetValue( es ); // show 3R error string + fs = m_eSeries.GetResults()[RES_EQUIV_CALC::S3R].e_name; + m_ESeries_Sol3R->SetValue( fs ); // show 3R formula string } - else // nothing better than 2R found + else // nothing better than 2R found { fs = _( "Not worth using" ); m_ESeries_Sol3R->SetValue( fs ); @@ -175,21 +176,22 @@ void PANEL_E_SERIES::OnCalculateESeries( wxCommandEvent& event ) fs = wxEmptyString; - if( m_eSeries.GetResults()[S4R].e_use ) // show 4R solution if available + if( m_eSeries.GetResults()[RES_EQUIV_CALC::S4R].e_use ) // show 4R solution if available { - fs = m_eSeries.GetResults()[S4R].e_name; + fs = m_eSeries.GetResults()[RES_EQUIV_CALC::S4R].e_name; - error = reqr + m_eSeries.GetResults()[S4R].e_value; // absolute value of solution + error = reqr + + m_eSeries.GetResults()[RES_EQUIV_CALC::S4R].e_value; // absolute value of solution error = ( reqr / error - 1 ) * 100; // error in percent if( error ) - es.Printf( "%+.2f",error ); + es.Printf( "%+.2f", error ); else es = _( "Exact" ); m_ESeriesError4R->SetValue( es ); } - else // no 4R solution + else // no 4R solution { fs = _( "Not worth using" ); es = wxEmptyString; @@ -200,16 +202,16 @@ void PANEL_E_SERIES::OnCalculateESeries( wxCommandEvent& event ) } -void PANEL_E_SERIES::OnESeriesSelection( wxCommandEvent& event ) +void PANEL_R_CALCULATOR::OnESeriesSelection( wxCommandEvent& event ) { if( event.GetEventObject() == m_e1 ) - m_eSeries.SetSeries( E1 ); + m_eSeries.SetSeries( ESERIES::E1 ); else if( event.GetEventObject() == m_e3 ) - m_eSeries.SetSeries( E3 ); + m_eSeries.SetSeries( ESERIES::E3 ); else if( event.GetEventObject() == m_e12 ) - m_eSeries.SetSeries( E12 ); + m_eSeries.SetSeries( ESERIES::E12 ); else if( event.GetEventObject() == m_e24 ) - m_eSeries.SetSeries( E24 ); + m_eSeries.SetSeries( ESERIES::E24 ); else - m_eSeries.SetSeries( E6 ); + m_eSeries.SetSeries( ESERIES::E6 ); } diff --git a/pcb_calculator/calculator_panels/panel_eseries.h b/pcb_calculator/calculator_panels/panel_r_calculator.h similarity index 74% rename from pcb_calculator/calculator_panels/panel_eseries.h rename to pcb_calculator/calculator_panels/panel_r_calculator.h index 8ad83f0bef..95c0364017 100644 --- a/pcb_calculator/calculator_panels/panel_eseries.h +++ b/pcb_calculator/calculator_panels/panel_r_calculator.h @@ -17,22 +17,23 @@ * with this program. If not, see . */ -#ifndef PANEL_E_SERIES_H -#define PANEL_E_SERIES_H +#ifndef PANEL_R_CALCULATOR_H +#define PANEL_R_CALCULATOR_H -#include "panel_eseries_base.h" #include +#include "panel_r_calculator_base.h" +#include "resistor_substitution_utils.h" class PCB_CALCULATOR_SETTINGS; -class PANEL_E_SERIES : public PANEL_E_SERIES_BASE +class PANEL_R_CALCULATOR : public PANEL_R_CALCULATOR_BASE { public: - PANEL_E_SERIES( wxWindow* parent, wxWindowID id = wxID_ANY, - const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, - long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString ); - ~PANEL_E_SERIES(); + PANEL_R_CALCULATOR( wxWindow* parent, wxWindowID id = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, + long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString ); + ~PANEL_R_CALCULATOR(); // Methods from CALCULATOR_PANEL that must be overridden void LoadSettings( PCB_CALCULATOR_SETTINGS* aCfg ) override; @@ -52,7 +53,7 @@ public: void OnESeriesSelection( wxCommandEvent& event ) override; private: - E_SERIES m_eSeries; + RES_EQUIV_CALC m_eSeries; }; #endif diff --git a/pcb_calculator/calculator_panels/panel_eseries_base.cpp b/pcb_calculator/calculator_panels/panel_r_calculator_base.cpp similarity index 89% rename from pcb_calculator/calculator_panels/panel_eseries_base.cpp rename to pcb_calculator/calculator_panels/panel_r_calculator_base.cpp index 4a843dd515..9d88f3b215 100644 --- a/pcb_calculator/calculator_panels/panel_eseries_base.cpp +++ b/pcb_calculator/calculator_panels/panel_r_calculator_base.cpp @@ -1,15 +1,15 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b) +// C++ code generated with wxFormBuilder (version 3.10.1-282-g1fa54006) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// -#include "panel_eseries_base.h" +#include "panel_r_calculator_base.h" /////////////////////////////////////////////////////////////////////////// -PANEL_E_SERIES_BASE::PANEL_E_SERIES_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : CALCULATOR_PANEL( parent, id, pos, size, style, name ) +PANEL_R_CALCULATOR_BASE::PANEL_R_CALCULATOR_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : CALCULATOR_PANEL( parent, id, pos, size, style, name ) { wxBoxSizer* bSizerESeries; bSizerESeries = new wxBoxSizer( wxVERTICAL ); @@ -203,22 +203,22 @@ PANEL_E_SERIES_BASE::PANEL_E_SERIES_BASE( wxWindow* parent, wxWindowID id, const bSizerESeries->Fit( this ); // Connect Events - m_e1->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnESeriesSelection ), NULL, this ); - m_e3->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnESeriesSelection ), NULL, this ); - m_e6->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnESeriesSelection ), NULL, this ); - m_e12->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnESeriesSelection ), NULL, this ); - m_e24->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnESeriesSelection ), NULL, this ); - m_buttonEScalculate->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnCalculateESeries ), NULL, this ); + m_e1->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnESeriesSelection ), NULL, this ); + m_e3->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnESeriesSelection ), NULL, this ); + m_e6->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnESeriesSelection ), NULL, this ); + m_e12->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnESeriesSelection ), NULL, this ); + m_e24->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnESeriesSelection ), NULL, this ); + m_buttonEScalculate->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnCalculateESeries ), NULL, this ); } -PANEL_E_SERIES_BASE::~PANEL_E_SERIES_BASE() +PANEL_R_CALCULATOR_BASE::~PANEL_R_CALCULATOR_BASE() { // Disconnect Events - m_e1->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnESeriesSelection ), NULL, this ); - m_e3->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnESeriesSelection ), NULL, this ); - m_e6->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnESeriesSelection ), NULL, this ); - m_e12->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnESeriesSelection ), NULL, this ); - m_e24->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnESeriesSelection ), NULL, this ); - m_buttonEScalculate->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_E_SERIES_BASE::OnCalculateESeries ), NULL, this ); + m_e1->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnESeriesSelection ), NULL, this ); + m_e3->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnESeriesSelection ), NULL, this ); + m_e6->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnESeriesSelection ), NULL, this ); + m_e12->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnESeriesSelection ), NULL, this ); + m_e24->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnESeriesSelection ), NULL, this ); + m_buttonEScalculate->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_R_CALCULATOR_BASE::OnCalculateESeries ), NULL, this ); } diff --git a/pcb_calculator/calculator_panels/panel_eseries_base.fbp b/pcb_calculator/calculator_panels/panel_r_calculator_base.fbp similarity index 98% rename from pcb_calculator/calculator_panels/panel_eseries_base.fbp rename to pcb_calculator/calculator_panels/panel_r_calculator_base.fbp index 6926ca35c2..2187511e6e 100644 --- a/pcb_calculator/calculator_panels/panel_eseries_base.fbp +++ b/pcb_calculator/calculator_panels/panel_r_calculator_base.fbp @@ -11,13 +11,13 @@ res UTF-8 connect - panel_eseries_base + panel_r_calculator_base 1000 none 1 - panel_eseries_base + panel_r_calculator_base . @@ -35,6 +35,7 @@ 1 + 0 1 impl_virtual @@ -43,10 +44,10 @@ wxID_ANY - PANEL_E_SERIES_BASE + PANEL_R_CALCULATOR_BASE -1,-1 - CALCULATOR_PANEL; calculator_panels/calculator_panel.h; + CALCULATOR_PANEL; calculator_panels/panel_r_calculator.h; forward_declare 0 @@ -119,6 +120,7 @@ Dock 0 Left + 0 1 1 @@ -180,6 +182,7 @@ Dock 0 Left + 0 1 1 @@ -244,6 +247,7 @@ Dock 0 Left + 0 1 1 @@ -305,6 +309,7 @@ Dock 0 Left + 0 1 1 @@ -366,6 +371,7 @@ Dock 0 Left + 0 1 1 @@ -430,6 +436,7 @@ Dock 0 Left + 0 1 1 @@ -491,6 +498,7 @@ Dock 0 Left + 0 1 1 @@ -552,6 +560,7 @@ Dock 0 Left + 0 1 1 @@ -616,6 +625,7 @@ Dock 0 Left + 0 1 1 @@ -679,6 +689,7 @@ Dock 0 Left + 0 1 1 @@ -746,6 +757,7 @@ Dock 0 Left + 0 1 1 @@ -811,6 +823,7 @@ Dock 0 Left + 0 1 1 @@ -876,6 +889,7 @@ Dock 0 Left + 0 1 1 @@ -941,6 +955,7 @@ Dock 0 Left + 0 1 1 @@ -1006,6 +1021,7 @@ Dock 0 Left + 0 1 1 @@ -1103,6 +1119,7 @@ Dock 0 Left + 0 1 1 @@ -1164,6 +1181,7 @@ Dock 0 Left + 0 1 1 @@ -1228,6 +1246,7 @@ Dock 0 Left + 0 1 1 @@ -1289,6 +1308,7 @@ Dock 0 Left + 0 1 1 @@ -1353,6 +1373,7 @@ Dock 0 Left + 0 1 1 @@ -1414,6 +1435,7 @@ Dock 0 Left + 0 1 1 @@ -1475,6 +1497,7 @@ Dock 0 Left + 0 1 1 @@ -1539,6 +1562,7 @@ Dock 0 Left + 0 1 1 @@ -1600,6 +1624,7 @@ Dock 0 Left + 0 1 1 @@ -1664,6 +1689,7 @@ Dock 0 Left + 0 1 1 @@ -1725,6 +1751,7 @@ Dock 0 Left + 0 1 1 @@ -1786,6 +1813,7 @@ Dock 0 Left + 0 1 1 @@ -1850,6 +1878,7 @@ Dock 0 Left + 0 1 1 @@ -1911,6 +1940,7 @@ Dock 0 Left + 0 1 1 @@ -1975,6 +2005,7 @@ Dock 0 Left + 0 1 1 @@ -2038,6 +2069,7 @@ Dock 0 Left + 0 1 1 @@ -2101,6 +2133,7 @@ Dock 0 Left + 0 1 1 @@ -2160,20 +2193,20 @@ - + 5 wxEXPAND|wxBOTTOM|wxRIGHT 1 - + bLowerESeries wxHORIZONTAL none - + 5 wxEXPAND|wxALL 1 - + wxID_ANY Help @@ -2206,6 +2239,7 @@ Dock 0 Left + 0 1 1 diff --git a/pcb_calculator/calculator_panels/panel_eseries_base.h b/pcb_calculator/calculator_panels/panel_r_calculator_base.h similarity index 85% rename from pcb_calculator/calculator_panels/panel_eseries_base.h rename to pcb_calculator/calculator_panels/panel_r_calculator_base.h index 9ba10798de..c03409bda8 100644 --- a/pcb_calculator/calculator_panels/panel_eseries_base.h +++ b/pcb_calculator/calculator_panels/panel_r_calculator_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b) +// C++ code generated with wxFormBuilder (version 3.10.1-282-g1fa54006) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -34,9 +34,9 @@ /////////////////////////////////////////////////////////////////////////////// -/// Class PANEL_E_SERIES_BASE +/// Class PANEL_R_CALCULATOR_BASE /////////////////////////////////////////////////////////////////////////////// -class PANEL_E_SERIES_BASE : public CALCULATOR_PANEL +class PANEL_R_CALCULATOR_BASE : public CALCULATOR_PANEL { private: @@ -82,9 +82,9 @@ class PANEL_E_SERIES_BASE : public CALCULATOR_PANEL public: - PANEL_E_SERIES_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString ); + PANEL_R_CALCULATOR_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString ); - ~PANEL_E_SERIES_BASE(); + ~PANEL_R_CALCULATOR_BASE(); }; diff --git a/pcb_calculator/eseries_display_help.h b/pcb_calculator/eseries_display_help.h new file mode 100644 index 0000000000..961ed97a07 --- /dev/null +++ b/pcb_calculator/eseries_display_help.h @@ -0,0 +1,14 @@ +// Do not edit this file, it is autogenerated by CMake from the .md file +_HKI( "Passive components are commonly made with E-series values appropriate to their precision.\n" +"Capacitors commonly use E12 values. 10% and 5% resistors commonly use E24 values. 1%\n" +"resistors use E96 values. Other series are not commonly used.\n" +"\n" +"To select a value begin with the calculated target value and then round it to 2\n" +"significant figures for E24 or below or 3 significant figures for E48 and up.\n" +"Then find the value in the table which is nearest to the significant figures\n" +"remaining and substitute it for those figures.\n" +"\n" +"For example if the calculated target value is 16,834.2Ω then this rounds to 16,800Ω.\n" +"The nearest value to 168 is 169 and the selected E96 value is 16.9kΩ.\n" +"\n" +"The value 0 is a special case and is not present in any series." ); diff --git a/pcb_calculator/eseries_display_help.md b/pcb_calculator/eseries_display_help.md new file mode 100644 index 0000000000..65c8ec7854 --- /dev/null +++ b/pcb_calculator/eseries_display_help.md @@ -0,0 +1,13 @@ +Passive components are commonly made with E-series values appropriate to their precision. +Capacitors commonly use E12 values. 10% and 5% resistors commonly use E24 values. 1% +resistors use E96 values. Other series are not commonly used. + +To select a value begin with the calculated target value and then round it to 2 +significant figures for E24 or below or 3 significant figures for E48 and up. +Then find the value in the table which is nearest to the significant figures +remaining and substitute it for those figures. + +For example if the calculated target value is 16,834.2Ω then this rounds to 16,800Ω. +The nearest value to 168 is 169 and the selected E96 value is 16.9kΩ. + +The value 0 is a special case and is not present in any series. \ No newline at end of file diff --git a/pcb_calculator/eseries_help.h b/pcb_calculator/eseries_help.h deleted file mode 100644 index 8e8299f611..0000000000 --- a/pcb_calculator/eseries_help.h +++ /dev/null @@ -1,24 +0,0 @@ -// Do not edit this file, it is autogenerated by CMake from the .md file -_HKI( "E-series are defined in IEC 60063.\n" -"\n" -"Available values are approximately equally spaced in a logarithmic scale.\n" -"\n" -" E24(5%): 1.0 1.1 1.2 1.3 1.5 1.6 1.8 2.0 2.2 2.4 2.7 3.0 3.3 3.6 3.9 4.3 4.7 5.1 5.6 6.2 6.8 7.5 8.2 9.1\n" -" E12(10%): 1.0 1.2 1.5 1.8 2.2 2.7 3.3 3.9 4.7 5.6 6.8 8.2\n" -" E6(20%): 1.0 - 1.5 - 2.2 - 3.3 - 4.7 - 6.8 -\n" -" E3(50%): 1.0 - - - 2.2 - - - 4.7 - - -\n" -" E1 : 1.0 - - - - - - - - - - -\n" -"\n" -"- This calculator finds combinations of standard E-series (between 10Ω and 1MΩ) to create arbitrary values.\n" -"- You can enter the required resistance from 0.0025 to 4000 kΩ.\n" -"- Solutions using up to 4 components are given.\n" -"\n" -"The requested value is always excluded from the solution set.
\n" -"Optionally up to two additional values can be excluded in case of component availability problems.\n" -"\n" -"Solutions are given in the following formats:\n" -"\n" -" R1 + R2 +...+ Rn resistors in series\n" -" R1 | R2 |...| Rn resistors in parallel\n" -" R1 + (R2|R3)... any combination of the above\n" -"" ); diff --git a/pcb_calculator/pcb_calculator_frame.cpp b/pcb_calculator/pcb_calculator_frame.cpp index a852a753c8..6a8a4e5437 100644 --- a/pcb_calculator/pcb_calculator_frame.cpp +++ b/pcb_calculator/pcb_calculator_frame.cpp @@ -42,13 +42,14 @@ #include #include #include -#include +#include #include #include #include #include #include #include +#include #include "widgets/wx_menubar.h" @@ -142,6 +143,7 @@ void PCB_CALCULATOR_FRAME::loadPages() m_treebook->AddPage( nullptr, _( "General system design" ) ); AddCalculator( new PANEL_REGULATOR( m_treebook ), _( "Regulators" ) ); + AddCalculator( new PANEL_R_CALCULATOR( m_treebook ), _( "Resistor Calculator" ) ); m_treebook->AddPage( nullptr, _( "Power, current and isolation" ) ); @@ -159,7 +161,7 @@ void PCB_CALCULATOR_FRAME::loadPages() m_treebook->AddPage( nullptr, _( "Memo" ) ); - AddCalculator( new PANEL_E_SERIES( m_treebook ), _( "E-Series" ) ); + AddCalculator( new PANEL_ESERIES_DISPLAY( m_treebook ), _( "E-Series" ) ); AddCalculator( new PANEL_COLOR_CODE( m_treebook ), _( "Color Code" ) ); AddCalculator( new PANEL_BOARD_CLASS( m_treebook ), _( "Board Classes" ) ); AddCalculator( new PANEL_GALVANIC_CORROSION( m_treebook ), _( "Galvanic Corrosion" ) ); diff --git a/pcb_calculator/r_calculator_help.h b/pcb_calculator/r_calculator_help.h new file mode 100644 index 0000000000..e587eacb2b --- /dev/null +++ b/pcb_calculator/r_calculator_help.h @@ -0,0 +1,14 @@ +// Do not edit this file, it is autogenerated by CMake from the .md file +_HKI( "- This calculator finds combinations of standard E-series (between 10Ω and 1MΩ) to create arbitrary values.\n" +"- You can enter the required resistance from 0.0025 to 4000 kΩ.\n" +"- Solutions using up to 4 components are given.\n" +"\n" +"The requested value is always excluded from the solution set.
\n" +"Optionally up to two additional values can be excluded in case of component availability problems.\n" +"\n" +"Solutions are given in the following formats:\n" +"\n" +" R1 + R2 +...+ Rn resistors in series\n" +" R1 | R2 |...| Rn resistors in parallel\n" +" R1 + (R2|R3)... any combination of the above\n" +"" ); diff --git a/pcb_calculator/eseries_help.md b/pcb_calculator/r_calculator_help.md similarity index 55% rename from pcb_calculator/eseries_help.md rename to pcb_calculator/r_calculator_help.md index 765cf1c2d5..135e51f0cc 100644 --- a/pcb_calculator/eseries_help.md +++ b/pcb_calculator/r_calculator_help.md @@ -1,13 +1,3 @@ -E-series are defined in IEC 60063. - -Available values are approximately equally spaced in a logarithmic scale. - - E24(5%): 1.0 1.1 1.2 1.3 1.5 1.6 1.8 2.0 2.2 2.4 2.7 3.0 3.3 3.6 3.9 4.3 4.7 5.1 5.6 6.2 6.8 7.5 8.2 9.1 - E12(10%): 1.0 1.2 1.5 1.8 2.2 2.7 3.3 3.9 4.7 5.6 6.8 8.2 - E6(20%): 1.0 - 1.5 - 2.2 - 3.3 - 4.7 - 6.8 - - E3(50%): 1.0 - - - 2.2 - - - 4.7 - - - - E1 : 1.0 - - - - - - - - - - - - - This calculator finds combinations of standard E-series (between 10Ω and 1MΩ) to create arbitrary values. - You can enter the required resistance from 0.0025 to 4000 kΩ. - Solutions using up to 4 components are given. diff --git a/pcb_calculator/resistor_substitution_utils.cpp b/pcb_calculator/resistor_substitution_utils.cpp new file mode 100644 index 0000000000..cfa8e9787f --- /dev/null +++ b/pcb_calculator/resistor_substitution_utils.cpp @@ -0,0 +1,393 @@ +/* + * This program source code file + * is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 + * Copyright (C) 2021-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, see . + */ + +#include +#include +#include +#include +#include "resistor_substitution_utils.h" +#include "eseries.h" + +/* + * If BENCHMARK is defined, any 4R E12 calculations will print its execution time to console + * My Hasswell Enthusiast reports 225 mSec what are reproducible within plusminus 2 percent + */ +//#define BENCHMARK + +#ifdef BENCHMARK +#include +#endif + + +// Return a string from aValue (aValue is expected in ohms) +// If aValue < 1000 the returned string is aValue with unit = R +// If aValue >= 1000 the returned string is aValue/1000 with unit = K +// with notation similar to 2K2 +// If aValue >= 1e6 the returned string is aValue/1e6 with unit = M +// with notation = 1M +static std::string strValue( double aValue ) +{ + std::string result; + + if( aValue < 1000.0 ) + { + result = std::to_string( static_cast( aValue ) ); + result += 'R'; + } + else + { + double div = 1e3; + char unit = 'K'; + + if( aValue >= 1e6 ) + { + div = 1e6; + unit = 'M'; + } + + aValue /= div; + + int valueAsInt = static_cast( aValue ); + result = std::to_string( valueAsInt ); + result += unit; + + // Add mantissa: 1 digit, suitable for series up to E24 + double mantissa = aValue - valueAsInt; + + if( mantissa > 0 ) + result += std::to_string( static_cast( ( mantissa * 10 ) + 0.5 ) ); + } + + return result; +} + + +RES_EQUIV_CALC::RES_EQUIV_CALC() +{ + // Build the list of available resistor values in each En serie + const ESERIES::ESERIES_VALUES listValuesE1 = ESERIES::E1_VALUES(); + const ESERIES::ESERIES_VALUES listValuesE3 = ESERIES::E3_VALUES(); + const ESERIES::ESERIES_VALUES listValuesE6 = ESERIES::E6_VALUES(); + const ESERIES::ESERIES_VALUES listValuesE12 = ESERIES::E12_VALUES(); + const ESERIES::ESERIES_VALUES listValuesE24 = ESERIES::E24_VALUES(); + // buildSeriesData must be called in the order of En series, because + // the list of series is expected indexed by En for the serie En + buildSeriesData( listValuesE1 ); + buildSeriesData( listValuesE3 ); + buildSeriesData( listValuesE6 ); + buildSeriesData( listValuesE12 ); + int count = buildSeriesData( listValuesE24 ); + + // Reserve a buffer for intermediate calculations: + // the buffer size is 2*count*count to store all combinations of 2 values + // there are 2*count*count = 29282 combinations for E24 + int bufsize = 2 * count * count; + m_combined_table.reserve( bufsize ); + + // Store predefined R_DATA items. + for( int ii = 0; ii < bufsize; ii++ ) + m_combined_table.emplace_back( "", 0.0 ); +} + + +int RES_EQUIV_CALC::buildSeriesData( const ESERIES::ESERIES_VALUES aList ) +{ + uint_fast32_t curr_decade = FIRST_VALUE; + int count = 0; + + std::vector curr_list; + + uint_fast16_t first_value_in_decade = aList[0]; + + for( ;; ) + { + double curr_r = LAST_VALUE; + + for( const uint16_t listvalue : aList ) + { + curr_r = 1.0 * curr_decade * listvalue / first_value_in_decade; + curr_list.emplace_back( strValue( curr_r ), curr_r ); + count++; + + if( curr_r >= LAST_VALUE ) + break; + } + + if( curr_r >= LAST_VALUE ) + break; + + curr_decade *= 10; + } + + m_tables.push_back( std::move( curr_list ) ); + + return count; +} + + +void RES_EQUIV_CALC::Exclude( double aValue ) +{ + if( aValue != 0.0 ) // if there is a value to exclude other than a wire jumper + { + for( R_DATA& i : m_tables[m_series] ) // then search it in the selected E-Series table + { + if( i.e_value == aValue ) // if the value to exclude is found + i.e_use = false; // disable its use + } + } +} + + +void RES_EQUIV_CALC::simple_solution( uint32_t aSize ) +{ + uint32_t i; + + m_results.at( S2R ).e_value = + std::numeric_limits::max(); // assume no 2R solution or max deviation + + for( i = 0; i < aSize; i++ ) + { + if( std::abs( m_combined_table.at( i ).e_value - m_required_value ) + < std::abs( m_results.at( S2R ).e_value ) ) + { + m_results[S2R].e_value = + m_combined_table[i].e_value - m_required_value; // save signed deviation in Ohms + m_results[S2R].e_name = m_combined_table[i].e_name; // save combination text + m_results[S2R].e_use = true; // this is a possible solution + } + } +} + + +void RES_EQUIV_CALC::combine4( uint32_t aSize ) +{ + uint32_t i, j; + double tmp; + + m_results[S4R].e_use = false; // disable 4R solution, until + m_results[S4R].e_value = m_results[S3R].e_value; // 4R becomes better than 3R solution + +#ifdef BENCHMARK + PROF_TIMER timer; // start timer to count execution time +#endif + + for( i = 0; i < aSize; i++ ) // 4R search outer loop + { // scan valid intermediate 2R solutions + for( j = 0; j < aSize; j++ ) // inner loop combines all with itself + { + tmp = m_combined_table[i].e_value + + m_combined_table[j].e_value; // calculate 2R+2R serial + tmp -= m_required_value; // calculate 4R deviation + + if( std::abs( tmp ) < std::abs( m_results.at( S4R ).e_value ) ) // if new 4R is better + { + m_results[S4R].e_value = tmp; // save amount of benefit + std::string s = "( "; + s.append( m_combined_table[i].e_name ); // mention 1st 2 component + s.append( " ) + ( " ); // in series + s.append( m_combined_table[j].e_name ); // with 2nd 2 components + s.append( " )" ); + m_results[S4R].e_name = s; // save the result and + m_results[S4R].e_use = true; // enable for later use + } + + tmp = ( m_combined_table[i].e_value * m_combined_table[j].e_value ) + / ( m_combined_table[i].e_value + + m_combined_table[j].e_value ); // calculate 2R|2R parallel + tmp -= m_required_value; // calculate 4R deviation + + if( std::abs( tmp ) < std::abs( m_results[S4R].e_value ) ) // if new 4R is better + { + m_results[S4R].e_value = tmp; // save amount of benefit + std::string s = "( "; + s.append( m_combined_table[i].e_name ); // mention 1st 2 component + s.append( " ) | ( " ); // in parallel + s.append( m_combined_table[j].e_name ); // with 2nd 2 components + s.append( " )" ); + m_results[S4R].e_name = s; // save the result + m_results[S4R].e_use = true; // enable later use + } + } + } + +#ifdef BENCHMARK + printf( "Calculation time = %d mS", timer.msecs() ); + fflush( 0 ); +#endif +} + + +void RES_EQUIV_CALC::NewCalc() +{ + for( R_DATA& i : m_combined_table ) + i.e_use = false; // before any calculation is done, assume that + + for( R_DATA& i : m_results ) + i.e_use = false; // no combinations and no results are available + + for( R_DATA& i : m_tables[m_series] ) + i.e_use = true; // all selected E-values available +} + + +uint32_t RES_EQUIV_CALC::combine2() +{ + uint32_t combi2R = 0; // target index counts calculated 2R combinations + std::string s; + + for( const R_DATA& i : m_tables[m_series] ) // outer loop to sweep selected source lookup table + { + if( i.e_use ) + { + for( const R_DATA& j : m_tables[m_series] ) // inner loop to combine values with itself + { + if( j.e_use ) + { + m_combined_table[combi2R].e_use = true; + m_combined_table[combi2R].e_value = + i.e_value + j.e_value; // calculate 2R serial + s = i.e_name; + s.append( " + " ); + m_combined_table[combi2R].e_name = s.append( j.e_name ); + combi2R++; // next destination + m_combined_table[combi2R].e_use = true; // calculate 2R parallel + m_combined_table[combi2R].e_value = + i.e_value * j.e_value / ( i.e_value + j.e_value ); + s = i.e_name; + s.append( " | " ); + m_combined_table[combi2R].e_name = s.append( j.e_name ); + combi2R++; // next destination + } + } + } + } + return combi2R; +} + + +void RES_EQUIV_CALC::combine3( uint32_t aSize ) +{ + uint32_t j = 0; + double tmp = 0; // avoid warning for being uninitialized + std::string s; + + m_results[S3R].e_use = false; // disable 3R solution, until 3R + m_results[S3R].e_value = m_results[S2R].e_value; // becomes better than 2R solution + + for( const R_DATA& i : m_tables[m_series] ) // 3R Outer loop to selected primary E series table + { + if( i.e_use ) // skip all excluded values + { + for( j = 0; j < aSize; j++ ) // inner loop combines with all 2R intermediate + { // results R+2R serial combi + tmp = m_combined_table[j].e_value + i.e_value; + tmp -= m_required_value; // calculate deviation + + if( std::abs( tmp ) < std::abs( m_results[S3R].e_value ) ) // compare if better + { // then take it + s = i.e_name; // mention 3rd component + s.append( " + ( " ); // in series + s.append( m_combined_table[j].e_name ); // with 2R combination + s.append( " )" ); + m_results[S3R].e_name = s; // save S3R result + m_results[S3R].e_value = tmp; // save amount of benefit + m_results[S3R].e_use = true; // enable later use + } + + tmp = i.e_value * m_combined_table[j].e_value + / ( i.e_value + m_combined_table[j].e_value ); // calculate R + 2R parallel + tmp -= m_required_value; // calculate deviation + + if( std::abs( tmp ) < std::abs( m_results[S3R].e_value ) ) // compare if better + { // then take it + s = i.e_name; // mention 3rd component + s.append( " | ( " ); // in parallel + s.append( m_combined_table[j].e_name ); // with 2R combination + s.append( " )" ); + m_results[S3R].e_name = s; + m_results[S3R].e_value = tmp; // save amount of benefit + m_results[S3R].e_use = true; // enable later use + } + } + } + } + + // If there is a 3R result with remaining deviation consider to search a possibly better + // 4R solution + // calculate 4R for small series always + if( m_results[S3R].e_use && tmp ) + combine4( aSize ); +} + + +void RES_EQUIV_CALC::Calculate() +{ + uint32_t no_of_2Rcombi = 0; + + no_of_2Rcombi = combine2(); // combine all 2R combinations for selected E serie + simple_solution( no_of_2Rcombi ); // search for simple 2 component solution + + if( m_results[S2R].e_value ) // if simple 2R result is not exact + combine3( no_of_2Rcombi ); // continiue searching for a possibly better solution + + strip3(); + strip4(); +} + + +void RES_EQUIV_CALC::strip3() +{ + std::string s; + + if( m_results[S3R].e_use ) // if there is a 3 term result available + { // what is connected either by two "|" or by 3 plus + s = m_results[S3R].e_name; + + if( ( std::count( s.begin(), s.end(), '+' ) == 2 ) + || ( std::count( s.begin(), s.end(), '|' ) == 2 ) ) + { // then strip one pair of braces + s.erase( s.find( "( " ), 2 ); // it is known sure, this is available + s.erase( s.find( " )" ), 2 ); // in any unstripped 3R result term + m_results[S3R].e_name = s; // use stripped result + } + } +} + + +void RES_EQUIV_CALC::strip4() +{ + std::string s; + + if( m_results[S4R].e_use ) // if there is a 4 term result available + { // what are connected either by 3 "+" or by 3 "|" + s = m_results[S4R].e_name; + + if( ( std::count( s.begin(), s.end(), '+' ) == 3 ) + || ( std::count( s.begin(), s.end(), '|' ) == 3 ) ) + { // then strip two pair of braces + s.erase( s.find( "( " ), 2 ); // it is known sure, they are available + s.erase( s.find( " )" ), 2 ); // in any unstripped 4R result term + s.erase( s.find( "( " ), 2 ); + s.erase( s.find( " )" ), 2 ); + m_results[S4R].e_name = s; // use stripped result + } + } +} diff --git a/pcb_calculator/resistor_substitution_utils.h b/pcb_calculator/resistor_substitution_utils.h new file mode 100644 index 0000000000..0240d64bb3 --- /dev/null +++ b/pcb_calculator/resistor_substitution_utils.h @@ -0,0 +1,187 @@ +/* + * This program source code file + * is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 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, see . + */ + +#pragma once + +#include +#include +#include +#include +#include "eseries.h" + +// First value of resistor in ohm +// This value is only pertinent to the resistor calculator. +// It is used to reduce the computational complexity of its calculations. +// There are valid resistor values using E-series numbers below this +// value and above the below LAST_VALUE. +#define FIRST_VALUE 10 + +// last value of resistor in ohm +// This value is only pertinent to the resistor calculator. See above. +#define LAST_VALUE 1e6 + +// R_DATA handles a resistor: string value, value and allowed to use +struct R_DATA +{ + R_DATA() : e_use( true ), e_value( 0.0 ) {} + + R_DATA( const std::string& aName, double aValue ) + { + e_use = true; + e_name = aName; + e_value = aValue; + } + + bool e_use; + std::string e_name; + double e_value; +}; + +class RES_EQUIV_CALC +/*! \brief Performs calculations on E-series values primarily to find target values. + * + * E_SERIES class stores and performs calcuations on E-series values. It currently + * is targeted toward the resistor calculator and hard codes some limitations + * to optimize its use in the resistor calculator. + * + * At this time these limitations are that this class ignores all E-series larger + * than E24 and it does not consider resistor values below 10 Ohm or above 1M Ohm. + */ +{ +public: + RES_EQUIV_CALC(); + + /** + * This calculator suggests solutions for 2R, 3R and 4R replacement combinations + */ + enum + { + S2R, + S3R, + S4R + }; + + /** + * If any value of the selected E-series not available, it can be entered as an exclude value. + * + * @param aValue is the value to exclude from calculation + * Values to exclude are set to false in the selected E-series source lookup table + */ + void Exclude( double aValue ); + + /** + * initialize next calculation and erase results from previous calculation + */ + void NewCalc(); + + /** + * called on calculate button to execute all the 2R, 3R and 4R calculations + */ + void Calculate(); + + /** + * Interface for CheckBox, RadioButton, RequriedResistor and calculated Results + */ + void SetSeries( uint32_t aSeries ) { m_series = aSeries; } + void SetRequiredValue( double aValue ) { m_required_value = aValue; } + + // Accessor: + const std::array& GetResults() { return m_results; } + +private: + /** + * Add values from aList to m_tables. Covers all decades between FIRST_VALUE and LAST_VALUE. + * @return the count of items added to m_tables. + */ + int buildSeriesData( const ESERIES::ESERIES_VALUES ); + + /** + * Build all 2R combinations from the selected E-series values + * + * Pre-calculated value combinations are saved in intermediate look up table m_combined_table + * @return is the number of found combinations what also depends from exclude values + */ + uint32_t combine2(); + + /** + * Search for closest two component solution + * + * @param aSize is the number of valid 2R combinations in m_combined_table on where to search + * The 2R result with smallest deviation will be saved in results + */ + void simple_solution( uint32_t aSize ); + + /** + * Check if there is a better 3 R solution than previous one using only two components. + * + * @param aSize gives the number of available combinations to be checked inside + * m_combined_table. Therefore m_combined_table is combined with the primary + * E-series look up table. The 3R result with smallest deviation will be saved + * in results if better than 2R + */ + void combine3( uint32_t aSize ); + + /** + * Check if there is a better four component solution. + * + * @param aSsize gives the number of 2R combinations to be checked inside m_combined_table + * Occupied calculation time depends from number of available E-series values with the power + * of 4 why execution for E12 is conditional with 4R check box for the case the previously + * found 3R solution is already exact + */ + void combine4( uint32_t aSize ); + + /* + * Strip redundant braces from three component result + * + * Example: R1+(R2+R3) become R1+R2+R3 + * and R1|(R2|R3) become R1|R2|R3 + * while R1+(R2|R3) or (R1+R2)|R3) remains untouched + */ + void strip3(); + + /* + * Strip redundant braces from four component result + * + * Example: (R1+R2)+(R3+R4) become R1+R2+R3+R4 + * and (R1|R2)|(R2|R3) become R1|R2|R3|R4 + * while (R1+R2)|(R3+R4) remains untouched + */ + void strip4(); + +private: + std::vector> m_tables; + + /* Note: intermediate calculations use m_combined_table + * if the biggest list is En, reserved array size should be 2*En*En of std::vector primary list. + * 2 component combinations including redundant swappable terms are for the moment + * ( using values between 10 ohms and 1Mohm ) + * 72 combinations for E1 + * 512 combinations for E3 + * 1922 combinations for E6 + * 7442 combinations for E12 + * 29282 combinations for E24 + */ + std::vector m_combined_table; // intermediate 2R combinations + + std::array m_results; // 2R, 3R and 4R results + uint32_t m_series = ESERIES::E6; // Radio Button State + double m_required_value = 0.0; // required Resistor +};