package net.sf.openrocket.rocketcomponent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import net.sf.openrocket.aerodynamics.AerodynamicForces;
import net.sf.openrocket.aerodynamics.FlightConditions;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.aerodynamics.barrowman.FinSetCalc;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.material.Material.Type;
import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
import net.sf.openrocket.rocketcomponent.FinSet.CrossSection;
import net.sf.openrocket.rocketcomponent.FinSet.TabRelativePosition;
import net.sf.openrocket.rocketcomponent.RocketComponent.Position;
import net.sf.openrocket.util.Color;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.LineStyle;
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
import org.junit.Test;
public class FinSetTest extends BaseTestCase {
@Test
public void testTrapezoidCGComputation() {
{
// This is a simple square fin with sides of 1.0.
TrapezoidFinSet fins = new TrapezoidFinSet();
fins.setFinCount(1);
fins.setFinShape(1.0, 1.0, 0.0, 1.0, .005);
Coordinate coords = fins.getCG();
assertEquals(1.0, fins.getFinArea(), 0.001);
assertEquals(0.5, coords.x, 0.001);
assertEquals(0.5, coords.y, 0.001);
}
{
// This is a trapezoid. Height 1, root 1, tip 1/2 no sweep.
// It can be decomposed into a rectangle followed by a triangle
// +---+
// | \
// | \
// +------+
TrapezoidFinSet fins = new TrapezoidFinSet();
fins.setFinCount(1);
fins.setFinShape(1.0, 0.5, 0.0, 1.0, .005);
Coordinate coords = fins.getCG();
assertEquals(0.75, fins.getFinArea(), 0.001);
assertEquals(0.3889, coords.x, 0.001);
assertEquals(0.4444, coords.y, 0.001);
}
}
@Test
public void testFreeformCGComputation() throws Exception {
{
// This is a trapezoid. Height 1, root 1, tip 1/2 no sweep.
// It can be decomposed into a rectangle followed by a triangle
// +---+
// | \
// | \
// +------+
FreeformFinSet fins = new FreeformFinSet();
fins.setFinCount(1);
Coordinate[] points = new Coordinate[] {
new Coordinate(0, 0),
new Coordinate(0, 1),
new Coordinate(.5, 1),
new Coordinate(1, 0)
};
fins.setPoints(points);
Coordinate coords = fins.getCG();
assertEquals(0.75, fins.getFinArea(), 0.001);
assertEquals(0.3889, coords.x, 0.001);
assertEquals(0.4444, coords.y, 0.001);
}
{
// This is the same trapezoid as previous free form, but it has
// some extra points along the lines.
FreeformFinSet fins = new FreeformFinSet();
fins.setFinCount(1);
Coordinate[] points = new Coordinate[] {
new Coordinate(0, 0),
new Coordinate(0, .5),
new Coordinate(0, 1),
new Coordinate(.25, 1),
new Coordinate(.5, 1),
new Coordinate(.75, .5),
new Coordinate(1, 0)
};
fins.setPoints(points);
Coordinate coords = fins.getCG();
assertEquals(0.75, fins.getFinArea(), 0.001);
assertEquals(0.3889, coords.x, 0.001);
assertEquals(0.4444, coords.y, 0.001);
}
{
// This is the same trapezoid as previous free form, but it has
// some extra points which are very close to previous points.
// in particular for points 0 & 1,
// y0 + y1 is very small.
FreeformFinSet fins = new FreeformFinSet();
fins.setFinCount(1);
Coordinate[] points = new Coordinate[] {
new Coordinate(0, 0),
new Coordinate(0, 1E-15),
new Coordinate(0, 1),
new Coordinate(1E-15, 1),
new Coordinate(.5, 1),
new Coordinate(.5, 1 - 1E-15),
new Coordinate(1, 1E-15),
new Coordinate(1, 0)
};
fins.setPoints(points);
Coordinate coords = fins.getCG();
assertEquals(0.75, fins.getFinArea(), 0.001);
assertEquals(0.3889, coords.x, 0.001);
assertEquals(0.4444, coords.y, 0.001);
}
}
@Test
public void testWildmanVindicatorShape() throws Exception {
// This fin shape is similar to the aft fins on the Wildman Vindicator.
// A user noticed that if the y values are similar but not equal,
// the compuation of CP was incorrect because of numerical instability.
//
// +-----------------+
// \ \
// \ \
// + \
// / \
// +---------------------+
//
FreeformFinSet fins = new FreeformFinSet();
fins.setFinCount(1);
Coordinate[] points = new Coordinate[] {
new Coordinate(0, 0),
new Coordinate(0.02143125, 0.01143),
new Coordinate(0.009524999999999999, 0.032543749999999996),
new Coordinate(0.041275, 0.032537399999999994),
new Coordinate(0.066675, 0)
};
fins.setPoints(points);
Coordinate coords = fins.getCG();
assertEquals(0.00130, fins.getFinArea(), 0.00001);
assertEquals(0.03423, coords.x, 0.00001);
assertEquals(0.01427, coords.y, 0.00001);
BodyTube bt = new BodyTube();
bt.addChild(fins);
FinSetCalc calc = new FinSetCalc(fins);
FlightConditions conditions = new FlightConditions(null);
AerodynamicForces forces = new AerodynamicForces();
WarningSet warnings = new WarningSet();
calc.calculateNonaxialForces(conditions, forces, warnings);
System.out.println(forces);
assertEquals(0.023409, forces.getCP().x, 0.0001);
}
@Test
public void testFreeFormCGWithNegativeY() throws Exception {
// This particular fin shape is currently not allowed in OR since the y values are negative
// however, it is possible to convert RockSim files and end up with fins which
// have negative y values.
// A user submitted an ork file which could not be simulated because the fin
// was constructed on a tail cone. It so happened that for one pair of points
// y_n = - y_(n+1) which caused a divide by zero and resulted in CGx = NaN.
// This Fin set is constructed to have the same problem. It is a square and rectagle
// where the two trailing edge corners of the rectangle satisfy y_0 = -y_1
//
// +---------+
// | |
// | |
// +----+ |
// | |
// | |
// +----+
FreeformFinSet fins = new FreeformFinSet();
fins.setFinCount(1);
Coordinate[] points = new Coordinate[] {
new Coordinate(0, 0),
new Coordinate(0, 1),
new Coordinate(2, 1),
new Coordinate(2, -1),
new Coordinate(1, -1),
new Coordinate(1, 0)
};
fins.setPoints(points);
Coordinate coords = fins.getCG();
assertEquals(3.0, fins.getFinArea(), 0.001);
assertEquals(3.5 / 3.0, coords.x, 0.001);
assertEquals(0.5 / 3.0, coords.y, 0.001);
}
@Test
public void testFreeformConvert() {
testFreeformConvert(new TrapezoidFinSet());
testFreeformConvert(new EllipticalFinSet());
testFreeformConvert(new FreeformFinSet());
}
private void testFreeformConvert(FinSet fin) {
FreeformFinSet converted;
Material mat = Material.newMaterial(Type.BULK, "foo", 0.1, true);
fin.setBaseRotation(1.1);
fin.setCantAngle(0.001);
fin.setCGOverridden(true);
fin.setColor(Color.BLACK);
fin.setComment("cmt");
fin.setCrossSection(CrossSection.ROUNDED);
fin.setFinCount(5);
fin.setFinish(Finish.ROUGH);
fin.setLineStyle(LineStyle.DASHDOT);
fin.setMassOverridden(true);
fin.setMaterial(mat);
fin.setOverrideCGX(0.012);
fin.setOverrideMass(0.0123);
fin.setOverrideSubcomponents(true);
fin.setPositionValue(0.1);
fin.setRelativePosition(Position.ABSOLUTE);
fin.setTabHeight(0.01);
fin.setTabLength(0.02);
fin.setTabRelativePosition(TabRelativePosition.END);
fin.setTabShift(0.015);
fin.setThickness(0.005);
converted = FreeformFinSet.convertFinSet((FinSet) fin.copy());
ComponentCompare.assertSimilarity(fin, converted, true);
assertEquals(converted.getComponentName(), converted.getName());
// Create test rocket
Rocket rocket = new Rocket();
Stage stage = new Stage();
BodyTube body = new BodyTube();
rocket.addChild(stage);
stage.addChild(body);
body.addChild(fin);
Listener l1 = new Listener("l1");
rocket.addComponentChangeListener(l1);
fin.setName("Custom name");
assertTrue(l1.changed);
assertEquals(ComponentChangeEvent.NONFUNCTIONAL_CHANGE, l1.changetype);
// Create copy
RocketComponent rocketcopy = rocket.copy();
Listener l2 = new Listener("l2");
rocketcopy.addComponentChangeListener(l2);
FinSet fincopy = (FinSet) rocketcopy.getChild(0).getChild(0).getChild(0);
FreeformFinSet.convertFinSet(fincopy);
assertTrue(l2.changed);
assertEquals(ComponentChangeEvent.TREE_CHANGE,
l2.changetype & ComponentChangeEvent.TREE_CHANGE);
}
private static class Listener implements ComponentChangeListener {
private boolean changed = false;
private int changetype = 0;
private final String name;
public Listener(String name) {
this.name = name;
}
@Override
public void componentChanged(ComponentChangeEvent e) {
assertFalse("Ensuring listener " + name + " has not been called.", changed);
changed = true;
changetype = e.getType();
}
}
}