KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eseries.cpp
Go to the documentation of this file.
1/*
2 * This program source code file
3 * is part of KiCad, a free EDA CAD application.
4 *
5 * Copyright (C) 2020 <[email protected]>
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
22#include <array>
23#include <algorithm>
24#include <limits>
25#include "eseries.h"
26
27/*
28 * If BENCHMARK is defined, any 4R E12 calculations will print its execution time to console
29 * My Hasswell Enthusiast reports 225 mSec what are reproducible within plusminus 2 percent
30 */
31//#define BENCHMARK
32
33#ifdef BENCHMARK
34#include <profile.h>
35#endif
36
37
38// Return a string from aValue (aValue is expected in ohms)
39// If aValue < 1000 the returned string is aValue with unit = R
40// If aValue >= 1000 the returned string is aValue/1000 with unit = K
41// with notation similar to 2K2
42// If aValue >= 1e6 the returned string is aValue/1e6 with unit = M
43// with notation = 1M
44static std::string strValue( double aValue )
45{
46 std::string result;
47
48 if( aValue < 1000.0 )
49 {
50 result = std::to_string( static_cast<int>( aValue ) );
51 result += 'R';
52 }
53 else
54 {
55 double div = 1e3;
56 char unit = 'K';
57
58 if( aValue >= 1e6 )
59 {
60 div = 1e6;
61 unit = 'M';
62 }
63
64 aValue /= div;
65
66 int integer = static_cast<int>( aValue );
67 result = std::to_string(integer);
68 result += unit;
69
70 // Add mantissa: 1 digit, suitable for series up to E24
71 double mantissa = aValue - integer;
72
73 if( mantissa > 0 )
74 result += std::to_string( static_cast<int>( (mantissa*10)+0.5 ) );
75 }
76
77 return result;
78}
79
80
82{
83 // Build the list of available resistor values in each En serie
84 double listValuesE1[] = { E1_VALUES };
85 double listValuesE3[] = { E3_VALUES };
86 double listValuesE6[] = { E6_VALUES };
87 double listValuesE12[] = { E12_VALUES };
88 double listValuesE24[] = { E24_VALUES };
89 // buildSeriesData must be called in the order of En series, because
90 // the list of series is expected indexed by En for the serie En
91 buildSeriesData( listValuesE1 );
92 buildSeriesData( listValuesE3 );
93 buildSeriesData( listValuesE6 );
94 buildSeriesData( listValuesE12 );
95 int count = buildSeriesData( listValuesE24 );
96
97 // Reserve a buffer for intermediate calculations:
98 // the buffer size is 2*count*count to store all combinaisons of 2 values
99 // there are 2*count*count = 29282 combinations for E24
100 int bufsize = 2*count*count;
101 m_combined_table.reserve( bufsize );
102
103 // Store predefined R_DATA items.
104 for( int ii = 0; ii < bufsize; ii++ )
105 m_combined_table.emplace_back( "", 0.0 );
106}
107
108
109int E_SERIES::buildSeriesData( const double aList[] )
110{
111 double curr_decade = FIRST_VALUE;
112 int count = 0;
113
114 std::vector<R_DATA> curr_list;
115
116 for( ; ; )
117 {
118 double curr_r = LAST_VALUE;
119
120 for( int ii = 0; ; ii++ )
121 {
122 if( aList[ii] == 0.0 ) // End of list
123 break;
124
125 curr_r = curr_decade * aList[ii];
126 curr_list.emplace_back( strValue( curr_r ), curr_r );
127 count++;
128
129 if( curr_r >= LAST_VALUE )
130 break;
131 }
132
133 if( curr_r >= LAST_VALUE )
134 break;
135
136 curr_decade *= 10;
137 }
138
139 m_tables.push_back( std::move( curr_list ) );
140
141 return count;
142}
143
144
145void E_SERIES::Exclude( double aValue )
146{
147 if( aValue != 0.0 ) // if there is a value to exclude other than a wire jumper
148 {
149 for( R_DATA& i : m_tables[m_series] ) // then search it in the selected E-Series table
150 {
151 if( i.e_value == aValue ) // if the value to exclude is found
152 i.e_use = false; // disable its use
153 }
154 }
155}
156
157
158void E_SERIES::simple_solution( uint32_t aSize )
159{
160 uint32_t i;
161
162 m_results.at( S2R ).e_value = std::numeric_limits<double>::max(); // assume no 2R solution or max deviation
163
164 for( i = 0; i < aSize; i++ )
165 {
166 if( std::abs( m_combined_table.at( i ).e_value - m_required_value ) < std::abs( m_results.at( S2R ).e_value ) )
167 {
168 m_results[S2R].e_value = m_combined_table[ i ].e_value - m_required_value; // save signed deviation in Ohms
169 m_results[S2R].e_name = m_combined_table[ i ].e_name; // save combination text
170 m_results[S2R].e_use = true; // this is a possible solution
171 }
172 }
173}
174
175
176void E_SERIES::combine4( uint32_t aSize )
177{
178 uint32_t i,j;
179 double tmp;
180
181 m_results[S4R].e_use = false; // disable 4R solution, until
182 m_results[S4R].e_value = m_results[S3R].e_value; // 4R becomes better than 3R solution
183
184#ifdef BENCHMARK
185 PROF_TIMER timer; // start timer to count execution time
186#endif
187
188 for( i = 0; i < aSize; i++ ) // 4R search outer loop
189 { // scan valid intermediate 2R solutions
190 for( j = 0; j < aSize; j++ ) // inner loop combines all with itself
191 {
192 tmp = m_combined_table[i].e_value + m_combined_table[j].e_value; // calculate 2R+2R serial
193 tmp -= m_required_value; // calculate 4R deviation
194
195 if( std::abs( tmp ) < std::abs( m_results.at(S4R).e_value ) ) // if new 4R is better
196 {
197 m_results[S4R].e_value = tmp; // save amount of benefit
198 std::string s = "( ";
199 s.append( m_combined_table[i].e_name ); // mention 1st 2 component
200 s.append( " ) + ( " ); // in series
201 s.append( m_combined_table[j].e_name ); // with 2nd 2 components
202 s.append( " )" );
203 m_results[S4R].e_name = s; // save the result and
204 m_results[S4R].e_use = true; // enable for later use
205 }
206
207 tmp = ( m_combined_table[i].e_value * m_combined_table[j].e_value ) /
208 ( m_combined_table[i].e_value + m_combined_table[j].e_value ); // calculate 2R|2R parallel
209 tmp -= m_required_value; // calculate 4R deviation
210
211 if( std::abs( tmp ) < std::abs( m_results[S4R].e_value ) ) // if new 4R is better
212 {
213 m_results[S4R].e_value = tmp; // save amount of benefit
214 std::string s = "( ";
215 s.append( m_combined_table[i].e_name ); // mention 1st 2 component
216 s.append( " ) | ( " ); // in parallel
217 s.append( m_combined_table[j].e_name ); // with 2nd 2 components
218 s.append( " )" );
219 m_results[S4R].e_name = s; // save the result
220 m_results[S4R].e_use = true; // enable later use
221 }
222 }
223 }
224
225#ifdef BENCHMARK
226 printf( "Calculation time = %d mS", timer.msecs() );
227 fflush( 0 );
228#endif
229}
230
231
233{
234 for( R_DATA& i : m_combined_table )
235 i.e_use = false; // before any calculation is done, assume that
236
237 for( R_DATA& i : m_results )
238 i.e_use = false; // no combinations and no results are available
239
240 for( R_DATA& i : m_tables[m_series])
241 i.e_use = true; // all selected E-values available
242}
243
244
246{
247 uint32_t combi2R = 0; // target index counts calculated 2R combinations
248 std::string s;
249
250 for( const R_DATA& i : m_tables[m_series] ) // outer loop to sweep selected source lookup table
251 {
252 if( i.e_use )
253 {
254 for( const R_DATA& j : m_tables[m_series] ) // inner loop to combine values with itself
255 {
256 if( j.e_use )
257 {
258 m_combined_table[combi2R].e_use = true;
259 m_combined_table[combi2R].e_value = i.e_value + j.e_value; // calculate 2R serial
260 s = i.e_name;
261 s.append( " + " );
262 m_combined_table[combi2R].e_name = s.append( j.e_name);
263 combi2R++; // next destination
264 m_combined_table[combi2R].e_use = true; // calculate 2R parallel
265 m_combined_table[combi2R].e_value = i.e_value * j.e_value / ( i.e_value + j.e_value );
266 s = i.e_name;
267 s.append( " | " );
268 m_combined_table[combi2R].e_name = s.append( j.e_name );
269 combi2R++; // next destination
270 }
271 }
272 }
273 }
274 return combi2R;
275}
276
277
278void E_SERIES::combine3( uint32_t aSize )
279{
280 uint32_t j = 0;
281 double tmp = 0; // avoid warning for being uninitialized
282 std::string s;
283
284 m_results[S3R].e_use = false; // disable 3R solution, until 3R
285 m_results[S3R].e_value = m_results[S2R].e_value; // becomes better than 2R solution
286
287 for( const R_DATA& i : m_tables[m_series] ) // 3R Outer loop to selected primary E series table
288 {
289 if( i.e_use ) // skip all excluded values
290 {
291 for( j = 0; j < aSize; j++ ) // inner loop combines with all 2R intermediate
292 { // results R+2R serial combi
293 tmp = m_combined_table[j].e_value + i.e_value;
294 tmp -= m_required_value; // calculate deviation
295
296 if( std::abs( tmp ) < std::abs( m_results[S3R].e_value ) ) // compare if better
297 { // then take it
298 s = i.e_name; // mention 3rd component
299 s.append( " + ( " ); // in series
300 s.append( m_combined_table[j].e_name ); // with 2R combination
301 s.append( " )" );
302 m_results[S3R].e_name = s; // save S3R result
303 m_results[S3R].e_value = tmp; // save amount of benefit
304 m_results[S3R].e_use = true; // enable later use
305 }
306
307 tmp = i.e_value * m_combined_table[j].e_value /
308 ( i.e_value + m_combined_table[j].e_value ); // calculate R + 2R parallel
309 tmp -= m_required_value; // calculate deviation
310
311 if( std::abs( tmp ) < std::abs( m_results[S3R].e_value ) ) // compare if better
312 { // then take it
313 s = i.e_name; // mention 3rd component
314 s.append( " | ( " ); // in parallel
315 s.append( m_combined_table[j].e_name ); // with 2R combination
316 s.append( " )" );
317 m_results[S3R].e_name = s;
318 m_results[S3R].e_value = tmp; // save amount of benefit
319 m_results[S3R].e_use = true; // enable later use
320 }
321 }
322 }
323 }
324
325 // If there is a 3R result with remaining deviation consider to search a possibly better
326 // 4R solution
327 // calculate 4R for small series always
328 if( m_results[S3R].e_use && tmp )
329 combine4( aSize );
330}
331
332
334{
335 uint32_t no_of_2Rcombi = 0;
336
337 no_of_2Rcombi = combine2(); // combine all 2R combinations for selected E serie
338 simple_solution( no_of_2Rcombi ); // search for simple 2 component solution
339
340 if( m_results[S2R].e_value ) // if simple 2R result is not exact
341 combine3( no_of_2Rcombi ); // continiue searching for a possibly better solution
342
343 strip3();
344 strip4();
345}
346
347
349{
350 std::string s;
351
352 if( m_results[S3R].e_use ) // if there is a 3 term result available
353 { // what is connected either by two "|" or by 3 plus
354 s = m_results[S3R].e_name;
355
356 if( ( std::count( s.begin(), s.end(), '+' ) == 2 )
357 || ( std::count( s.begin(), s.end(), '|' ) == 2 ) )
358 { // then strip one pair of braces
359 s.erase( s.find( "( " ), 2 ); // it is known sure, this is available
360 s.erase( s.find( " )" ), 2 ); // in any unstripped 3R result term
361 m_results[S3R].e_name = s; // use stripped result
362 }
363 }
364}
365
366
368{
369 std::string s;
370
371 if( m_results[S4R].e_use ) // if there is a 4 term result available
372 { // what are connected either by 3 "+" or by 3 "|"
373 s = m_results[S4R].e_name;
374
375 if( ( std::count( s.begin(), s.end(), '+' ) == 3 )
376 || ( std::count( s.begin(), s.end(), '|' ) == 3 ) )
377 { // then strip two pair of braces
378 s.erase( s.find( "( " ), 2 ); // it is known sure, they are available
379 s.erase( s.find( " )" ), 2 ); // in any unstripped 4R result term
380 s.erase( s.find( "( " ), 2 );
381 s.erase( s.find( " )" ), 2 );
382 m_results[S4R].e_name = s; // use stripped result
383 }
384 }
385}
386
387
void combine4(uint32_t aSize)
Check if there is a better four component solution.
Definition: eseries.cpp:176
void Exclude(double aValue)
If any value of the selected E-series not available, it can be entered as an exclude value.
Definition: eseries.cpp:145
void strip3()
Definition: eseries.cpp:348
E_SERIES()
Definition: eseries.cpp:81
uint32_t combine2()
Build all 2R combinations from the selected E-series values.
Definition: eseries.cpp:245
int buildSeriesData(const double aList[])
Add values from aList to m_tables.
Definition: eseries.cpp:109
std::vector< R_DATA > m_combined_table
Definition: eseries.h:195
void Calculate()
called on calculate button to execute all the 2R, 3R and 4R calculations
Definition: eseries.cpp:333
std::vector< std::vector< R_DATA > > m_tables
Definition: eseries.h:183
void NewCalc()
initialize next calculation and erase results from previous calculation
Definition: eseries.cpp:232
std::array< R_DATA, S4R+1 > m_results
Definition: eseries.h:197
void simple_solution(uint32_t aSize)
Search for closest two component solution.
Definition: eseries.cpp:158
uint32_t m_series
Definition: eseries.h:198
double m_required_value
Definition: eseries.h:199
void strip4()
Definition: eseries.cpp:367
void combine3(uint32_t aSize)
Check if there is a better 3 R solution than previous one using only two components.
Definition: eseries.cpp:278
A small class to help profiling.
Definition: profile.h:47
double msecs(bool aSinceLast=false)
Definition: profile.h:147
static std::string strValue(double aValue)
Definition: eseries.cpp:44
#define FIRST_VALUE
Definition: eseries.h:51
@ S3R
Definition: eseries.h:67
@ S2R
Definition: eseries.h:67
@ S4R
Definition: eseries.h:67
#define E12_VALUES
Definition: eseries.h:41
#define LAST_VALUE
Definition: eseries.h:54
#define E1_VALUES
Definition: eseries.h:47
#define E3_VALUES
Definition: eseries.h:45
#define E24_VALUES
E-Values derived from a geometric sequence formula by Charles Renard were already accepted and widely...
Definition: eseries.h:38
#define E6_VALUES
Definition: eseries.h:43
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:418
Definition: eseries.h:71