/*
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;
// 0 = switch
// 1 = switch end 1
// 2 = switch end 2
// ...
// 3n = coil
// 3n+1 = coil
// 3n+2 = end of coil resistor
class RelayElm extends CircuitElm {
double inductance;
Inductor ind;
double r_on, r_off, onCurrent;
Point coilPosts[], coilLeads[], swposts[][], swpoles[][], ptSwitch[];
Point lines[];
double coilCurrent, switchCurrent[], coilCurCount, switchCurCount[];
double d_position, coilR;
int i_position;
int poleCount;
int openhs;
final int nSwitch0 = 0;
final int nSwitch1 = 1;
final int nSwitch2 = 2;
int nCoil1, nCoil2, nCoil3;
final int FLAG_SWAP_COIL = 1;
public RelayElm(int xx, int yy) {
super(xx, yy);
ind = new Inductor(sim);
inductance = .2;
ind.setup(inductance, 0, Inductor.FLAG_BACK_EULER);
noDiagonal = true;
onCurrent = .02;
r_on = .05;
r_off = 1e6;
coilR = 20;
coilCurrent = coilCurCount = 0;
poleCount = 1;
setupPoles();
}
public RelayElm(int xa, int ya, int xb, int yb, int f,
StringTokenizer st) {
super(xa, ya, xb, yb, f);
poleCount = new Integer(st.nextToken()).intValue();
inductance = new Double(st.nextToken()).doubleValue();
coilCurrent = new Double(st.nextToken()).doubleValue();
r_on = new Double(st.nextToken()).doubleValue();
r_off = new Double(st.nextToken()).doubleValue();
onCurrent = new Double(st.nextToken()).doubleValue();
coilR = new Double(st.nextToken()).doubleValue();
noDiagonal = true;
ind = new Inductor(sim);
ind.setup(inductance, coilCurrent, Inductor.FLAG_BACK_EULER);
setupPoles();
}
void setupPoles() {
nCoil1 = 3*poleCount;
nCoil2 = nCoil1+1;
nCoil3 = nCoil1+2;
if (switchCurrent == null || switchCurrent.length != poleCount) {
switchCurrent = new double[poleCount];
switchCurCount = new double[poleCount];
}
}
int getDumpType() { return 178; }
String dump() {
return super.dump() + " " + poleCount + " " +
inductance + " " + coilCurrent + " " +
r_on + " " + r_off + " " + onCurrent + " " + coilR;
}
void draw(Graphics g) {
int i, p;
for (i = 0; i != 2; i++) {
setVoltageColor(g, volts[nCoil1+i]);
drawThickLine(g, coilLeads[i], coilPosts[i]);
}
int x = ((flags & FLAG_SWAP_COIL) != 0) ? 1 : 0;
drawCoil(g, dsign*6, coilLeads[x], coilLeads[1-x],
volts[nCoil1+x], volts[nCoil2-x]);
// draw lines
g.setColor(Color.darkGray);
for (i = 0; i != poleCount; i++) {
if (i == 0)
interpPoint(point1, point2, lines[i*2 ], .5,
openhs*2+5*dsign-i*openhs*3);
else
interpPoint(point1, point2, lines[i*2], .5,
(int) (openhs*(-i*3+3-.5+d_position))+5*dsign);
interpPoint(point1, point2, lines[i*2+1], .5,
(int) (openhs*(-i*3-.5+d_position))-5*dsign);
g.drawLine(lines[i*2].x, lines[i*2].y, lines[i*2+1].x, lines[i*2+1].y);
}
for (p = 0; p != poleCount; p++) {
int po = p*3;
for (i = 0; i != 3; i++) {
// draw lead
setVoltageColor(g, volts[nSwitch0+po+i]);
drawThickLine(g, swposts[p][i], swpoles[p][i]);
}
interpPoint(swpoles[p][1], swpoles[p][2], ptSwitch[p], d_position);
//setVoltageColor(g, volts[nSwitch0]);
g.setColor(Color.lightGray);
drawThickLine(g, swpoles[p][0], ptSwitch[p]);
switchCurCount[p] = updateDotCount(switchCurrent[p],
switchCurCount[p]);
drawDots(g, swposts[p][0], swpoles[p][0], switchCurCount[p]);
if (i_position != 2)
drawDots(g, swpoles[p][i_position+1], swposts[p][i_position+1],
switchCurCount[p]);
}
coilCurCount = updateDotCount(coilCurrent, coilCurCount);
drawDots(g, coilPosts[0], coilLeads[0], coilCurCount);
drawDots(g, coilLeads[0], coilLeads[1], coilCurCount);
drawDots(g, coilLeads[1], coilPosts[1], coilCurCount);
drawPosts(g);
setBbox(coilPosts[0], coilLeads[1], 0);
adjustBbox(swpoles[poleCount-1][0], swposts[poleCount-1][1]); // XXX
}
@Override double getCurrentIntoPoint(int xa, int ya) {
if (xa == coilPosts[0].x && ya == coilPosts[0].y)
return -coilCurrent;
if (xa == coilPosts[1].x && ya == coilPosts[1].y)
return coilCurrent;
int i;
for (i = 0; i != poleCount; i++) {
if (xa == swposts[i][0].x && ya == swposts[i][0].y)
return -switchCurrent[i];
if (i_position != 2 && xa == swposts[i][i_position+1].x &&
ya == swposts[i][i_position+1].y)
return switchCurrent[i];
}
return 0;
}
void setPoints() {
super.setPoints();
setupPoles();
allocNodes();
openhs = -dsign*16;
// switch
calcLeads(32);
swposts = new Point[poleCount][3];
swpoles = new Point[poleCount][3];
int i, j;
for (i = 0; i != poleCount; i++) {
for (j = 0; j != 3; j++) {
swposts[i][j] = new Point();
swpoles[i][j] = new Point();
}
interpPoint(lead1, lead2, swpoles[i][0], 0, -openhs*3*i);
interpPoint(lead1, lead2, swpoles[i][1], 1, -openhs*3*i-openhs);
interpPoint(lead1, lead2, swpoles[i][2], 1, -openhs*3*i+openhs);
interpPoint(point1, point2, swposts[i][0], 0, -openhs*3*i);
interpPoint(point1, point2, swposts[i][1], 1, -openhs*3*i-openhs);
interpPoint(point1, point2, swposts[i][2], 1, -openhs*3*i+openhs);
}
// coil
coilPosts = newPointArray(2);
coilLeads = newPointArray(2);
ptSwitch = newPointArray(poleCount);
int x = ((flags & FLAG_SWAP_COIL) != 0) ? 1 : 0;
interpPoint(point1, point2, coilPosts[0], x, openhs*2);
interpPoint(point1, point2, coilPosts[1], x, openhs*3);
interpPoint(point1, point2, coilLeads[0], .5, openhs*2);
interpPoint(point1, point2, coilLeads[1], .5, openhs*3);
// lines
lines = newPointArray(poleCount*2);
}
Point getPost(int n) {
if (n < 3*poleCount)
return swposts[n / 3][n % 3];
return coilPosts[n-3*poleCount];
}
int getPostCount() { return 2+poleCount*3; }
int getInternalNodeCount() { return 1; }
void reset() {
super.reset();
ind.reset();
coilCurrent = coilCurCount = 0;
int i;
for (i = 0; i != poleCount; i++)
switchCurrent[i] = switchCurCount[i] = 0;
}
double a1, a2, a3, a4;
void stamp() {
// inductor from coil post 1 to internal node
ind.stamp(nodes[nCoil1], nodes[nCoil3]);
// resistor from internal node to coil post 2
sim.stampResistor(nodes[nCoil3], nodes[nCoil2], coilR);
int i;
for (i = 0; i != poleCount*3; i++)
sim.stampNonLinear(nodes[nSwitch0+i]);
}
void startIteration() {
ind.startIteration(volts[nCoil1]-volts[nCoil3]);
// magic value to balance operate speed with reset speed semi-realistically
double magic = 1.3;
double pmult = Math.sqrt(magic+1);
double p = coilCurrent*pmult/onCurrent;
d_position = Math.abs(p*p) - 1.3;
if (d_position < 0)
d_position = 0;
if (d_position > 1)
d_position = 1;
if (d_position < .1)
i_position = 0;
else if (d_position > .9)
i_position = 1;
else
i_position = 2;
//System.out.println("ind " + this + " " + current + " " + voltdiff);
}
// we need this to be able to change the matrix for each step
boolean nonLinear() { return true; }
void doStep() {
double voltdiff = volts[nCoil1]-volts[nCoil3];
ind.doStep(voltdiff);
int p;
for (p = 0; p != poleCount*3; p += 3) {
sim.stampResistor(nodes[nSwitch0+p], nodes[nSwitch1+p],
i_position == 0 ? r_on : r_off);
sim.stampResistor(nodes[nSwitch0+p], nodes[nSwitch2+p],
i_position == 1 ? r_on : r_off);
}
}
void calculateCurrent() {
double voltdiff = volts[nCoil1]-volts[nCoil3];
coilCurrent = ind.calculateCurrent(voltdiff);
// actually this isn't correct, since there is a small amount
// of current through the switch when off
int p;
for (p = 0; p != poleCount; p++) {
if (i_position == 2)
switchCurrent[p] = 0;
else
switchCurrent[p] =
(volts[nSwitch0+p*3]-volts[nSwitch1+p*3+i_position])/r_on;
}
}
void getInfo(String arr[]) {
arr[0] = i_position == 0 ? "relay (off)" :
i_position == 1 ? "relay (on)" : "relay";
int i;
int ln = 1;
for (i = 0; i != poleCount; i++)
arr[ln++] = "I" + (i+1) + " = " + getCurrentDText(switchCurrent[i]);
arr[ln++] = "coil I = " + getCurrentDText(coilCurrent);
arr[ln++] = "coil Vd = " +
getVoltageDText(volts[nCoil1] - volts[nCoil2]);
}
public EditInfo getEditInfo(int n) {
if (n == 0)
return new EditInfo("Inductance (H)", inductance, 0, 0);
if (n == 1)
return new EditInfo("On Resistance (ohms)", r_on, 0, 0);
if (n == 2)
return new EditInfo("Off Resistance (ohms)", r_off, 0, 0);
if (n == 3)
return new EditInfo("On Current (A)", onCurrent, 0, 0);
if (n == 4)
return new EditInfo("Number of Poles", poleCount, 1, 4).
setDimensionless();
if (n == 5)
return new EditInfo("Coil Resistance (ohms)", coilR, 0, 0);
if (n == 6) {
EditInfo ei = new EditInfo("", 0, -1, -1);
ei.checkbox = new Checkbox("Swap Coil Direction",
(flags & FLAG_SWAP_COIL) != 0);
return ei;
}
return null;
}
public void setEditValue(int n, EditInfo ei) {
if (n == 0 && ei.value > 0) {
inductance = ei.value;
ind.setup(inductance, coilCurrent, Inductor.FLAG_BACK_EULER);
}
if (n == 1 && ei.value > 0)
r_on = ei.value;
if (n == 2 && ei.value > 0)
r_off = ei.value;
if (n == 3 && ei.value > 0)
onCurrent = ei.value;
if (n == 4 && ei.value >= 1) {
poleCount = (int) ei.value;
setPoints();
}
if (n == 5 && ei.value > 0)
coilR = ei.value;
if (n == 6) {
if (ei.checkbox.getState())
flags |= FLAG_SWAP_COIL;
else
flags &= ~FLAG_SWAP_COIL;
setPoints();
}
}
boolean getConnection(int n1, int n2) {
return (n1 / 3 == n2 / 3);
}
int getShortcut() { return 'R'; }
}