From 2e7b3da1c0dc44286935627a5aa6d576da1f71ca Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Wed, 30 Jan 2019 15:10:41 -0700 Subject: [PATCH 1/3] Addresses Issue 481 Modify BarrowmanCalculator to use InstanceMap and InstanceContext to iterate through all components. This addresses 481 by actually computing nonaxial forces for all components, instead of computing a single instanceable component and using it as a representative of all instances. Modify calculateNonAxialForces to accept a Transformation argument, so they can use rotations from instances (only actually affects FinSetCalc and TubeFinSetCalc) --- .../aerodynamics/BarrowmanCalculator.java | 100 ++++++++---------- .../aerodynamics/barrowman/FinSetCalc.java | 6 +- .../aerodynamics/barrowman/LaunchLugCalc.java | 3 +- .../barrowman/RocketComponentCalc.java | 6 +- .../barrowman/SymmetricComponentCalc.java | 3 +- .../barrowman/TubeFinSetCalc.java | 5 +- .../sf/openrocket/util/Transformation.java | 20 ++++ .../aerodynamics/FinSetCalcTest.java | 7 +- .../SymmetricComponentCalcTest.java | 7 +- .../rocketcomponent/FreeformFinSetTest.java | 4 +- 10 files changed, 94 insertions(+), 67 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index 675ffde0f..260e57f4a 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -2,6 +2,7 @@ package net.sf.openrocket.aerodynamics; import static net.sf.openrocket.util.MathUtil.pow2; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -10,6 +11,9 @@ import java.util.LinkedList; import java.util.Map; import java.util.Queue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.sf.openrocket.aerodynamics.barrowman.FinSetCalc; import net.sf.openrocket.aerodynamics.barrowman.RocketComponentCalc; import net.sf.openrocket.rocketcomponent.ComponentAssembly; @@ -17,6 +21,8 @@ import net.sf.openrocket.rocketcomponent.ExternalComponent; import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.FlightConfiguration; +import net.sf.openrocket.rocketcomponent.InstanceContext; +import net.sf.openrocket.rocketcomponent.InstanceMap; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.SymmetricComponent; @@ -24,6 +30,7 @@ import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.PolyInterpolator; import net.sf.openrocket.util.Reflection; +import net.sf.openrocket.util.Transformation; /** * An aerodynamic calculator that uses the extended Barrowman method to @@ -36,6 +43,8 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { private static final String BARROWMAN_PACKAGE = "net.sf.openrocket.aerodynamics.barrowman"; private static final String BARROWMAN_SUFFIX = "Calc"; + private static final Logger log = LoggerFactory.getLogger(BarrowmanCalculator.class); + private Map calcMap = null; @@ -176,65 +185,50 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { warnings.add( Warning.DIAMETER_DISCONTINUITY); } - AerodynamicForces total = calculateAssemblyNonAxialForces(configuration.getRocket(), configuration, conditions, calculators, warnings, ""); - - return total; - } - - private AerodynamicForces calculateAssemblyNonAxialForces( final RocketComponent component, - FlightConfiguration configuration, FlightConditions conditions, - Map calculators, WarningSet warnings, - String indent) { - + final InstanceMap imap = configuration.getActiveInstances(); + final AerodynamicForces assemblyForces= new AerodynamicForces().zero(); - -// System.err.println(String.format("%s@@ %s <%s>", indent, component.getName(), component.getClass().getSimpleName())); - - // ==== calculate child forces ==== - for (RocketComponent child: component.getChildren()) { - AerodynamicForces childForces = calculateAssemblyNonAxialForces( child, configuration, conditions, calculators, warnings, indent+" "); - assemblyForces.merge(childForces); - } - - // calculate *this* component's forces - RocketComponentCalc calcObj = calcMap.get(component); - if(null != calcObj) { - AerodynamicForces componentForces = new AerodynamicForces().zero(); - calcObj.calculateNonaxialForces(conditions, componentForces, warnings); + + // iterate through all components + for(Map.Entry> entry: imap.entrySet() ) { + final RocketComponent comp = entry.getKey(); + RocketComponentCalc calcObj = calcMap.get(comp); + log.debug("comp=" + comp); - Coordinate cp_comp = componentForces.getCP(); - - Coordinate cp_weighted = cp_comp.setWeight(cp_comp.weight); - Coordinate cp_absolute = component.toAbsolute(cp_weighted)[0]; - if(1 < component.getInstanceCount()) { - cp_absolute = cp_absolute.setY(0.).setZ(0.); + if (null != calcObj) { + // iterate across component instances + final ArrayList contextList = entry.getValue(); + + for(InstanceContext context: contextList ) { + // since FinSetCalc calculates forces for entire FinSet + // only consider first fin in each set (happily, + // in each instance of a PodSet or similar + // instanceable RocketComponent with childre, the + // child FinSets all start numbering with 0 + if (!(comp instanceof FinSet) || (context.instanceNumber == 0)) { + AerodynamicForces instanceForces = new AerodynamicForces().zero(); + + calcObj.calculateNonaxialForces(conditions, context.transform, instanceForces, warnings); + log.debug("instanceForces[" + context.instanceNumber + "]=" + instanceForces); + Coordinate cp_comp = instanceForces.getCP(); + + Coordinate cp_abs = context.transform.transform(cp_comp); + + instanceForces.setCP(cp_abs); + double CN_instanced = instanceForces.getCN(); + instanceForces.setCm(CN_instanced * instanceForces.getCP().x / conditions.getRefLength()); + + log.debug("instanceForces=" + instanceForces); + assemblyForces.merge(instanceForces); + } + } } - - componentForces.setCP(cp_absolute); - double CN_instanced = componentForces.getCN(); - componentForces.setCm(CN_instanced * componentForces.getCP().x / conditions.getRefLength()); - -// if( 0.0001 < Math.abs(componentForces.getCNa())){ -// final Coordinate cp = assemblyForces.getCP(); -// System.err.println(String.format("%s....Component.CNa: %g @ CP: { %f, %f, %f}", indent, componentForces.getCNa(), cp.x, cp.y, cp.z)); -// } - - assemblyForces.merge(componentForces); - } - -// if( 0.0001 < Math.abs(0 - assemblyForces.getCNa())){ -// final Coordinate cp = assemblyForces.getCP(); -// System.err.println(String.format("%s....Assembly.CNa: %g @ CP: { %f, %f, %f}", indent, assemblyForces.getCNa(), cp.x, cp.y, cp.z)); -// } - - if( component.allowsChildren() && (component.getInstanceCount() > 1)) { - return assemblyForces.multiplex(component.getInstanceCount()); - }else { - return assemblyForces; } + + log.debug("assemblyForces=" + assemblyForces); + return assemblyForces; } - @Override public boolean isContinuous( final Rocket rkt){ return testIsContinuous( rkt); diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java index e56f26df6..66e193de5 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java @@ -16,7 +16,7 @@ import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.LinearInterpolator; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.PolyInterpolator; - +import net.sf.openrocket.util.Transformation; public class FinSetCalc extends RocketComponentCalc { @@ -84,7 +84,7 @@ public class FinSetCalc extends RocketComponentCalc { * (normal and side forces, pitch, yaw and roll moments, CP position, CNa). */ @Override - public void calculateNonaxialForces(FlightConditions conditions, + public void calculateNonaxialForces(FlightConditions conditions, Transformation transform, AerodynamicForces forces, WarningSet warnings) { if (span < 0.001) { @@ -114,7 +114,7 @@ public class FinSetCalc extends RocketComponentCalc { // Multiple fins with fin-fin interference double cna; double theta = conditions.getTheta(); - double angle = baseRotation; + double angle = baseRotation + transform.getXrotation(); // Compute basic CNa without interference effects if (finCount == 1 || finCount == 2) { diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/LaunchLugCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/LaunchLugCalc.java index cd7fea012..99329280d 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/LaunchLugCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/LaunchLugCalc.java @@ -6,6 +6,7 @@ import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.rocketcomponent.LaunchLug; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.Transformation; public class LaunchLugCalc extends RocketComponentCalc { @@ -24,7 +25,7 @@ public class LaunchLugCalc extends RocketComponentCalc { } @Override - public void calculateNonaxialForces(FlightConditions conditions, + public void calculateNonaxialForces(FlightConditions conditions, Transformation transform, AerodynamicForces forces, WarningSet warnings) { // Nothing to be done } diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/RocketComponentCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/RocketComponentCalc.java index 71f4ef0cf..900b507d9 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/RocketComponentCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/RocketComponentCalc.java @@ -4,6 +4,7 @@ import net.sf.openrocket.aerodynamics.AerodynamicForces; import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Transformation; public abstract class RocketComponentCalc { @@ -20,11 +21,12 @@ public abstract class RocketComponentCalc { * computed around the local origin. * * @param conditions the flight conditions. + * @param transform transformation from InstanceMap to get rotations rotations * @param forces the object in which to store the values. * @param warnings set in which to store possible warnings. */ - public abstract void calculateNonaxialForces(FlightConditions conditions, - AerodynamicForces forces, WarningSet warnings); + public abstract void calculateNonaxialForces(FlightConditions conditions, Transformation transform, + AerodynamicForces forces, WarningSet warnings); /** diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/SymmetricComponentCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/SymmetricComponentCalc.java index 1bfcfa2ee..6fa72428b 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/SymmetricComponentCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/SymmetricComponentCalc.java @@ -16,6 +16,7 @@ import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.LinearInterpolator; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.PolyInterpolator; +import net.sf.openrocket.util.Transformation; @@ -95,7 +96,7 @@ public class SymmetricComponentCalc extends RocketComponentCalc { * subsonic speeds. */ @Override - public void calculateNonaxialForces(FlightConditions conditions, + public void calculateNonaxialForces(FlightConditions conditions, Transformation transform, AerodynamicForces forces, WarningSet warnings) { // Pre-calculate and store the results diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java index d6e1d56d7..1efab0c04 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java @@ -17,6 +17,7 @@ import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.LinearInterpolator; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.PolyInterpolator; +import net.sf.openrocket.util.Transformation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -92,7 +93,7 @@ public class TubeFinSetCalc extends RocketComponentCalc { * pitch, yaw and roll moments, CP position, CNa). */ @Override - public void calculateNonaxialForces(FlightConditions conditions, + public void calculateNonaxialForces(FlightConditions conditions, Transformation transform, AerodynamicForces forces, WarningSet warnings) { if (span < 0.001) { @@ -124,7 +125,7 @@ public class TubeFinSetCalc extends RocketComponentCalc { // Multiple fins with fin-fin interference double cna; double theta = conditions.getTheta(); - double angle = baseRotation; + double angle = baseRotation + transform.getXrotation(); // Compute basic CNa without interference effects if (finCount == 1 || finCount == 2) { diff --git a/core/src/net/sf/openrocket/util/Transformation.java b/core/src/net/sf/openrocket/util/Transformation.java index d31bb7906..b40d9041c 100644 --- a/core/src/net/sf/openrocket/util/Transformation.java +++ b/core/src/net/sf/openrocket/util/Transformation.java @@ -374,5 +374,25 @@ public class Transformation implements java.io.Serializable { public Coordinate getTranslationVector() { return this.translate; } + + /** + * Calculate X, Y, and Z rotation angles from rotation matrices + */ + public double getXrotation() { + return Math.atan2((rotation[2][1] - rotation[1][2])/2.0, + (rotation[1][1] + rotation[2][2])/2.0); + } + + public double getYrotation() { + return Math.atan2((rotation[0][2] - rotation[2][0])/2.0, + (rotation[0][0] + rotation[2][2])/2.0); + } + + public double getZrotation() { + return Math.atan2((rotation[1][0] - rotation[0][1])/2.0, + (rotation[0][0] + rotation[1][1])/2.0); + + } + } diff --git a/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java b/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java index 7eb12199f..ab677fb32 100644 --- a/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java @@ -16,6 +16,7 @@ import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.TestRockets; +import net.sf.openrocket.util.Transformation; public class FinSetCalcTest { protected final double EPSILON = 0.0001; @@ -50,6 +51,7 @@ public class FinSetCalcTest { assertEquals(" Estes Alpha III fins have wrong height: ", 0.05, fins.getHeight(), EPSILON); FlightConfiguration config = rocket.getSelectedConfiguration(); + Transformation transform = Transformation.IDENTITY; FlightConditions conditions = new FlightConditions(config); WarningSet warnings = new WarningSet(); AerodynamicForces forces = new AerodynamicForces(); @@ -57,7 +59,7 @@ public class FinSetCalcTest { // vvv TEST MEH! vvv - calcObj.calculateNonaxialForces(conditions, forces, warnings); + calcObj.calculateNonaxialForces(conditions, transform, forces, warnings); // ^^^ double exp_cna_fins = 24.146933; @@ -84,6 +86,7 @@ public class FinSetCalcTest { assertEquals(" Estes Alpha III fins have wrong height: ", 0.05, fins.getHeight(), EPSILON); FlightConfiguration config = rocket.getSelectedConfiguration(); + Transformation transform = Transformation.IDENTITY; FlightConditions conditions = new FlightConditions(config); WarningSet warnings = new WarningSet(); AerodynamicForces forces = new AerodynamicForces(); @@ -91,7 +94,7 @@ public class FinSetCalcTest { // vvv TEST MEH! vvv - calcObj.calculateNonaxialForces(conditions, forces, warnings); + calcObj.calculateNonaxialForces(conditions, transform, forces, warnings); // ^^^ double exp_cna_fins = 32.195911; diff --git a/core/test/net/sf/openrocket/aerodynamics/SymmetricComponentCalcTest.java b/core/test/net/sf/openrocket/aerodynamics/SymmetricComponentCalcTest.java index 3fb44f26e..83bee1322 100644 --- a/core/test/net/sf/openrocket/aerodynamics/SymmetricComponentCalcTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/SymmetricComponentCalcTest.java @@ -18,6 +18,7 @@ import net.sf.openrocket.rocketcomponent.Transition; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.TestRockets; +import net.sf.openrocket.util.Transformation; public class SymmetricComponentCalcTest { protected final double EPSILON = MathUtil.EPSILON*1000; @@ -52,13 +53,14 @@ public class SymmetricComponentCalcTest { FlightConfiguration config = rocket.getSelectedConfiguration(); FlightConditions conditions = new FlightConditions(config); + Transformation transform = Transformation.IDENTITY; WarningSet warnings = new WarningSet(); AerodynamicForces forces = new AerodynamicForces(); SymmetricComponentCalc calcObj = new SymmetricComponentCalc( nose ); conditions.setAOA(0.0); // vvv TEST MEH! vvv - calcObj.calculateNonaxialForces(conditions, forces, warnings); + calcObj.calculateNonaxialForces(conditions, transform, forces, warnings); // ^^^ double cna_nose = 2; @@ -82,13 +84,14 @@ public class SymmetricComponentCalcTest { FlightConfiguration config = rocket.getSelectedConfiguration(); FlightConditions conditions = new FlightConditions(config); + Transformation transform = Transformation.IDENTITY; WarningSet warnings = new WarningSet(); AerodynamicForces forces = new AerodynamicForces(); SymmetricComponentCalc calcObj = new SymmetricComponentCalc( nose ); conditions.setAOA(0.0); // vvv TEST vvv - calcObj.calculateNonaxialForces(conditions, forces, warnings); + calcObj.calculateNonaxialForces(conditions, transform, forces, warnings); // ^^^ ^^^ double l_nose = nose.getLength(); diff --git a/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java b/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java index 38499750c..a0c0d0697 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/FreeformFinSetTest.java @@ -23,6 +23,7 @@ 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 net.sf.openrocket.util.Transformation; public class FreeformFinSetTest extends BaseTestCase { @@ -1203,9 +1204,10 @@ public class FreeformFinSetTest extends BaseTestCase { bt.addChild(fins); FinSetCalc calc = new FinSetCalc(fins); FlightConditions conditions = new FlightConditions(null); + Transformation transform = Transformation.IDENTITY; AerodynamicForces forces = new AerodynamicForces(); WarningSet warnings = new WarningSet(); - calc.calculateNonaxialForces(conditions, forces, warnings); + calc.calculateNonaxialForces(conditions, transform, forces, warnings); //System.out.println(forces); assertEquals(0.023409, forces.getCP().x, 0.0001); } From 13f7c583fd112bca0761a70b8fa692b31ce388bd Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Mon, 4 Feb 2019 11:34:35 -0700 Subject: [PATCH 2/3] Changed logging in BarrowmanCalculator.java to use stderr (easier to use with unittest) Added "End Plate Test" rocket to TestRockets and BarrowmanCalcTest --- .../aerodynamics/BarrowmanCalculator.java | 18 +-- .../net/sf/openrocket/util/Coordinate.java | 4 +- .../net/sf/openrocket/util/TestRockets.java | 150 +++++++++++++++++- .../aerodynamics/BarrowmanCalculatorTest.java | 32 +++- 4 files changed, 183 insertions(+), 21 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index 260e57f4a..5e7942360 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -11,9 +11,6 @@ import java.util.LinkedList; import java.util.Map; import java.util.Queue; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import net.sf.openrocket.aerodynamics.barrowman.FinSetCalc; import net.sf.openrocket.aerodynamics.barrowman.RocketComponentCalc; import net.sf.openrocket.rocketcomponent.ComponentAssembly; @@ -43,9 +40,6 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { private static final String BARROWMAN_PACKAGE = "net.sf.openrocket.aerodynamics.barrowman"; private static final String BARROWMAN_SUFFIX = "Calc"; - private static final Logger log = LoggerFactory.getLogger(BarrowmanCalculator.class); - - private Map calcMap = null; private double cacheDiameter = -1; @@ -193,8 +187,8 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { for(Map.Entry> entry: imap.entrySet() ) { final RocketComponent comp = entry.getKey(); RocketComponentCalc calcObj = calcMap.get(comp); - log.debug("comp=" + comp); - + + // System.err.println("comp=" + comp); if (null != calcObj) { // iterate across component instances final ArrayList contextList = entry.getValue(); @@ -209,23 +203,23 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { AerodynamicForces instanceForces = new AerodynamicForces().zero(); calcObj.calculateNonaxialForces(conditions, context.transform, instanceForces, warnings); - log.debug("instanceForces[" + context.instanceNumber + "]=" + instanceForces); Coordinate cp_comp = instanceForces.getCP(); Coordinate cp_abs = context.transform.transform(cp_comp); + if ((comp instanceof FinSet) && (((FinSet)comp).getFinCount() > 2)) + cp_abs = cp_abs.setY(0.0).setZ(0.0); instanceForces.setCP(cp_abs); double CN_instanced = instanceForces.getCN(); instanceForces.setCm(CN_instanced * instanceForces.getCP().x / conditions.getRefLength()); - - log.debug("instanceForces=" + instanceForces); + // System.err.println("instanceForces=" + instanceForces); assemblyForces.merge(instanceForces); } } } } - log.debug("assemblyForces=" + assemblyForces); + // System.err.println("assemblyForces=" + assemblyForces); return assemblyForces; } diff --git a/core/src/net/sf/openrocket/util/Coordinate.java b/core/src/net/sf/openrocket/util/Coordinate.java index 51c8b00f1..b4d93a76a 100644 --- a/core/src/net/sf/openrocket/util/Coordinate.java +++ b/core/src/net/sf/openrocket/util/Coordinate.java @@ -331,9 +331,9 @@ public final class Coordinate implements Cloneable, Serializable { @Override public String toString() { if (isWeighted()) - return String.format("(%.3f,%.3f,%.3f,w=%.3f)", x, y, z, weight); + return String.format("(%.5f,%.5f,%.5f,w=%.5f)", x, y, z, weight); else - return String.format("(%.3f,%.3f,%.3f)", x, y, z); + return String.format("(%.5f,%.5f,%.5f)", x, y, z); } // high-precision output, for use with verifying calculations diff --git a/core/src/net/sf/openrocket/util/TestRockets.java b/core/src/net/sf/openrocket/util/TestRockets.java index 0f025371e..e6b6c2fc9 100644 --- a/core/src/net/sf/openrocket/util/TestRockets.java +++ b/core/src/net/sf/openrocket/util/TestRockets.java @@ -3,6 +3,7 @@ package net.sf.openrocket.util; import java.util.Random; import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.file.openrocket.OpenRocketSaver; import net.sf.openrocket.database.Databases; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.OpenRocketDocumentFactory; @@ -39,6 +40,7 @@ import net.sf.openrocket.rocketcomponent.MassComponent; import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.Parachute; import net.sf.openrocket.rocketcomponent.ParallelStage; +import net.sf.openrocket.rocketcomponent.PodSet; import net.sf.openrocket.rocketcomponent.RecoveryDevice; import net.sf.openrocket.rocketcomponent.ReferenceType; import net.sf.openrocket.rocketcomponent.Rocket; @@ -851,6 +853,14 @@ public class TestRockets { // This function is used for unit, integration tests, DO NOT CHANGE (without updating tests). + // Comments starting with cp: are center of pressure calculations + // for components of the rocket. Note that a cp: without a weight + // has a weight of 0 (see for example body tubes) -- included for + // completeness. + // Instanced components (ie components on the side boosters) have + // a cp: for each instance. + // The unit tests change the number of fins on the side boosters; + // cp: comments are shown for 1-fin, 2-fin, and 3-fin cases public static Rocket makeFalcon9Heavy() { Rocket rocket = new Rocket(); rocket.setName("Falcon9H Scale Rocket"); @@ -876,10 +886,12 @@ public class TestRockets { payloadFairingNoseCone.setAftShoulderThickness( 0.001 ); payloadFairingNoseCone.setAftShoulderCapped( false ); payloadStage.addChild(payloadFairingNoseCone); + // cp:(0.05900,0.00000,0.00000,w=2.00000) BodyTube payloadBody = new BodyTube(0.132, 0.052, 0.001); payloadBody.setName("PL Fairing Body"); payloadStage.addChild(payloadBody); + // cp:(0.18400,0.00000,0.00000) Transition payloadFairingTail = new Transition(); payloadFairingTail.setName("PL Fairing Transition"); @@ -888,10 +900,12 @@ public class TestRockets { payloadFairingTail.setForeRadiusAutomatic(true); payloadFairingTail.setAftRadiusAutomatic(true); payloadStage.addChild(payloadFairingTail); + // cp:(0.25665,0.00000,0.00000,w=-0.90366) BodyTube upperStageBody= new BodyTube(0.18, 0.0385, 0.001); upperStageBody.setName("Upper Stage Body "); payloadStage.addChild( upperStageBody); + // cp:(0.35400,0.00000,0.00000) { // Parachute @@ -916,6 +930,7 @@ public class TestRockets { BodyTube interstage= new BodyTube(0.12, 0.0385, 0.001); interstage.setName("Interstage"); payloadStage.addChild( interstage); + // cp:(0.50400,0.00000,0.00000) } // ====== Core Stage ====== @@ -930,6 +945,7 @@ public class TestRockets { coreBody.setName("Core Stage Body"); coreBody.setMotorMount(true); coreStage.addChild( coreBody); + // cp:(0.96400,0.00000,0.00000) { MotorConfiguration coreMotorConfig = new MotorConfiguration(coreBody, selFCID); Motor mtr = TestRockets.generateMotor_M1350_75mm(); @@ -961,12 +977,15 @@ public class TestRockets { boosterCone.setAftShoulderThickness( 0.001 ); boosterCone.setAftShoulderCapped( false ); boosterStage.addChild( boosterCone); + // cp:(0.52400,0.07700,0.00000,w=1.09634) + // cp:(0.52400,-0.07700,0.00000,w=1.09634) BodyTube boosterBody = new BodyTube(0.8, 0.0385, 0.001); boosterBody.setName("Booster Body"); boosterBody.setOuterRadiusAutomatic(true); boosterStage.addChild( boosterBody); - + // cp:(0.96400,0.07700,0.00000) + // cp:(0.96400,-0.07700,0.00000) { InnerTube boosterMotorTubes = new InnerTube(); boosterMotorTubes.setName("Booster Motor Tubes"); @@ -997,6 +1016,12 @@ public class TestRockets { boosterFins.setSweep(0.18); boosterFins.setAxialMethod(AxialMethod.BOTTOM); boosterFins.setAxialOffset(0.0); + // cp:(1.17873,0.10422,0.02722,w=6.07405) (1 fin case) + // cp:(1.17873,-0.10422,-0.02722,w=6.07405) (1 fin case) + // cp:(1.17873,0.10422,0.02722,w=12.14810) (2 fin case) + // cp:(1.17873,-0.10422,-0.02722,w=12.14810) (2 fin case) + // cp:(1.17873,0.00000,0.00000,w=9.11107) (3 fin case) + // cp:(1.17873,0.00000,0.00000,w=9.11107) (3 fin case) } } @@ -1009,6 +1034,128 @@ public class TestRockets { return rocket; } + + // This is a simple four-fin rocket with large endplates on the + // fins, for testing CG and CP calculations with fins on pods. + // not a complete rocket (no motor mount nor recovery system) + + // it's parameterized to make things easy to change, but be sure + // to adjust the unit test to match! + public static Rocket makeEndPlateRocket() { + + // rocket design parameters + double radius = 0.01; // note this is diameter/2! + int finCount = 4; // also determines pod count + + // nose cone + double noseThick = 0.002; + double noseLength = 0.05; + + // body tube + double bodyWallThick = 0.002; + double bodyLength = 0.254; + + // main body tube fins + int bodyFinCount = 4; + double bodyFinRootChord = 0.05; + double bodyFinTipChord = bodyFinRootChord; + double bodyFinHeight = 0.025; + double bodyFinSweep = 0.0; + AxialMethod bodyFinAxialMethod = AxialMethod.BOTTOM; + double bodyFinAxialOffset = 0.0; + double bodyFinThickness = 0.003; + + // pods for end plates + int podSetCount = bodyFinCount; + double podSetOffset = bodyFinHeight; + AxialMethod podSetAxialMethod = bodyFinAxialMethod; + double podSetAxialOffset = 0.0; + + // "phantom" tube on pods to give us somewhere to connect end + // plates + double phantomLength = bodyFinTipChord; + double phantomRadius = 0; + double phantomWallThickness = 0; + + // end plates + int endPlateCount = 2; + double endPlateRootChord = bodyFinTipChord; + double endPlateTipChord = endPlateRootChord; + double endPlateSweep = 0.0; + double endPlateThickness = bodyFinThickness; + double endPlateHeight = bodyFinHeight; + double endPlateRotation = Math.PI/2.0; + AxialMethod endPlateAxialMethod = AxialMethod.BOTTOM; + double endPlateAxialOffset = 0; + + // create bare rocket + Rocket rocket = new Rocket(); + rocket.enableEvents(); + rocket.setName("End Plate Test"); + + // rocket has one stage + AxialStage sustainer = new AxialStage(); + rocket.addChild(sustainer); + sustainer.setName("Sustainer"); + + // nose cone + NoseCone noseCone = new NoseCone(Transition.Shape.OGIVE, noseLength, radius); + sustainer.addChild(noseCone); + noseCone.setName("Nose Cone"); + // cp:(0.02303,0.00000,0.00000,w=2.00000) + + // body tube + BodyTube bodyTube = new BodyTube(bodyLength, radius, bodyWallThick); + sustainer.addChild(bodyTube); + bodyTube.setName("Body tube"); + // cp:(0.17700,0.00000,0.00000) + + // Trapezoidal fin set on body tube + TrapezoidFinSet bodyFinSet = new TrapezoidFinSet(bodyFinCount, bodyFinRootChord, bodyFinTipChord, bodyFinSweep, bodyFinHeight); + bodyTube.addChild(bodyFinSet); + bodyFinSet.setName("Body Tube FinSet"); + bodyFinSet.setAxialMethod(bodyFinAxialMethod); + bodyFinSet.setAxialOffset(bodyFinAxialOffset); + bodyFinSet.setThickness(bodyFinThickness); + // cp:(0.26650,0.00000,0.00000,w=15.24857) + + // Pod set to put an end plate on each fin + PodSet podSet = new PodSet(); + bodyTube.addChild(podSet); + podSet.setName("Pod Set"); + podSet.setInstanceCount(podSetCount); + podSet.setRadiusOffset(podSetOffset); + podSet.setAxialMethod(podSetAxialMethod); + podSet.setAxialOffset(podSetAxialOffset); + + // 0-diameter "body tube" to give us something to hook the + // endplates to. Note that this causes a "thick fins" + // warning. + BodyTube phantom = new BodyTube(phantomLength, phantomRadius, phantomWallThickness); + podSet.addChild(phantom); + phantom.setName("Phantom"); + // cp:(0.25400,0.03540,0.00000) + // cp:(0.25400,0.00000,0.03540) + // cp:(0.25400,-0.03540,0.00000) + // cp:(0.25400,-0.00000,-0.03540) + + // end plates + TrapezoidFinSet endPlate = new TrapezoidFinSet(endPlateCount, endPlateRootChord, endPlateTipChord, endPlateSweep, endPlateHeight); + phantom.addChild(endPlate); + endPlate.setName("End plates"); + endPlate.setBaseRotation(endPlateRotation); + endPlate.setAxialMethod(endPlateAxialMethod); + endPlate.setAxialOffset(endPlateAxialOffset); + endPlate.setThickness(endPlateThickness); + // cp:(0.26650,0.03540,0.00000,w=0.00000) + // cp:(0.26650,0.00000,0.03540,w=11.86000) + // cp:(0.26650,-0.03540,0.00000,w=0.00000) + // cp:(0.26650,-0.00000,-0.03540,w=11.86000) + + // Total cp:(0.25461,-0.00000,0.00000,w=40.96857) + + return rocket; + } /* * Create a new file version 1.00 rocket @@ -1406,6 +1553,7 @@ public class TestRockets { public static OpenRocketDocument makeTestRocket_v108_withBoosters() { Rocket rocket = makeFalcon9Heavy(); OpenRocketDocument document = OpenRocketDocumentFactory.createDocumentFromRocket(rocket); + return document; } diff --git a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java index 6e09fc678..ebcc07d78 100644 --- a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java @@ -86,7 +86,7 @@ public class BarrowmanCalculatorTest { assertEquals(" Estes Alpha III CNa value is incorrect:", exp_cna, cp_calc.weight, EPSILON); assertEquals(" Estes Alpha III cp x value is incorrect:", exp_cpx, cp_calc.x, EPSILON); - assertEquals(" Estes Alpha III cp x value is incorrect:", 0.0, cp_calc.y, EPSILON); + assertEquals(" Estes Alpha III cp y value is incorrect:", 0.0, cp_calc.y, EPSILON); } @Test @@ -107,7 +107,8 @@ public class BarrowmanCalculatorTest { assertEquals(" Estes Alpha III cp x value is incorrect:", expCPx, calcCP.x, EPSILON); assertEquals(" Estes Alpha III CNa value is incorrect:", exp_cna, calcCP.weight, EPSILON); } - + + // Component CP calculations resulting in expected test values are in comments in TestRockets.makeFalcon9Heavy() @Test public void testCPParallelBoosters() { final Rocket rocket = TestRockets.makeFalcon9Heavy(); @@ -128,21 +129,40 @@ public class BarrowmanCalculatorTest { }{ boosterFins.setFinCount(2); final Coordinate cp_2fin = calc.getCP(config, conditions, warnings); - assertEquals(" Falcon 9 Heavy CNa value is incorrect:", 15.43711197, cp_2fin.weight, EPSILON); - assertEquals(" Falcon 9 Heavy CP x value is incorrect:", 0.99464238, cp_2fin.x, EPSILON); + assertEquals(" Falcon 9 Heavy CNa value is incorrect:", 27.585207667168696, cp_2fin.weight, EPSILON); + assertEquals(" Falcon 9 Heavy CP x value is incorrect:", 1.0757127676940474, cp_2fin.x, EPSILON); assertEquals(" Falcon 9 Heavy CP y value is incorrect:", 0.0, cp_2fin.y, EPSILON); assertEquals(" Falcon 9 Heavy CP z value is incorrect:", 0.0, cp_2fin.z, EPSILON); }{ boosterFins.setFinCount(1); final Coordinate cp_1fin = calc.getCP(config, conditions, warnings); - assertEquals(" Falcon 9 Heavy CNa value is incorrect:", 9.36306412, cp_1fin.weight, EPSILON); - assertEquals(" Falcon 9 Heavy CP x value is incorrect:", 0.87521867, cp_1fin.x, EPSILON); + assertEquals(" Falcon 9 Heavy CNa value is incorrect:", 15.43711196967902, cp_1fin.weight, EPSILON); + assertEquals(" Falcon 9 Heavy CP x value is incorrect:", 0.99464, cp_1fin.x, EPSILON); assertEquals(" Falcon 9 Heavy CP y value is incorrect:", 0f, cp_1fin.y, EPSILON); assertEquals(" Falcon 9 Heavy CP z value is incorrect:", 0f, cp_1fin.z, EPSILON); }{ // absent -- 3.28901627g @[0.31469937,0.05133333,0.00000000] } } + + // test rocket with endplates on fins. Comments tracing + // calculation of CP are in TestRockets.makeEndPlateRocket(). + @Test + public void testEndPlateCP() { + final Rocket rocket = TestRockets.makeEndPlateRocket(); + final FlightConfiguration config = new FlightConfiguration(rocket, null); + // rocket.setFlightConfiguration(config.getId(), config); + // rocket.setSelectedConfiguration(config.getId()); + final AerodynamicCalculator calc = new BarrowmanCalculator(); + final FlightConditions conditions = new FlightConditions(config); + final WarningSet warnings = new WarningSet(); + + final Coordinate cp = calc.getCP(config, conditions, warnings); + assertEquals(" Endplate rocket cp x value is incorrect:", 0.25461, cp.x, EPSILON); + assertEquals(" Endplate rocket cp y value is incorrect:", 0.0, cp.y, EPSILON); + assertEquals(" Endplate rocket cp z value is incorrect:", 0.0, cp.z, EPSILON); + assertEquals(" Endplate rocket CNa value is incorrect:", 40.96857, cp.weight, EPSILON); + } @Test public void testGetWorstCP() { From eb3a129e67363eebea86563207709b576cf91a30 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Mon, 11 Feb 2019 15:37:21 -0700 Subject: [PATCH 3/3] Modify FinSetCalc to only produce result for one fin, to be summed in BarrowmanCalculator Modify BarrowmanCalculator to actually sum fin nonaxial forces Modify FinSetCalcTest to sum fins in test3Fin() and test4Fin() Extra bonus: if at some point we allow fins to be other than radially symmetrical, this code should still work (not tested) --- .../aerodynamics/AerodynamicForces.java | 1 - .../aerodynamics/BarrowmanCalculator.java | 35 +++++------- .../aerodynamics/barrowman/FinSetCalc.java | 13 +---- .../aerodynamics/FinSetCalcTest.java | 53 ++++++++++--------- 4 files changed, 43 insertions(+), 59 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/AerodynamicForces.java b/core/src/net/sf/openrocket/aerodynamics/AerodynamicForces.java index 904098a9d..ff40734ca 100644 --- a/core/src/net/sf/openrocket/aerodynamics/AerodynamicForces.java +++ b/core/src/net/sf/openrocket/aerodynamics/AerodynamicForces.java @@ -392,7 +392,6 @@ public class AerodynamicForces implements Cloneable, Monitorable { } public AerodynamicForces merge(AerodynamicForces other) { - this.cp = cp.average(other.getCP()); this.CNa = CNa + other.getCNa(); this.CN = CN + other.getCN(); diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index 5e7942360..4667476c8 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -194,27 +194,20 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { final ArrayList contextList = entry.getValue(); for(InstanceContext context: contextList ) { - // since FinSetCalc calculates forces for entire FinSet - // only consider first fin in each set (happily, - // in each instance of a PodSet or similar - // instanceable RocketComponent with childre, the - // child FinSets all start numbering with 0 - if (!(comp instanceof FinSet) || (context.instanceNumber == 0)) { - AerodynamicForces instanceForces = new AerodynamicForces().zero(); - - calcObj.calculateNonaxialForces(conditions, context.transform, instanceForces, warnings); - Coordinate cp_comp = instanceForces.getCP(); - - Coordinate cp_abs = context.transform.transform(cp_comp); - if ((comp instanceof FinSet) && (((FinSet)comp).getFinCount() > 2)) - cp_abs = cp_abs.setY(0.0).setZ(0.0); - - instanceForces.setCP(cp_abs); - double CN_instanced = instanceForces.getCN(); - instanceForces.setCm(CN_instanced * instanceForces.getCP().x / conditions.getRefLength()); - // System.err.println("instanceForces=" + instanceForces); - assemblyForces.merge(instanceForces); - } + AerodynamicForces instanceForces = new AerodynamicForces().zero(); + + calcObj.calculateNonaxialForces(conditions, context.transform, instanceForces, warnings); + Coordinate cp_comp = instanceForces.getCP(); + + Coordinate cp_abs = context.transform.transform(cp_comp); + if ((comp instanceof FinSet) && (((FinSet)comp).getFinCount() > 2)) + cp_abs = cp_abs.setY(0.0).setZ(0.0); + + instanceForces.setCP(cp_abs); + double CN_instanced = instanceForces.getCN(); + instanceForces.setCm(CN_instanced * instanceForces.getCP().x / conditions.getRefLength()); + // System.err.println("instanceForces=" + instanceForces); + assemblyForces.merge(instanceForces); } } } diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java index 66e193de5..4e97a8300 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java @@ -117,18 +117,7 @@ public class FinSetCalc extends RocketComponentCalc { double angle = baseRotation + transform.getXrotation(); // Compute basic CNa without interference effects - if (finCount == 1 || finCount == 2) { - // Basic CNa from geometry - double mul = 0; - for (int i = 0; i < finCount; i++) { - mul += MathUtil.pow2(Math.sin(theta - angle)); - angle += 2 * Math.PI / finCount; - } - cna = cna1 * mul; - } else { - // Basic CNa assuming full efficiency - cna = cna1 * finCount / 2.0; - } + cna = cna1 * MathUtil.pow2(Math.sin(theta - angle)); // logger.debug("Component cna = {}", cna); diff --git a/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java b/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java index ab677fb32..bfdefe084 100644 --- a/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/FinSetCalcTest.java @@ -38,6 +38,27 @@ public class FinSetCalcTest { // } } + private AerodynamicForces sumFins(TrapezoidFinSet fins, Rocket rocket) + { + FlightConfiguration config = rocket.getSelectedConfiguration(); + FlightConditions conditions = new FlightConditions(config); + WarningSet warnings = new WarningSet(); + AerodynamicForces assemblyForces = new AerodynamicForces().zero(); + AerodynamicForces componentForces = new AerodynamicForces(); + + FinSetCalc calcObj = new FinSetCalc( fins ); + + // Need to sum forces for fins + for (Integer i = 0; i < fins.getFinCount(); i++) { + calcObj.calculateNonaxialForces(conditions, + Transformation.rotate_x(Math.PI * i / fins.getFinCount()), + componentForces, warnings); + assemblyForces.merge(componentForces); + } + + return assemblyForces; + } + @Test public void test3Fin() { Rocket rocket = TestRockets.makeEstesAlphaIII(); @@ -49,19 +70,10 @@ public class FinSetCalcTest { assertEquals(" Estes Alpha III fins have wrong tip chord:", 0.03, fins.getTipChord(), EPSILON); assertEquals(" Estes Alpha III fins have wrong sweep: ", 0.02, fins.getSweep(), EPSILON); assertEquals(" Estes Alpha III fins have wrong height: ", 0.05, fins.getHeight(), EPSILON); - - FlightConfiguration config = rocket.getSelectedConfiguration(); - Transformation transform = Transformation.IDENTITY; - FlightConditions conditions = new FlightConditions(config); - WarningSet warnings = new WarningSet(); - AerodynamicForces forces = new AerodynamicForces(); - FinSetCalc calcObj = new FinSetCalc( fins ); - - - // vvv TEST MEH! vvv - calcObj.calculateNonaxialForces(conditions, transform, forces, warnings); - // ^^^ - + + // get the forces for the three fins + AerodynamicForces forces = sumFins(fins, rocket); + double exp_cna_fins = 24.146933; double exp_cpx_fins = 0.0193484; @@ -84,18 +96,9 @@ public class FinSetCalcTest { assertEquals(" Estes Alpha III fins have wrong tip chord:", 0.03, fins.getTipChord(), EPSILON); assertEquals(" Estes Alpha III fins have wrong sweep: ", 0.02, fins.getSweep(), EPSILON); assertEquals(" Estes Alpha III fins have wrong height: ", 0.05, fins.getHeight(), EPSILON); - - FlightConfiguration config = rocket.getSelectedConfiguration(); - Transformation transform = Transformation.IDENTITY; - FlightConditions conditions = new FlightConditions(config); - WarningSet warnings = new WarningSet(); - AerodynamicForces forces = new AerodynamicForces(); - FinSetCalc calcObj = new FinSetCalc( fins ); - - - // vvv TEST MEH! vvv - calcObj.calculateNonaxialForces(conditions, transform, forces, warnings); - // ^^^ + + // get the forces for the four fins + AerodynamicForces forces = sumFins(fins, rocket); double exp_cna_fins = 32.195911; double exp_cpx_fins = 0.0193484;