From 5b94bedb5f35f26b505a37d2cc70ae2d83201de4 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Tue, 1 Sep 2020 19:55:13 -0400 Subject: [PATCH 1/2] [fixes #755] Correctly calculate cg.x override --- .../openrocket/masscalc/MassCalculation.java | 31 +++++------ .../net/sf/openrocket/util/TestRockets.java | 35 ++++++++++++ .../masscalc/MassCalculatorTest.java | 55 +++++++++++++++++-- 3 files changed, 99 insertions(+), 22 deletions(-) diff --git a/core/src/net/sf/openrocket/masscalc/MassCalculation.java b/core/src/net/sf/openrocket/masscalc/MassCalculation.java index 048ac3cab..996ad14ce 100644 --- a/core/src/net/sf/openrocket/masscalc/MassCalculation.java +++ b/core/src/net/sf/openrocket/masscalc/MassCalculation.java @@ -341,6 +341,17 @@ public class MassCalculation { // mass data for *this component only* in the rocket-frame compCM = parentTransform.transform( compCM.add(component.getPosition()) ); + if (component.getOverrideSubcomponents()) { + if (component.isMassOverridden()) { + double newMass = MathUtil.max(component.getOverrideMass(), MIN_MASS); + Coordinate newCM = this.getCM().setWeight( newMass ); + this.setCM( newCM ); + } + + if (component.isCGOverridden()) { + this.setCM( this.getCM().setX( compCM.x + component.getOverrideCGX())); + } + } this.addMass( compCM ); if(null != analysisMap){ @@ -357,28 +368,14 @@ public class MassCalculation { } } - double compIx = component.getRotationalUnitInertia() * compCM.weight; - double compIt = component.getLongitudinalUnitInertia() * compCM.weight; - RigidBody componentInertia = new RigidBody( compCM, compIx, compIt, compIt ); - + final double compIx = component.getRotationalUnitInertia() * compCM.weight; + final double compIt = component.getLongitudinalUnitInertia() * compCM.weight; + final RigidBody componentInertia = new RigidBody( compCM, compIx, compIt, compIt ); this.addInertia( componentInertia ); // // vvv DEBUG // if( 0 < compCM.weight ) { // System.err.println(String.format( "%s....componentData: %s", prefix, compCM.toPreciseString() )); // } - - if (component.getOverrideSubcomponents()) { - if (component.isMassOverridden()) { - double newMass = MathUtil.max(component.getOverrideMass(), MIN_MASS); - Coordinate newCM = this.getCM().setWeight( newMass ); - this.setCM( newCM ); - } - - if (component.isCGOverridden()) { - Coordinate newCM = this.getCM().setX( component.getOverrideCGX() ); - this.setCM( newCM ); - } - } } // // vvv DEBUG diff --git a/core/src/net/sf/openrocket/util/TestRockets.java b/core/src/net/sf/openrocket/util/TestRockets.java index 129344544..cf61a013a 100644 --- a/core/src/net/sf/openrocket/util/TestRockets.java +++ b/core/src/net/sf/openrocket/util/TestRockets.java @@ -638,6 +638,41 @@ public class TestRockets { return rocket; } + // This is not a production rocket -- it is construction, purely for testing Mass Overrides + // This function is used for unit, integration tests, DO NOT CHANGE (without updating tests). + public static final Rocket makeSimple2Stage(){ + Rocket rocket = new Rocket(); + rocket.createFlightConfiguration( TEST_FCID_0 ); + rocket.setName("Simple 2-Stage Rocket"); + + final double bodytubeLength = 0.10; + final double bodytubeRadius = 0.01; + final double bodytubeThickness = 0.001; + { + final AxialStage sustainerStage = new AxialStage(); + sustainerStage.setName("Sustainer Stage"); + rocket.addChild(sustainerStage); + + final BodyTube bodytube = new BodyTube(bodytubeLength, bodytubeRadius, bodytubeThickness); + bodytube.setName("Sustainer Body Tube"); + sustainerStage.addChild(bodytube); + }{ + final AxialStage boosterStage = new AxialStage(); + boosterStage.setName("Booster Stage"); + rocket.addChild(boosterStage); + + final BodyTube boosterBody = new BodyTube(bodytubeLength, bodytubeRadius, bodytubeThickness); + boosterBody.setName("Booster Body Tube"); + boosterStage.addChild(boosterBody); + } + + rocket.setSelectedConfiguration( TEST_FCID_0 ); + rocket.getSelectedConfiguration().setAllStages(); + + rocket.enableEvents(); + return rocket; + } + public static Rocket makeBigBlue() { Rocket rocket; AxialStage stage; diff --git a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java index 07875f0e1..bac326e2f 100644 --- a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java +++ b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java @@ -22,7 +22,6 @@ public class MassCalculatorTest extends BaseTestCase { @Test public void testAlphaIIIStructure() { - System.err.println("testing AlphaIII structure"); Rocket rocket = TestRockets.makeEstesAlphaIII(); rocket.setName("AlphaIII." + Thread.currentThread().getStackTrace()[1].getMethodName()); @@ -54,7 +53,6 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals("Alpha III Longitudinal MOI calculated incorrectly: ", expMOIlong, actualMOIlong, EPSILON); // if we use a mass override, setting to same mass, we should get same result - System.err.println("calculating AlphaIII with mass override"); AxialStage sustainer = (AxialStage) rocket.getChild(0); sustainer.setOverrideSubcomponents(true); @@ -160,7 +158,54 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals(" Motor Mass " + desig + " is incorrect: ", expMass, actualMotorData.getMass(), EPSILON); } } - + + @Test + public void testStageOverride() { + final Rocket rocket = TestRockets.makeSimple2Stage(); + final AxialStage sustainerStage = (AxialStage) rocket.getChild(0); + final AxialStage boosterStage = (AxialStage) rocket.getChild(1); + final FlightConfiguration config = rocket.getSelectedConfiguration(); + + { // [0] verify / document structure + final BodyTube sustainerBody = (BodyTube) sustainerStage.getChild(0); + assertEquals(0.0, sustainerBody.getPosition().x, EPSILON); + assertEquals(0.1, sustainerBody.getLength(), EPSILON); + + final BodyTube boosterBody = (BodyTube) boosterStage.getChild(0); + assertEquals(0.10, boosterBody.getComponentLocations()[0].x, EPSILON); + assertEquals(0.10, boosterBody.getLength(), EPSILON); + } + + { // [1] test Rocket CM, before: + final RigidBody actualStructure = MassCalculator.calculateStructure(config); + + final double actualRocketDryMass = actualStructure.cm.weight; + final double expRocketDryMass = 0.0081178754; + assertEquals(" Alpha III Empty Mass is incorrect: ", expRocketDryMass, actualRocketDryMass, EPSILON); + + final Coordinate actualRocketDryCM = actualStructure.cm; + final double expCMx = 0.10; + assertEquals("Simple Rocket CM.x is incorrect: ", expCMx, actualRocketDryCM.x, EPSILON); + } + + boosterStage.setOverrideSubcomponents(true); + boosterStage.setCGOverridden(true); + boosterStage.setOverrideCGX(0.0); + + + { // [1] test Rocket CM, before: + final RigidBody actualStructure = MassCalculator.calculateStructure(config); + + final double actualRocketDryMass = actualStructure.cm.weight; + final double expRocketDryMass = 0.0081178754; + assertEquals(" Alpha III Empty Mass is incorrect: ", expRocketDryMass, actualRocketDryMass, EPSILON); + + final Coordinate actualRocketDryCM = actualStructure.cm; + final double expCMx = 0.075; + assertEquals("Simple Rocket CM.x is incorrect: ", expCMx, actualRocketDryCM.x, EPSILON); + } + } + @Test public void testFalcon9HComponentMasses() { Rocket rkt = TestRockets.makeFalcon9Heavy(); @@ -723,7 +768,7 @@ public class MassCalculatorTest extends BaseTestCase { double expTotalMass = overrideMass; assertEquals(" Booster Launch Mass is incorrect: ", expTotalMass, calcTotalMass, EPSILON); - double expCMx = 6.0; + double expCMx = 6.484; Coordinate expCM = new Coordinate(expCMx, 0, 0, expTotalMass); assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, boosterSetCM.x, EPSILON); assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, boosterSetCM.y, EPSILON); @@ -735,7 +780,7 @@ public class MassCalculatorTest extends BaseTestCase { double boosterMOI_xx = burnout.getRotationalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON); - double expMOI_tr = 14.815925423036177; + double expMOI_tr = 17.86133586701; double boosterMOI_tr = burnout.getLongitudinalInertia(); assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); } From bd09354fd324a40cc74ea506e8ed685c7494b2da Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Wed, 2 Sep 2020 22:33:39 -0400 Subject: [PATCH 2/2] [fixes #755] Correctly calculates mass override (particularly on stages) --- .../openrocket/masscalc/MassCalculation.java | 40 ++++------ .../masscalc/MassCalculatorTest.java | 75 ++++++++++++++++--- 2 files changed, 81 insertions(+), 34 deletions(-) diff --git a/core/src/net/sf/openrocket/masscalc/MassCalculation.java b/core/src/net/sf/openrocket/masscalc/MassCalculation.java index 996ad14ce..0a318e350 100644 --- a/core/src/net/sf/openrocket/masscalc/MassCalculation.java +++ b/core/src/net/sf/openrocket/masscalc/MassCalculation.java @@ -53,11 +53,9 @@ public class MassCalculation { // =========== Instance Functions ======================== public void merge( final MassCalculation other ) { - if( MIN_MASS < other.getMass()) { - // Adjust Center-of-mass - this.addMass( other.getCM() ); - this.bodies.addAll( other.bodies ); - } + // Adjust Center-of-mass + this.addMass( other.getCM() ); + this.bodies.addAll( other.bodies ); } public void addInertia( final RigidBody data ) { @@ -320,25 +318,11 @@ public class MassCalculation { } } - if( MIN_MASS < children.getMass() ) { - this.merge( children ); - // // vvv DEBUG - // System.err.println(String.format( "%s....assembly mass (incl/children): %s", prefix, this.toCMDebug())); - } + this.merge( children ); if (this.config.isComponentActive(component) ){ Coordinate compCM = component.getComponentCG(); - - if (!component.getOverrideSubcomponents()) { - if (component.isMassOverridden()) { - compCM = compCM.setWeight(MathUtil.max(component.getOverrideMass(), MIN_MASS)); - // // vvv DEBUG - // System.err.println(String.format( "%s....mass overridden to: %s", prefix, compCM.toPreciseString())); - } - if (component.isCGOverridden()) - compCM = compCM.setXYZ(component.getOverrideCG()); - } - + // mass data for *this component only* in the rocket-frame compCM = parentTransform.transform( compCM.add(component.getPosition()) ); if (component.getOverrideSubcomponents()) { @@ -351,18 +335,24 @@ public class MassCalculation { if (component.isCGOverridden()) { this.setCM( this.getCM().setX( compCM.x + component.getOverrideCGX())); } + }else { + if (component.isMassOverridden()) { + compCM = compCM.setWeight(MathUtil.max(component.getOverrideMass(), MIN_MASS)); + } + if (component.isCGOverridden()) { + compCM = compCM.setXYZ(component.getOverrideCG()); + } + this.addMass( compCM ); } - this.addMass( compCM ); - + if(null != analysisMap){ + final CMAnalysisEntry entry = analysisMap.get(component.hashCode()); if( component instanceof ComponentAssembly) { // For ComponentAssemblies, record the _assembly_ information - CMAnalysisEntry entry = analysisMap.get(component.hashCode()); entry.updateEachMass(children.getMass() / component.getInstanceCount()); entry.updateAverageCM(this.centerOfMass); }else{ // For actual components, record the mass of the component, and disregard children - CMAnalysisEntry entry = analysisMap.get(component.hashCode()); entry.updateEachMass(compCM.weight); entry.updateAverageCM(compCM); } diff --git a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java index bac326e2f..078f7d963 100644 --- a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java +++ b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java @@ -160,7 +160,7 @@ public class MassCalculatorTest extends BaseTestCase { } @Test - public void testStageOverride() { + public void testStageCMxOverride() { final Rocket rocket = TestRockets.makeSimple2Stage(); final AxialStage sustainerStage = (AxialStage) rocket.getChild(0); final AxialStage boosterStage = (AxialStage) rocket.getChild(1); @@ -181,28 +181,85 @@ public class MassCalculatorTest extends BaseTestCase { final double actualRocketDryMass = actualStructure.cm.weight; final double expRocketDryMass = 0.0081178754; - assertEquals(" Alpha III Empty Mass is incorrect: ", expRocketDryMass, actualRocketDryMass, EPSILON); + assertEquals(expRocketDryMass, actualRocketDryMass, EPSILON); final Coordinate actualRocketDryCM = actualStructure.cm; final double expCMx = 0.10; - assertEquals("Simple Rocket CM.x is incorrect: ", expCMx, actualRocketDryCM.x, EPSILON); + assertEquals(expCMx, actualRocketDryCM.x, EPSILON); } boosterStage.setOverrideSubcomponents(true); boosterStage.setCGOverridden(true); boosterStage.setOverrideCGX(0.0); + { // [1] test Rocket CM, before: + final RigidBody actualStructure = MassCalculator.calculateStructure(config); + + final double actualRocketDryMass = actualStructure.cm.weight; + final double expRocketDryMass = 0.0081178754; + assertEquals(expRocketDryMass, actualRocketDryMass, EPSILON); + + final Coordinate actualRocketDryCM = actualStructure.cm; + final double expCMx = 0.075; + assertEquals(expCMx, actualRocketDryCM.x, EPSILON); + } + } + + + @Test + public void testStageMassOverride() { + final Rocket rocket = TestRockets.makeSimple2Stage(); + final AxialStage sustainerStage = (AxialStage) rocket.getChild(0); + final AxialStage boosterStage = (AxialStage) rocket.getChild(1); + final FlightConfiguration config = rocket.getSelectedConfiguration(); + + { // [0] verify / document structure + final double expMass = 0.0040589377; + final BodyTube sustainerBody = (BodyTube) sustainerStage.getChild(0); + assertEquals(0.0, sustainerBody.getPosition().x, EPSILON); + assertEquals(0.1, sustainerBody.getLength(), EPSILON); + assertEquals(expMass, sustainerBody.getMass(), EPSILON); + + final BodyTube boosterBody = (BodyTube) boosterStage.getChild(0); + assertEquals(0.10, boosterBody.getComponentLocations()[0].x, EPSILON); + assertEquals(0.10, boosterBody.getLength(), EPSILON); + assertEquals(expMass, sustainerBody.getMass(), EPSILON); + } { // [1] test Rocket CM, before: final RigidBody actualStructure = MassCalculator.calculateStructure(config); final double actualRocketDryMass = actualStructure.cm.weight; final double expRocketDryMass = 0.0081178754; - assertEquals(" Alpha III Empty Mass is incorrect: ", expRocketDryMass, actualRocketDryMass, EPSILON); + assertEquals(expRocketDryMass, actualRocketDryMass, EPSILON); final Coordinate actualRocketDryCM = actualStructure.cm; - final double expCMx = 0.075; - assertEquals("Simple Rocket CM.x is incorrect: ", expCMx, actualRocketDryCM.x, EPSILON); + final double expCMx = 0.10; + assertEquals(expCMx, actualRocketDryCM.x, EPSILON); + } + + final BodyTube boosterBody = (BodyTube) boosterStage.getChild(0); + final double bodyMass = boosterBody.getMass(); + assertEquals(0.0040589377, bodyMass, EPSILON); + + boosterStage.setOverrideSubcomponents(true); + boosterStage.setMassOverridden(true); + boosterStage.setOverrideMass(bodyMass); + + boosterBody.setOverrideSubcomponents(false); + boosterBody.setMassOverridden(true); + boosterBody.setOverrideMass(0.0); + + { // [1] test Rocket CM, before: + final RigidBody actualStructure = MassCalculator.calculateStructure(config); + + final double actualRocketDryMass = actualStructure.cm.weight; + final double expRocketDryMass = 0.0081178754; + assertEquals(expRocketDryMass, actualRocketDryMass, EPSILON); + + final Coordinate actualRocketDryCM = actualStructure.cm; + final double expCMx = 0.10; + assertEquals(expCMx, actualRocketDryCM.x, EPSILON); } } @@ -869,7 +926,7 @@ public class MassCalculatorTest extends BaseTestCase { double calcTotalMass = structure.getMass(); assertEquals(" Booster Launch Mass is incorrect: ", expMass, calcTotalMass, EPSILON); - final double expCMx = 1.1191303646; + final double expCMx = 0.76762318688; Coordinate expCM = new Coordinate(expCMx, 0, 0, expMass); assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, structure.getCM().x, EPSILON); assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, structure.getCM().y, EPSILON); @@ -877,11 +934,11 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals(" Booster Launch CM is incorrect: ", expCM, structure.getCM()); // Validate MOI - final double expMOI_axial = 0.005885793421431532; + final double expMOI_axial = 0.0038576236; double boosterMOI_xx = structure.getRotationalInertia(); assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON); - final double expMOI_tr = 0.04098909591063; + final double expMOI_tr = 0.123258667252; double boosterMOI_tr = structure.getLongitudinalInertia(); assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); }