Last active
July 4, 2023 12:25
-
-
Save Szpadel/9386d4eef06bb75c050db48175c104ba to your computer and use it in GitHub Desktop.
Resistor Divider CALCulator
This file contains hidden or 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/env python3 | |
import argparse | |
import itertools | |
from rich import print | |
from rich.table import Table | |
def format_resistor_value(number): | |
formatted_number = format(number, ".1f").rstrip('0').rstrip('.') | |
return formatted_number | |
def parse_resistor_values(resistor_values): | |
parsed_values = [] | |
for value in resistor_values: | |
if 'K' in value: | |
parsed_values.append(float(value.replace('K', '')) * 1000) | |
elif 'M' in value: | |
parsed_values.append(float(value.replace('M', '')) * 1000000) | |
else: | |
parsed_values.append(float(value)) | |
return parsed_values | |
def format_resistor_values(resistor_values): | |
formatted_values = [] | |
for value in resistor_values: | |
if value >= 1000000: | |
formatted_values.append(format_resistor_value(value/1000000) + 'M') | |
elif value >= 1000: | |
formatted_values.append(format_resistor_value(value/1000) + 'K') | |
else: | |
formatted_values.append(format_resistor_value(value)) | |
return ', '.join(formatted_values) | |
def parallel_resistor(*resistors): | |
return 1 / sum(1/r for r in resistors) | |
def series_resistor(*resistors): | |
return sum(resistors) | |
def voltage_divider(vin, r1, r2): | |
return vin * (r2 / (r1 + r2)) | |
def find_combinations_within_tolerance(vin, vout, resistors, resistor_count, voltage_tolerance): | |
combinations = [] | |
for r in range(1, resistor_count+1): | |
combinations.extend(list(itertools.combinations_with_replacement(resistors, r))) | |
valid_output_values = [] | |
for comb in combinations: | |
# try R1 in series and R2 in series and parallel | |
r1_s = series_resistor(*comb) | |
for r_comb in combinations: | |
r2_s = series_resistor(*r_comb) | |
r2_p = parallel_resistor(*r_comb) | |
vout_s = voltage_divider(vin, r1_s, r2_s) | |
vout_p = voltage_divider(vin, r1_s, r2_p) | |
if abs((vout - vout_s) / vout) <= voltage_tolerance: | |
valid_output_values.append((comb, r_comb, vout_s, 'series', 'series', vout - vout_s, r1_s, r2_s)) | |
if abs((vout - vout_p) / vout) <= voltage_tolerance: | |
valid_output_values.append((comb, r_comb, vout_p, 'series', 'parallel', vout - vout_p, r1_s, r2_p)) | |
# try R1 in parallel and R2 in series and parallel | |
r1_p = parallel_resistor(*comb) | |
for r_comb in combinations: | |
r2_s = series_resistor(*r_comb) | |
r2_p = parallel_resistor(*r_comb) | |
vout_s = voltage_divider(vin, r1_p, r2_s) | |
vout_p = voltage_divider(vin, r1_p, r2_p) | |
if abs((vout - vout_s) / vout) <= voltage_tolerance: | |
valid_output_values.append((comb, r_comb, vout_s, 'parallel', 'series', vout - vout_s, r1_p, r2_s)) | |
if abs((vout - vout_p) / vout) <= voltage_tolerance: | |
valid_output_values.append((comb, r_comb, vout_p, 'parallel', 'parallel', vout - vout_p, r1_p, r2_p)) | |
return valid_output_values | |
def main(): | |
parser = argparse.ArgumentParser(description='Find the closest voltage divider resistor combinations') | |
parser.add_argument('--vin', type=float, required=True, help='input voltage in volts') | |
parser.add_argument('--vout', type=float, required=True, help='desired output voltage in volts') | |
parser.add_argument('--resistor_values', nargs='+', required=True, help='resistor values in ohms. Format: 10 1K 2M etc.') | |
parser.add_argument('--resistor_count', type=int, default=2, help='number of resistors that can be used') | |
parser.add_argument('--num_results', type=int, default=5, help='number of best results') | |
parser.add_argument('--voltage_tolerance', type=float, default=0.05, help='minimum voltage tolerance in volts') | |
args = parser.parse_args() | |
parsed_resistor_values = parse_resistor_values(args.resistor_values) | |
combinations = find_combinations_within_tolerance(args.vin, args.vout, parsed_resistor_values, args.resistor_count, args.voltage_tolerance) | |
combinations.sort(key=lambda x: (len(x[0]) + len(x[1]), abs(x[5]))) # sort by least resistors used and absolute voltage difference | |
print(f"[bold cyan]Searching for voltage divider resistor combinations for the following parameters:[/bold cyan]") | |
print(f"Input Voltage (Vin): [bold magenta]{args.vin}V[/bold magenta]") | |
print(f"Desired Output Voltage (Vout): [bold magenta]{args.vout}V[/bold magenta]") | |
print(f"Voltage Tolerance: [bold magenta]{args.voltage_tolerance*100}%[/bold magenta]") | |
print(f"Resistor Values: [bold magenta]{format_resistor_values(parsed_resistor_values)} Ohms[/bold magenta]") | |
print(f"Maximum Number of Resistors: [bold magenta]{args.resistor_count}[/bold magenta]\n") | |
print("""Vin ───[ R1 ]───╮ | |
├─── Vout | |
GND ───[ R2 ]───╯ | |
""") | |
table = Table(show_header=True, header_style="bold magenta") | |
table.add_column("Individual resistors for R1", style="dim") | |
table.add_column("R1 Configuration", style="dim") | |
table.add_column("Final R1 (Ohms)", style="dim") | |
table.add_column("Individual resistors for R2", style="dim") | |
table.add_column("R2 Configuration", style="dim") | |
table.add_column("Final R2 (Ohms)", style="dim") | |
table.add_column("Vout (Volts)", style="dim") | |
table.add_column("Voltage Difference to Target (Volts)", style="dim") | |
for comb in combinations[:args.num_results]: | |
table.add_row(format_resistor_values(comb[0]), comb[3], format_resistor_values([comb[6]]), format_resistor_values(comb[1]), comb[4], format_resistor_values([comb[7]]), "{:.4f}".format(comb[2]), "{:.4f}".format(comb[5])) | |
print(table) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment