diff --git a/core/src/net/sf/openrocket/masscalc/MassCalculator.java b/core/src/net/sf/openrocket/masscalc/MassCalculator.java index 4c2206e35..405f171aa 100644 --- a/core/src/net/sf/openrocket/masscalc/MassCalculator.java +++ b/core/src/net/sf/openrocket/masscalc/MassCalculator.java @@ -65,8 +65,9 @@ public class MassCalculator implements Monitorable { // private MassData launchData = null; // private Vector< MassData> motorData = new Vector(); - // unless in active development, this should be set to false. - //public boolean debug = false; + // this turns on copious amounts of debug. Recommend leaving this false + // until reaching code that causes interesting conditions. + public boolean debug = false; ////////////////// Constructors /////////////////// public MassCalculator() { @@ -301,17 +302,13 @@ public class MassCalculator implements Monitorable { private void calculateStageCache(FlightConfiguration config) { int stageCount = config.getActiveStageCount(); -// if(debug){ -// System.err.println(">> Calculating CG cache for config: "+config.toShort()+" with "+stageCount+" stages"); -// } + if(debug){ + System.err.println(">> Calculating massData cache for config: "+config.toShort()+" with "+stageCount+" stages"); + } if( 0 < stageCount ){ for( AxialStage curStage : config.getActiveStages()){ int index = curStage.getStageNumber(); MassData stageData = calculateAssemblyMassData( curStage); - if( curStage instanceof BoosterSet ){ - // hacky correction for the fact Booster Stages aren't direct subchildren to the rocket - stageData = stageData.move( curStage.getParent().getOffset() ); - } cache.put(index, stageData); } } @@ -325,27 +322,25 @@ public class MassCalculator implements Monitorable { * of the specified component, not global coordinates. */ private MassData calculateAssemblyMassData(RocketComponent component) { -// return calculateAssemblyMassData(component, "...."); -// } -// -// private MassData calculateAssemblyMassData(RocketComponent component, String indent) { + return calculateAssemblyMassData(component, "...."); + } + + private MassData calculateAssemblyMassData(RocketComponent component, String indent) { Coordinate parentCM = component.getComponentCG(); double parentIx = component.getRotationalUnitInertia() * parentCM.weight; double parentIt = component.getLongitudinalUnitInertia() * parentCM.weight; MassData parentData = new MassData( parentCM, parentIx, parentIt); -// if(( debug) &&( 0 < component.getChildCount()) && (MIN_MASS < parentCM.weight)){ -// //System.err.println(String.format("%-32s: %s ",indent+">>["+ component.getName()+"]", parentData.toCMDebug() )); -// System.err.println(String.format("%-32s: %s ",indent+">>["+ component.getName()+"]", parentData.toDebug() )); -// } - if (!component.getOverrideSubcomponents()) { if (component.isMassOverridden()) parentCM = parentCM.setWeight(MathUtil.max(component.getOverrideMass(), MIN_MASS)); if (component.isCGOverridden()) parentCM = parentCM.setXYZ(component.getOverrideCG()); } + if(( debug) &&( 0 < component.getChildCount()) && (MIN_MASS < parentCM.weight)){ + System.err.println(String.format("%-32s: %s ",indent+">>["+ component.getName()+"]", parentData.toDebug() )); + } MassData childrenData = MassData.ZERO_DATA; // Combine data for subcomponents @@ -356,7 +351,7 @@ public class MassCalculator implements Monitorable { } // child data, relative to parent's reference frame - MassData childData = calculateAssemblyMassData(child);//, indent+"...."); + MassData childData = calculateAssemblyMassData(child, indent+"...."); childrenData = childrenData.add( childData ); } @@ -400,35 +395,47 @@ public class MassCalculator implements Monitorable { // combine the parent's and children's data resultantData = parentData.add( childrenData); - - // Override total data -// if (component.getOverrideSubcomponents()) { -// if (component.isMassOverridden()) { -// double oldMass = parentCM.weight; -// double newMass = MathUtil.max(component.getOverrideMass(), MIN_MASS); -// longitudinalInertia = longitudinalInertia * newMass / oldMass; -// rotationalInertia = rotationalInertia * newMass / oldMass; -// parentCM = parentCM.setWeight(newMass); -// } -// if (component.isCGOverridden()) { -// double oldx = parentCM.x; -// double newx = component.getOverrideCGX(); -// longitudinalInertia += parentCM.weight * MathUtil.pow2(oldx - newx); -// parentCM = parentCM.setX(newx); -// } -// } - -// if( debug){ -// //System.err.println(String.format("%-32s: %s ", indent+"<<["+component.getName()+"][asbly]", resultantData.toCMDebug())); -// System.err.println(String.format("%-32s: %s ", indent+"@@["+component.getName()+"][asbly]", resultantData.toDebug())); -// } -// + if( debug){ + System.err.println(String.format("%-32s: %s ", indent+"<==>["+component.getName()+"][asbly]", resultantData.toDebug())); + } + // move to parent's reference point resultantData = resultantData.move( component.getOffset() ); -// if( debug){ -// //System.err.println(String.format("%-32s: %s ", indent+"<<["+component.getName()+"][asbly]", resultantData.toCMDebug())); -// System.err.println(String.format("%-32s: %s ", indent+"<<["+component.getName()+"][asbly]", resultantData.toDebug())); -// } + if( component instanceof BoosterSet ){ + // hacky correction for the fact Booster Stages aren't direct subchildren to the rocket + resultantData = resultantData.move( component.getParent().getOffset() ); + } + + // Override total data + if (component.getOverrideSubcomponents()) { + if( debug){ + System.err.println(String.format("%-32s: %s ", indent+"vv["+component.getName()+"][asbly]", resultantData.toDebug())); + } + if (component.isMassOverridden()) { + double oldMass = resultantData.getMass(); + double newMass = MathUtil.max(component.getOverrideMass(), MIN_MASS); + Coordinate newCM = resultantData.getCM().setWeight(newMass); + + double newIxx = resultantData.getIxx() * newMass / oldMass; + double newIyy = resultantData.getIyy() * newMass / oldMass; + double newIzz = resultantData.getIzz() * newMass / oldMass; + + resultantData = new MassData( newCM, newIxx, newIyy, newIzz ); + } + if (component.isCGOverridden()) { + double oldx = resultantData.getCM().x; + double newx = component.getOverrideCGX(); + Coordinate delta = new Coordinate(newx-oldx, 0, 0); + if(debug){ + System.err.println(String.format("%-32s: x: %g => %g (%g)", indent+" 88", oldx, newx, delta.x)); + } + resultantData = resultantData.move( delta ); + } + } + + if( debug){ + System.err.println(String.format("%-32s: %s ", indent+"<<["+component.getName()+"][asbly]", resultantData.toDebug())); + } return resultantData; diff --git a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java index 4a612901d..1dd1684f1 100644 --- a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java +++ b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java @@ -389,4 +389,101 @@ public class MassCalculatorTest extends BaseTestCase { assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); } + + @Test + public void testMassOverride() { + Rocket rocket = TestRockets.makeFalcon9Heavy(); + FlightConfiguration config = rocket.getDefaultConfiguration(); + rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName()); + + BoosterSet boosters = (BoosterSet) rocket.getChild(1).getChild(1); + int boostNum = boosters.getStageNumber(); + config.setAllStages(false); + config.setOnlyStage( boostNum); + +// String treeDump = rocket.toDebugTree(); +// System.err.println( treeDump); + + double overrideMass = 0.5; + boosters.setMassOverridden(true); + boosters.setOverrideMass(overrideMass); + + { + // Validate Mass + MassCalculator mc = new MassCalculator(); + //mc.debug = true; + Coordinate boosterSetCM = mc.getCM( rocket.getDefaultConfiguration(), MassCalcType.NO_MOTORS); + double calcTotalMass = boosterSetCM.weight; + + double expTotalMass = overrideMass; + assertEquals(" Booster Launch Mass is incorrect: ", expTotalMass, calcTotalMass, EPSILON); + + double expCMx = 0.9615865040919498; + 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); + assertEquals(" Booster Launch CM.z is incorrect: ", expCM.z, boosterSetCM.z, EPSILON); + assertEquals(" Booster Launch CM is incorrect: ", expCM, boosterSetCM); + + // Validate MOI + double oldMass = 0.23590802751203407; + double scaleMass = overrideMass / oldMass; + //mc.debug = true; + double expMOI_axial = .00144619 * scaleMass; + double boosterMOI_xx= mc.getRotationalInertia( config, MassCalcType.NO_MOTORS); + assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON); + + double expMOI_tr = 0.01845152840733412 * scaleMass; + double boosterMOI_tr= mc.getLongitudinalInertia( config, MassCalcType.NO_MOTORS); + assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); + } + + } + + + @Test + public void testCMOverride() { + Rocket rocket = TestRockets.makeFalcon9Heavy(); + FlightConfiguration config = rocket.getDefaultConfiguration(); + rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName()); + + BoosterSet boosters = (BoosterSet) rocket.getChild(1).getChild(1); + int boostNum = boosters.getStageNumber(); + config.setAllStages(false); + config.setOnlyStage( boostNum); + + //String treeDump = rocket.toDebugTree(); + //System.err.println( treeDump); + + double overrideCMx = 0.5; + boosters.setCGOverridden(true); + boosters.setOverrideCGX(overrideCMx); // only allows x-axis corrections + { + // Validate Mass + MassCalculator mc = new MassCalculator(); + //mc.debug = true; + Coordinate boosterSetCM = mc.getCM( rocket.getDefaultConfiguration(), MassCalcType.NO_MOTORS); + + double expMass = 0.23590802751203407; + double calcTotalMass = boosterSetCM.weight; + assertEquals(" Booster Launch Mass is incorrect: ", expMass, calcTotalMass, EPSILON); + + double expCMx = overrideCMx; //0.9615865040919498; + Coordinate expCM = new Coordinate( expCMx, 0, 0, expMass); + assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, boosterSetCM.x, EPSILON); + assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, boosterSetCM.y, EPSILON); + assertEquals(" Booster Launch CM.z is incorrect: ", expCM.z, boosterSetCM.z, EPSILON); + assertEquals(" Booster Launch CM is incorrect: ", expCM, boosterSetCM); + + // Validate MOI + double expMOI_axial = .00144619 ; + double boosterMOI_xx= mc.getRotationalInertia( config, MassCalcType.NO_MOTORS); + assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON); + + double expMOI_tr = 0.01845152840733412 ; + double boosterMOI_tr= mc.getLongitudinalInertia( config, MassCalcType.NO_MOTORS); + assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON); + } + + } }