diff --git a/src/com/lushprojects/circuitjs1/client/CirSim.java b/src/com/lushprojects/circuitjs1/client/CirSim.java index 8b848ced..6d990cd4 100644 --- a/src/com/lushprojects/circuitjs1/client/CirSim.java +++ b/src/com/lushprojects/circuitjs1/client/CirSim.java @@ -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(''); win.document.close(); - win.print(); + setTimeout(function(){win.print();},1000); }-*/; void doDCAnalysis() { @@ -5265,18 +5266,51 @@ MouseOutHandler, MouseWheelHandler { DiodeModel.clearDumpedFlags(); Vector extList = new Vector(); boolean sel = isSelection(); + + // mapping of node labels -> node numbers + HashMap nodeNameHash = new HashMap(); + + // mapping of node numbers -> equivalent node numbers (if they both have the same label) + HashMap nodeNumberHash = new HashMap(); + + // 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; diff --git a/src/com/lushprojects/circuitjs1/client/EditCompositeModelDialog.java b/src/com/lushprojects/circuitjs1/client/EditCompositeModelDialog.java index 4971e625..63c47cdb 100644 --- a/src/com/lushprojects/circuitjs1/client/EditCompositeModelDialog.java +++ b/src/com/lushprojects/circuitjs1/client/EditCompositeModelDialog.java @@ -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; diff --git a/src/com/lushprojects/circuitjs1/client/LabeledNodeElm.java b/src/com/lushprojects/circuitjs1/client/LabeledNodeElm.java index 26975f49..3febd4ee 100644 --- a/src/com/lushprojects/circuitjs1/client/LabeledNodeElm.java +++ b/src/com/lushprojects/circuitjs1/client/LabeledNodeElm.java @@ -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 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); } } diff --git a/src/com/lushprojects/circuitjs1/client/OpAmpRealElm.java b/src/com/lushprojects/circuitjs1/client/OpAmpRealElm.java index 13e0f111..7a0e130f 100644 --- a/src/com/lushprojects/circuitjs1/client/OpAmpRealElm.java +++ b/src/com/lushprojects/circuitjs1/client/OpAmpRealElm.java @@ -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("Model", 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(); + } } } diff --git a/war/opampreal.html b/war/opampreal.html new file mode 100644 index 00000000..a3a9ddd1 --- /dev/null +++ b/war/opampreal.html @@ -0,0 +1,19 @@ + +

Real Op-Amps

+The ideal op-amp element uses an ideal approximation to op-amp behavior. It has infinite slew rate +and output current. +

+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. +

+You can also modify the slew rate or output current, which will modify the circuit to change these values from the default. +

+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. +

+The LM324 can act very glitchy as well if the slew rate is increased. Decreasing the time step size proportionately should fix this. + diff --git a/war/subcircuits.html b/war/subcircuits.html index 96ac3e6c..7b9b66e9 100644 --- a/war/subcircuits.html +++ b/war/subcircuits.html @@ -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. Here is an example using the rectifier. When you save/load this circuit, it will also save/load the subcircuit model so you can use it in other circuits.

You can also use subcircuits to rearrange pins on the built-in chips. Here is an example with the 555. And here is that subcircuit model being used in a circuit. +

+If you select part of a circuit before using File->Create Subcircuit, then only the selected elements will be considered part of +the subcircuit. +

+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.