/* * InductorSim9.java - Calculates air-core inductor values, given coil dimensions. * Provides extravagent graphical user interface for a simple program. * * Permission is granted to modify this program provided you: * a) cite the author, Barry (coilgun@oz.net) in at least source code form, and * b) do not charge for distribution of derivative works. * * Created on June 9, 2002, 6:54 PM * Written using NetBeans IDE 3.3.1 * Copyright Barry Hansen, (c)1999, 2003 * All rights reserved. * * @author Barry (coilgun@oz.net) * @version 1.9 * @see http://www.coilgun.info/home.htm */ import java.awt.*; import java.awt.event.ItemEvent; public class InductorSim9 extends java.applet.Applet { public String sVersion = "v1.9"; static private final Rectangle appletSize = new Rectangle(0,0, 540, 430); // state variables: complete description of the user input private float m_fOD = MAX_OD_SIZE_SMALL*75/100; // coil outside diameter (mm) private float m_fID = MAX_OD_SIZE_SMALL*5/10; // coil inside diameter (mm) private float m_fLength = 35; // coil length (mm) // array of wire sizes that we know about (AWG 6...28) CopperWire[] wire = CopperWire.initialize(); // helper class for formatting numbers private Nearest nearest = new Nearest(); // conversion factors for painting private float fScaleX; // units are "canvas pixels per mm" private float fScaleY; private int m_nOffsetX; // units are "canvas pixels" private int m_nOffsetY; // constants for coil dimensions static private final int MAX_ID_SIZE_SMALL = 90; static private final int MAX_OD_SIZE_SMALL = 100; static private final int MAX_ID_SIZE_LARGE = 490; static private final int MAX_OD_SIZE_LARGE = 500; static private final int MAX_COIL_LENGTH_SMALL = 150; // mm static private final int MAX_COIL_LENGTH_LARGE = 600; static private final int SLIDER_WIDTH = 10; // mm static private final Point UPLEFT = new Point(90,90); // degrees, x=start, y=arc angle static private final Point UPRIGHT = new Point(0,90); // x = 0 = 3 o'clock static private final Point LOWLEFT = new Point(180,90); // y = +ve = ccw static private final Point LOWRIGHT = new Point(270,90); // constants for dimension conversions static private final float MM_PER_INCH = 25.4F; static private final float INCHES_PER_MM = 1.F/25.4F; static private final float FEET_PER_MM = INCHES_PER_MM / 12.F; static private final float FEET_PER_METER = FEET_PER_MM * 1000.F; static private final float POUNDS_PER_KG = 2.205F; /** getParameterInfo is a bean pattern */ public String[][] getParameterInfo() { return null; } /** * getAppletInfo is a bean pattern the browser can use to get * information about this applet. It can also be used to fill * in an About box. * @return String - short description of the applet. */ public String getAppletInfo() { return "Name: InductorSim9 " + sVersion + "\r\n" + "Author: Barry (barry@coilgun.info)\r\n" + "Calculates air-core inductor values, given coil dimensions.\r\n"; } /** * Recalculate and display results for the current coil setting. * This assumes all our state variables are already up to date from the UI. */ public void recalculateEverything() { // Look at UI to see which type of wire is selected int index = listBoxWireSize.getSelectedIndex(); //System.out.println( "Selected index is " + index + " for wire size " + wire[index].Name() ); CopperWire w = wire[index]; // Calculate physical characteristics (number of turns, length) int nTurnsPerLayer = (int)(m_fLength / w.Diameter()); int nNumberOfLayers = (int)((m_fOD - m_fID) / 2.F / w.Diameter()); int nTotalTurns = nTurnsPerLayer * nNumberOfLayers; float fWireLength = (float)(Math.PI * (m_fOD + m_fID)/2.F * nTotalTurns / 1000.F); // meters // Calculate electrical characteristics (mH, ohms) // Use standard approximation for multilayer coil // 0.8(N * A)^2 // L = --------------------- (microhenries) // (6A) + (9B) + (10C) // where: // N = number of turns // A = avg coil radius = tube radius + half of coil thickness (inches) // B = coil length (inches) // C = coil thickness (inches) // (Don't use Wheeler's Formula for Inductance because it's for a spiral wound coil) // @see http://www.icorp.net/users/kev/tesla/form.txt // L(uH) = (r^2) * (N^2) / (9*r + 10*h) float A = (m_fOD + m_fID) / 2.F / 2.F * INCHES_PER_MM; // inches float B = m_fLength * INCHES_PER_MM; // inches float C = (m_fOD - m_fID) / 2.F * INCHES_PER_MM; // inches float N = nTotalTurns; // turns float fInductance = 0.8F * N * N * A * A / (6.F*A + 9.F*B +10.F*C) / 1000.F; // mH float fResistance = w.Resistance(fWireLength); // ohms float fWireWeight = w.Weight(fWireLength); // kg // Update output section of UI with these computed values labelWindingDensityValue.setText( nearest.Tenth(w.WindingDensity()) + " turns/cm"); labelTurnsPerLayerValue.setText( Integer.toString(nTurnsPerLayer) + " turns" ); if (nNumberOfLayers == 1) labelNumberOfLayersValue.setText( Integer.toString(nNumberOfLayers) + " layer" ); else labelNumberOfLayersValue.setText( Integer.toString(nNumberOfLayers) + " layers" ); labelNumberOfTurnsValue.setText( Integer.toString(nTotalTurns) + " turns" ); labelWireLengthValue.setText( nearest.Hundredth(fWireLength) + " m" ); labelInductanceValue.setText( nearest.Thousandth(fInductance) + " mH" ); labelResistanceValue.setText( nearest.Thousandth(fResistance) + " ohms" ); labelWeightValue.setText( nearest.Hundredth(fWireWeight) + " kg" ); labelWireDiameterValue.setText( nearest.Hundredth(w.Diameter()) + " mm" ); // tell the coil picture to repaint itself repaint(); } /** * Update data labels for each control, according to slider position */ void setLabelOD() { // read slider value in mm m_fOD = VScrollBarOD.getValue(); // ensure OD > ID by reducing ID if needed if (m_fOD <= m_fID) { VScrollBarID.setValue( (int)(m_fOD - 1) ); setLabelID(); } String s; // create metric OD string "99 mm" or english string "1.23 in" s = Integer.toString( Math.round(m_fOD) ) + " mm"; labelODvalue.setText(s); } void setLabelID() { // read slider value in mm m_fID = VScrollBarID.getValue(); // ensure OD > ID by increasing OD if needed if (m_fID >= m_fOD) { VScrollBarOD.setValue( (int)(m_fID + 1) ); setLabelOD(); } String s; // create metric ID string "99 mm" or english string "1.23 in" s = Integer.toString( Math.round(m_fID) ) + " mm"; labelIDvalue.setText(s); } void setLabelLength() { // read slider value in mm m_fLength = HScrollBarLength.getValue(); String s; // create metric string "99 mm" or english string "1.23 in" s = Integer.toString( Math.round(m_fLength) ) + " mm"; labelLengthValue.setText(s); } /** * Draw a stylized coil, with the current dimensions * * @param g handle to graphics */ public void paint(Graphics g) { paintBackground(g); paintCoil(g); } /** * Draw any graphical elements that are not alread pre-canned on the Form. * Right now, that consists merely of a separator line above the results. * * @param g handle to graphics */ private void paintBackground(Graphics g) { // prepare to measure height of strings on the window Font font = getFont(); FontMetrics fm = getFontMetrics(font); // Debug: draw a red rectangle around the output results area drawCanvasBorder(g,canvas2); // draw border around perimeter int nRight = appletSize.width; int nBottom = appletSize.height; int nRadius = 16; final Rectangle arc = new Rectangle(0,0,2*nRadius,2*nRadius); Rectangle outputArea = canvas2.getBounds(); // _______________ // 1 / \ 2 // | | // | | // | ___________/ 3 // | / 4 \ 9 // | | | // 6 \__5/\_7__________/ 8 drawRoundCorner(g, UPLEFT, 0,0); // 1. upper left corner drawRoundCorner(g, UPRIGHT, nRight-2*nRadius,0); // 2. upper right corner drawRoundCorner(g, LOWRIGHT, nRight-2*nRadius,outputArea.y-2*nRadius); // 3. middle right corner drawRoundCorner(g, UPLEFT, outputArea.x, outputArea.y); // 4. middle left corner drawRoundCorner(g, LOWRIGHT, outputArea.x-2*nRadius,nBottom-2*nRadius); // 5. bottom middle corner drawRoundCorner(g, LOWLEFT, 0,nBottom-2*nRadius); // 6. lower left corner drawRoundCorner(g, LOWLEFT, outputArea.x, nBottom-2*nRadius); // 7. another lower left corner drawRoundCorner(g, LOWRIGHT, nRight-2*nRadius, nBottom-2*nRadius); // 8. bottomostest rightest corner drawRoundCorner(g, UPRIGHT, nRight-2*nRadius, outputArea.y); // 9. another corner g.drawLine(nRadius,0, nRight-nRadius,0); // top border g.drawLine(nRight,nRadius, nRight,outputArea.y-nRadius); // right border g.drawLine(outputArea.x+nRadius,outputArea.y, nRight-nRadius, outputArea.y); // middle border g.drawLine(nRadius,nBottom,outputArea.x-nRadius,nBottom); // bottom border g.drawLine(0,nRadius, 0,nBottom-nRadius); // left border g.drawLine(outputArea.x+nRadius, outputArea.y, nRight-nRadius, outputArea.y); g.drawLine(outputArea.x,outputArea.y+nRadius, outputArea.x,nBottom-nRadius); g.drawLine(outputArea.x+nRadius, nBottom, nRight-nRadius, nBottom); g.drawLine(nRight,nBottom-nRadius, nRight, outputArea.y+nRadius); } private void drawRoundCorner(Graphics g, Point arc_angle, int x, int y) { final int nRadius = 16; // origin of rectangle containing arc // | size of rectangle containing arc // | | arc angles // | | | g.drawArc(x,y, 2*nRadius,2*nRadius, arc_angle.x,arc_angle.y); } /** * Debug: highlight a canvas by drawing a red border * @param g graphics context * @param c canvas */ private void drawCanvasBorder(Graphics g, java.awt.Component c) { /* Color savedColor = g.getColor(); g.setColor(Color.red); Rectangle rCanvas = c.getBounds(); g.drawRect(rCanvas.x,rCanvas.y,rCanvas.width,rCanvas.height); g.setColor(savedColor); */ } /** * Draw an outline of a coil, scaled and placed to fill the canvas. * The visible left end is an ellipse bounded by (x0,y0) and (x2,y2). * The right end is half an ellipse bounded by (x3,y0) and (x5,y2) * but only the right half of the ellipse is drawn. * All dimensions and sliders are in millimeters. * * +-------+---------------------------+ y0 = 0 * : / \ : / \ : * :/ \: / \: * ( o ) ( o ) y1 (mm) * :\ /: \ /: * : \ / : \ / : * +-------+---------------------------+ y2 * x0 x1 x2 (mm) x3 x4 x5 * =0 * * @param g ref to Graphics object */ private void paintCoil(Graphics g) { // compute a coil, using u.l. corner as (0,0) and mm units float x0 = 0.f; float x1 = m_fOD/2.f; float x2 = m_fOD; float x3 = x0 + m_fLength; float x4 = x1 + m_fLength; float x5 = x2 + m_fLength; float y0 = 0.f; float y1 = m_fOD/2.f; float y2 = m_fOD; // Move the drawing origin to the u.l. corner of the canvas area. // We're not drawing on the canvas, the canvas itself is invisible and // we just use its coordinates to tell us where to draw on the applet. Rectangle rCanvas = canvas1.getBounds(); // Debug: draw a red rectangle around the canvas drawCanvasBorder(g, canvas1); // make (0,0) to upper left corner of canvas g.translate(rCanvas.x, rCanvas.y); // Translate the entire coil drawing downward from the u.l., but just // far enough so the coil's centerline is in the middle of the canvas. m_nOffsetY = (int)((rCanvas.height - m_fOD*fScaleY) / 2.F); // Translate coil drawing rightward such that the center of the // left ellipse stays in one place. float fMaxOD = VScrollBarOD.getMaximum() // max OD slider value (integer mm) - VScrollBarOD.getVisibleAmount(); // width of slider itself m_nOffsetX = (int)((fMaxOD - m_fOD)/2.F * fScaleX); // DEBUG: draw green square at offset // g.setColor(Color.green); // g.drawRect(m_nOffsetX-5,m_nOffsetY-5, 10,10); // g.setColor(Color.black); // draw the coil outline (O.D.) drawArc (g, x0,y0, x2-x0,y2-y0, 0,360); drawArc (g, x3,y0, x2-x0,y2-y0, -90,180); drawLine(g, x1,y0, x4,y0); drawLine(g, x1,y2, x4,y2); // add small arcs to indicate the first few wires // compute actual pitch from wire size int wireIndex = listBoxWireSize.getSelectedIndex(); float wireDiameter = wire[wireIndex].Diameter(); // how many arcs to draw? it should be limited to the number that fit inside the coil's image int nTurnsPerLayer = (int)(m_fLength / wireDiameter); int nArcLimit = 4; if (nTurnsPerLayer < nArcLimit) nArcLimit = nTurnsPerLayer; double start_angle = 270.F; // 270 degrees = six o'clock double draw_angle = 75.F; // +ve degrees = draw ccw Rectangle arcBound = new Rectangle((int)x3,(int)y0, (int)(x5-x3),(int)(y2-y0)); g.setColor(Color.gray); for (int loop=1; loop<=nArcLimit; loop++) { arcBound.x -= wireDiameter; drawArc (g, arcBound.x,arcBound.y, arcBound.width,arcBound.height, start_angle,draw_angle); start_angle += 0.F; draw_angle -= 8.F; } g.setColor(Color.black); // can we see a portion of the coil's back end through hole in the middle? if (m_fLength < m_fID) { // yes we can see a little bit of the back opening through the hole! // find the u.l. corner of a bounding box around the back ellipse for the // inside diameter which is centered around (x4, y1) float bx1 = x4 - m_fID/2.f; float by1 = y1 - m_fID/2.f; // find the half-angle of arc that is visible: // // ( sqrt(2*2*b*b*k*k - len*len) ) // angle = arctan( --------------------------- ) // ( k * len ) // I want to write this: // float arc_angle = Math.atan( Math.sqrt(m_fOD*m_fOD - len*len)/(len) ); // but stupid old Java requires tons of float/double conversions! double arctan_arg = (float)Math.sqrt(m_fID*m_fID - m_fLength*m_fLength)/( m_fLength); double arc_angle = Math.toDegrees( Math.atan( arctan_arg ) ); start_angle = 180.F - arc_angle; draw_angle = 2.F*arc_angle; drawArc (g, bx1,by1, m_fID,m_fID, start_angle, draw_angle); g.setColor(Color.black); } // draw the coil bore (I.D.) float fOriginX = (m_fOD/2.F - m_fID/2.F); float fOriginY = (m_fOD/2.F - m_fID/2.F); drawArc (g, fOriginX,fOriginY, m_fID,m_fID, 0,360); // tick marks as visual aid g.setColor(Color.blue); drawLine(g, x1,y0-3, x1,y0+0); drawLine(g, x1,y2-0, x1,y2+3); drawLine(g, x4,y0-3, x4,y0+0); drawLine(g, x4,y2-0, x4,y2+3); // draw the center mark "+" at left end of coil drawLine(g, x1-3,y1, x1+3, y1); drawLine(g, x1,y1-3, x1,y1+3); } /** * drawArc - same as g.drawArc() but input is a 'float' dimension * in mm units and it moves and resizes everything to fit the canvas. * This means scaling and translating: y=mx+b * The factors and offsets are set in our init() routine. * * Draws the outline of a circular or elliptical arc covering the specified rectangle. * The center of the arc is the center of the rectangle whose origin is (x, y) and * whose size is specified by the fWidth and fHeight arguments. * * @param g - ref to graphics object * @param x - the x coordinate of the upper-left corner of the arc to be drawn * @param y - the y coordinate of the upper-left corner of the arc to be drawn * @param fWidth - the fWidth of a box bounding the ellipse * @param fHeight - the fHeight of a box bounding the ellipse * @param startAngle - degrees, where 0 is the 3 o'clock position * @param arcAngle - degrees */ private void drawArc( Graphics g, float x, float y, float fWidth, float fHeight, double startAngle, double arcAngle) { int nWidth = (int)(fWidth * fScaleX); int nHeight = (int)(fHeight * fScaleY); g.drawArc( valueToX(x), valueToY(y), nWidth, nHeight, (int)startAngle, (int)arcAngle ); } /** * drawLine - same as g.drawLine() but input is a 'float' dimension * in mm units and it moves and resizes everything to fit the canvas. * This means scaling and translating: y=mx+b * The factors and offsets are set in our init() routine. * * @param g ref to Graphics object * @param x1 - first x endpoint, mm * @param y1 - first y endpoint, mm * @param x2 - second x endpoint, mm * @param y2 - second y endpoint, mm */ private void drawLine( Graphics g, float x1, float y1, float x2, float y2) { g.drawLine( valueToX(x1), valueToY(y1), valueToX(x2), valueToY(y2)); } /** * valueToX - converts dimension (mm) to canvas coordinate (pixel) * * @param fValueX horizontal distance in mm from u.l. * @return canvas coordinate. */ private int valueToX(float fValueX) { return (int)(fValueX * fScaleX) + m_nOffsetX; } /** * valueToY - converts dimension (mm) to canvas coordinate (pixel) * * @param fValueY vertical distance downward in mm from u.l. * @return canvas coordinate. */ private int valueToY(float fValueY) { return (int)(fValueY * fScaleY) + m_nOffsetY; } /** Initializes the applet */ public void init() { initComponents(); canvas1.setVisible(false); canvas2.setVisible(false); initWireListbox(); // Initialize some middle-of-the-range values for a starting coil listBoxWireSize.select(CopperWire.NumberOfSizes / 2); VScrollBarOD.setValue( MAX_OD_SIZE_SMALL*75/100 ); VScrollBarID.setValue( MAX_OD_SIZE_SMALL*5/10 ); HScrollBarLength.setValues( 35, // initial position (pixels) 10, // visible size (pixels) 1, // minimum value (mm) 150+SLIDER_WIDTH);// maximum value = (maximum mm) + (visible pixels) // Update slider readout text to match the slider settings setLabelOD(); setLabelID(); setLabelLength(); // compute conversion rates from mm dimensions onto canvas coordinates initScaleFactors(); // Make first calculations of starting coil recalculateEverything(); } private void initScaleFactors() { // Compute scale factors that will convert coil dimensions (mm) // into canvas coordinates (i.e. pixels) float fMaxOD = VScrollBarOD.getMaximum() // max OD slider value - VScrollBarOD.getVisibleAmount(); // width of slider itself float fMaxLen = HScrollBarLength.getMaximum() // max Length slider value - HScrollBarLength.getVisibleAmount(); // width of slider itself float fMaxX = fMaxOD/2.F // left half ellipse + fMaxLen // body of coil + fMaxOD/2.F; // right half ellipse float fMaxY = fMaxOD; Rectangle rCanvas = canvas1.getBounds(); fScaleX = rCanvas.width/fMaxX; fScaleY = rCanvas.height/fMaxY; } /** * Take care of GUI items that change when you swap AWG - SWG - Metric */ private void initWireListbox() { // Populate the Wire Size (AWG,SWG,metric) listbox listBoxWireSize.removeAll(); for (int ii=0; ii MAX_OD_SIZE_SMALL) m_fOD = MAX_OD_SIZE_SMALL; if (m_fID > MAX_ID_SIZE_SMALL) m_fID = MAX_ID_SIZE_SMALL; if (m_fLength > MAX_COIL_LENGTH_SMALL) m_fLength = MAX_COIL_LENGTH_SMALL; initScaleFactors(); recalculateEverything(); } private void actionSizeLarge(java.awt.event.ItemEvent evt) { VScrollBarID.setMaximum(MAX_ID_SIZE_LARGE); VScrollBarOD.setMaximum(MAX_OD_SIZE_LARGE + SLIDER_WIDTH); HScrollBarLength.setMaximum(MAX_COIL_LENGTH_LARGE + SLIDER_WIDTH); initScaleFactors(); recalculateEverything(); } private void buttonNextWireActionPerformed(java.awt.event.ActionEvent evt) { // Cycle through AWG - SWG - metric wire sizes wire = CopperWire.nextGaugeType(); initWireListbox(); recalculateEverything(); } private void listBoxWireSizeItemStateChanged(java.awt.event.ItemEvent evt) { recalculateEverything(); } private void HScrollBarLengthAdjustmentValueChanged(java.awt.event.AdjustmentEvent evt) { setLabelLength(); recalculateEverything(); } private void VScrollBarODAdjustmentValueChanged(java.awt.event.AdjustmentEvent evt) { setLabelOD(); recalculateEverything(); } private void VScrollBarIDAdjustmentValueChanged(java.awt.event.AdjustmentEvent evt) { setLabelID(); recalculateEverything(); } // Variables declaration - do not modify//GEN-BEGIN:variables private java.awt.Label labelNumberOfLayers; private java.awt.Label labelVersion; private java.awt.Canvas canvas1; private java.awt.Canvas canvas2; private java.awt.Label labelIDvalue; private java.awt.Label labelID; private java.awt.Label labelWeight; private java.awt.Scrollbar VScrollBarOD; private java.awt.Label labelInductance; private java.awt.Label labelWireDiameter; private java.awt.Label labelNumberOfTurns; private java.awt.Label labelOD; private java.awt.Label labelLengthValue; private java.awt.Label labelWireDiameterValue; private java.awt.Label labelNumberOfTurnsValue; private java.awt.Scrollbar HScrollBarLength; private java.awt.Label labelODvalue; private java.awt.Label labelWindingDensityValue; private java.awt.List listBoxWireSize; private java.awt.Label labelWeightValue; private java.awt.Label labelLength; private java.awt.Label labelTitle; private java.awt.Label labelWireLengthValue; private java.awt.Scrollbar VScrollBarID; private java.awt.Label labelResistance; private java.awt.Label labelInductanceValue; private java.awt.Label labelTurnsPerLayerValue; private java.awt.Button buttonNextWire; private java.awt.Label labelWindingDensity; private java.awt.Label labelResistanceValue; private java.awt.Label labelWireLength; private java.awt.Label labelTurnsPerLayer; private java.awt.Label labelNumberOfLayersValue; private java.awt.Label labelMaximumSize1; private java.awt.Label labelMaximumSize2; private java.awt.CheckboxGroup sizeButtonGroup; private java.awt.Checkbox jrbSizeSmall; private java.awt.Checkbox jrbSizeLarge; }