Last few months I spend more time with soldering iron than keyboard,
so here's a script for finding an arbitrary resistor value from two
standard values in series or parallel combination -- it's a common
issue in electronics design.
Bash wrapper is used simply to read the command line input value,
I'm using gawk 3.1.7 on slackware-11.0
#! /bin/bash
#
# rcalc -- last edit 2009-11-20
#
# calculate resistor pair value from e24 series to make up arbitrary value
#
# Copyright (c) 2009 Grant Coady <http://bugsplatter.id.au> GPLv2
#
# When designing and building electronic projects I mostly use 1% resistors
# that come in the E24 series (24 values per decade).
#
# Frequently there's a need for some arbitrary value (between 10R and 1M
# in this script) resistor that can be made with a series or parallel
# combination of two standard values.
#
# This script searches the E24 standard value space for pairs of resistors
# that will produce or come close to the desired arbitrary resistor value.
#
# Usage:
# ./rcalc <value>
#
# Example:
# ~$ ./rcalc 89000
# Result Ra Rb Connect Error
# 88800.00 82000 6800 series -0.22%
# 88888.89 200000 160000 parallel -0.12%
# 89000.00 56000 33000 series
# 89000.00 62000 27000 series
# 89130.43 820000 100000 parallel +0.15%
# 89137.93 470000 110000 parallel +0.15%
# 89189.19 220000 150000 parallel +0.21%
#
#
gawk -v target=$1 '
BEGIN {
print "Result Ra Rb Connect Error"
max_error = 0.005 # +/- 0.5%
max_multiplier = 10000 # try four decades
format = "%8.2f %7d %7d %-8s %+4.2f%%"
formnz = "%8.2f %7d %7d %-8s"
limit_hi = target * (1 + max_error)
limit_lo = target * (1 - max_error)
$0 = "10 11 12 13 15 16 18 20 22 24 27 30 33 36 39 43 47 51 56 62 68 75 82 91"
for (i = 1; i < 25; i++) {
e24[i] = $i
}
for (u = 1; u < 25; u++) {
for (v = 1; v < 25; v++) {
for (i = 1; i <= max_multiplier; i *= 10) {
x = e24[u] * i
if (x == target) {
continue
}
for (j = 1; j <= max_multiplier; j *= 10) {
y = e24[v] * j
if (y == target) {
continue
}
combo(e24[u] * i, e24[v] * j)
}
}
}
}
exit # skip file reader
}
function combo(a, b, c)
{
# parallel
c = a * b / (a + b)
combo2(a, b, c, "parallel")
# series
c = a + b
combo2(a, b, c, "series")
}
function combo2(a, b, c, d, e, f)
{
# avoid duplicates and ignore result when error too big
if (a < b || c < limit_lo || c > limit_hi) { return }
e = 100 * (c - target) / target # percentage error
f = (e == 0 ? formnz : format) # select output format
result[n++] = sprintf(f, c, a, b, d, e)
}
END {
# sort by result value, print list
n = asort(result, sort_result)
for (i = 1; i <= n; i++) {
print sort_result[i]
}
}
'
# end
Play golf, tell me it can be done as a one-liner :)
Maybe I should use awk's file reader for the E24 series' values from
a file -- make it automagically work with E12, E24 and E96 series?
Grant.
--
http://bugsplatter.id.au
Nice! Here's something along similar lines - find a crystal & integer divisor
that will produce a desired frequency:
ftp://ftp.armory.com/pub/scripts/find_cryst
My most-developed awk program for electronics purposes -
generate tables of wire gauge parameters:
ftp://ftp.armory.com/pub/scripts/awg
John
--
John DuBois spc...@armory.com KC6QKZ/AE http://www.armory.com/~spcecdt/
> Hi there,
>
> Last few months I spend more time with soldering iron than keyboard, so
> here's a script for finding an arbitrary resistor value from two
> standard values in series or parallel combination -- it's a common issue
> in electronics design.
>
> Bash wrapper is used simply to read the command line input value, I'm
> using gawk 3.1.7 on slackware-11.0
>
> #! /bin/bash
> #
> # rcalc -- last edit 2009-11-20
> #
> # calculate resistor pair value from e24 series to make up arbitrary
> value #
...
(I'm snipping a lot because I'll addup some lenghty code again below, sorry)
> $0 = "10 11 12 13 15 16 18 20 22 24 27 30 33 36 39 43 47 51 56 62 68 75
> 82 91"
I like that idea about the taming of the $0 in the beginning.
> Play golf, tell me it can be done as a one-liner :)
Er. a one-liner for this!? I don't think I'm capable :D)
Now, as you asked about golf I pasted below a try at it,
some parts are just for fun sake and sincerely I think your
version is the best for readability and eventual maintenance/extension :-)
But, you asked for some golf and that was a PAR-99 ;-)
>
> Maybe I should use awk's file reader for the E24 series' values from a
> file -- make it automagically work with E12, E24 and E96 series?
Why not? After all that'd be a even better tool then wouldn't it?
OK, here's the slightly bunkered version, note that I didn't
touch the algorithm part nor the printing style parts of course,
I just tried and apply a few tricks when I saw places I could
replace a double time with a strike, now I reckon that or instance
the abandon of the 'unused' x and y would be better optimized by
reusing them
------------(example for your original code):
y = e24[v] * j
...
combo(e24[u] * i, y)
------------
but as you didn't I took a chance about it as well :-)
-------------
gawk -v target=$1 '
BEGIN {
print "Result Ra Rb Connect Error"
max_error = 0.005 # +/- 0.5%
max_multiplier = 10000 # try four decades
format = "%8.2f %7d %7d %-8s %+4.2f%%"
formnz = "%8.2f %7d %7d %-8s"
limit_hi = target * (1 + max_error)
limit_lo = target * (1 - max_error)
$0 = "10 11 12 13 15 16 18 20 22 24 27 30 33 36 39 43 47 51 56 62 68 75 82 91"
for(i=25;--i;e24[i]=$i);
for (u = 25; --u ; ) {
for (v = 25; --v ; ) {
for (i = 1; i <= max_multiplier; i *= 10) {
if (target - (e24[u] * i) ) {
for (j = 1; j <= max_multiplier; j *= 10) {
if ( target - (e24[v] * j)) combo(e24[u] * i, e24[v] * j)
}
}
}
}
}
exit # skip file reader
}
function combo(a, b, c)
{
# parallel
c = a * b / (a + b)
combo2(a, b, c, "parallel")
# series
c = a + b
combo2(a, b, c, "series")
}
function combo2(a, b, c, d, e, f)
{
# avoid duplicates and ignore result when error too big
if (!(a < b || c < limit_lo || c > limit_hi)) {
e = 100 * (c - target) / target # percentage error
f = (e ? format : formnz ) # select output format
result[n++] = sprintf(f, c, a, b, d, e)
}
}
END {
# sort by result value, print list
for (n = i = asort(result, sort_result); n-- ; print sort_result[i-n]);
}
'
-------------
>Fri, 20 Nov 2009 08:58:14 +1100, Grant did cat :
>
>> Hi there,
>>
>> Last few months I spend more time with soldering iron than keyboard, so
>> here's a script for finding an arbitrary resistor value from two
>> standard values in series or parallel combination -- it's a common issue
>> in electronics design.
>>
>> Bash wrapper is used simply to read the command line input value, I'm
>> using gawk 3.1.7 on slackware-11.0
>>
>> #! /bin/bash
>> #
>> # rcalc -- last edit 2009-11-20
>> #
>> # calculate resistor pair value from e24 series to make up arbitrary
>> value #
>...
>(I'm snipping a lot because I'll addup some lenghty code again below, sorry)
>
>> $0 = "10 11 12 13 15 16 18 20 22 24 27 30 33 36 39 43 47 51 56 62 68 75
>> 82 91"
>
>I like that idea about the taming of the $0 in the beginning.
>
>> Play golf, tell me it can be done as a one-liner :)
>
>Er. a one-liner for this!? I don't think I'm capable :D)
Well, someone might've surprised us all ;)
>Now, as you asked about golf I pasted below a try at it,
>some parts are just for fun sake and sincerely I think your
>version is the best for readability and eventual maintenance/extension :-)
I like code plain and obvious :)
>But, you asked for some golf and that was a PAR-99 ;-)
>
>>
>> Maybe I should use awk's file reader for the E24 series' values from a
>> file -- make it automagically work with E12, E24 and E96 series?
>
>Why not? After all that'd be a even better tool then wouldn't it?
Hmm, not really (for me -- others might like to do that), E12 not so
relevant these days (local shop stocks E24 series 1% for 10R..1M,
121 values). E96 gets you within one percent of any value -- but the
local shop doesn't stock them.
>
> OK, here's the slightly bunkered version, note that I didn't
>touch the algorithm part nor the printing style parts of course,
>I just tried and apply a few tricks when I saw places I could
>replace a double time with a strike, now I reckon that or instance
>the abandon of the 'unused' x and y would be better optimized by
>reusing them
>------------(example for your original code):
>y = e24[v] * j
>...
>combo(e24[u] * i, y)
>------------
> but as you didn't I took a chance about it as well :-)
Well, you didn't break the algorithm. This is a brute search of the
available values space.
>
>-------------
>gawk -v target=$1 '
>BEGIN {
> print "Result Ra Rb Connect Error"
>
> max_error = 0.005 # +/- 0.5%
> max_multiplier = 10000 # try four decades
>
> format = "%8.2f %7d %7d %-8s %+4.2f%%"
> formnz = "%8.2f %7d %7d %-8s"
>
> limit_hi = target * (1 + max_error)
> limit_lo = target * (1 - max_error)
>
>$0 = "10 11 12 13 15 16 18 20 22 24 27 30 33 36 39 43 47 51 56 62 68 75 82 91"
>
> for(i=25;--i;e24[i]=$i);
>
> for (u = 25; --u ; ) {
> for (v = 25; --v ; ) {
> for (i = 1; i <= max_multiplier; i *= 10) {
> if (target - (e24[u] * i) ) {
> for (j = 1; j <= max_multiplier; j *= 10) {
> if ( target - (e24[v] * j)) combo(e24[u] * i, e24[v] * j)
> }
> }
> }
> }
> }
> exit # skip file reader
Switch 25 -> 24 to not be so obscure, yah, I know I used a 25, but I didn't
think of prefix vs postfix, nor counting down instead of up -- so many ways...
$0 = "10 11 12 13 15 16 18 20 22 24 27 30 33 36 39 43 47 51 56 62 68 75 82 91"
for(i = 24; i--; e24[i] = $i);
for (u = 24; u-- ; ) {
for (v = 24; v-- ; ) {
...
}
}
Thanks for playing :)
Grant.
--
http://bugsplatter.id.au
> Switch 25 -> 24 to not be so obscure,
I thought about it, and for the same reasons of clarity ,-)
but it brings in some complications and in the end you may
get an expression harder to read than the previous one :D)
> yah, I know I used a 25, but I
> didn't think of prefix vs postfix, nor counting down instead of up -- so
> many ways...
>
> $0 = "10 11 12 13 15 16 18 20 22 24 27 30 33 36 39 43 47 51 56 62 68 75
> 82 91"
>
Yup, for instance, that form will prolly bring on
a div by zero some time:
> for(i = 24; i--; e24[i] = $i);
the following variant would be OK but as you see that's a bit more obfuscated ,-)
for(i = 24; i; e24[i--] = $i);
(
and, more important it'd be prone to be in the 'impredictable'
field because of the fuzz in the precedence of 'limit' Vs 'expr'
(in for(start; limit; expr) ) for the interpretation of the value of
the running "i" counter
)
anyway, that was a fun game and I like games, cheers :-)
<code and alternate code snipped>
Here's a recursive version that allows you to set the desired value
and error bound and number of resister to combine. For values 3 or
more resistors this can be a little slow especially for small desired
resistances as there are many 27 par 1,5M and such combinations.
This isn't really great code, but it passed the time while I waited
for the doctor.
Try gawk -f findres.awk | sort -n
findres.awk
=======================================================================
BEGIN {
resistors[0] = 10;
resistors[1] = 11;
resistors[2] = 12;
resistors[3] = 13;
resistors[4] = 15;
resistors[5] = 16;
resistors[6] = 18;
resistors[7] = 20;
resistors[8] = 22;
resistors[9] = 24;
resistors[10] = 27;
resistors[11] = 30;
resistors[12] = 33;
resistors[13] = 36;
resistors[14] = 39;
resistors[15] = 43;
resistors[16] = 47;
resistors[17] = 51;
resistors[18] = 56;
resistors[19] = 62;
resistors[20] = 68;
resistors[21] = 75;
resistors[22] = 82;
resistors[23] = 91;
num_resistors = 3;
maxError = .05;
value = 27.5;
findCombo(0, value, maxError, num_resistors, "");
}
function findCombo(givenValue, desiredTotal, desiredMaxError, numRes,
outString, mult,i,Rx,total,error,printThis)
{
if (numRes < 1) {
return;
}
for (mult=0.1; mult <= 1.0e6; mult *= 10) {
for (i=0; i<24; ++i) {
Rx = resistors[i] * mult;
if (givenValue == 0) printThis = sprintf("(%8.2g)",Rx);
# serial handler
if (givenValue != 0) printThis = sprintf("(%8.2g ser %s)",Rx,outString);
total = Rx + givenValue;
error = abs(((total-desiredTotal)/desiredTotal) * 100);
if (error < desiredMaxError)
printf("%7.4f percent error %d resistors value:%9.4f combo:%s\n",
error,num_resistors-numRes+1,total,printThis);
findCombo(total, desiredTotal, desiredMaxError, numRes-1, printThis);
# parallel handler
if (givenValue == 0) continue;
if (givenValue != 0) printThis = sprintf("(%8.2g par %s)",Rx,outString);
total = (Rx * givenValue)/(Rx + givenValue);
error = abs(((total-desiredTotal)/desiredTotal) * 100);
if (error < desiredMaxError)
printf("%7.4f percent error %d resistors value:%9.4f combo:%s\n",
error,num_resistors-numRes+1,total,printThis);
findCombo(total, desiredTotal, desiredMaxError, numRes-1, printThis);
}
}
}
function abs(val)
{
if (val < 0)
return -val;
else
return val;
}