[fix] MassCalculator now accounts for rotation in Component Assembly Instance Trees

This commit is contained in:
Daniel_M_Williams 2020-05-03 12:10:08 -04:00
parent 91652e7e4f
commit cce900d408
2 changed files with 459 additions and 395 deletions

View File

@ -266,18 +266,25 @@ public class MassCalculation {
final RocketComponent component = this.root; final RocketComponent component = this.root;
final Transformation parentTransform = this.transform; final Transformation parentTransform = this.transform;
final int instanceCount = component.getInstanceCount(); final int instanceCount = component.getInstanceCount();
Coordinate[] instanceLocations = component.getInstanceLocations(); final Coordinate[] allInstanceOffsets = component.getInstanceLocations();
final double[] allInstanceAngles = component.getInstanceAngles();
// // vvv DEBUG // // vvv DEBUG
// if( this.config.isComponentActive(component) ){ // if( this.config.isComponentActive(component) ){
// System.err.println(String.format( "%s[%s]....", prefix, component.getName())); // System.err.println(String.format( "%s>>[%s]....", prefix, component.getName()));
// } // }
// iterate over the aggregated instances for the whole tree. // iterate over the aggregated instances for the whole tree.
MassCalculation children = this.copy(component, parentTransform ); MassCalculation children = this.copy(component, parentTransform );
for( int instanceNumber = 0; instanceNumber < instanceCount; ++instanceNumber) { for( int currentInstanceNumber = 0; currentInstanceNumber < instanceCount; ++currentInstanceNumber) {
Coordinate currentLocation = instanceLocations[instanceNumber]; final Coordinate currentInstanceOffset = allInstanceOffsets[currentInstanceNumber];
Transformation currentTransform = parentTransform.applyTransformation( Transformation.getTranslationTransform( currentLocation )); final Transformation offsetTransform = Transformation.getTranslationTransform( currentInstanceOffset );
final double currentInstanceAngle = allInstanceAngles[currentInstanceNumber];
final Transformation angleTransform = Transformation.getAxialRotation(currentInstanceAngle);
final Transformation currentTransform = parentTransform.applyTransformation(offsetTransform)
.applyTransformation(angleTransform);
for (RocketComponent child : component.getChildren()) { for (RocketComponent child : component.getChildren()) {
// child data, relative to rocket reference frame // child data, relative to rocket reference frame
@ -302,8 +309,9 @@ public class MassCalculation {
if (!component.getOverrideSubcomponents()) { if (!component.getOverrideSubcomponents()) {
if (component.isMassOverridden()) { if (component.isMassOverridden()) {
// System.err.println("mass override=" + component.getOverrideMass());
compCM = compCM.setWeight(MathUtil.max(component.getOverrideMass(), MIN_MASS)); 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()) if (component.isCGOverridden())
compCM = compCM.setXYZ(component.getOverrideCG()); compCM = compCM.setXYZ(component.getOverrideCG());
@ -318,7 +326,7 @@ public class MassCalculation {
RigidBody componentInertia = new RigidBody( compCM, compIx, compIt, compIt ); RigidBody componentInertia = new RigidBody( compCM, compIx, compIt, compIt );
this.addInertia( componentInertia ); this.addInertia( componentInertia );
// vvv DEBUG // // vvv DEBUG
// if( 0 < compCM.weight ) { // if( 0 < compCM.weight ) {
// System.err.println(String.format( "%s....componentData: %s", prefix, compCM.toPreciseString() )); // System.err.println(String.format( "%s....componentData: %s", prefix, compCM.toPreciseString() ));
// } // }
@ -339,7 +347,7 @@ public class MassCalculation {
// // vvv DEBUG // // vvv DEBUG
// if( this.config.isComponentActive(component) && 0 < this.getMass() ) { // if( this.config.isComponentActive(component) && 0 < this.getMass() ) {
// System.err.println(String.format( "%s....<< return assemblyData: %s (tree @%s)", prefix, this.toCMDebug(), component.getName() )); // System.err.println(String.format( "%s....<< return data @ %s: %s", prefix, component.getName(), this.toCMDebug() ));
// } // }
// // ^^^ DEBUG // // ^^^ DEBUG

View File

@ -1,24 +1,14 @@
package net.sf.openrocket.masscalc; package net.sf.openrocket.masscalc;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import net.sf.openrocket.rocketcomponent.*;
import net.sf.openrocket.rocketcomponent.position.AngleMethod;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
import org.junit.Test; import org.junit.Test;
import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.BodyComponent;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
import net.sf.openrocket.rocketcomponent.InnerTube;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.Parachute;
import net.sf.openrocket.rocketcomponent.ParallelStage;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.ShockCord;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.simulation.SimulationConditions; import net.sf.openrocket.simulation.SimulationConditions;
import net.sf.openrocket.simulation.SimulationStatus; import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Coordinate;
@ -28,7 +18,7 @@ import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
public class MassCalculatorTest extends BaseTestCase { public class MassCalculatorTest extends BaseTestCase {
// tolerance for compared double test results // tolerance for compared double test results
private static final double EPSILON = 0.000001; private static final double EPSILON = 0.00000001; // note: this precision matches MathUtil.java
@Test @Test
public void testAlphaIIIStructure() { public void testAlphaIIIStructure() {
@ -37,6 +27,7 @@ public class MassCalculatorTest extends BaseTestCase {
rocket.setName("AlphaIII." + Thread.currentThread().getStackTrace()[1].getMethodName()); rocket.setName("AlphaIII." + Thread.currentThread().getStackTrace()[1].getMethodName());
FlightConfiguration config = rocket.getEmptyConfiguration(); FlightConfiguration config = rocket.getEmptyConfiguration();
config.setAllStages(); config.setAllStages();
final RigidBody actualStructure = MassCalculator.calculateStructure(config); final RigidBody actualStructure = MassCalculator.calculateStructure(config);
@ -54,8 +45,8 @@ public class MassCalculatorTest extends BaseTestCase {
assertEquals("Simple Rocket CM is incorrect: ", expCM, actualRocketDryCM); assertEquals("Simple Rocket CM is incorrect: ", expCM, actualRocketDryCM);
double expMOIrot = 5.441190348849436E-5; double expMOIrot = 5.394503232245634E-5;
double expMOIlong = 3.03467093714368E-4; double expMOIlong = 3.029224106873237E-4;
double actualMOIrot = actualStructure.getRotationalInertia(); double actualMOIrot = actualStructure.getRotationalInertia();
double actualMOIlong = actualStructure.getLongitudinalInertia(); double actualMOIlong = actualStructure.getLongitudinalInertia();
@ -153,13 +144,15 @@ public class MassCalculatorTest extends BaseTestCase {
RigidBody actualMotorData = MassCalculator.calculateMotor(status); RigidBody actualMotorData = MassCalculator.calculateMotor(status);
double expMass = activeMotor.getTotalMass(simTime); double expMass = activeMotor.getTotalMass(simTime);
assertEquals(" Motor Mass " + desig + " is incorrect: ", expMass, actualMotorData.getMass(), EPSILON); assertEquals(" Motor Mass " + desig + " is incorrect: ", expMass, actualMotorData.getMass(), EPSILON);
}{ }
{
final double simTime = 1.03; // middle final double simTime = 1.03; // middle
status.setSimulationTime(simTime); status.setSimulationTime(simTime);
RigidBody actualMotorData = MassCalculator.calculateMotor(status); RigidBody actualMotorData = MassCalculator.calculateMotor(status);
double expMass = activeMotor.getTotalMass(simTime); double expMass = activeMotor.getTotalMass(simTime);
assertEquals(" Motor Mass " + desig + " is incorrect: ", expMass, actualMotorData.getMass(), EPSILON); assertEquals(" Motor Mass " + desig + " is incorrect: ", expMass, actualMotorData.getMass(), EPSILON);
}{ }
{
final double simTime = 2.03; // after burnout final double simTime = 2.03; // after burnout
status.setSimulationTime(simTime); status.setSimulationTime(simTime);
RigidBody actualMotorData = MassCalculator.calculateMotor(status); RigidBody actualMotorData = MassCalculator.calculateMotor(status);
@ -473,8 +466,8 @@ public class MassCalculatorTest extends BaseTestCase {
final RigidBody actualStructureData = MassCalculator.calculateStructure(config); final RigidBody actualStructureData = MassCalculator.calculateStructure(config);
final Coordinate actualCM = actualStructureData.cm; final Coordinate actualCM = actualStructureData.cm;
double expMass = 0.116287; double expMass = 0.1162869499;
double expCMx = 0.278070785749; double expCMx = 0.2780707857;
assertEquals("Upper Stage Mass is incorrect: ", expMass, actualCM.weight, EPSILON); assertEquals("Upper Stage Mass is incorrect: ", expMass, actualCM.weight, EPSILON);
assertEquals("Upper Stage CM.x is incorrect: ", expCMx, actualCM.x, EPSILON); assertEquals("Upper Stage CM.x is incorrect: ", expCMx, actualCM.x, EPSILON);
@ -849,4 +842,67 @@ public class MassCalculatorTest extends BaseTestCase {
} }
@Test
public void testSimplePhantomPodRocket() {
Rocket rocket = new Rocket();
rocket.setName("Test Phantom Pods");
rocket.createFlightConfiguration(TestRockets.TEST_FCID_0);
rocket.setSelectedConfiguration(TestRockets.TEST_FCID_0);
final FlightConfiguration config = rocket.getSelectedConfiguration();
final AxialStage stage = new AxialStage();
stage.setName("Primary Stage");
rocket.addChild(stage);
final BodyTube primaryBody = new BodyTube(0.4, 0.02);
primaryBody.setThickness(0.001);
primaryBody.setName("Primary Body");
stage.addChild(primaryBody);
final PodSet pods = new PodSet();
pods.setName("Pods");
pods.setInstanceCount(2);
primaryBody.addChild(pods);
pods.setAxialMethod(AxialMethod.BOTTOM); pods.setAxialOffset(0.0);
pods.setAngleMethod(AngleMethod.RELATIVE); pods.setAngleOffset(0.0);
pods.setRadiusMethod(RadiusMethod.FREE); pods.setRadiusOffset(0.04);
final BodyTube podBody = new BodyTube(0.0, 0.02);
podBody.setThickness(0.001);
podBody.setName("Primary Body");
pods.addChild(podBody);
final TrapezoidFinSet fins = new TrapezoidFinSet(1, 0.05, 0.05, 0.0, 0.001);
fins.setName("podFins");
fins.setThickness(0.01);
fins.setMassOverridden(true); fins.setOverrideMass(0.02835);
fins.setOverrideSubcomponents(false);
fins.setAxialOffset(-0.01); fins.setAxialMethod(AxialMethod.BOTTOM);
podBody.addChild(fins);
rocket.enableEvents();
config.setAllStages();
// =======================================================================
// DEBUG
System.err.println(rocket.toDebugTree());
RigidBody structure = MassCalculator.calculateStructure(config);
final double expMass = 0.0900260149;
double calcTotalMass = structure.getMass();
assertEquals(" Booster Launch Mass is incorrect: ", expMass, calcTotalMass, EPSILON);
final double expCMx = 0.3039199615;
final double expCMy = 0.0;
final double expCMz = 0.0;
Coordinate expCM = new Coordinate(expCMx, expCMy, expCMz, 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);
assertEquals(" Booster Launch CM.z is incorrect: ", expCM.z, structure.getCM().z, EPSILON);
assertEquals(" Booster Launch CM is incorrect: ", expCM, structure.getCM());
}
} }