/*
Copyright (C) Paul Falstad and Iain Sharp
This file is part of CircuitJS1.
CircuitJS1 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 2 of the License, or
(at your option) any later version.
CircuitJS1 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 CircuitJS1. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lushprojects.circuitjs1.client;
//import java.awt.*;
//import java.util.StringTokenizer;
class OpAmpElm extends CircuitElm {
int opsize, opheight, opwidth, opaddtext;
double maxOut, minOut, gain, gbw;
boolean reset;
final int FLAG_SWAP = 1;
final int FLAG_SMALL = 2;
final int FLAG_LOWGAIN = 4;
public OpAmpElm(int xx, int yy) {
super(xx, yy);
noDiagonal = true;
maxOut = 15;
minOut = -15;
gbw = 1e6;
setSize(sim.smallGridCheckItem.getState() ? 1 : 2);
setGain();
}
public OpAmpElm(int xa, int ya, int xb, int yb, int f,
StringTokenizer st) {
super(xa, ya, xb, yb, f);
maxOut = 15;
minOut = -15;
// GBW has no effect in this version of the simulator, but we
// retain it to keep the file format the same
gbw = 1e6;
try {
maxOut = new Double(st.nextToken()).doubleValue();
minOut = new Double(st.nextToken()).doubleValue();
gbw = new Double(st.nextToken()).doubleValue();
volts[0] = new Double(st.nextToken()).doubleValue();
volts[1] = new Double(st.nextToken()).doubleValue();
} catch (Exception e) {
}
noDiagonal = true;
setSize((f & FLAG_SMALL) != 0 ? 1 : 2);
setGain();
}
void setGain() {
// gain of 100000 breaks e-amp-dfdx.txt
// gain was 1000, but it broke amp-schmitt.txt
gain = ((flags & FLAG_LOWGAIN) != 0) ? 1000 : 100000;
}
String dump() {
return super.dump() + " " + maxOut + " " + minOut + " " + gbw + " " + volts[0] + " " + volts[1];
}
boolean nonLinear() { return true; }
void draw(Graphics g) {
setBbox(point1, point2, opheight*2);
setVoltageColor(g, volts[0]);
drawThickLine(g, in1p[0], in1p[1]);
setVoltageColor(g, volts[1]);
drawThickLine(g, in2p[0], in2p[1]);
setVoltageColor(g, volts[2]);
drawThickLine(g, lead2, point2);
g.setColor(needsHighlight() ? selectColor : lightGrayColor);
setPowerColor(g, true);
drawThickPolygon(g, triangle);
g.setFont(plusFont);
drawCenteredText(g, "-", textp[0].x, textp[0].y-2, true);
drawCenteredText(g, "+", textp[1].x, textp[1].y , true);
curcount = updateDotCount(current, curcount);
drawDots(g, point2, lead2, curcount);
drawPosts(g);
}
double getPower() { return volts[2]*current; }
Point in1p[], in2p[], textp[];
Polygon triangle;
Font plusFont;
void setSize(int s) {
opsize = s;
opheight = 8*s;
opwidth = 13*s;
flags = (flags & ~FLAG_SMALL) | ((s == 1) ? FLAG_SMALL : 0);
}
void setPoints() {
super.setPoints();
if (dn > 150 && this == sim.dragElm)
setSize(2);
int ww = opwidth;
if (ww > dn/2)
ww = (int) (dn/2);
calcLeads(ww*2);
int hs = opheight*dsign;
if ((flags & FLAG_SWAP) != 0)
hs = -hs;
in1p = newPointArray(2);
in2p = newPointArray(2);
textp = newPointArray(2);
interpPoint2(point1, point2, in1p[0], in2p[0], 0, hs);
interpPoint2(lead1 , lead2, in1p[1], in2p[1], 0, hs);
interpPoint2(lead1 , lead2, textp[0], textp[1], .2, hs);
Point tris[] = newPointArray(2);
interpPoint2(lead1, lead2, tris[0], tris[1], 0, hs*2);
triangle = createPolygon(tris[0], tris[1], lead2);
plusFont = new Font("SansSerif", 0, opsize == 2 ? 14 : 10);
}
int getPostCount() { return 3; }
Point getPost(int n) {
return (n == 0) ? in1p[0] : (n == 1) ? in2p[0] : point2;
}
int getVoltageSourceCount() { return 1; }
void getInfo(String arr[]) {
arr[0] = "op-amp";
arr[1] = "V+ = " + getVoltageText(volts[1]);
arr[2] = "V- = " + getVoltageText(volts[0]);
// sometimes the voltage goes slightly outside range, to make
// convergence easier. so we hide that here.
double vo = Math.max(Math.min(volts[2], maxOut), minOut);
arr[3] = "Vout = " + getVoltageText(vo);
arr[4] = "Iout = " + getCurrentText(-current);
arr[5] = "range = " + getVoltageText(minOut) + " to " +
getVoltageText(maxOut);
}
double lastvd;
void stamp() {
int vn = sim.nodeList.size()+voltSource;
sim.stampNonLinear(vn);
sim.stampMatrix(nodes[2], vn, 1);
}
void doStep() {
double vd = volts[1] - volts[0];
if (Math.abs(lastvd-vd) > .1)
sim.converged = false;
else if (volts[2] > maxOut+.1 || volts[2] < minOut-.1)
sim.converged = false;
double x = 0;
int vn = sim.nodeList.size()+voltSource;
double dx = 0;
if (vd >= maxOut/gain && (lastvd >= 0 || sim.getrand(4) == 1)) {
dx = 1e-4;
x = maxOut - dx*maxOut/gain;
} else if (vd <= minOut/gain && (lastvd <= 0 || sim.getrand(4) == 1)) {
dx = 1e-4;
x = minOut - dx*minOut/gain;
} else
dx = gain;
//System.out.println("opamp " + vd + " " + volts[2] + " " + dx + " " + x + " " + lastvd + " " + sim.converged);
// newton-raphson
sim.stampMatrix(vn, nodes[0], dx);
sim.stampMatrix(vn, nodes[1], -dx);
sim.stampMatrix(vn, nodes[2], 1);
sim.stampRightSide(vn, x);
lastvd = vd;
/*if (sim.converged)
System.out.println((volts[1]-volts[0]) + " " + volts[2] + " " + initvd);*/
}
// there is no current path through the op-amp inputs, but there
// is an indirect path through the output to ground.
boolean getConnection(int n1, int n2) { return false; }
boolean hasGroundConnection(int n1) {
return (n1 == 2);
}
double getVoltageDiff() { return volts[2] - volts[1]; }
int getDumpType() { return 'a'; }
public EditInfo getEditInfo(int n) {
if (n == 0)
return new EditInfo("Max Output (V)", maxOut, 1, 20);
if (n == 1)
return new EditInfo("Min Output (V)", minOut, -20, 0);
return null;
}
public void setEditValue(int n, EditInfo ei) {
if (n == 0)
maxOut = ei.value;
if (n == 1)
minOut = ei.value;
}
int getShortcut() { return 'a'; }
@Override double getCurrentIntoNode(int n) {
if (n==2)
return -current;
return 0;
}
@Override double getCurrentIntoPoint(int xa, int ya) {
if (xa == x2 && ya == y2)
return -current;
return 0;
}
}