You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

387 lines
14 KiB

  1. /*
  2. * This program source code file
  3. * is part of KiCad, a free EDA CAD application.
  4. *
  5. * Copyright (C) 2020 <janvi@veith.net>
  6. * Copyright (C) 2021-2023 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software: you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation, either version 3 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <array>
  22. #include <algorithm>
  23. #include <limits>
  24. #include "eseries.h"
  25. /*
  26. * If BENCHMARK is defined, any 4R E12 calculations will print its execution time to console
  27. * My Hasswell Enthusiast reports 225 mSec what are reproducible within plusminus 2 percent
  28. */
  29. //#define BENCHMARK
  30. #ifdef BENCHMARK
  31. #include <profile.h>
  32. #endif
  33. // Return a string from aValue (aValue is expected in ohms)
  34. // If aValue < 1000 the returned string is aValue with unit = R
  35. // If aValue >= 1000 the returned string is aValue/1000 with unit = K
  36. // with notation similar to 2K2
  37. // If aValue >= 1e6 the returned string is aValue/1e6 with unit = M
  38. // with notation = 1M
  39. static std::string strValue( double aValue )
  40. {
  41. std::string result;
  42. if( aValue < 1000.0 )
  43. {
  44. result = std::to_string( static_cast<int>( aValue ) );
  45. result += 'R';
  46. }
  47. else
  48. {
  49. double div = 1e3;
  50. char unit = 'K';
  51. if( aValue >= 1e6 )
  52. {
  53. div = 1e6;
  54. unit = 'M';
  55. }
  56. aValue /= div;
  57. int integer = static_cast<int>( aValue );
  58. result = std::to_string(integer);
  59. result += unit;
  60. // Add mantissa: 1 digit, suitable for series up to E24
  61. double mantissa = aValue - integer;
  62. if( mantissa > 0 )
  63. result += std::to_string( static_cast<int>( (mantissa*10)+0.5 ) );
  64. }
  65. return result;
  66. }
  67. E_SERIES::E_SERIES()
  68. {
  69. // Build the list of available resistor values in each En serie
  70. double listValuesE1[] = { E1_VALUES };
  71. double listValuesE3[] = { E3_VALUES };
  72. double listValuesE6[] = { E6_VALUES };
  73. double listValuesE12[] = { E12_VALUES };
  74. double listValuesE24[] = { E24_VALUES };
  75. // buildSeriesData must be called in the order of En series, because
  76. // the list of series is expected indexed by En for the serie En
  77. buildSeriesData( listValuesE1 );
  78. buildSeriesData( listValuesE3 );
  79. buildSeriesData( listValuesE6 );
  80. buildSeriesData( listValuesE12 );
  81. int count = buildSeriesData( listValuesE24 );
  82. // Reserve a buffer for intermediate calculations:
  83. // the buffer size is 2*count*count to store all combinaisons of 2 values
  84. // there are 2*count*count = 29282 combinations for E24
  85. int bufsize = 2*count*count;
  86. m_combined_table.reserve( bufsize );
  87. // Store predefined R_DATA items.
  88. for( int ii = 0; ii < bufsize; ii++ )
  89. m_combined_table.emplace_back( "", 0.0 );
  90. }
  91. int E_SERIES::buildSeriesData( const double aList[] )
  92. {
  93. double curr_decade = FIRST_VALUE;
  94. int count = 0;
  95. std::vector<R_DATA> curr_list;
  96. for( ; ; )
  97. {
  98. double curr_r = LAST_VALUE;
  99. for( int ii = 0; ; ii++ )
  100. {
  101. if( aList[ii] == 0.0 ) // End of list
  102. break;
  103. curr_r = curr_decade * aList[ii];
  104. curr_list.emplace_back( strValue( curr_r ), curr_r );
  105. count++;
  106. if( curr_r >= LAST_VALUE )
  107. break;
  108. }
  109. if( curr_r >= LAST_VALUE )
  110. break;
  111. curr_decade *= 10;
  112. }
  113. m_tables.push_back( std::move( curr_list ) );
  114. return count;
  115. }
  116. void E_SERIES::Exclude( double aValue )
  117. {
  118. if( aValue != 0.0 ) // if there is a value to exclude other than a wire jumper
  119. {
  120. for( R_DATA& i : m_tables[m_series] ) // then search it in the selected E-Series table
  121. {
  122. if( i.e_value == aValue ) // if the value to exclude is found
  123. i.e_use = false; // disable its use
  124. }
  125. }
  126. }
  127. void E_SERIES::simple_solution( uint32_t aSize )
  128. {
  129. uint32_t i;
  130. m_results.at( S2R ).e_value = std::numeric_limits<double>::max(); // assume no 2R solution or max deviation
  131. for( i = 0; i < aSize; i++ )
  132. {
  133. if( std::abs( m_combined_table.at( i ).e_value - m_required_value ) < std::abs( m_results.at( S2R ).e_value ) )
  134. {
  135. m_results[S2R].e_value = m_combined_table[ i ].e_value - m_required_value; // save signed deviation in Ohms
  136. m_results[S2R].e_name = m_combined_table[ i ].e_name; // save combination text
  137. m_results[S2R].e_use = true; // this is a possible solution
  138. }
  139. }
  140. }
  141. void E_SERIES::combine4( uint32_t aSize )
  142. {
  143. uint32_t i,j;
  144. double tmp;
  145. m_results[S4R].e_use = false; // disable 4R solution, until
  146. m_results[S4R].e_value = m_results[S3R].e_value; // 4R becomes better than 3R solution
  147. #ifdef BENCHMARK
  148. PROF_TIMER timer; // start timer to count execution time
  149. #endif
  150. for( i = 0; i < aSize; i++ ) // 4R search outer loop
  151. { // scan valid intermediate 2R solutions
  152. for( j = 0; j < aSize; j++ ) // inner loop combines all with itself
  153. {
  154. tmp = m_combined_table[i].e_value + m_combined_table[j].e_value; // calculate 2R+2R serial
  155. tmp -= m_required_value; // calculate 4R deviation
  156. if( std::abs( tmp ) < std::abs( m_results.at(S4R).e_value ) ) // if new 4R is better
  157. {
  158. m_results[S4R].e_value = tmp; // save amount of benefit
  159. std::string s = "( ";
  160. s.append( m_combined_table[i].e_name ); // mention 1st 2 component
  161. s.append( " ) + ( " ); // in series
  162. s.append( m_combined_table[j].e_name ); // with 2nd 2 components
  163. s.append( " )" );
  164. m_results[S4R].e_name = s; // save the result and
  165. m_results[S4R].e_use = true; // enable for later use
  166. }
  167. tmp = ( m_combined_table[i].e_value * m_combined_table[j].e_value ) /
  168. ( m_combined_table[i].e_value + m_combined_table[j].e_value ); // calculate 2R|2R parallel
  169. tmp -= m_required_value; // calculate 4R deviation
  170. if( std::abs( tmp ) < std::abs( m_results[S4R].e_value ) ) // if new 4R is better
  171. {
  172. m_results[S4R].e_value = tmp; // save amount of benefit
  173. std::string s = "( ";
  174. s.append( m_combined_table[i].e_name ); // mention 1st 2 component
  175. s.append( " ) | ( " ); // in parallel
  176. s.append( m_combined_table[j].e_name ); // with 2nd 2 components
  177. s.append( " )" );
  178. m_results[S4R].e_name = s; // save the result
  179. m_results[S4R].e_use = true; // enable later use
  180. }
  181. }
  182. }
  183. #ifdef BENCHMARK
  184. printf( "Calculation time = %d mS", timer.msecs() );
  185. fflush( 0 );
  186. #endif
  187. }
  188. void E_SERIES::NewCalc()
  189. {
  190. for( R_DATA& i : m_combined_table )
  191. i.e_use = false; // before any calculation is done, assume that
  192. for( R_DATA& i : m_results )
  193. i.e_use = false; // no combinations and no results are available
  194. for( R_DATA& i : m_tables[m_series])
  195. i.e_use = true; // all selected E-values available
  196. }
  197. uint32_t E_SERIES::combine2()
  198. {
  199. uint32_t combi2R = 0; // target index counts calculated 2R combinations
  200. std::string s;
  201. for( const R_DATA& i : m_tables[m_series] ) // outer loop to sweep selected source lookup table
  202. {
  203. if( i.e_use )
  204. {
  205. for( const R_DATA& j : m_tables[m_series] ) // inner loop to combine values with itself
  206. {
  207. if( j.e_use )
  208. {
  209. m_combined_table[combi2R].e_use = true;
  210. m_combined_table[combi2R].e_value = i.e_value + j.e_value; // calculate 2R serial
  211. s = i.e_name;
  212. s.append( " + " );
  213. m_combined_table[combi2R].e_name = s.append( j.e_name);
  214. combi2R++; // next destination
  215. m_combined_table[combi2R].e_use = true; // calculate 2R parallel
  216. m_combined_table[combi2R].e_value = i.e_value * j.e_value / ( i.e_value + j.e_value );
  217. s = i.e_name;
  218. s.append( " | " );
  219. m_combined_table[combi2R].e_name = s.append( j.e_name );
  220. combi2R++; // next destination
  221. }
  222. }
  223. }
  224. }
  225. return combi2R;
  226. }
  227. void E_SERIES::combine3( uint32_t aSize )
  228. {
  229. uint32_t j = 0;
  230. double tmp = 0; // avoid warning for being uninitialized
  231. std::string s;
  232. m_results[S3R].e_use = false; // disable 3R solution, until 3R
  233. m_results[S3R].e_value = m_results[S2R].e_value; // becomes better than 2R solution
  234. for( const R_DATA& i : m_tables[m_series] ) // 3R Outer loop to selected primary E series table
  235. {
  236. if( i.e_use ) // skip all excluded values
  237. {
  238. for( j = 0; j < aSize; j++ ) // inner loop combines with all 2R intermediate
  239. { // results R+2R serial combi
  240. tmp = m_combined_table[j].e_value + i.e_value;
  241. tmp -= m_required_value; // calculate deviation
  242. if( std::abs( tmp ) < std::abs( m_results[S3R].e_value ) ) // compare if better
  243. { // then take it
  244. s = i.e_name; // mention 3rd component
  245. s.append( " + ( " ); // in series
  246. s.append( m_combined_table[j].e_name ); // with 2R combination
  247. s.append( " )" );
  248. m_results[S3R].e_name = s; // save S3R result
  249. m_results[S3R].e_value = tmp; // save amount of benefit
  250. m_results[S3R].e_use = true; // enable later use
  251. }
  252. tmp = i.e_value * m_combined_table[j].e_value /
  253. ( i.e_value + m_combined_table[j].e_value ); // calculate R + 2R parallel
  254. tmp -= m_required_value; // calculate deviation
  255. if( std::abs( tmp ) < std::abs( m_results[S3R].e_value ) ) // compare if better
  256. { // then take it
  257. s = i.e_name; // mention 3rd component
  258. s.append( " | ( " ); // in parallel
  259. s.append( m_combined_table[j].e_name ); // with 2R combination
  260. s.append( " )" );
  261. m_results[S3R].e_name = s;
  262. m_results[S3R].e_value = tmp; // save amount of benefit
  263. m_results[S3R].e_use = true; // enable later use
  264. }
  265. }
  266. }
  267. }
  268. // If there is a 3R result with remaining deviation consider to search a possibly better
  269. // 4R solution
  270. // calculate 4R for small series always
  271. if( m_results[S3R].e_use && tmp )
  272. combine4( aSize );
  273. }
  274. void E_SERIES::Calculate()
  275. {
  276. uint32_t no_of_2Rcombi = 0;
  277. no_of_2Rcombi = combine2(); // combine all 2R combinations for selected E serie
  278. simple_solution( no_of_2Rcombi ); // search for simple 2 component solution
  279. if( m_results[S2R].e_value ) // if simple 2R result is not exact
  280. combine3( no_of_2Rcombi ); // continiue searching for a possibly better solution
  281. strip3();
  282. strip4();
  283. }
  284. void E_SERIES::strip3()
  285. {
  286. std::string s;
  287. if( m_results[S3R].e_use ) // if there is a 3 term result available
  288. { // what is connected either by two "|" or by 3 plus
  289. s = m_results[S3R].e_name;
  290. if( ( std::count( s.begin(), s.end(), '+' ) == 2 )
  291. || ( std::count( s.begin(), s.end(), '|' ) == 2 ) )
  292. { // then strip one pair of braces
  293. s.erase( s.find( "( " ), 2 ); // it is known sure, this is available
  294. s.erase( s.find( " )" ), 2 ); // in any unstripped 3R result term
  295. m_results[S3R].e_name = s; // use stripped result
  296. }
  297. }
  298. }
  299. void E_SERIES::strip4()
  300. {
  301. std::string s;
  302. if( m_results[S4R].e_use ) // if there is a 4 term result available
  303. { // what are connected either by 3 "+" or by 3 "|"
  304. s = m_results[S4R].e_name;
  305. if( ( std::count( s.begin(), s.end(), '+' ) == 3 )
  306. || ( std::count( s.begin(), s.end(), '|' ) == 3 ) )
  307. { // then strip two pair of braces
  308. s.erase( s.find( "( " ), 2 ); // it is known sure, they are available
  309. s.erase( s.find( " )" ), 2 ); // in any unstripped 4R result term
  310. s.erase( s.find( "( " ), 2 );
  311. s.erase( s.find( " )" ), 2 );
  312. m_results[S4R].e_name = s; // use stripped result
  313. }
  314. }
  315. }