A friend who is studying Engineering at university asked me for help tutoring him for one of his degree projects where he was tasked with writing a voltage divider calculator:
You are required to design a program to calculate the best possible standard resistors to use for a voltage divider circuit to get the required circuit gain. The circuit will contain 2 to 4 resistors as in the following circuit diagram.
...
The allowed resistors in kΩ are: 1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2, 10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82, 100, 1e9
Note that the last resistor is 1 GΩ, to represent an open circuit. This is allowed because resistors R2 and/or R4 may not be required, in which case they may be replaced by an open circuit. Resistors R1 and R3 cannot be zero. Only resistors R2 and R4 may be this value.
There's some more explanation in the assignment, including information on how to calculate the resistance values p1 and p2, and the gain of the circuit.
In order to be able to help my friend with his assignment, I decided to get my head around the problem by coding it in Javascript. It took about half an hour to throw together a program that somewhat closely resembled what the lecturer is after. I then figured that with the magic of Vue, I should be able to write a component that dynamically calculates the answers as you type in your required gain. Here is the end result:
# Code
This is the Node.js code I came up with initially to complete the task. It's not pretty, but it tries to follow the rules (e.g. passing by reference) and variable names set by the assignment:
function Input() {
}
function Calculate(reqGain, stdR, R) { // Find the closest gain possible to the required value
var P1, P2, gain; // Create variables to hold our calculated values
var aclGain = 10;
for (R1 = 0; R1 < stdR.length - 1; R1++) { // For each resistor value for R1, except for the open circuit
for (R2 = R1; R2 < stdR.length; R2++) { // For each resistor value for R2 that is the same or larger than R1
for (R3 = 0; R3 < stdR.length - 1; R3++) { // For each resistor value for R3, except for the open circuit
for (R4 = R3; R4 < stdR.length; R4++) { // For each resistor value for R4 that is the same or larger than R3
P1 = (stdR[R1] * stdR[R2]) / (stdR[R1] + stdR[R2]); // Calculate P1
P2 = (stdR[R3] * stdR[R4]) / (stdR[R3] + stdR[R4]); // Calculate P2
gain = P2 / (P1 + P2); // Calculate the gain
if (Math.abs(reqGain - gain) < Math.abs(reqGain - aclGain) || (Math.abs(reqGain - gain) == Math.abs(reqGain - aclGain)) && P1 + P2 > R.P1 + R.P2) { // If the calculated gain is closer to the required gain than our existing best result, or if it's the same result with a higher combined resistance
aclGain = gain; // Save our newly calculated gain
["R1", "R2", "R3", "R4"].forEach(res => {
R[res] = stdR[eval(res)];
});
["P1", "P2"].forEach(res => {
R[res] = eval(res);
});
}
}
}
}
}
return aclGain; // Return our result
}
function Output(reqGain, aclGain, R) {
console.log("Required gain = " + reqGain.toFixed(6)); // Print the gain we wanted
console.log("Actual gain = " + aclGain.toFixed(6)); // Print the best gain we can do
["R1", "R2", "R3", "R4"].forEach(res => {
console.log(res + " = " + R[res].toFixed(1) + " kohm"); // Print our resistor value
});
["P1", "P2"].forEach(res => {
console.log(res + " = " + R[res].toFixed(4) + " kohm"); // Print our resistance value
});
}
function main() {
const rl = require('readline').createInterface(process.stdin, process.stdout); // Load the readline library, to allow us to receive a command line input
rl.setPrompt('Enter the voltage divider gain (0 to 1, 0 to exit): '); // Set our readline prompt to ask for the gain
rl.on('close', function() { // If the program is closed
console.log("END OF PROGRAM"); // Print our end of program message
});
const stdR = [1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2, 10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82, 100, 1000000000]; // Create an array of resistor values, in kohm
console.log("VOLTAGE DIVIDER"); // Print a title
rl.prompt(); // Show our prompt, asking for the gain
rl.on('line', function(gain) { // When a gain has been entered
var error = false;
if (!isNaN(gain)) { // If the gain is a valid number
if (gain > 0 && gain < 1) { // If the gain is larger than 0 and smaller than 1
var R = {R1: 0, R2: 0, R3: 0, R4: 0, P1: 0, P2: 0}; // Create an initial result, with values that can be compared against
var reqGain = Number.parseFloat(gain); // Convert our gain to a float
var aclGain = Calculate(reqGain, stdR, R); // Get the closest gain we can get to our desired gain, along with the resistor and P1/P2 values
Output(reqGain, aclGain, R);
} else if (gain != 0) { // If the gain is 0
error = true;
}
} else {
console.log("Enter a number!"); // Explain that a number is needed for the gain
error = true;
}
if (error) {
console.log("Gain must be greater than zero and less than one!"); // Explain the lower and upper limits for the gain
console.log("Enter zero if you want to exit the program."); // Explain how to exit the program
rl.prompt(); // Ask for a gain again
} else {
rl.close(); // Close our readline
}
});
}
main();
Here's a more JavaScript native way of doing the same task:
const rl = require('readline').createInterface(process.stdin, process.stdout); // Load the readline library, to allow us to receive a command line input
function calc(required) { // Find the closest gain possible to the required value
const resistors = [1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2, 10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82, 100, 1000000000]; // Create an array of resistor valies, in kohm
var result = {gain: 10, P1: 0, P2: 0}; // Create an initial result, with values that can be compared against
var P1, P2, gain; // Create variables to hold our calculated values
for (R1 = 0; R1 < resistors.length - 1; R1++) { // For each resistor value for R1, except for the open circuit
for (R2 = R1; R2 < resistors.length; R2++) { // For each resistor value for R2 that is the same or larger than R1
for (R3 = 0; R3 < resistors.length - 1; R3++) { // For each resistor value for R3, except for the open circuit
for (R4 = R3; R4 < resistors.length; R4++) { // For each resistor value for R4 that is the same or larger than R3
P1 = (resistors[R1] * resistors[R2]) / (resistors[R1] + resistors[R2]); // Calculate P1
P2 = (resistors[R3] * resistors[R4]) / (resistors[R3] + resistors[R4]); // Calculate P2
gain = P2 / (P1 + P2); // Calculate the gain
if (Math.abs(required - gain) < Math.abs(required - result.gain) || (Math.abs(required - gain) == Math.abs(required - result.gain)) && P1 + P2 > result.P1 + result.P2) { // If the calculated gain is closer to the required gain than our existing best result, or if it's the same result with a higher combined resistance
result = {gain: gain, R1: resistors[R1], R2: resistors[R2], R3: resistors[R3], R4: resistors[R4], P1: P1, P2: P2}; // Save our newly calculated gain and other values to our result
}
}
}
}
}
return result; // Return our result
}
function result(gain, answer) {
console.log("Required gain = " + gain.toFixed(6)); // Print the gain we wanted
console.log("Actual gain = " + answer.gain.toFixed(6)); // Print the best gain we can do
["R1", "R2", "R3", "R4"].forEach(res => { // For each resistor
console.log(res + " = " + answer[res].toFixed(1) + " kohm"); // Print our resistor value
});
["P1", "P2"].forEach(res => { // For each P
console.log(res + " = " + answer[res].toFixed(4) + " kohm"); // Print our resistance value
});
}
function warn() {
console.log("Gain must be greater than zero and less than one!"); // Explain the lower and upper limits for the gain
console.log("Enter zero if you want to exit the program."); // Explain how to exit the program
}
console.log("VOLTAGE DIVIDER"); // Print a title
rl.setPrompt('Enter the voltage divider gain (0 to 1, 0 to exit): '); // Set our readline prompt to ask for the gain
rl.prompt(); // Show our prompt, asking for the gain
rl.on('line', gain => { // When a gain has been entered
if (!isNaN(gain)) { // If the gain is a valid number
if (gain == 0) { // If the gain is 0
rl.close(); // Close our readline
} else if (gain > 0 && gain < 1) { // If the gain is larger than 0 and smaller than 1
gain = Number.parseFloat(gain); // Convert our gain to a float
var answer = calc(gain); // Get the closest gain we can get to our desired gain, along with the resistor and P1/P2 values
result(gain, answer);
rl.close(); // Close our readline
} else { // If the gain is less than 0, or 1 or more
warn(); // Give invalid value warning
rl.prompt(); // Ask for a gain again
}
} else {
console.log("Enter a number!"); // Explain that a number is needed for the gain
warn(); // Give invalid value warning
rl.prompt(); // Ask for a gain again
}
});
rl.on('close', () => { // If the program is closed
console.log("END OF PROGRAM"); // Print our end of program message
});
This is what the code looks like when converted into a vue component.
<template>
<form>
<h3>VOLTAGE DIVIDER</h3>
<h4>Enter the voltage divider gain ( > 0 to < 1 )</h4>
<label for="gain">Required gain = </label>
<input id="gain" v-model="gain" maxlength="8" /><br />
<p>Actual Gain = {{result.gain.toFixed(6)}}</p>
<p>R1 = {{result.r1.toFixed(1)}} kohm</p>
<p>R2 = {{result.r2.toFixed(1)}} kohm</p>
<p>R3 = {{result.r3.toFixed(1)}} kohm</p>
<p>R4 = {{result.r4.toFixed(1)}} kohm</p>
<p>P1 = {{result.p1.toFixed(4)}} kohm</p>
<p>P2 = {{result.p2.toFixed(4)}} kohm</p>
</form>
</template>
<script>
export default {
data() {
return {
gain: 0.5,
resistors: [1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2, 10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82, 100, 1000000000],
result: {gain: 10, r1: 0, r2: 0, r3: 0, r4: 0, p1: 0, p2: 0}
};
},
watch: {
gain: {
immediate: true,
handler(num) {
var required = Number.parseFloat(num);
if (!isNaN(required) && required > 0 && required < 1) {
var p1, p2, gain;
for (var r1 = 0; r1 < this.resistors.length - 1; r1++) {
for (var r2 = r1; r2 < this.resistors.length; r2++) {
for (var r3 = 0; r3 < this.resistors.length - 1; r3++) {
for (var r4 = r3; r4 < this.resistors.length; r4++) {
p1 = (this.resistors[r1] * this.resistors[r2]) / (this.resistors[r1] + this.resistors[r2]);
p2 = (this.resistors[r3] * this.resistors[r4]) / (this.resistors[r3] + this.resistors[r4]);
gain = p2 / (p1 + p2);
if (Math.abs(required - gain) < Math.abs(required - this.result.gain) || (Math.abs(required - gain) == Math.abs(required - this.result.gain)) && p1 + p2 > this.result.p1 + this.result.p2) {
this.result = {gain: gain, r1: this.resistors[r1], r2: this.resistors[r2], r3: this.resistors[r3], r4: this.resistors[r4], p1: p1, p2: p2};
}
}
}
}
}
} else {
this.result = {gain: 9.999999, r1: 0, r2: 0, r3: 0, r4: 0, p1: 0, p2: 0};
}
}
}
}
}
</script>
Here's the C++ version of the code, as required by the assignment.
#include "pch.h"
#include <iostream>
#include "math.h"
#include <iomanip>
#include <cstddef>
using namespace std;
float Input() {
float gain;
bool error;
do {
error = false;
cout << "Enter the voltage divider gain(0 to 1, 0 to exit): ";
cin >> gain; // Save the user input to our gain
if (cin.fail()) { // If our unput isn't a float
cin.clear(); // Clear the error
cin.ignore(); // Ignore the error
error = true; // Set our error boolean to true, to loop back and ask for our gain again
cout << "Enter a number!" << endl; // Explain that a number is needed for the gain
}
else if (gain < 0 || gain >= 1) { // If the gain is less than 0, or 1 or more
error = true; // Set our error boolean to true, to loop back and ask for our gain again
}
if (error) { // If there was an error with the input
cout << "Gain must be greater than zero and less than one!" << endl; // Explain the lower and upper limits for the gain
cout << "Enter zero if you want to exit the program." << endl; // Explain how to exit the program
}
} while (error);
return gain;
}
float Calculate(float reqGain, float stdR[26], float& R1, float& R2, float& R3, float& R4, float& P1, float& P2) {
float calcGain, calcP1, calcP2;
float aclGain = 10; // Set an initial gain to compare against. This needs to be larger than 2, or smaller than -1, so that it is always replaced when doing our first comparison
for (int i1 = 0; i1 < 25; i1++) { // For each resistor value for r1, except for the open circuit
for (int i2 = i1; i2 < 26; i2++) { // For each resistor value for r2 that is the same or larger than r1
for (int i3 = 0; i3 < 25; i3++) { // For each resistor value for r3, except for the open circuit
for (int i4 = i3; i4 < 26; i4++) { // For each resistor value for r4 that is the same or larger than r3
calcP1 = (stdR[i1] * stdR[i2]) / (stdR[i1] + stdR[i2]); // Calculate p1
calcP2 = (stdR[i3] * stdR[i4]) / (stdR[i3] + stdR[i4]); // Calculate p2
calcGain = calcP2 / (calcP1 + calcP2); // Calculate the gain
if (fabs(reqGain - calcGain) < fabs(reqGain - aclGain) || ((fabs(reqGain - calcGain) == fabs(reqGain - aclGain)) && calcP1 + calcP1 > P1 + P2)) { // If the calculated gain is closer to the required gain than our existing best result, or if it's the same result with a higher combined resistance
aclGain = calcGain; // Save our newly calculated gain
P1 = calcP1; // Save our newly calculated P1
P2 = calcP2;// Save our newly calculated P2
R1 = stdR[i1];// Save our newly calculated R1
R2 = stdR[i2];// Save our newly calculated R2
R3 = stdR[i3];// Save our newly calculated R3
R4 = stdR[i4];// Save our newly calculated R4
}
}
}
}
}
return aclGain; // Return our result
}
void Output(float reqGain, float aclGain, float R1, float R2, float R3, float R4, float P1, float P2) {
cout << "Required gain = " << fixed << setprecision(6) << reqGain << endl; // Print the gain we wanted
cout << "Actual gain = " << fixed << setprecision(6) << aclGain << endl; // Print the best gain we can do
cout << "R1 = " << fixed << setprecision(1) << R1 << " kohm" << endl; // Print our R1 resistor value
cout << "R2 = " << fixed << setprecision(1) << R2 << " kohm" << endl; // Print our R2 resistor value
cout << "R3 = " << fixed << setprecision(1) << R3 << " kohm" << endl; // Print our R3 resistor value
cout << "R4 = " << fixed << setprecision(1) << R4 << " kohm" << endl; // Print our R4 resistor value
cout << "P1 = " << fixed << setprecision(2) << P1 << " kohm" << endl; // Print our P1 resistance value
cout << "P2 = " << fixed << setprecision(2) << P2 << " kohm" << endl; // Print our P2 resistance value
}
int main() {
float stdR[26] = { 1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2, 10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82, 100, 1000000000 }; // Create an array of resistor values, in kohm
float R1, R2, R3, R4, P1, P2;
cout << "VOLTAGE DIVIDER" << endl; // Print a title
float reqGain = Input(); // Get our gain from the command prompt
if (reqGain != 0) { // If we're not exiting the program
float aclGain = Calculate(reqGain, stdR, R1, R2, R3, R4, P1, P2); // Get the closest gain we can get to our desired gain, along with the resistor and p1/p2 values
Output(reqGain, aclGain, R1, R2, R3, R4, P1, P2); // Print out our values
}
cout << "END OF PROGRAM";
}
This code was written in Visual Studio Community 2017, but also compiles in mingw and produces a 64kB executable file (rename this to .exe if you download it) with this command:
mingw ConsoleApplication1.cpp -o divider.exe