[fix] MassCalculator now accounts for rotation in Component Assembly Instance Trees
This commit is contained in:
parent
91652e7e4f
commit
cce900d408
@ -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
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user