/* 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 TransformerElm extends CircuitElm { double inductance, ratio, couplingCoef; Point ptEnds[], ptCoil[], ptCore[]; double current[], curcount[]; Point dots[]; int width, polarity; public static final int FLAG_REVERSE = 4; public TransformerElm(int xx, int yy) { super(xx, yy); inductance = 4; ratio = polarity = 1; width = 32; noDiagonal = true; couplingCoef = .999; current = new double[2]; curcount = new double[2]; } public TransformerElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { super(xa, ya, xb, yb, f); width = max(32, abs(yb-ya)); inductance = new Double(st.nextToken()).doubleValue(); ratio = new Double(st.nextToken()).doubleValue(); current = new double[2]; curcount = new double[2]; current[0] = new Double(st.nextToken()).doubleValue(); current[1] = new Double(st.nextToken()).doubleValue(); couplingCoef = .999; try { couplingCoef = new Double(st.nextToken()).doubleValue(); } catch (Exception e) { } noDiagonal = true; polarity = ((flags & FLAG_REVERSE) != 0) ? -1 : 1; } void drag(int xx, int yy) { xx = sim.snapGrid(xx); yy = sim.snapGrid(yy); width = max(32, abs(yy-y)); if (xx == x) yy = y; x2 = xx; y2 = yy; setPoints(); } int getDumpType() { return 'T'; } String dump() { return super.dump() + " " + inductance + " " + ratio + " " + current[0] + " " + current[1] + " " + couplingCoef; } boolean isTrapezoidal() { return (flags & Inductor.FLAG_BACK_EULER) == 0; } void draw(Graphics g) { int i; for (i = 0; i != 4; i++) { setVoltageColor(g, volts[i]); drawThickLine(g, ptEnds[i], ptCoil[i]); } for (i = 0; i != 2; i++) { setPowerColor(g, current[i]*(volts[i]-volts[i+2])); drawCoil(g, dsign*(i == 1 ? -6*polarity : 6), ptCoil[i], ptCoil[i+2], volts[i], volts[i+2]); } g.setColor(needsHighlight() ? selectColor : lightGrayColor); for (i = 0; i != 2; i++) { drawThickLine(g, ptCore[i], ptCore[i+2]); if (dots != null) g.fillOval(dots[i].x-2, dots[i].y-2, 5, 5); curcount[i] = updateDotCount(current[i], curcount[i]); } for (i = 0; i != 2; i++) { drawDots(g, ptEnds[i], ptCoil[i], curcount[i]); drawDots(g, ptCoil[i], ptCoil[i+2], curcount[i]); drawDots(g, ptEnds[i+2], ptCoil[i+2], -curcount[i]); } drawPosts(g); setBbox(ptEnds[0], ptEnds[polarity == 1 ? 3 : 1], 0); } void setPoints() { super.setPoints(); point2.y = point1.y; ptEnds = newPointArray(4); ptCoil = newPointArray(4); ptCore = newPointArray(4); ptEnds[0] = point1; ptEnds[1] = point2; interpPoint(point1, point2, ptEnds[2], 0, -dsign*width); interpPoint(point1, point2, ptEnds[3], 1, -dsign*width); double ce = .5-12/dn; double cd = .5-2/dn; int i; for (i = 0; i != 4; i += 2) { interpPoint(ptEnds[i], ptEnds[i+1], ptCoil[i], ce); interpPoint(ptEnds[i], ptEnds[i+1], ptCoil[i+1], 1-ce); interpPoint(ptEnds[i], ptEnds[i+1], ptCore[i], cd); interpPoint(ptEnds[i], ptEnds[i+1], ptCore[i+1], 1-cd); } if (polarity == -1) { dots = new Point[2]; double dotp = Math.abs(7./width); dots[0] = interpPoint(ptCoil[0], ptCoil[2], dotp, -7*dsign); dots[1] = interpPoint(ptCoil[3], ptCoil[1], dotp, -7*dsign); Point x = ptEnds[1]; ptEnds[1] = ptEnds[3]; ptEnds[3] = x; x = ptCoil[1]; ptCoil[1] = ptCoil[3]; ptCoil[3] = x; } else dots = null; } Point getPost(int n) { return ptEnds[n]; } int getPostCount() { return 4; } void reset() { current[0] = current[1] = volts[0] = volts[1] = volts[2] = volts[3] = curcount[0] = curcount[1] = 0; } double a1, a2, a3, a4; void stamp() { // equations for transformer: // v1 = L1 di1/dt + M di2/dt // v2 = M di1/dt + L2 di2/dt // we invert that to get: // di1/dt = a1 v1 + a2 v2 // di2/dt = a3 v1 + a4 v2 // integrate di1/dt using trapezoidal approx and we get: // i1(t2) = i1(t1) + dt/2 (i1(t1) + i1(t2)) // = i1(t1) + a1 dt/2 v1(t1) + a2 dt/2 v2(t1) + // a1 dt/2 v1(t2) + a2 dt/2 v2(t2) // the norton equivalent of this for i1 is: // a. current source, I = i1(t1) + a1 dt/2 v1(t1) + a2 dt/2 v2(t1) // b. resistor, G = a1 dt/2 // c. current source controlled by voltage v2, G = a2 dt/2 // and for i2: // a. current source, I = i2(t1) + a3 dt/2 v1(t1) + a4 dt/2 v2(t1) // b. resistor, G = a3 dt/2 // c. current source controlled by voltage v2, G = a4 dt/2 // // For backward euler, // // i1(t2) = i1(t1) + a1 dt v1(t2) + a2 dt v2(t2) // // So the current source value is just i1(t1) and we use // dt instead of dt/2 for the resistor and VCCS. // // first winding goes from node 0 to 2, second is from 1 to 3 double l1 = inductance; double l2 = inductance*ratio*ratio; double m = couplingCoef*Math.sqrt(l1*l2); // build inverted matrix double deti = 1/(l1*l2-m*m); double ts = isTrapezoidal() ? sim.timeStep/2 : sim.timeStep; a1 = l2*deti*ts; // we multiply dt/2 into a1..a4 here a2 = -m*deti*ts; a3 = -m*deti*ts; a4 = l1*deti*ts; sim.stampConductance(nodes[0], nodes[2], a1); sim.stampVCCurrentSource(nodes[0], nodes[2], nodes[1], nodes[3], a2); sim.stampVCCurrentSource(nodes[1], nodes[3], nodes[0], nodes[2], a3); sim.stampConductance(nodes[1], nodes[3], a4); sim.stampRightSide(nodes[0]); sim.stampRightSide(nodes[1]); sim.stampRightSide(nodes[2]); sim.stampRightSide(nodes[3]); } void startIteration() { double voltdiff1 = volts[0]-volts[2]; double voltdiff2 = volts[1]-volts[3]; if (isTrapezoidal()) { curSourceValue1 = voltdiff1*a1+voltdiff2*a2+current[0]; curSourceValue2 = voltdiff1*a3+voltdiff2*a4+current[1]; } else { curSourceValue1 = current[0]; curSourceValue2 = current[1]; } } double curSourceValue1, curSourceValue2; void doStep() { sim.stampCurrentSource(nodes[0], nodes[2], curSourceValue1); sim.stampCurrentSource(nodes[1], nodes[3], curSourceValue2); } void calculateCurrent() { double voltdiff1 = volts[0]-volts[2]; double voltdiff2 = volts[1]-volts[3]; current[0] = voltdiff1*a1 + voltdiff2*a2 + curSourceValue1; current[1] = voltdiff1*a3 + voltdiff2*a4 + curSourceValue2; } @Override double getCurrentIntoPoint(int xa, int ya) { if (xa == ptEnds[0].x && ya == ptEnds[0].y) return -current[0]; if (xa == ptEnds[2].x && ya == ptEnds[2].y) return current[0]; if (xa == ptEnds[1].x && ya == ptEnds[1].y) return -current[1]; return current[1]; } void getInfo(String arr[]) { arr[0] = "transformer"; arr[1] = "L = " + getUnitText(inductance, "H"); arr[2] = "Ratio = 1:" + ratio; arr[3] = "Vd1 = " + getVoltageText(volts[0]-volts[2]); arr[4] = "Vd2 = " + getVoltageText(volts[1]-volts[3]); arr[5] = "I1 = " + getCurrentText(current[0]); arr[6] = "I2 = " + getCurrentText(current[1]); } boolean getConnection(int n1, int n2) { if (comparePair(n1, n2, 0, 2)) return true; if (comparePair(n1, n2, 1, 3)) return true; return false; } public EditInfo getEditInfo(int n) { if (n == 0) return new EditInfo("Primary Inductance (H)", inductance, .01, 5); if (n == 1) return new EditInfo("Ratio", ratio, 1, 10).setDimensionless(); if (n == 2) return new EditInfo("Coupling Coefficient", couplingCoef, 0, 1). setDimensionless(); if (n == 3) { EditInfo ei = new EditInfo("", 0, -1, -1); ei.checkbox = new Checkbox("Trapezoidal Approximation", isTrapezoidal()); return ei; } if (n == 4) { EditInfo ei = new EditInfo("", 0, -1, -1); ei.checkbox = new Checkbox("Swap Secondary Polarity", polarity == -1); return ei; } return null; } public void setEditValue(int n, EditInfo ei) { if (n == 0) inductance = ei.value; if (n == 1) ratio = ei.value; if (n == 2 && ei.value > 0 && ei.value < 1) couplingCoef = ei.value; if (n == 3) { if (ei.checkbox.getState()) flags &= ~Inductor.FLAG_BACK_EULER; else flags |= Inductor.FLAG_BACK_EULER; } if (n == 4) { polarity = (ei.checkbox.getState()) ? -1 : 1; if (ei.checkbox.getState()) flags |= FLAG_REVERSE; else flags &= ~FLAG_REVERSE; setPoints(); } } }