mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-13 10:42:09 +10:00
submini tubes
This commit is contained in:
@@ -30,20 +30,29 @@ import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import java.util.Arrays;
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
|
||||
import org.diylc.awt.StringUtils;
|
||||
import org.diylc.common.Display;
|
||||
import org.diylc.common.HorizontalAlignment;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractLabeledComponent;
|
||||
import org.diylc.components.AbstractMakerBoard;
|
||||
import org.diylc.components.semiconductors.ICPinNames;
|
||||
import org.diylc.components.semiconductors.PinLabels;
|
||||
import org.diylc.components.transform.TO220Transformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDatasheetSupport;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.Theme;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.annotations.AiNote;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
@@ -51,11 +60,15 @@ import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.diylc.utils.Constants;
|
||||
|
||||
@AiNote("The tube is drawn as seen from above, from the component side of the board. Leads are numbered as in the data book, lead 1 first. In the circular arrangement standing upright, the first control point is the body centre and the leads follow it counter-clockwise from the gap in the circle - the mirror of the data book's bottom view; there the top lead, if any, is the centre point. In-line and folded tubes have lead 1 at the first control point, and the top lead is the last control point")
|
||||
@AiNote("'Pin Names' is a comma-separated list of lead names in lead order with the top lead last: P plate, G triode grid, G1/G2/G3 grids, K cathode, IS internal shield, F or H plus the lead number for filament or heater ends; sections of a multi-section tube are numbered first (1P, 2G); leads joined inside end in _1, _2; NC and IC leads are never connected. 'Pin Labels' draws the names (default), the lead numbers or nothing")
|
||||
@AiNote("A datasheet model is a tube type such as 6021 or 6N16B and sets 'Pin Arrangement', 'Top Lead', 'Lead Count', 'Diameter', 'Thickness', 'Length', 'Pin Names' and 'Value'; 'Diameter' is the broad side of a flat bulb, the width seen when the tube lies folded. Set 'Value' to the same tube type together with the model")
|
||||
@ComponentDescriptor(name = "Sub-Mini Tube", author = "Branislav Stojkovic", category = "Tubes",
|
||||
instanceNamePrefix = "V", description = "Sub-miniature (pencil) vacuum tube",
|
||||
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE,
|
||||
transformer = TO220Transformer.class)
|
||||
public class SubminiTube extends AbstractLabeledComponent<String> {
|
||||
transformer = TO220Transformer.class, enableDatasheet = true, datasheetCreationStepCount = 2,
|
||||
datasheetModelColumn = 1)
|
||||
public class SubminiTube extends AbstractLabeledComponent<String> implements IDatasheetSupport {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -86,16 +99,41 @@ public class SubminiTube extends AbstractLabeledComponent<String> {
|
||||
private PinArrangement leadArrangement = PinArrangement.Circular;
|
||||
private boolean topLead = false;
|
||||
private Size diameter = DIAMETER;
|
||||
private Size thickness = null;
|
||||
private Size length = LENGTH;
|
||||
private PinCount pinCount = PinCount._8;
|
||||
private Size leadSpacing = LEAD_SPACING;
|
||||
private Color labelColor = LABEL_COLOR;
|
||||
private String pinNames = null;
|
||||
private PinLabels pinLabels = null;
|
||||
transient private String[] parsedPinNames;
|
||||
|
||||
public SubminiTube() {
|
||||
super();
|
||||
updateControlPoints();
|
||||
}
|
||||
|
||||
public SubminiTube(String[] model) {
|
||||
this();
|
||||
|
||||
applyModel(model);
|
||||
}
|
||||
|
||||
// columns: group, tube type, pin arrangement, top lead (yes/no), bulb width, bulb thickness,
|
||||
// bulb length, then one name per lead in lead order with the top lead last
|
||||
@Override
|
||||
public void applyModel(String[] model) {
|
||||
boolean topLead = "yes".equalsIgnoreCase(model[3]);
|
||||
setValue(model[1]);
|
||||
setPinArrangement(PinArrangement.valueOf(model[2]));
|
||||
setTopLead(topLead);
|
||||
setDiameter(Size.parseSize(model[4]));
|
||||
setThickness(Size.parseSize(model[5]));
|
||||
setLength(Size.parseSize(model[6]));
|
||||
setPinCount(PinCount.valueOf("_" + (model.length - 7 - (topLead ? 1 : 0))));
|
||||
setPinNames(String.join(",", Arrays.copyOfRange(model, 7, model.length)));
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
public String getValue() {
|
||||
return value;
|
||||
@@ -226,17 +264,18 @@ public class SubminiTube extends AbstractLabeledComponent<String> {
|
||||
Point2D firstPoint = controlPoints[0];
|
||||
|
||||
double angleIncrement = Math.PI * 2 / (pinCount + 1);
|
||||
double initialAngleOffset = angleIncrement;
|
||||
|
||||
double r = (pinCount + 1) * leadSpacing / (2 * Math.PI);
|
||||
|
||||
controlPoints = new Point2D[pinCount + 1];
|
||||
double theta = initialAngleOffset + getOrientation().toRadians();
|
||||
// seen from above the leads run counter-clockwise from the gap, the mirror of the data book's
|
||||
// bottom view; with y pointing down that is a decreasing angle
|
||||
double theta = getOrientation().toRadians() - angleIncrement;
|
||||
controlPoints[0] = firstPoint;
|
||||
for (int i = 0; i < pinCount; i++) {
|
||||
controlPoints[i + 1] = new Point2D.Double((int) (firstPoint.getX() + Math.cos(theta) * r),
|
||||
(int) (firstPoint.getY() + Math.sin(theta) * r));
|
||||
theta += angleIncrement;
|
||||
theta -= angleIncrement;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -290,9 +329,16 @@ public class SubminiTube extends AbstractLabeledComponent<String> {
|
||||
default:
|
||||
throw new RuntimeException("Unexpected orientation: " + orientation);
|
||||
}
|
||||
} else {
|
||||
} else if (getPinArrangement() == PinArrangement.Circular) {
|
||||
body[0] = new Area(new Ellipse2D.Double(centerX - diameter / 2, centerY - diameter / 2, diameter,
|
||||
diameter));
|
||||
} else {
|
||||
// a flat bulb stands with its broad side along the lead row
|
||||
double thickness = getThickness().convertToPixels();
|
||||
boolean rowAlongY = orientation == Orientation.DEFAULT || orientation == Orientation._180;
|
||||
double w = rowAlongY ? thickness : diameter;
|
||||
double h = rowAlongY ? diameter : thickness;
|
||||
body[0] = new Area(new Ellipse2D.Double(centerX - w / 2, centerY - h / 2, w, h));
|
||||
}
|
||||
}
|
||||
return body;
|
||||
@@ -473,6 +519,67 @@ public class SubminiTube extends AbstractLabeledComponent<String> {
|
||||
}
|
||||
|
||||
g2d.drawString(label, (int)(x + dx), (int)(y + dy));
|
||||
|
||||
if (getPinLabels() != PinLabels.NONE) {
|
||||
drawLeadLabels(g2d, mainArea.getBounds2D());
|
||||
}
|
||||
}
|
||||
|
||||
// In a row the labels sit square to it on the side away from the body; on a standing circular
|
||||
// tube they point outwards from the centre, past the edge of the bulb. The top lead leaves the far
|
||||
// end of the bulb, away from the row, and is left unlabelled.
|
||||
private void drawLeadLabels(Graphics2D g2d, Rectangle2D bodyBounds) {
|
||||
g2d.setFont(ICPinNames.PIN_NAME_FONT);
|
||||
double pinRadius = LEAD_THICKNESS.convertToPixels() / 2;
|
||||
boolean circular = getPinArrangement() == PinArrangement.Circular && !folded;
|
||||
int rowCount = controlPoints.length - (getTopLead() && !circular ? 1 : 0);
|
||||
double alongX = 0;
|
||||
double alongY = 0;
|
||||
double awayX = 0;
|
||||
double awayY = 0;
|
||||
if (!circular && rowCount > 1) {
|
||||
double rowDistance = controlPoints[0].distance(controlPoints[1]);
|
||||
alongX = (controlPoints[1].getX() - controlPoints[0].getX()) / rowDistance;
|
||||
alongY = (controlPoints[1].getY() - controlPoints[0].getY()) / rowDistance;
|
||||
awayX = alongY;
|
||||
awayY = -alongX;
|
||||
double rowCenterX = (controlPoints[0].getX() + controlPoints[rowCount - 1].getX()) / 2;
|
||||
double rowCenterY = (controlPoints[0].getY() + controlPoints[rowCount - 1].getY()) / 2;
|
||||
if ((bodyBounds.getCenterX() - rowCenterX) * awayX
|
||||
+ (bodyBounds.getCenterY() - rowCenterY) * awayY > 1) {
|
||||
awayX = -awayX;
|
||||
awayY = -awayY;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < controlPoints.length; i++) {
|
||||
int lead = getLeadNumber(i);
|
||||
if (lead == 0 || (circular && i == 0)) {
|
||||
continue;
|
||||
}
|
||||
String label;
|
||||
if (getPinLabels() == PinLabels.NUMBERS) {
|
||||
label = Integer.toString(lead);
|
||||
} else {
|
||||
String name = ICPinNames.get(getParsedPinNames(), lead);
|
||||
if (name == null) {
|
||||
continue;
|
||||
}
|
||||
label = AbstractMakerBoard.getDisplayPinLabel(name);
|
||||
}
|
||||
Point2D point = controlPoints[i];
|
||||
if (circular) {
|
||||
double dx = point.getX() - controlPoints[0].getX();
|
||||
double dy = point.getY() - controlPoints[0].getY();
|
||||
double r = Math.sqrt(dx * dx + dy * dy);
|
||||
double reach = Math.max(r + pinRadius, bodyBounds.getWidth() / 2) + ICPinNames.PIN_NAME_GAP
|
||||
+ g2d.getFontMetrics().stringWidth(label) / 2d;
|
||||
StringUtils.drawCenteredText(g2d, label, controlPoints[0].getX() + dx / r * reach,
|
||||
controlPoints[0].getY() + dy / r * reach, HorizontalAlignment.CENTER,
|
||||
VerticalAlignment.CENTER);
|
||||
} else if (i < rowCount) {
|
||||
ICPinNames.drawOutside(g2d, label, point, awayX, awayY, bodyBounds, pinRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -581,6 +688,19 @@ public class SubminiTube extends AbstractLabeledComponent<String> {
|
||||
public void setDiameter(Size diameter) {
|
||||
this.diameter = diameter;
|
||||
updateControlPoints();
|
||||
body = null;
|
||||
}
|
||||
|
||||
// the narrow side of a flat bulb; a round bulb, and every file saved before the property
|
||||
// existed, has none and is as thick as it is wide
|
||||
@EditableProperty
|
||||
public Size getThickness() {
|
||||
return thickness == null ? getDiameter() : thickness;
|
||||
}
|
||||
|
||||
public void setThickness(Size thickness) {
|
||||
this.thickness = thickness;
|
||||
body = null;
|
||||
}
|
||||
|
||||
@EditableProperty
|
||||
@@ -590,6 +710,7 @@ public class SubminiTube extends AbstractLabeledComponent<String> {
|
||||
|
||||
public void setLength(Size length) {
|
||||
this.length = length;
|
||||
body = null;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Lead Count")
|
||||
@@ -630,6 +751,57 @@ public class SubminiTube extends AbstractLabeledComponent<String> {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
int lead = getLeadNumber(index);
|
||||
if (lead == 0) {
|
||||
return null;
|
||||
}
|
||||
String name = ICPinNames.get(getParsedPinNames(), lead);
|
||||
return name == null ? Integer.toString(lead) : name;
|
||||
}
|
||||
|
||||
// the data book's lead number of a control point, 0 for the centre of a standing circular tube
|
||||
// without a top lead; the top lead is numbered after the last regular lead
|
||||
private int getLeadNumber(int index) {
|
||||
if (getPinArrangement() == PinArrangement.Circular && !folded) {
|
||||
if (index == 0) {
|
||||
return getTopLead() ? getPinCount().getValue() + 1 : 0;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
return index + 1;
|
||||
}
|
||||
|
||||
private String[] getParsedPinNames() {
|
||||
if (parsedPinNames == null) {
|
||||
parsedPinNames = ICPinNames.parse(getPinNames());
|
||||
}
|
||||
return parsedPinNames;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Pin Names")
|
||||
public String getPinNames() {
|
||||
return pinNames;
|
||||
}
|
||||
|
||||
public void setPinNames(String pinNames) {
|
||||
this.pinNames = pinNames;
|
||||
this.parsedPinNames = null;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Pin Labels")
|
||||
public PinLabels getPinLabels() {
|
||||
if (pinLabels == null) {
|
||||
pinLabels = PinLabels.NAMES;
|
||||
}
|
||||
return pinLabels;
|
||||
}
|
||||
|
||||
public void setPinLabels(PinLabels pinLabels) {
|
||||
this.pinLabels = pinLabels;
|
||||
}
|
||||
|
||||
public static enum PinArrangement {
|
||||
Inline("In-line"), Circular("Circular");
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
Diode,6D6A,Inline,no,7.2 mm,7.2 mm,36 mm,P,H2,H3,K
|
||||
Dual Triode,6N16B,Circular,no,10.2 mm,10.2 mm,36 mm,2P,1K,1G,H4,1P,2K,2G,H8
|
||||
Dual Triode,6N17B,Circular,no,10.2 mm,10.2 mm,36 mm,2P,1K,1G,H4,1P,2K,2G,H8
|
||||
Dual Triode,6N21B,Circular,no,10.2 mm,10.2 mm,40 mm,1K,IS,1G,1P,H5,2K,NC,2G,2P,H10
|
||||
Dual Triode,6021,Circular,no,0.4 in,0.4 in,1.375 in,2P,2G,H3,2K,1K,H6,1G,1P
|
||||
Dual Triode,6111,Circular,no,0.4 in,0.4 in,1.375 in,2P,2G,H3,2K,1K,H6,1G,1P
|
||||
Dual Triode,6112,Circular,no,0.4 in,0.4 in,1.375 in,2P,2G,H3,2K,1K,H6,1G,1P
|
||||
Duo-Diode,5896,Circular,no,0.4 in,0.4 in,1.375 in,2P,2K,H3,IS,1P,H6,1K,NC
|
||||
Pentode,1Zh18B,Circular,yes,8.5 mm,8.5 mm,42 mm,F1,F2,NC,NC,G2,G1,P
|
||||
Pentode,1Zh24B,Circular,yes,8.5 mm,8.5 mm,42 mm,F1,F2,IS,G3,G2,G1,P
|
||||
Pentode,1Zh29B,Circular,yes,8.5 mm,8.5 mm,40 mm,HCT,G3,G2,F4,IS,F6,G1,P
|
||||
Pentode,5886,Inline,no,0.4 in,0.285 in,1.5 in,P,G2,F3,F4,NC,NC,G1
|
||||
Pentode (RF/IF),6Zh1B,Inline,no,10.2 mm,10.2 mm,36 mm,P,G2,H3,H4,G3,K,G1
|
||||
Pentode (RF/IF),6Zh2B,Inline,no,10.2 mm,10.2 mm,38 mm,P,G3,G2,H4,H5,K,G1
|
||||
Pentode (RF/IF),5636,Circular,no,0.4 in,0.4 in,1.375 in,G1,K_1,H3,G3,P,H6,G2,K_2
|
||||
Pentode (RF/IF),5678,Inline,no,0.4 in,0.285 in,1.5 in,P,G2,F3,G1,F5
|
||||
Pentode (RF/IF),5840,Circular,no,0.4 in,0.4 in,1.375 in,G1,K_1,H3,K_2,P,H6,G2,K_3
|
||||
Pentode (RF/IF),5899,Circular,no,0.4 in,0.4 in,1.375 in,G1,K_1,H3,K_2,P,H6,G2,K_3
|
||||
Pentode (RF/IF),6205,Circular,no,0.4 in,0.4 in,1.375 in,G1,K_1,H3,G3,P,H6,G2,K_2
|
||||
Pentode (RF/IF),6206,Circular,no,0.4 in,0.4 in,1.375 in,G1,K_1,H3,K_2,P,H6,G2,K_3
|
||||
Power Pentode,1P24B,Circular,yes,10.5 mm,10.5 mm,45 mm,HCT,F2,G2,IS,NC,F6,G3,NC,G1,P
|
||||
Power Pentode,6P30B,Circular,no,10.8 mm,10.8 mm,48 mm,K,H2,G2,P,NC,H6,G1,G3
|
||||
Power Pentode,5672,Inline,no,0.385 in,0.285 in,1.5 in,P,G2,F3,G1,F5
|
||||
Power Pentode,5902,Circular,no,0.4 in,0.4 in,1.75 in,G1,K_1,H3,K_2,P,H6,G2,K_3
|
||||
Power Pentode,6088,Inline,no,0.385 in,0.285 in,1.5 in,P,G2,F3,G1,F5
|
||||
Triode,6S6B,Inline,no,10.2 mm,10.2 mm,36 mm,P,H2,H3,G,K
|
||||
Triode,6S7B,Inline,no,10.2 mm,10.2 mm,36 mm,P,H2,H3,G,K
|
||||
Triode,6S31B,Circular,no,10.2 mm,10.2 mm,39 mm,K,H2,NC,NC,P,NC,H7,G,NC,NC
|
||||
Triode,6S32B,Circular,no,10.2 mm,10.2 mm,30 mm,K,NC,G,NC,H5,NC,P,NC,NC,H10
|
||||
Triode,5703,Inline,no,0.4 in,0.4 in,1.5 in,P,NC,H3,H4,G,K
|
||||
Triode,5718,Circular,no,0.4 in,0.4 in,1.375 in,G,NC,H3,NC,K,H6,NC,P
|
||||
Triode,5719,Circular,no,0.4 in,0.4 in,1.375 in,G,NC,H3,NC,K,H6,NC,P
|
||||
Triode,5744,Inline,no,0.4 in,0.4 in,1.5 in,P,H2,H3,G,K
|
||||
Triode,5977,Circular,no,0.4 in,0.4 in,1.375 in,G,NC,H3,NC,K,H6,NC,P
|
||||
Triode,6814,Circular,no,0.4 in,0.4 in,1.375 in,G,NC,H3,NC,K,H6,NC,P
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
|
||||
DIY Layout Creator (DIYLC).
|
||||
Copyright (c) 2009-2025 held jointly by the individual authors.
|
||||
|
||||
This file is part of DIYLC.
|
||||
|
||||
DIYLC 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
DIYLC 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 DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
package org.diylc.components.tube;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.diylc.netlist.Node;
|
||||
import org.diylc.presenter.DatasheetService;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SubminiTubeDatasheetTest {
|
||||
|
||||
private static final int FIRST_LEAD_COLUMN = 7;
|
||||
|
||||
@Test
|
||||
public void everyRowApplies() {
|
||||
for (String[] row : datasheet()) {
|
||||
SubminiTube tube = new SubminiTube(row);
|
||||
String context = String.join(",", row);
|
||||
Assert.assertEquals(context, row[1], tube.getValue());
|
||||
Assert.assertTrue(context, row[3].equals("yes") || row[3].equals("no"));
|
||||
int leads = row.length - FIRST_LEAD_COLUMN - (tube.getTopLead() ? 1 : 0);
|
||||
Assert.assertEquals(context, leads, tube.getPinCount().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tubesAreUnique() {
|
||||
Set<String> tubes = new HashSet<String>();
|
||||
for (String[] row : datasheet()) {
|
||||
Assert.assertTrue("repeated tube " + row[1], tubes.add(row[1].toUpperCase()));
|
||||
}
|
||||
}
|
||||
|
||||
/** Two leads share a net only when both say so with a numbered suffix, like K_1 and K_2. */
|
||||
@Test
|
||||
public void leadsShareANetOnlyThroughTheSuffix() {
|
||||
for (String[] row : datasheet()) {
|
||||
for (int i = FIRST_LEAD_COLUMN; i < row.length; i++) {
|
||||
for (int j = i + 1; j < row.length; j++) {
|
||||
String rail = Node.railName(row[i]);
|
||||
if (rail != null && rail.equals(Node.railName(row[j]))) {
|
||||
Assert.assertTrue(row[1] + " leads " + row[i] + " and " + row[j],
|
||||
row[i].matches(".*_\\d+") && row[j].matches(".*_\\d+"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tubeIsFoundByTypeNotByGroup() {
|
||||
DatasheetService service = DatasheetService.getInstance();
|
||||
String[] first = datasheet().get(0);
|
||||
Assert.assertEquals(1, service.findSeries(SubminiTube.class, first[1]).size());
|
||||
Assert.assertTrue(service.findSeries(SubminiTube.class, first[0]).isEmpty());
|
||||
}
|
||||
|
||||
private static List<String[]> datasheet() {
|
||||
List<String[]> datasheet = DatasheetService.getInstance().loadDatasheet(SubminiTube.class);
|
||||
Assert.assertNotNull(datasheet);
|
||||
Assert.assertFalse(datasheet.isEmpty());
|
||||
return datasheet;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
|
||||
DIY Layout Creator (DIYLC).
|
||||
Copyright (c) 2009-2025 held jointly by the individual authors.
|
||||
|
||||
This file is part of DIYLC.
|
||||
|
||||
DIYLC 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
DIYLC 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 DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
package org.diylc.components.tube;
|
||||
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.components.tube.SubminiTube.PinArrangement;
|
||||
import org.diylc.components.tube.SubminiTube.PinCount;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SubminiTubeTest {
|
||||
|
||||
/** The centre of a standing circular tube is not a lead; lead 1 is the first on the circle. */
|
||||
@Test
|
||||
public void circularCentreIsNotALead() {
|
||||
SubminiTube tube = new SubminiTube();
|
||||
tube.setPinArrangement(PinArrangement.Circular);
|
||||
tube.setPinCount(PinCount._8);
|
||||
Assert.assertNull(tube.getControlPointNodeName(0));
|
||||
Assert.assertEquals("1", tube.getControlPointNodeName(1));
|
||||
Assert.assertEquals("8", tube.getControlPointNodeName(8));
|
||||
}
|
||||
|
||||
/** Standing upright, the top lead of a circular tube rises from the centre. */
|
||||
@Test
|
||||
public void circularTopLeadIsTheCentre() {
|
||||
SubminiTube tube = new SubminiTube();
|
||||
tube.setPinArrangement(PinArrangement.Circular);
|
||||
tube.setPinCount(PinCount._5);
|
||||
tube.setTopLead(true);
|
||||
tube.setPinNames("A,B,C,D,E,TOP");
|
||||
Assert.assertEquals("TOP", tube.getControlPointNodeName(0));
|
||||
Assert.assertEquals("A", tube.getControlPointNodeName(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inlineLeadsStartAtTheFirstPointAndEndWithTheTopLead() {
|
||||
SubminiTube tube = new SubminiTube();
|
||||
tube.setPinArrangement(PinArrangement.Inline);
|
||||
tube.setPinCount(PinCount._3);
|
||||
tube.setTopLead(true);
|
||||
tube.setPinNames("K,G,,P");
|
||||
Assert.assertEquals(4, tube.getControlPointCount());
|
||||
Assert.assertEquals("K", tube.getControlPointNodeName(0));
|
||||
// a blank name falls back to the lead number
|
||||
Assert.assertEquals("3", tube.getControlPointNodeName(2));
|
||||
Assert.assertEquals("P", tube.getControlPointNodeName(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modelSetsTheTubeAndLeavesTheMounting() {
|
||||
SubminiTube tube = new SubminiTube();
|
||||
tube.setOrientation(Orientation._90);
|
||||
tube.setFolded(true);
|
||||
tube.applyModel(new String[] {"Dual Triode", "TEST", "Inline", "yes", "10 mm", "7 mm", "40 mm",
|
||||
"1P", "1G", "1K", "F4", "F5", "2K", "2G", "2P", "IS"});
|
||||
|
||||
Assert.assertEquals("TEST", tube.getValue());
|
||||
Assert.assertEquals(PinArrangement.Inline, tube.getPinArrangement());
|
||||
Assert.assertTrue(tube.getTopLead());
|
||||
Assert.assertEquals(PinCount._8, tube.getPinCount());
|
||||
Assert.assertEquals(new Size(10d, SizeUnit.mm), tube.getDiameter());
|
||||
Assert.assertEquals(new Size(7d, SizeUnit.mm), tube.getThickness());
|
||||
Assert.assertEquals(new Size(40d, SizeUnit.mm), tube.getLength());
|
||||
Assert.assertEquals(Orientation._90, tube.getOrientation());
|
||||
Assert.assertTrue(tube.getFolded());
|
||||
Assert.assertEquals("IS", tube.getControlPointNodeName(8));
|
||||
}
|
||||
|
||||
/** Files saved before the property existed draw a round bulb, as they always did. */
|
||||
@Test
|
||||
public void bulbWithoutThicknessIsRound() {
|
||||
SubminiTube tube = new SubminiTube();
|
||||
Assert.assertEquals(tube.getDiameter(), tube.getThickness());
|
||||
}
|
||||
|
||||
/**
|
||||
* Seen from above, leads run counter-clockwise from the gap, which faces right by default: lead 1
|
||||
* is just above the gap and the last lead just below it.
|
||||
*/
|
||||
@Test
|
||||
public void circularLeadsRunCounterClockwiseFromTheGap() {
|
||||
SubminiTube tube = new SubminiTube();
|
||||
tube.setPinArrangement(PinArrangement.Circular);
|
||||
tube.setPinCount(PinCount._8);
|
||||
double centreY = tube.getControlPoint(0).getY();
|
||||
double centreX = tube.getControlPoint(0).getX();
|
||||
Assert.assertTrue(tube.getControlPoint(1).getY() < centreY);
|
||||
Assert.assertTrue(tube.getControlPoint(1).getX() > centreX);
|
||||
Assert.assertTrue(tube.getControlPoint(8).getY() > centreY);
|
||||
Assert.assertTrue(tube.getControlPoint(8).getX() > centreX);
|
||||
}
|
||||
}
|
||||
@@ -165,6 +165,6 @@ tube.DiodeSymbol = Directly heated, Heaters
|
||||
tube.DuoDiodeSymbol = Directly heated, Heaters
|
||||
tube.Jumbo4PinTubeSocket = fixed
|
||||
tube.PentodeSymbol = Heaters, Suppressor grid
|
||||
tube.SubminiTube = Folded, Lead Count, Pin Arrangement
|
||||
tube.SubminiTube = Folded, Lead Count, Pin Arrangement, Top Lead
|
||||
tube.TriodeSymbol = Directly heated, Heaters
|
||||
tube.TubeSocket = Base
|
||||
|
||||
Reference in New Issue
Block a user