From 9c3fce45170929482192425e92ec8b0ac96670f2 Mon Sep 17 00:00:00 2001 From: kruland2607 Date: Wed, 3 Oct 2012 10:37:58 -0500 Subject: [PATCH 1/6] Added couple of simple unit tests for computing area and cg for Trapezoidal and Free Form FinSets. --- .../rocketcomponent/FinSetTest.java | 159 +++++++++++++++--- 1 file changed, 133 insertions(+), 26 deletions(-) diff --git a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java index 321efbe8c..3ad01f852 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java @@ -1,6 +1,8 @@ package net.sf.openrocket.rocketcomponent; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.awt.Color; @@ -11,26 +13,131 @@ 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.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 testCGComputation() throws Exception { + + { + 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); + } + + { + 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); + } + + { + // This is the same shape as the previous TrapezoidFinSet. + 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); + } + + { + // Add some superfluous points which are on the outline but "far apart" + 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); + } + + { + // add some points which are close + 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); + } + + { + // Different shaped figure. Two rectangles crafted so two pairs of points 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); + // FIXME - this computes NaN + //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); @@ -52,57 +159,57 @@ public class FinSetTest extends BaseTestCase { 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); @@ -110,5 +217,5 @@ public class FinSetTest extends BaseTestCase { changetype = e.getType(); } } - + } From 4d77c817d8d9ae336930f7d9e6c04a37aa54b376 Mon Sep 17 00:00:00 2001 From: kruland2607 Date: Fri, 5 Oct 2012 13:00:56 -0500 Subject: [PATCH 2/6] Merged changes fixing NaN when computing fin CG. --- core/src/net/sf/openrocket/rocketcomponent/FinSet.java | 2 +- core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index 4b8d58058..40aac2f90 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -440,7 +440,7 @@ public abstract class FinSet extends ExternalComponent { double da = (y0 + y1) * (x1 - x0) / 2; finArea += da; - if (Math.abs(y0 - y1) < 0.00001) { + if (Math.abs(y0 + y1) < 0.00001) { finCGx += (x0 + x1) / 2 * da; finCGy += y0 / 2 * da; } else { diff --git a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java index 3ad01f852..881773e36 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java @@ -119,13 +119,13 @@ public class FinSetTest extends BaseTestCase { fins.setPoints(points); Coordinate coords = fins.getCG(); assertEquals(3.0, fins.getFinArea(), 0.001); - // FIXME - this computes NaN - //assertEquals(3.5/3.0, coords.x, 0.001); - //assertEquals(0.5/3.0, coords.y, 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()); From 372bcae9a7f62f30de75cb958cbca8c5c803d2e4 Mon Sep 17 00:00:00 2001 From: kruland2607 Date: Fri, 5 Oct 2012 16:01:17 -0500 Subject: [PATCH 3/6] If the user selected a really small (0) timestep, the simulation gets into an infinite loop. Don't use a user selected timestep smaller than MIN_TIME_STEP. --- .../net/sf/openrocket/simulation/RK4SimulationStepper.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java b/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java index 8548ec91e..fdc4ba626 100644 --- a/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java +++ b/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java @@ -138,8 +138,9 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { */ double[] dt = new double[8]; Arrays.fill(dt, Double.MAX_VALUE); - - dt[0] = status.getSimulationConditions().getTimeStep(); + + // If the user selected a really small timestep, use MIN_TIME_STEP instead. + dt[0] = MathUtil.max(status.getSimulationConditions().getTimeStep(),MIN_TIME_STEP); dt[1] = maxTimeStep; dt[2] = status.getSimulationConditions().getMaximumAngleStep() / store.lateralPitchRate; dt[3] = Math.abs(MAX_ROLL_STEP_ANGLE / store.flightConditions.getRollRate()); @@ -159,7 +160,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { limitingValue = i; } } - + double minTimeStep = status.getSimulationConditions().getTimeStep() / 20; if (store.timestep < minTimeStep) { log.verbose("Too small time step " + store.timestep + " (limiting factor " + limitingValue + "), using " + From d8edf413550646e8f311970ba22f782b607ec6cd Mon Sep 17 00:00:00 2001 From: kruland2607 Date: Fri, 5 Oct 2012 16:10:42 -0500 Subject: [PATCH 4/6] Ignore simulation time steps <= 0 when loading ork file. --- .../openrocket/file/openrocket/importt/OpenRocketLoader.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java index 4efdf9dbd..b61ca55d9 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java @@ -72,6 +72,7 @@ import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.simulation.FlightEvent.Type; +import net.sf.openrocket.simulation.RK4SimulationStepper; import net.sf.openrocket.simulation.SimulationOptions; import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.startup.Application; @@ -1494,7 +1495,7 @@ class SimulationConditionsHandler extends AbstractElementHandler { } else if (element.equals("atmosphere")) { atmosphereHandler.storeSettings(conditions, warnings); } else if (element.equals("timestep")) { - if (Double.isNaN(d)) { + if (Double.isNaN(d) || d <= 0 ) { warnings.add("Illegal time step defined, ignoring."); } else { conditions.setTimeStep(d); From ecac762940e6a00425f70360db07375bf826be8b Mon Sep 17 00:00:00 2001 From: kruland2607 Date: Sat, 6 Oct 2012 21:03:08 -0500 Subject: [PATCH 5/6] Break up the area/cg test cases and add some documentation describing what they are doing. --- .../rocketcomponent/FinSetTest.java | 183 +++++++++++------- 1 file changed, 112 insertions(+), 71 deletions(-) diff --git a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java index 881773e36..14299440c 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/FinSetTest.java @@ -22,9 +22,10 @@ import org.junit.Test; public class FinSetTest extends BaseTestCase { @Test - public void testCGComputation() throws Exception { + 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); @@ -36,6 +37,12 @@ public class FinSetTest extends BaseTestCase { } { + // 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); @@ -45,83 +52,117 @@ public class FinSetTest extends BaseTestCase { 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 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. - { - // This is the same shape as the previous TrapezoidFinSet. - 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); - } - - { - // Add some superfluous points which are on the outline but "far apart" - 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); - } - - { - // add some points which are close - 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); - } - - { - // Different shaped figure. Two rectangles crafted so two pairs of points y_0 = - y_1 - FreeformFinSet fins = new FreeformFinSet(); - fins.setFinCount(1); - Coordinate[] points = new Coordinate[] { + // 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); - } + }; + 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); } From d305f9a9529563eead875da5796048fc77af2168 Mon Sep 17 00:00:00 2001 From: kruland2607 Date: Sun, 7 Oct 2012 22:24:33 -0500 Subject: [PATCH 6/6] Cleaned up imports. --- .../sf/openrocket/file/openrocket/importt/OpenRocketLoader.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java index b61ca55d9..0915a9353 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/OpenRocketLoader.java @@ -72,7 +72,6 @@ import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.simulation.FlightEvent.Type; -import net.sf.openrocket.simulation.RK4SimulationStepper; import net.sf.openrocket.simulation.SimulationOptions; import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.startup.Application;