From ac5ee7537a4971857193215d2861ac04ca612b5a Mon Sep 17 00:00:00 2001 From: SiboVG Date: Fri, 16 Sep 2022 15:05:28 +0200 Subject: [PATCH 1/7] Remove unused imports --- core/src/net/sf/openrocket/rocketcomponent/AxialStage.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java index 8e0f533c5..ff8d9820b 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java @@ -1,8 +1,5 @@ package net.sf.openrocket.rocketcomponent; -import java.util.ArrayList; -import java.util.Collection; - import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.position.AxialMethod; import net.sf.openrocket.startup.Application; From 6585e071409704f771d5efd79cdc4aa1c3001d3a Mon Sep 17 00:00:00 2001 From: SiboVG Date: Mon, 19 Sep 2022 09:24:38 +0200 Subject: [PATCH 2/7] [#1617] Set stage with no children as inactive --- .../rocketcomponent/FlightConfiguration.java | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java index 72fdacac1..9fbd3d2c9 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java @@ -241,8 +241,10 @@ public class FlightConfiguration implements FlightConfigurableParameter 0 && + stages.get(stageNumber) != null && stages.get(stageNumber).active; } public Collection getAllComponents() { @@ -379,12 +381,8 @@ public class FlightConfiguration implements FlightConfigurableParameter activeStages = new ArrayList<>(); for (StageFlags flags : this.stages.values()) { - if (flags.active) { - AxialStage stage = rocket.getStage(flags.stageNumber); - if (stage == null) { - continue; - } - activeStages.add(stage); + if (isStageActive(flags.stageNumber)) { + activeStages.add( rocket.getStage(flags.stageNumber)); } } @@ -392,13 +390,7 @@ public class FlightConfiguration implements FlightConfigurableParameter Date: Mon, 19 Sep 2022 14:32:46 +0200 Subject: [PATCH 3/7] Disable stage selector if stage has no children + ttips --- core/resources/l10n/messages.properties | 2 ++ .../gui/components/StageSelector.java | 21 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index b1719fd2f..9fae17c83 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -64,6 +64,8 @@ RocketPanel.lbl.Stability = Stability: RocketPanel.checkbox.ShowCGCP = Show CG/CP RocketPanel.checkbox.ShowCGCP.ttip = Disabling this checkbox hides the CG and CP markings in the rocket view. RocketPanel.lbl.Stages = Stages: +RocketPanel.btn.Stages.Toggle.ttip = Toggle this button to activate or deactive the corresponding stage in your design. +RocketPanel.btn.Stages.NoChildren.ttip = This stages does not have any child components and is therefore marked as inactive.
Add components to the stage to activate it. RocketPanel.ttip.Rotation = Change the rocket's roll rotation (only affects the rocket view) ! BasicFrame diff --git a/swing/src/net/sf/openrocket/gui/components/StageSelector.java b/swing/src/net/sf/openrocket/gui/components/StageSelector.java index da1aace86..f0e9ce289 100644 --- a/swing/src/net/sf/openrocket/gui/components/StageSelector.java +++ b/swing/src/net/sf/openrocket/gui/components/StageSelector.java @@ -11,18 +11,21 @@ import javax.swing.JToggleButton; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.gui.widgets.SelectColorToggleButton; +import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.StateChangeListener; @SuppressWarnings("serial") public class StageSelector extends JPanel implements StateChangeListener { + private static final Translator trans = Application.getTranslator(); private final Rocket rocket; private List buttons = new ArrayList(); @@ -61,9 +64,16 @@ public class StageSelector extends JPanel implements StateChangeListener { private class StageAction extends AbstractAction { private final AxialStage stage; - + public StageAction(final AxialStage stage) { this.stage = stage; + if (this.stage.getChildCount() == 0) { + putValue(SHORT_DESCRIPTION, trans.get("RocketPanel.btn.Stages.NoChildren.ttip")); + setEnabled(false); + } else { + putValue(SHORT_DESCRIPTION, trans.get("RocketPanel.btn.Stages.Toggle.ttip")); + } + updateUI(); } @Override @@ -77,6 +87,15 @@ public class StageSelector extends JPanel implements StateChangeListener { @Override public void actionPerformed(ActionEvent e) { + // Don't toggle the state if the stage has no children (and is therefore inactive) + if (stage.getChildCount() == 0) { + putValue(SHORT_DESCRIPTION, trans.get("RocketPanel.btn.Stages.NoChildren.ttip")); + setEnabled(false); + return; + } else { + setEnabled(true); + putValue(SHORT_DESCRIPTION, trans.get("RocketPanel.btn.Stages.Toggle.ttip")); + } rocket.getSelectedConfiguration().toggleStage(stage.getStageNumber()); rocket.fireComponentChangeEvent(ComponentChangeEvent.AEROMASS_CHANGE | ComponentChangeEvent.MOTOR_CHANGE ); } From 08116c3d32ad82b0fd0a4889d260c3621f8ba1d1 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Tue, 20 Sep 2022 17:48:17 +0200 Subject: [PATCH 4/7] Replace stage active flag checks with isStageActive This is more reliable as it takes other factors into account (e.g. if the stage has no children) --- .../sf/openrocket/rocketcomponent/FlightConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java index 9fbd3d2c9..effe1da8c 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java @@ -399,7 +399,7 @@ public class FlightConfiguration implements FlightConfigurableParameter Date: Tue, 20 Sep 2022 23:23:59 +0200 Subject: [PATCH 5/7] Fix typo --- core/resources/l10n/messages.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 9fae17c83..4b1c2b4b8 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -64,7 +64,7 @@ RocketPanel.lbl.Stability = Stability: RocketPanel.checkbox.ShowCGCP = Show CG/CP RocketPanel.checkbox.ShowCGCP.ttip = Disabling this checkbox hides the CG and CP markings in the rocket view. RocketPanel.lbl.Stages = Stages: -RocketPanel.btn.Stages.Toggle.ttip = Toggle this button to activate or deactive the corresponding stage in your design. +RocketPanel.btn.Stages.Toggle.ttip = Toggle this button to activate or deactivate the corresponding stage in your design. RocketPanel.btn.Stages.NoChildren.ttip = This stages does not have any child components and is therefore marked as inactive.
Add components to the stage to activate it. RocketPanel.ttip.Rotation = Change the rocket's roll rotation (only affects the rocket view) From 9d8ca907bbb022679e1a7382eb130bb1006ce9c0 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Fri, 23 Sep 2022 02:43:02 +0200 Subject: [PATCH 6/7] Add unit tests for simulating empty stages --- .../aerodynamics/BarrowmanCalculatorTest.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java index 828a49447..a26de0d72 100644 --- a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java @@ -46,6 +46,26 @@ public class BarrowmanCalculatorTest { // Application.setInjector(injector); // } } + + /** + * Test a completely empty rocket. + */ + @Test + public void testEmptyRocket() { + // First test completely empty rocket + Rocket rocket = new Rocket(); + FlightConfiguration config = rocket.getSelectedConfiguration(); + BarrowmanCalculator calc = new BarrowmanCalculator(); + FlightConditions conditions = new FlightConditions(config); + WarningSet warnings = new WarningSet(); + + Coordinate cp_calc = calc.getCP(config, conditions, warnings); + + assertEquals(" Empty rocket CNa value is incorrect:", 0.0, cp_calc.weight , 0.0); + assertEquals(" Empty rocket cp x value is incorrect:", 0.0, cp_calc.x , 0.0); + assertEquals(" Empty rocket cp y value is incorrect:", 0.0, cp_calc.y , 0.0); + assertEquals(" Empty rocket cp z value is incorrect:", 0.0, cp_calc.z , 0.0); + } @Test public void testCPSimpleDry() { @@ -351,4 +371,67 @@ public class BarrowmanCalculatorTest { assertEquals(" Alpha III With Pods rocket cp z value is incorrect:", cpNoPods.z, cpPods.z, EPSILON); assertEquals(" Alpha III With Pods rocket CNa value is incorrect:", cpPods.weight, cpNoPods.weight - 3.91572, EPSILON); } + + /** + * Tests whether adding extra empty stages has an effect. + */ + @Test + public void testEmptyStages() { + // Reference rocket + Rocket rocketRef = TestRockets.makeEstesAlphaIII(); + FlightConfiguration configRef = rocketRef.getSelectedConfiguration(); + BarrowmanCalculator calcRef = new BarrowmanCalculator(); + FlightConditions conditionsRef = new FlightConditions(configRef); + WarningSet warnings = new WarningSet(); + + Coordinate cp_calcRef = calcRef.getCP(configRef, conditionsRef, warnings); + + // First test with adding an empty stage in the front of the design + Rocket rocketFront = TestRockets.makeEstesAlphaIII(); + AxialStage stage1 = new AxialStage(); // To be placed in front of the design + rocketFront.addChild(stage1, 0); + FlightConfiguration configFront = rocketFront.getSelectedConfiguration(); + BarrowmanCalculator calcFront = new BarrowmanCalculator(); + FlightConditions conditionsFront = new FlightConditions(configFront); + warnings = new WarningSet(); + + Coordinate cp_calcFront = calcFront.getCP(configFront, conditionsFront, warnings); + + assertEquals(" Estes Alpha III with front empty stage CNa value is incorrect:", cp_calcRef.weight, cp_calcFront.weight , EPSILON); + assertEquals(" Estes Alpha III with front empty stage cp x value is incorrect:", cp_calcRef.x, cp_calcFront.x , EPSILON); + assertEquals(" Estes Alpha III with front empty stage cp y value is incorrect:", cp_calcRef.y, cp_calcFront.y , EPSILON); + assertEquals(" Estes Alpha III with front empty stage cp z value is incorrect:", cp_calcRef.z, cp_calcFront.z , EPSILON); + + // Now test with adding an empty stage in the rear of the design + Rocket rocketRear = TestRockets.makeEstesAlphaIII(); + AxialStage stage2 = new AxialStage(); // To be placed in the rear of the design + rocketRear.addChild(stage2); + FlightConfiguration configRear = rocketRear.getSelectedConfiguration(); + BarrowmanCalculator calcRear = new BarrowmanCalculator(); + FlightConditions conditionsRear = new FlightConditions(configRear); + warnings = new WarningSet(); + + Coordinate cp_calcRear = calcRear.getCP(configRear, conditionsRear, warnings); + + assertEquals(" Estes Alpha III with rear empty stage CNa value is incorrect:", cp_calcRef.weight, cp_calcRear.weight , EPSILON); + assertEquals(" Estes Alpha III with rear empty stage cp x value is incorrect:", cp_calcRef.x, cp_calcRear.x , EPSILON); + assertEquals(" Estes Alpha III with rear empty stage cp y value is incorrect:", cp_calcRef.y, cp_calcRear.y , EPSILON); + assertEquals(" Estes Alpha III with rear empty stage cp z value is incorrect:", cp_calcRef.z, cp_calcRear.z , EPSILON); + + // Test with multiple empty stages + Rocket rocketMulti = rocketFront; + AxialStage stage3 = new AxialStage(); // To be placed in the rear of the design + rocketMulti.addChild(stage3); + FlightConfiguration configMulti = rocketMulti.getSelectedConfiguration(); + BarrowmanCalculator calcMulti = new BarrowmanCalculator(); + FlightConditions conditionsMulti = new FlightConditions(configMulti); + warnings = new WarningSet(); + + Coordinate cp_calcMulti = calcMulti.getCP(configMulti, conditionsMulti, warnings); + + assertEquals(" Estes Alpha III with multiple empty stages CNa value is incorrect:", cp_calcRef.weight, cp_calcMulti.weight , EPSILON); + assertEquals(" Estes Alpha III with multiple empty stages cp x value is incorrect:", cp_calcRef.x, cp_calcMulti.x , EPSILON); + assertEquals(" Estes Alpha III with multiple empty stages cp y value is incorrect:", cp_calcRef.y, cp_calcMulti.y , EPSILON); + assertEquals(" Estes Alpha III with multiple empty stages cp z value is incorrect:", cp_calcRef.z, cp_calcMulti.z , EPSILON); + } } From cf7238c1283db88fe3e88e458c5bdf1db5ddaa69 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Fri, 23 Sep 2022 19:51:53 +0200 Subject: [PATCH 7/7] Add empty stage unit tests for mass calc --- .../masscalc/MassCalculatorTest.java | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java index abbef4482..07ee2fdbb 100644 --- a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java +++ b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java @@ -23,6 +23,29 @@ public class MassCalculatorTest extends BaseTestCase { // tolerance for compared double test results private static final double EPSILON = 0.00000001; // note: this precision matches MathUtil.java + @Test + public void testEmptyRocket() { + Rocket rocket = new Rocket(); + FlightConfiguration config = rocket.getEmptyConfiguration(); + + final RigidBody actualStructure = MassCalculator.calculateStructure(config); + final double actualRocketDryMass = actualStructure.cm.weight; + final Coordinate actualRocketDryCM = actualStructure.cm; + + assertEquals(" Empty Rocket Empty Mass is incorrect: ", 0, actualRocketDryMass, 0); + + Coordinate expCM = new Coordinate(0, 0, 0, 0); + assertEquals("Empty Rocket CM.x is incorrect: ", expCM.x, actualRocketDryCM.x, 0); + assertEquals("Empty Rocket CM.y is incorrect: ", expCM.y, actualRocketDryCM.y, 0); + assertEquals("Empty Rocket CM.z is incorrect: ", expCM.z, actualRocketDryCM.z, 0); + assertEquals("Empty Rocket CM is incorrect: ", expCM, actualRocketDryCM); + + double actualMOIrot = actualStructure.getRotationalInertia(); + double actualMOIlong = actualStructure.getLongitudinalInertia(); + assertEquals("Empty Rocket Rotational MOI calculated incorrectly: ", 0, actualMOIrot, 0); + assertEquals("Empty Rocket Longitudinal MOI calculated incorrectly: ", 0, actualMOIlong, 0); + } + @Test public void testAlphaIIIStructure() { Rocket rocket = TestRockets.makeEstesAlphaIII(); @@ -1144,5 +1167,69 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals(" Booster Launch CM is incorrect: ", expCM, structure.getCM()); } + + @Test + public void testEmptyStages() { + Rocket rocketRef = TestRockets.makeEstesAlphaIII(); // Reference rocket + FlightConfiguration configRef = rocketRef.getEmptyConfiguration(); + configRef.setAllStages(); + + final RigidBody structureRef = MassCalculator.calculateStructure(configRef); + final double rocketDryMassRef = structureRef.cm.weight; + final Coordinate rocketDryCMRef = structureRef.cm; + + Rocket rocket = TestRockets.makeEstesAlphaIII(); + AxialStage stage1 = new AxialStage(); // To be added to the front of the rocket + AxialStage stage2 = new AxialStage(); // To be added to the rear of the rocket + rocket.addChild(stage1, 0); + rocket.addChild(stage2); + FlightConfiguration config = rocket.getEmptyConfiguration(); + config.setAllStages(); + + final RigidBody structure = MassCalculator.calculateStructure(config); + final double rocketDryMass = structure.cm.weight; + final Coordinate rocketDryCM = structure.cm; + + assertEquals(" Empty Stages Rocket Empty Mass is incorrect: ", rocketDryMassRef, rocketDryMass, EPSILON); + + assertEquals("Empty Stages Rocket CM.x is incorrect: ", rocketDryCMRef.x, rocketDryCM.x, EPSILON); + assertEquals("Empty Stages Rocket CM.y is incorrect: ", rocketDryCMRef.y, rocketDryCM.y, EPSILON); + assertEquals("Empty Stages Rocket CM.z is incorrect: ", rocketDryCMRef.z, rocketDryCM.z, EPSILON); + assertEquals("Empty Stages Rocket CM is incorrect: ", rocketDryCMRef, rocketDryCM); + + double MOIrotRef = structureRef.getRotationalInertia(); + double MOIlongRef = structureRef.getLongitudinalInertia(); + + double MOIrot = structure.getRotationalInertia(); + double MOIlong = structure.getLongitudinalInertia(); + assertEquals("Empty Stages Rocket Rotational MOI calculated incorrectly: ", MOIrotRef, MOIrot, EPSILON); + assertEquals("Empty Stages Rocket Longitudinal MOI calculated incorrectly: ", MOIlongRef, MOIlong, EPSILON); + + // if we use a mass override, setting to same mass, we should get same result + AxialStage sustainerRef = (AxialStage) rocketRef.getChild(0); + sustainerRef.setSubcomponentsOverridden(true); + sustainerRef.setMassOverridden(true); + sustainerRef.setOverrideMass(rocketDryMassRef); + + AxialStage sustainer = (AxialStage) rocket.getChild(0); + sustainer.setSubcomponentsOverridden(true); + sustainer.setMassOverridden(true); + sustainer.setOverrideMass(rocketDryMass); + + final RigidBody overrideStructureRef = MassCalculator.calculateStructure(configRef); + final Coordinate overrideRocketDryCMRef = overrideStructureRef.cm; + + final RigidBody overrideStructure = MassCalculator.calculateStructure(config); + final Coordinate overrideRocketDryCM = overrideStructure.cm; + + assertEquals("Empty Stages Rocket Override CM is incorrect: ", overrideRocketDryCMRef, overrideRocketDryCM); + + double overrideMOIrotRef = overrideStructureRef.getRotationalInertia(); + double overrideMOIlongRef = overrideStructureRef.getLongitudinalInertia(); + double overrideMOIrot = overrideStructure.getRotationalInertia(); + double overrideMOIlong = overrideStructure.getLongitudinalInertia(); + assertEquals("Empty Stages Rocket Rotational MOI calculated incorrectly: ", overrideMOIrotRef, overrideMOIrot, EPSILON); + assertEquals("Empty Stages Rocket Longitudinal MOI calculated incorrectly: ", overrideMOIlongRef, overrideMOIlong, EPSILON); + } }