add LM324 model to real op-amp

allow internal labeled nodes in subcircuits
printing would often try to print the circuit before the image was loaded
This commit is contained in:
pf
2019-05-26 14:09:32 -07:00
parent f5e0fc7248
commit 97b00a1e53
6 changed files with 152 additions and 34 deletions

View File

@@ -70,6 +70,7 @@ import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.event.dom.client.ClickHandler;
@@ -5174,7 +5175,7 @@ MouseOutHandler, MouseWheelHandler {
win.document.open();
win.document.write('<img src="'+img+'"/>');
win.document.close();
win.print();
setTimeout(function(){win.print();},1000);
}-*/;
void doDCAnalysis() {
@@ -5265,18 +5266,51 @@ MouseOutHandler, MouseWheelHandler {
DiodeModel.clearDumpedFlags();
Vector<ExtListEntry> extList = new Vector<ExtListEntry>();
boolean sel = isSelection();
// mapping of node labels -> node numbers
HashMap<String, Integer> nodeNameHash = new HashMap<String, Integer>();
// mapping of node numbers -> equivalent node numbers (if they both have the same label)
HashMap<Integer, Integer> nodeNumberHash = new HashMap<Integer, Integer>();
// find all the labeled nodes, get a list of them, and create a node number map
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (sel && !ce.isSelected())
continue;
if (ce instanceof WireElm)
continue;
if (ce instanceof LabeledNodeElm) {
String label = ((LabeledNodeElm) ce).text;
LabeledNodeElm lne = (LabeledNodeElm) ce;
String label = lne.text;
Integer map = nodeNameHash.get(label);
// this node name already seen? map the new node number to the old one
if (map != null) {
Integer val = nodeNumberHash.get(lne.getNode(0));
if (val != null && !val.equals(map)) {
Window.alert("Can't have a node with two labels!");
return null;
}
nodeNumberHash.put(lne.getNode(0), map);
continue;
}
nodeNameHash.put(label, lne.getNode(0));
// put an entry in nodeNumberHash so we can detect if we try to map it to something else later
nodeNumberHash.put(lne.getNode(0), lne.getNode(0));
if (lne.isInternal())
continue;
// create ext list entry for external nodes
ExtListEntry ent = new ExtListEntry(label, ce.getNode(0));
extList.add(ent);
continue;
}
}
// output all the elements
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (sel && !ce.isSelected())
continue;
if (ce instanceof WireElm || ce instanceof LabeledNodeElm)
continue;
if (ce instanceof GraphicElm)
continue;
int j;
@@ -5284,13 +5318,12 @@ MouseOutHandler, MouseWheelHandler {
nodeList += "\r";
nodeList += ce.getClass().getSimpleName();
for (j = 0; j != ce.getPostCount(); j++) {
nodeList += " " + ce.getNode(j);
int n = ce.getNode(j);
Integer nobj = nodeNumberHash.get(n);
int n0 = (nobj == null) ? n : nobj;
nodeList += " " + n0;
}
// String m = ce.dumpModel();
// if (m != null && !m.isEmpty())
// models += m + "\n";
// save positions
int x1 = ce.x; int y1 = ce.y;
int x2 = ce.x2; int y2 = ce.y2;

View File

@@ -76,11 +76,6 @@ public class EditCompositeModelDialog extends DialogBox implements MouseDownHand
});
int i;
int postCount = model.extList.size();
for (i = 0; i != postCount-1; i++)
if (model.extList.get(i).name.equals(model.extList.get(i+1).name)) {
Window.alert(CirSim.LS("Input names must be unique"));
return false;
}
model.sizeX = 2;
model.sizeY = (postCount+1)/2;

View File

@@ -23,6 +23,7 @@ import java.util.HashMap;
class LabeledNodeElm extends CircuitElm {
final int FLAG_ESCAPE = 4;
final int FLAG_INTERNAL = 1;
public LabeledNodeElm(int xx, int yy) {
super(xx, yy);
@@ -43,12 +44,13 @@ class LabeledNodeElm extends CircuitElm {
}
String dump() {
flags |= FLAG_ESCAPE;
return super.dump() + " " + " " + CustomLogicModel.escape(text);
return super.dump() + " " + CustomLogicModel.escape(text);
}
String text;
static HashMap<String,Integer> nodeList;
int nodeNumber;
boolean isInternal() { return (flags & FLAG_INTERNAL) != 0; }
public static native void console(String text)
/*-{
@@ -134,11 +136,17 @@ class LabeledNodeElm extends CircuitElm {
ei.text = text;
return ei;
}
if (n == 1) {
EditInfo ei = new EditInfo("", 0, -1, -1);
ei.checkbox = new Checkbox("Internal Node", isInternal());
return ei;
}
return null;
}
public void setEditValue(int n, EditInfo ei) {
if (n == 0) {
if (n == 0)
text = ei.textf.getText();
}
if (n == 1)
flags = ei.changeFlag(flags, FLAG_INTERNAL);
}
}

View File

@@ -3,7 +3,7 @@ package com.lushprojects.circuitjs1.client;
public class OpAmpRealElm extends CompositeElm {
// from https://commons.wikimedia.org/wiki/File:OpAmpTransistorLevel_Colored_Labeled.svg
private static String modelString =
private static String model741String =
"NTransistorElm 3 8 9\rNTransistorElm 2 8 10\rPTransistorElm 11 12 9\rPTransistorElm 11 13 10\rNTransistorElm 14 12 1\r" + // Q1-5
"NTransistorElm 14 13 5\rNTransistorElm 12 7 14\rPTransistorElm 8 8 7\rPTransistorElm 8 11 7\rNTransistorElm 17 11 16\r" + // Q6-10
"NTransistorElm 17 17 4\rPTransistorElm 18 18 7\rPTransistorElm 18 20 7\rNTransistorElm 20 7 25\rNTransistorElm 13 22 24\r" + // Q11-15
@@ -12,10 +12,22 @@ public class OpAmpRealElm extends CompositeElm {
"ResistorElm 15 6\rResistorElm 6 25\r" + // output resistors
"ResistorElm 4 1\rResistorElm 4 14\rResistorElm 4 5\rResistorElm 4 16\rResistorElm 4 24\rResistorElm 4 23\rResistorElm 17 18\r" +
"ResistorElm 22 21\rResistorElm 21 20\r";
private static int[] modelExternalNodes = { 2, 3, 6, 7, 4 }; // , 1, 5 };
private static int[] model741ExternalNodes = { 2, 3, 6, 7, 4 }; // , 1, 5 };
// 0 = input -, 1 = input +, 2 = output, 3 = V+, 4 = V-, 5, 6 = offset null
private static double[] resistances = { 50, 25, 1e3, 50e3, 1e3, 5e3, 50e3, 50, 39e3, 7500, 4500 };
private static String lm324ModelString =
"TransistorElm 1 2 3\rCurrentElm 4 3\rTransistorElm 2 2 5\rTransistorElm 2 6 5\rCapacitorElm 6 7\rCurrentElm 4 8\rCurrentElm 4 7\rTransistorElm 8 4 9\r" +
"TransistorElm 7 4 10\rTransistorElm 10 4 11\rTransistorElm 11 7 12\rResistorElm 11 12\rTransistorElm 7 5 12\rCurrentElm 12 5\rTransistorElm 6 5 8\r" +
"ResistorElm 9 5\rTransistorElm 9 7 5\rTransistorElm 13 6 3";
private static int[] lm324ExternalNodes = { 1, 13, 12, 4, 5 };
private static String lm324ModelDump =
"0 -1 -0 0 10000/0 0.000006/0 1 0 0 100/0 1 0 0 100/0 1e-11 0/0 0.000004/0 0.0001/0 1 0 0 100/0 1 0 0 100/0 1 0 0 100/0 1 0 0 100/0 25/0 -1 0 0 100/0 0.00005/" +
"0 -1 0 0 100/0 10000/0 1 0 0 100/0 -1 0 0 10000";
static final int MODEL_741 = 0;
static final int MODEL_324 = 1;
private static double[] model741resistances = { 50, 25, 1e3, 50e3, 1e3, 5e3, 50e3, 50, 39e3, 7500, 4500 };
int modelType;
final int opheight = 16;
@@ -23,40 +35,54 @@ public class OpAmpRealElm extends CompositeElm {
double curCounts[];
double slewRate;
double currentLimit;
double capValue;
final double defaultCurrentLimit = .0231;
final int FLAG_SWAP = 2;
public OpAmpRealElm(int xx, int yy) {
super(xx, yy, modelString, modelExternalNodes);
super(xx, yy); // , model741String, model741ExternalNodes);
noDiagonal = true;
slewRate = .6;
currentLimit = defaultCurrentLimit;
modelType = 741;
init741();
modelType = MODEL_741;
initModel();
}
public OpAmpRealElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) {
super(xa, ya, xb, yb, f, null, modelString, modelExternalNodes);
super(xa, ya, xb, yb, f); // , null, model741String, model741ExternalNodes);
noDiagonal = true;
slewRate = Double.parseDouble(st.nextToken());
getCapacitor().voltdiff = Double.parseDouble(st.nextToken());
capValue = Double.parseDouble(st.nextToken());
currentLimit = defaultCurrentLimit;
modelType = 741;
modelType = MODEL_741;
try {
currentLimit = Double.parseDouble(st.nextToken());
modelType = Integer.parseInt(st.nextToken());
} catch (Exception e) {}
init741();
initModel();
}
private void initModel() {
flags |= FLAG_ESCAPE;
if (modelType == MODEL_741)
init741();
else
init324();
curCounts = new double[5];
setPoints();
}
private void init741() {
loadComposite(null, model741String, model741ExternalNodes);
// adjust capacitor value to get desired slew rate
getCapacitor().capacitance = 30e-12 / (slewRate/.6);
getCapacitor().voltdiff = capValue;
// set resistor values
int i;
for (i = 0; i != 11; i++)
((ResistorElm) compElmList.get(21+i)).resistance = resistances[i];
((ResistorElm) compElmList.get(21+i)).resistance = model741resistances[i];
// adjust output stage resistor values and transistor betas to increase current if desired
double currentMult = currentLimit / defaultCurrentLimit;
@@ -65,15 +91,31 @@ public class OpAmpRealElm extends CompositeElm {
((TransistorElm) compElmList.get(13)).setBeta(currentMult * 100); // Q14
((TransistorElm) compElmList.get(18)).setBeta(currentMult * 100); // Q20
curCounts = new double[5];
}
private void init324() {
StringTokenizer st = new StringTokenizer(lm324ModelDump, "/");
loadComposite(st, lm324ModelString, lm324ExternalNodes);
// adjust capacitor value to get desired slew rate
getCapacitor().capacitance = 10e-12 / (slewRate/.55);
getCapacitor().voltdiff = capValue;
// adjust output stage resistor values and transistor betas to increase current if desired
double currentMult = currentLimit / defaultCurrentLimit;
((ResistorElm) compElmList.get(11)).resistance /= currentMult;
((TransistorElm) compElmList.get(9)).setBeta(currentMult * 100);
((TransistorElm) compElmList.get(10)).setBeta(currentMult * 100);
((TransistorElm) compElmList.get(12)).setBeta(currentMult * 100);
((TransistorElm) compElmList.get(16)).setBeta(currentMult * 100);
}
public void reset() {
super.reset();
curCounts = new double[5];
}
CapacitorElm getCapacitor() { return ((CapacitorElm) compElmList.get(20)); }
CapacitorElm getCapacitor() { return ((CapacitorElm) compElmList.get(modelType == MODEL_741 ? 20 : 4)); }
public String dump() {
return super.dumpWithMask(0) + " " + slewRate + " " + getCapacitor().voltdiff + " " + currentLimit + " " + modelType;
@@ -159,7 +201,8 @@ public class OpAmpRealElm extends CompositeElm {
}
void getInfo(String arr[]) {
arr[0] = "op-amp (741)";
String type = (modelType == MODEL_741) ? "LM741" : "LM324";
arr[0] = "op-amp (" + type + ")";
arr[1] = "V+ = " + getVoltageText(volts[1]);
arr[2] = "V- = " + getVoltageText(volts[0]);
arr[3] = "Vout = " + getVoltageText(volts[2]);
@@ -176,21 +219,35 @@ public class OpAmpRealElm extends CompositeElm {
ei.checkbox = new Checkbox("Swap Inputs", (flags & FLAG_SWAP) != 0);
return ei;
}
if (n == 3) {
EditInfo ei = new EditInfo("<a href=\"opampreal.html\">Model</a>", modelType);
ei.choice = new Choice();
ei.choice.add("LM741");
ei.choice.add("LM324");
ei.choice.select(modelType);
return ei;
}
return null;
}
public void setEditValue(int n, EditInfo ei) {
if (n == 0) {
slewRate = ei.value;
init741();
initModel();
}
if (n == 1) {
currentLimit = ei.value;
init741();
initModel();
}
if (n == 2) {
flags = ei.changeFlag(flags, FLAG_SWAP);
setPoints();
}
if (n == 3) {
modelType = ei.choice.getSelectedIndex();
capValue = 0;
initModel();
}
}
}

19
war/opampreal.html Normal file
View File

@@ -0,0 +1,19 @@
<html><head>
<meta http-equiv="content-type" content="text/html; charset=windows-1252"><style>#header + #content > #left > #rlblock_left,
#content > #right > .dose > .dosesingle,
#content > #center > .dose > .dosesingle
{display:none !important;}</style><style>img[src="http://s05.flagcounter.com/count/pTvk/bg=FFFFFF/txt=000000/border=CCCCCC/columns=6/maxflags=36/viewers=0/labels=0/"]
{display:none !important;}</style></head><body><h1>Real Op-Amps</h1>
The ideal op-amp element uses an ideal approximation to op-amp behavior. It has infinite <a href="https://en.wikipedia.org/wiki/Slew_rate">slew rate</a>
and output current.
<p>
The real op-amp element uses a subcircuit to emulate a real op-amp implementation with finite slew rate and output current. Presently,
the two implementation options are LM741 and LM324.
<p>
You can also modify the slew rate or output current, which will modify the circuit to change these values from the default.
<p>
These subcircuits are complicated, so you may run problems with convergence, especially if you increase the slew rate. Try decreasing
the time step size if this happens.
<p>
The LM324 can act very glitchy as well if the slew rate is increased. Decreasing the time step size proportionately should fix this.
</body></html>

View File

@@ -14,4 +14,10 @@ Be sure to save this circuit (the subcircuit implementation), because it can't b
Now you can create a circuit using the subcircuit model. <a href="circuitjs.html?cct=$+1+0.000005+10.20027730826997+59+5+50%0A.+fullrect+0+2+2+4+in%5Cp+1+0+2+in-+3+1+2+out%5Cp+2+0+3+out-+4+1+3+DiodeElm%5Cs1%5Cs2%5CrDiodeElm%5Cs3%5Cs2%5CrDiodeElm%5Cs4%5Cs1%5CrDiodeElm%5Cs4%5Cs3+2%5C%5Csdefault%5Cs2%5C%5Csdefault%5Cs2%5C%5Csdefault%5Cs2%5C%5Csdefault%0A410+688+288+784+368+1+fullrect+2%5Csdefault+2%5Csdefault+2%5Csdefault+2%5Csdefault%0Aw+688+288+688+240+0%0Aw+688+320+688+368+0%0Aw+688+368+640+368+0%0Aw+688+240+640+240+0%0Av+640+368+640+240+0+1+40+5+0+0+0.5%0Aw+784+288+784+240+0%0Aw+784+320+784+368+0%0Aw+784+368+832+368+0%0Ar+832+240+832+368+0+1000%0Aw+784+240+832+240+0%0Ao+9+64+0+4099+5+0.025+0+2+9+3%0A">Here is an example using the rectifier.</a> When you save/load this circuit, it will also save/load the subcircuit model so you can use it in other circuits.
<p>
You can also use subcircuits to rearrange pins on the built-in chips. <a href="circuitjs.html?cct=$+1+0.000005+10.20027730826997+50+5+50%0A165+800+368+848+368+6+0%0A207+864+336+864+288+0+Vin%0A207+800+400+752+400+0+dis%0A207+800+464+736+464+0+tr%0A207+800+496+736+496+0+th%0A207+864+528+864+592+0+ctl%0A207+896+528+896+592+0+gnd%0A207+928+432+992+432+0+out%0A207+928+400+992+400+0+rst%0A">Here is an example with the 555</a>. And <a href="circuitjs.html?cct=$+1+0.000005+5.023272298708815+64+7+50%0Aw+112+192+112+224+0%0Aw+112+224+176+224+0%0Ac+112+224+112+288+0+3e-7+6.326871610496845%0Ag+112+288+112+304+0%0Ar+112+192+112+64+0+1000000%0Aw+112+64+240+64+0%0AR+112+64+80+64+0+0+40+10+0+0+0.5%0AO+352+160+416+160+0%0Aw+240+64+304+64+0%0Aw+304+64+304+128+0%0Aw+352+160+352+32+0%0Ar+352+32+160+32+0+10000%0Aw+160+32+160+96+0%0Aw+112+192+160+192+0%0A.+555+0+2+4+8+ctl+5+3+3+dis+1+1+3+gnd+8+0+2+out+6+2+2+rst+7+3+2+th+3+2+3+tr+2+1+2+Vin+4+0+3+TimerElm%5Cs1%5Cs2%5Cs3%5Cs4%5Cs5%5Cs6%5Cs7%5Cs8+6%5C%5Cs0%0A410+208+128+224+160+1+555+6%5Cs9.999632723888661%0Aw+304+128+336+128+0%0Aw+336+128+336+272+0%0Aw+336+272+208+272+0%0Aw+160+192+176+192+0%0Aw+176+192+176+160+0%0Aw+176+160+208+160+0%0Aw+208+192+192+192+0%0Aw+176+224+176+288+0%0Aw+176+288+320+288+0%0Aw+320+288+320+192+0%0Aw+320+192+304+192+0%0Aw+160+96+160+192+0%0Aw+160+96+320+96+0%0Ag+208+128+208+144+0%0Aw+208+224+208+272+0%0Aw+192+192+192+320+0%0Aw+192+320+352+320+0%0Aw+352+320+352+160+0%0Ao+2+32+0+4099+10+0.00078125+0+2+2+3%0Ao+7+16+0+4106+10+0.00009765625+1+1%0A">here is that subcircuit model being used in a circuit.</a>
<p>
If you select part of a circuit before using File->Create Subcircuit, then only the selected elements will be considered part of
the subcircuit.
<p>
If you want to use labeled nodes in the subcircuit definition but don't want them to be one of the inputs/outputs, then check the "Internal Node"
checkbox in those labeled nodes' edit menus.
</body></html>