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.

397 lines
15 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 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 <string_utils.h>
  23. #include "class_regulator_data.h"
  24. #include "pcb_calculator_frame.h"
  25. #ifdef BENCHMARK
  26. #include <sys/time.h>
  27. #endif
  28. #include "eserie.h"
  29. wxString eseries_help =
  30. #include "eserie_help.h"
  31. eserie r;
  32. void eserie::Exclude( double aValue )
  33. {
  34. if( aValue ) // if there is a value to exclude other than a wire jumper
  35. {
  36. for( r_data& i : luts[m_series] ) // then search it in the selected E-Serie lookup table
  37. {
  38. if( i.e_value == aValue ) // if value to exclude found
  39. i.e_use = false; // disable its use
  40. }
  41. }
  42. }
  43. void eserie::simple_solution( uint32_t aSize )
  44. {
  45. uint32_t i;
  46. m_results[S2R].e_value = std::numeric_limits<double>::max(); // assume no 2R solution or max deviation
  47. for( i = 0; i < aSize; i++ )
  48. {
  49. if( abs( m_cmb_lut[i].e_value - m_required_value ) < abs( m_results[S2R].e_value ) )
  50. {
  51. m_results[S2R].e_value = m_cmb_lut[i].e_value - m_required_value; // save signed deviation in Ohms
  52. m_results[S2R].e_name = m_cmb_lut[i].e_name; // save combination text
  53. m_results[S2R].e_use = true; // this is a possible solution
  54. }
  55. }
  56. }
  57. void eserie::combine4( uint32_t aSize )
  58. {
  59. uint32_t i,j;
  60. double tmp;
  61. std::string s;
  62. m_results[S4R].e_use = false; // disable 4R solution, until
  63. m_results[S4R].e_value = m_results[S3R].e_value; // 4R becomes better than 3R solution
  64. #ifdef BENCHMARK
  65. PROF_COUNTER combine4_timer; // start timer to count execution time
  66. #endif
  67. for( i = 0; i < aSize; i++ ) // 4R search outer loop
  68. { // scan valid intermediate 2R solutions
  69. for( j = 0; j < aSize; j++ ) // inner loop combines all with itself
  70. {
  71. tmp = m_cmb_lut[i].e_value + m_cmb_lut[j].e_value; // calculate 2R+2R serial
  72. tmp -= m_required_value; // calculate 4R deviation
  73. if( abs( tmp ) < abs( m_results[S4R].e_value ) ) // if new 4R is better
  74. {
  75. m_results[S4R].e_value = tmp; // save amount of benefit
  76. std::string s = "( ";
  77. s.append( m_cmb_lut[i].e_name ); // mention 1st 2 component
  78. s.append( " ) + ( " ); // in series
  79. s.append( m_cmb_lut[j].e_name ); // with 2nd 2 components
  80. s.append( " )" );
  81. m_results[S4R].e_name = s; // save the result and
  82. m_results[S4R].e_use = true; // enable for later use
  83. }
  84. tmp = ( m_cmb_lut[i].e_value * m_cmb_lut[j].e_value ) /
  85. ( m_cmb_lut[i].e_value + m_cmb_lut[j].e_value ); // calculate 2R|2R parallel
  86. tmp -= m_required_value; // calculate 4R deviation
  87. if( abs( tmp ) < abs( m_results[S4R].e_value ) ) // if new 4R is better
  88. {
  89. m_results[S4R].e_value = tmp; // save amount of benefit
  90. std::string s = "( ";
  91. s.append( m_cmb_lut[i].e_name ); // mention 1st 2 component
  92. s.append( " ) | ( " ); // in parallel
  93. s.append( m_cmb_lut[j].e_name ); // with 2nd 2 components
  94. s.append( " )" );
  95. m_results[S4R].e_name = s; // save the result
  96. m_results[S4R].e_use = true; // enable later use
  97. }
  98. }
  99. }
  100. #ifdef BENCHMARK
  101. if( m_series == E12 )
  102. std::cout<<"4R Time = "<<combine4_timer.msecs()<<" mSec"<<std::endl;
  103. #endif
  104. }
  105. void eserie::NewCalc( void )
  106. {
  107. for( r_data& i : m_cmb_lut )
  108. i.e_use = false; // before any calculation is done, assume that
  109. for( r_data& i : m_results )
  110. i.e_use = false; // no combinations and no results are available
  111. for( r_data& i : luts[m_series])
  112. i.e_use = true; // all selected E-values available
  113. }
  114. uint32_t eserie::combine2( void )
  115. {
  116. uint32_t combi2R = 0; // target index counts calculated 2R combinations
  117. std::string s;
  118. for( const r_data& i : luts[m_series] ) // outer loop to sweep selected source lookup table
  119. {
  120. if( i.e_use )
  121. {
  122. for( const r_data& j : luts[m_series] ) // inner loop to combine values with itself
  123. {
  124. if( j.e_use )
  125. {
  126. m_cmb_lut[combi2R].e_use = true;
  127. m_cmb_lut[combi2R].e_value = i.e_value + j.e_value; // calculate 2R serial
  128. s = i.e_name;
  129. s.append( " + " );
  130. m_cmb_lut[combi2R].e_name = s.append( j.e_name);
  131. combi2R++; // next destination
  132. m_cmb_lut[combi2R].e_use = true; // calculate 2R parallel
  133. m_cmb_lut[combi2R].e_value = i.e_value * j.e_value /
  134. ( i.e_value + j.e_value );
  135. s = i.e_name;
  136. s.append( " | " );
  137. m_cmb_lut[combi2R].e_name = s.append( j.e_name );
  138. combi2R++; // next destination
  139. }
  140. }
  141. }
  142. }
  143. return ( combi2R );
  144. }
  145. void eserie::combine3( uint32_t aSize )
  146. {
  147. uint32_t j = 0;
  148. double tmp = 0; // avoid warning for being uninitialized
  149. std::string s;
  150. m_results[S3R].e_use = false; // disable 3R solution, until
  151. m_results[S3R].e_value = m_results[S2R].e_value; // 3R becomes better than 2R solution
  152. for( const r_data& i : luts[m_series] ) // 3R Outer loop to selected primary E serie LUT
  153. {
  154. if( i.e_use ) // skip all excluded values
  155. {
  156. for( j = 0; j < aSize; j++ ) // inner loop combines with all 2R intermediate results
  157. { // R+2R serial combi
  158. tmp = m_cmb_lut[j].e_value + i.e_value;
  159. tmp -= m_required_value; // calculate deviation
  160. if( abs( tmp ) < abs( m_results[S3R].e_value ) ) // compare if better
  161. { // then take it
  162. s = i.e_name; // mention 3rd component
  163. s.append( " + ( " ); // in series
  164. s.append( m_cmb_lut[j].e_name ); // with 2R combination
  165. s.append( " )" );
  166. m_results[S3R].e_name = s; // save S3R result
  167. m_results[S3R].e_value = tmp; // save amount of benefit
  168. m_results[S3R].e_use = true; // enable later use
  169. }
  170. tmp = i.e_value * m_cmb_lut[j].e_value /
  171. ( i.e_value + m_cmb_lut[j].e_value ); // calculate R + 2R parallel
  172. tmp -= m_required_value; // calculate deviation
  173. if( abs( tmp ) < abs( m_results[S3R].e_value ) ) // compare if better
  174. { // then take it
  175. s = i.e_name; // mention 3rd component
  176. s.append( " | ( " ); // in parallel
  177. s.append( m_cmb_lut[j].e_name ); // with 2R combination
  178. s.append( " )" );
  179. m_results[S3R].e_name = s;
  180. m_results[S3R].e_value = tmp; // save amount of benefit
  181. m_results[S3R].e_use = true; // enable later use
  182. }
  183. }
  184. }
  185. }
  186. // if there is a 3R result with remaining deviation
  187. if(( m_results[S3R].e_use == true ) && tmp )
  188. { // consider to search a possibly better 4R solution
  189. combine4( aSize ); // calculate 4R for small series always
  190. }
  191. }
  192. void eserie::Calculate( void )
  193. {
  194. uint32_t no_of_2Rcombi = 0;
  195. no_of_2Rcombi = combine2(); // combine all 2R combinations for selected E serie
  196. simple_solution( no_of_2Rcombi ); // search for simple 2 component solution
  197. if( m_results[S2R].e_value ) // if simple 2R result is not exact
  198. combine3( no_of_2Rcombi ); // continiue searching for a possibly better solution
  199. strip3();
  200. strip4();
  201. }
  202. void eserie::strip3( void )
  203. {
  204. std::string s;
  205. if( m_results[S3R].e_use ) // if there is a 3 term result available
  206. { // what is connected either by two "|" or by 3 plus
  207. s = m_results[S3R].e_name;
  208. if( ( std::count( s.begin(), s.end(), '+' ) == 2 )
  209. || ( std::count( s.begin(), s.end(), '|' ) == 2 ) )
  210. { // then strip one pair of braces
  211. s.erase( s.find( "(" ), 1 ); // it is known sure, this is available
  212. s.erase( s.find( ")" ), 1 ); // in any unstripped 3R result term
  213. m_results[S3R].e_name = s; // use stripped result
  214. }
  215. }
  216. }
  217. void eserie::strip4( void )
  218. {
  219. std::string s;
  220. if( m_results[S4R].e_use ) // if there is a 4 term result available
  221. { // what are connected either by 3 "+" or by 3 "|"
  222. s = m_results[S4R].e_name;
  223. if( ( std::count( s.begin(), s.end(), '+' ) == 3 )
  224. || ( std::count( s.begin(), s.end(), '|' ) == 3 ) )
  225. { // then strip two pair of braces
  226. s.erase( s.find( "(" ), 1 ); // it is known sure, they are available
  227. s.erase( s.find( ")" ), 1 ); // in any unstripped 4R result term
  228. s.erase( s.find( "(" ), 1 );
  229. s.erase( s.find( ")" ), 1 );
  230. m_results[S4R].e_name = s; // use stripped result
  231. }
  232. }
  233. }
  234. void PCB_CALCULATOR_FRAME::OnCalculateESeries( wxCommandEvent& event )
  235. {
  236. double reqr; // required resistor stored in local copy
  237. double error, err3 = 0;
  238. wxString es, fs; // error and formula strings
  239. reqr = ( 1000 * DoubleFromString( m_ResRequired->GetValue() ) );
  240. r.SetRequiredValue( reqr ); // keep a local copy of required resistor value
  241. r.NewCalc(); // assume all values available
  242. /*
  243. * Exclude itself. For the case, a value from the available series is found as required value,
  244. * the calculator assumes this value needs a replacement for the reason of being not available.
  245. * Two further exclude values can be entered to exclude and are skipped as not being available.
  246. * All values entered in KiloOhms are converted to Ohm for internal calculation
  247. */
  248. r.Exclude( 1000 * DoubleFromString( m_ResRequired->GetValue()));
  249. r.Exclude( 1000 * DoubleFromString( m_ResExclude1->GetValue()));
  250. r.Exclude( 1000 * DoubleFromString( m_ResExclude2->GetValue()));
  251. r.Calculate();
  252. fs = r.get_rslt()[S2R].e_name; // show 2R solution formula string
  253. m_ESeries_Sol2R->SetValue( fs );
  254. error = reqr + r.get_rslt()[S2R].e_value; // absolute value of solution
  255. error = ( reqr / error - 1 ) * 100; // error in percent
  256. if( error )
  257. {
  258. if( std::abs( error ) < 0.01 )
  259. es.Printf( "<%.2f", 0.01 );
  260. else
  261. es.Printf( "%+.2f",error);
  262. }
  263. else
  264. {
  265. es = _( "Exact" );
  266. }
  267. m_ESeriesError2R->SetValue( es ); // anyway show 2R error string
  268. if( r.get_rslt()[S3R].e_use ) // if 3R solution available
  269. {
  270. err3 = reqr + r.get_rslt()[S3R].e_value; // calculate the 3R
  271. err3 = ( reqr / err3 - 1 ) * 100; // error in percent
  272. if( err3 )
  273. {
  274. if( std::abs( err3 ) < 0.01 )
  275. es.Printf( "<%.2f", 0.01 );
  276. else
  277. es.Printf( "%+.2f",err3);
  278. }
  279. else
  280. {
  281. es = _( "Exact" );
  282. }
  283. m_ESeriesError3R->SetValue( es ); // show 3R error string
  284. fs = r.get_rslt()[S3R].e_name;
  285. m_ESeries_Sol3R->SetValue( fs ); // show 3R formula string
  286. }
  287. else // nothing better than 2R found
  288. {
  289. fs = _( "Not worth using" );
  290. m_ESeries_Sol3R->SetValue( fs );
  291. m_ESeriesError3R->SetValue( wxEmptyString );
  292. }
  293. fs = wxEmptyString;
  294. if( r.get_rslt()[S4R].e_use ) // show 4R solution if available
  295. {
  296. fs = r.get_rslt()[S4R].e_name;
  297. error = reqr + r.get_rslt()[S4R].e_value; // absolute value of solution
  298. error = ( reqr / error - 1 ) * 100; // error in percent
  299. if( error )
  300. es.Printf( "%+.2f",error );
  301. else
  302. es = _( "Exact" );
  303. m_ESeriesError4R->SetValue( es );
  304. }
  305. else // no 4R solution
  306. {
  307. fs = _( "Not worth using" );
  308. es = wxEmptyString;
  309. m_ESeriesError4R->SetValue( es );
  310. }
  311. m_ESeries_Sol4R->SetValue( fs );
  312. }
  313. void PCB_CALCULATOR_FRAME::OnESeriesSelection( wxCommandEvent& event )
  314. {
  315. if( event.GetEventObject() == m_e1 )
  316. r.SetSeries( E1 );
  317. else if( event.GetEventObject() == m_e3 )
  318. r.SetSeries( E3 );
  319. else if( event.GetEventObject() == m_e12 )
  320. r.SetSeries( E12 );
  321. else
  322. r.SetSeries( E6 );
  323. }
  324. void PCB_CALCULATOR_FRAME::initESeriesPanel() // initialize ESeries tab at each pcb-calculator start
  325. {
  326. wxString msg;
  327. // show markdown formula explanation in lower help panel
  328. ConvertMarkdown2Html( wxGetTranslation( eseries_help ), msg );
  329. m_panelESeriesHelp->SetPage( msg );
  330. }