Last active
May 17, 2021 17:21
-
-
Save calebreister/707ed0398d424ff9c55e8424f6f280cb to your computer and use it in GitHub Desktop.
Python script to determine optimal resistor combinations. Its arguments are a desired resistance and either 10%, 5% (default), or 1% tolerance value. I intend to refine and expand this code and then implement it in several other languages (Matlab, Lua, TI-BASIC, ...). Based loosely on http://www.qsl.net/in3otd/parallr.html.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
#Copyright (C) 2016 Caleb Reister | |
#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 <http://www.gnu.org/licenses/>. | |
#Script to determine the best series or parallel resistor combination to obtain | |
#a given value using standard resistor values. | |
#argv[1]: desired resistance | |
#argv[2]: tolerance (10%, 5%, or 1%) | |
#import pdb | |
import sys | |
from math import * | |
from array import * | |
def findIndex(l, value): | |
#Finds index in list l that is closest to value. | |
#Uses a binary search. | |
low = 0 | |
high = len(l)-1 | |
while low+1 < high: | |
mid = (low+high) // 2 #// is integer division | |
if l[mid] > value: | |
high = mid | |
elif l[mid] < value: | |
low = mid | |
else: | |
return mid | |
if abs(l[high]-value) < abs(l[low]-value): | |
return high | |
else: | |
return low | |
def toSI(d, unit, digits=2): | |
#Pretty print for SI numbers | |
#d: number the print | |
#unit: SI unit to use | |
#digits: level of precision (number of decimal digits) | |
incPrefixes = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] | |
decPrefixes = ['m', 'µ', 'n', 'p', 'f', 'a', 'z', 'y'] | |
if d != 0: | |
degree = int(floor(log10(abs(d)) / 3)) | |
else: | |
degree = 0 | |
prefix = '' | |
if degree != 0: | |
if degree > 0: | |
if degree - 1 < len(incPrefixes): | |
prefix = incPrefixes[degree - 1] | |
else: | |
prefix = incPrefixes[-1] | |
degree = len(incPrefixes) | |
elif degree < 0: | |
if -degree - 1 < len(decPrefixes): | |
prefix = decPrefixes[-degree - 1] | |
else: | |
prefix = decPrefixes[-1] | |
degree = -len(decPrefixes) | |
scaled = float(d * pow(1000, -degree)) | |
s = '{scaled:3.{dig}f}{prefix}{unit}'.format(scaled=scaled, prefix=prefix, dig=digits, unit=unit) | |
else: | |
s = '{d:>3.{dig}f}{unit}'.format(d=d, dig=digits, unit=unit) | |
return(s) | |
#Desired resistance | |
try: | |
rd = float(sys.argv[1]) | |
except IndexError: | |
print('Syntax: resistor.py R [tolerance]') | |
quit() | |
tol = 0 | |
#Set default tolerance to 5% | |
try: | |
sys.argv[2] | |
except IndexError: | |
sys.argv.append('5%') | |
print('No tolerance provided. Assuming 5%.') | |
if sys.argv[2] == '10%' or sys.argv[2] == '10': | |
rbase = [1, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2] | |
tol = 0.10 | |
elif sys.argv[2] == '5%' or sys.argv[2] == '5': | |
rbase = [1, 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] | |
tol = 0.05 | |
elif sys.argv[2] == '1%' or sys.argv[2] == '1': | |
rbase = [1.00, 1.02, 1.05, 1.07, 1.10, 1.13, 1.15, 1.18, 1.21, 1.24, 1.27, 1.30, 1.33, 1.37, 1.40, 1.43, 1.47, 1.50, 1.54, 1.58, 1.62, 1.65, 1.69, 1.74, 1.78, 1.82, 1.87, 1.91, 1.96, 2.00, 2.05, 2.10, 2.15, 2.21, 2.26, 2.32, 2.37, 2.43, 2.49, 2.55, 2.61, 2.67, 2.74, 2.80, 2.87, 2.94, 3.01, 3.09, 3.16, 3.24, 3.32, 3.40, 3.48, 3.57, 3.65, 3.74, 3.83, 3.92, 4.02, 4.12, 4.22, 4.32, 4.42, 4.53, 4.64, 4.75, 4.87, 4.99, 5.11, 5.23, 5.36, 5.49, 5.62, 5.76, 5.90, 6.04, 6.19, 6.34, 6.49, 6.65, 6.81, 6.98, 7.15, 7.32, 7.50, 7.68, 7.87, 8.06, 8.25, 8.45, 8.66, 8.87, 9.09, 9.31, 9.53, 9.76] | |
tol = 0.01 | |
else: | |
raise ValueError("Please choose between 10%, 5%, or 1% tolerance.") | |
R = array('f') #Resistance | |
G = array('f') #Conductance | |
#Generate resistance array, sorted low to high | |
for b in range(0,7): | |
for a in rbase: | |
R.append(a*10**b) | |
#Generate conductance array, sorted low to high | |
for r in R: | |
G.insert(0, 1/r) | |
#Setup to compute series combinations | |
#Start with r1 being the closest value in R to rd | |
#and r2 being 0. | |
r1i = findIndex(R, rd) | |
rr = R[r1i] #Result of operation (r1+r2 or r1||r2) | |
err = (rr-rd)/rd #Error of rres relative to rd | |
out = [] | |
if abs(err) <= tol: | |
out.append([R[r1i], ' +', 0, rr, err]) | |
#Compute possible series combinations: r1i is decremented by 1 | |
#each iteration and used to find r2i. NOTE: this does NOT give | |
#every possible combination within tolerance of rd. It chooses | |
#the optimum value of r2i for a given r1i using findIndex. This | |
#is most likely more efficient than testing for every value | |
#within tolerance. | |
while R[r1i] >= rd/2 or r1i == 0: | |
r1i = r1i-1 | |
r2d = rd - R[r1i] | |
r2i = findIndex(R, r2d) | |
rr = R[r1i] | |
err = rr/rd - 1 | |
#Append result to output if it is within tolerance. This will | |
#be true in most cases. | |
if abs(err) <= tol: | |
out.append([R[r1i], ' +', R[r2i], rr, err]) | |
#Setup for parallel combinations | |
gd = 1/rd | |
g1i = findIndex(G, gd) | |
#Compute parallel combinations | |
#Again, this does not find every possible combination, only the | |
#best value given the other resistor. | |
while G[g1i] >= (gd)/2: | |
g2d = gd - G[g1i] | |
g2i = findIndex(G, g2d) | |
gr = G[g1i] + G[g2i] | |
err = gd/gr - 1.0 | |
if abs(err) <= tol: | |
out.append([1/G[g1i], '||', 1/G[g2i], 1/gr, err]) | |
g1i = g1i-1 | |
#Sort and print result | |
out.sort(key=lambda i: abs(i[4])) #Sort from lowest to highest error | |
print('Desired Resistance: ', toSI(rd, 'Ohm', 5)) | |
for i in out: | |
print('{:>10}'.format(toSI(i[0], 'Ohm')), ' ', i[1], | |
'{:>10}'.format(toSI(i[2], 'Ohm')), ' = ', | |
'{:10}'.format(toSI(i[3], 'Ohm')), ' ', | |
'({:+3.3%})'.format(i[4])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment