[Bugfix] Verify / Fix Mass Calculation Code
- MassCalculator class: Debug toggle will print debug-level output to stderr fixed / reimplemented: getCG(...) -> getCM( FlightConfiguration, MassCalcType); -> getRotationalInertia( FlightConfiguration, MassCalcType); -> getLongitudinalInertia( FlightConfiguration, MassCalcType); - MassData class: can now add MassData classes Instanced componets w/children: take into account component mass... propellantMass field is vague: no indication whether it's include in the inertia or not. longitudinalInertia => rollMOI (?) rotationalInertia => yaw / pitch MOI (?) assorted other fixes - added unit test classes: ... .masscalc.MassCalculatorTest; ... .masscalc.MassDataTest;
This commit is contained in:
parent
cac0a52990
commit
4c7c3be9f7
@ -1,22 +1,24 @@
|
||||
package net.sf.openrocket.masscalc;
|
||||
|
||||
import static net.sf.openrocket.util.MathUtil.pow2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.MotorInstance;
|
||||
import net.sf.openrocket.motor.MotorInstanceConfiguration;
|
||||
import net.sf.openrocket.motor.MotorInstanceId;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.rocketcomponent.AxialStage;
|
||||
import net.sf.openrocket.rocketcomponent.BoosterSet;
|
||||
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.Instanceable;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.simulation.MassData;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.Monitorable;
|
||||
@ -46,22 +48,29 @@ public class MassCalculator implements Monitorable {
|
||||
public abstract Coordinate getCG(Motor motor);
|
||||
}
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MassCalculator.class);
|
||||
|
||||
private static final double MIN_MASS = 0.001 * MathUtil.EPSILON;
|
||||
private static final Logger log = LoggerFactory.getLogger(MassCalculator.class);
|
||||
|
||||
private int rocketMassModID = -1;
|
||||
private int rocketTreeModID = -1;
|
||||
|
||||
|
||||
/*
|
||||
* Cached data. All CG data is in absolute coordinates. All moments of inertia
|
||||
* are relative to their respective CG.
|
||||
*/
|
||||
private Coordinate[] cgCache = null;
|
||||
private double longitudinalInertiaCache[] = null;
|
||||
private double rotationalInertiaCache[] = null;
|
||||
private HashMap< Integer, MassData> cache = new HashMap<Integer, MassData >();
|
||||
// private MassData dryData = null;
|
||||
// private MassData launchData = null;
|
||||
// private Vector< MassData> motorData = new Vector<MassData>();
|
||||
|
||||
// unless in active development, this should be set to false.
|
||||
public boolean debug = false;
|
||||
|
||||
////////////////// Constructors ///////////////////
|
||||
public MassCalculator() {
|
||||
}
|
||||
|
||||
////////////////// Mass property calculations ///////////////////
|
||||
|
||||
@ -75,25 +84,43 @@ public class MassCalculator implements Monitorable {
|
||||
* @return the CG of the configuration
|
||||
*/
|
||||
public Coordinate getCG(FlightConfiguration configuration, MassCalcType type) {
|
||||
checkCache(configuration);
|
||||
calculateStageCache(configuration);
|
||||
|
||||
Coordinate dryCG = null;
|
||||
|
||||
// Stage contribution
|
||||
for (AxialStage stage : configuration.getActiveStages()) {
|
||||
int stageNumber = stage.getStageNumber();
|
||||
dryCG = cgCache[stageNumber].average(dryCG);
|
||||
return getCM( configuration, type);
|
||||
}
|
||||
|
||||
if (dryCG == null)
|
||||
dryCG = Coordinate.NUL;
|
||||
public Coordinate getCM(FlightConfiguration config, MassCalcType type) {
|
||||
checkCache(config);
|
||||
calculateStageCache(config);
|
||||
|
||||
MotorInstanceConfiguration motorConfig = new MotorInstanceConfiguration(configuration);
|
||||
Coordinate motorCG = getMotorCG(configuration, motorConfig, MassCalcType.LAUNCH_MASS);
|
||||
// Stage contribution
|
||||
Coordinate dryCM = Coordinate.ZERO;
|
||||
for (AxialStage stage : config.getActiveStages()) {
|
||||
Integer stageNumber = stage.getStageNumber();
|
||||
MassData stageData = cache.get( stageNumber);
|
||||
if( null == stageData ){
|
||||
throw new BugException("method: calculateStageCache(...) is faulty-- returned null data for an active stage: "+stage.getName()+"("+stage.getStageNumber()+")");
|
||||
}
|
||||
dryCM = stageData.cm.average(dryCM);
|
||||
if( debug){
|
||||
System.err.println(" stageData <<@"+stageNumber+"mass: "+dryCM.weight+" @"+dryCM.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Coordinate totalCG = dryCG.average(motorCG);
|
||||
return totalCG;
|
||||
Coordinate totalCM=null;
|
||||
if( MassCalcType.NO_MOTORS == type ){
|
||||
totalCM = dryCM;
|
||||
}else{
|
||||
MassData motorData = getMotorMassData(config, type);
|
||||
Coordinate motorCM = motorData.getCM();
|
||||
totalCM = dryCM.average(motorCM);
|
||||
}
|
||||
|
||||
if(debug){
|
||||
Coordinate cm = totalCM;
|
||||
System.err.println(String.format("==>> Combined Mass: %5.3gg @( %g, %g, %g)",
|
||||
cm.weight, cm.x, cm.y, cm.z ));
|
||||
}
|
||||
|
||||
return totalCM;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,128 +130,119 @@ public class MassCalculator implements Monitorable {
|
||||
* @param motors the motor configuration
|
||||
* @return the CG of the configuration
|
||||
*/
|
||||
public Coordinate getCG(FlightConfiguration configuration, MotorInstanceConfiguration motors) {
|
||||
checkCache(configuration);
|
||||
calculateStageCache(configuration);
|
||||
|
||||
Coordinate dryCG = getCG(configuration, MassCalcType.NO_MOTORS);
|
||||
Coordinate motorCG = getMotorCG(configuration, motors, MassCalcType.LAUNCH_MASS);
|
||||
|
||||
Coordinate totalCG = dryCG.average(motorCG);
|
||||
return totalCG;
|
||||
private MassData getMotorMassData(FlightConfiguration config, MassCalcType type) {
|
||||
if( MassCalcType.NO_MOTORS == type ){
|
||||
return MassData.ZERO_DATA;
|
||||
}
|
||||
|
||||
public Coordinate getMotorCG(FlightConfiguration config, MotorInstanceConfiguration motors, MassCalcType type) {
|
||||
Coordinate motorCG = Coordinate.ZERO;
|
||||
|
||||
// Add motor CGs
|
||||
if (motors != null) {
|
||||
for (MotorInstance inst : config.getActiveMotors()) {
|
||||
// DEVEL
|
||||
if(MotorInstanceId.EMPTY_ID == inst.getMotorID()){
|
||||
throw new IllegalArgumentException(" detected empty motor from <FlightConfiguration>.getActiveMotors()");
|
||||
|
||||
MassData motorData = MassData.ZERO_DATA;
|
||||
|
||||
// vvvv DEVEL vvvv
|
||||
if( debug){
|
||||
System.err.println("====== ====== getMotorCM: (type: "+type.name()+") ====== ====== ====== ====== ====== ======");
|
||||
System.err.println(" [Number] [Name] [mass]");
|
||||
}
|
||||
if( null == inst.getMount()){
|
||||
throw new NullPointerException(" detected null mount");
|
||||
}
|
||||
if( null == inst.getMotor()){
|
||||
throw new NullPointerException(" detected null motor");
|
||||
}
|
||||
// END DEVEL
|
||||
// ^^^^ DEVEL ^^^^
|
||||
|
||||
int motorCount = 0;
|
||||
for (MotorInstance inst : config.getActiveMotors() ) {
|
||||
//ThrustCurveMotor motor = (ThrustCurveMotor) inst.getMotor();
|
||||
|
||||
Coordinate position = inst.getPosition();
|
||||
Coordinate curCG = type.getCG(inst.getMotor()).add(position);
|
||||
motorCG = motorCG.average(curCG);
|
||||
Coordinate curMotorCM = type.getCG(inst.getMotor()).add(position);
|
||||
double Ir = inst.getRotationalInertia();
|
||||
double It = inst.getLongitudinalInertia();
|
||||
|
||||
MassData instData = new MassData( curMotorCM, Ir, It);
|
||||
motorData = motorData.add( instData );
|
||||
|
||||
// BEGIN DEVEL
|
||||
if( debug){
|
||||
System.err.println(String.format(" motor %2d: %s %s", //%5.3gg @( %g, %g, %g)",
|
||||
motorCount, inst.getMotor().getDesignation(), instData.toDebug()));
|
||||
System.err.println(String.format(" >> %s",
|
||||
motorData.toDebug()));
|
||||
}
|
||||
motorCount++;
|
||||
// END DEVEL
|
||||
}
|
||||
return motorCG;
|
||||
|
||||
return motorData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the longitudinal inertia of the rocket with the specified motor instance
|
||||
* configuration.
|
||||
*
|
||||
* @param configuration the current motor instance configuration
|
||||
* @param motors the motor configuration
|
||||
* @param config the current motor instance configuration
|
||||
* @param type the type of analysis to pull
|
||||
* @return the longitudinal inertia of the rocket
|
||||
*/
|
||||
public double getLongitudinalInertia(FlightConfiguration configuration, MotorInstanceConfiguration motors) {
|
||||
checkCache(configuration);
|
||||
calculateStageCache(configuration);
|
||||
public double getLongitudinalInertia(FlightConfiguration config, MassCalcType type) {
|
||||
checkCache(config);
|
||||
calculateStageCache(config);
|
||||
|
||||
final Coordinate totalCG = getCG(configuration, motors);
|
||||
double totalInertia = 0;
|
||||
MassData structureData = MassData.ZERO_DATA;
|
||||
|
||||
// Stages
|
||||
for (AxialStage stage : configuration.getActiveStages()) {
|
||||
for (AxialStage stage : config.getActiveStages()) {
|
||||
int stageNumber = stage.getStageNumber();
|
||||
Coordinate stageCG = cgCache[stageNumber];
|
||||
|
||||
totalInertia += (longitudinalInertiaCache[stageNumber] +
|
||||
stageCG.weight * MathUtil.pow2(stageCG.x - totalCG.x));
|
||||
MassData stageData = cache.get(stageNumber);
|
||||
structureData = structureData.add(stageData);
|
||||
}
|
||||
|
||||
MassData motorData = MassData.ZERO_DATA;
|
||||
if( MassCalcType.NO_MOTORS != type ){
|
||||
motorData = getMotorMassData(config, type);
|
||||
}
|
||||
|
||||
|
||||
// Motors
|
||||
if (motors != null) {
|
||||
for (MotorInstance motor : configuration.getActiveMotors()) {
|
||||
int stage = ((RocketComponent) motor.getMount()).getStageNumber();
|
||||
if (configuration.isStageActive(stage)) {
|
||||
Coordinate position = motor.getPosition();
|
||||
Coordinate cg = motor.getCG().add(position);
|
||||
MassData totalData = structureData.add( motorData);
|
||||
if(debug){
|
||||
System.err.println(String.format("==>> Combined MassData: %s", totalData.toDebug()));
|
||||
|
||||
double inertia = motor.getLongitudinalInertia();
|
||||
totalInertia += inertia + cg.weight * MathUtil.pow2(cg.x - totalCG.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return totalInertia;
|
||||
return totalData.getLongitudinalInertia();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute the rotational inertia of the provided configuration with specified motors.
|
||||
*
|
||||
* @param configuration the current motor instance configuration
|
||||
* @param motors the motor configuration
|
||||
* @param config the current motor instance configuration
|
||||
* @param type the type of analysis to get
|
||||
* @return the rotational inertia of the configuration
|
||||
*/
|
||||
public double getRotationalInertia(FlightConfiguration configuration, MotorInstanceConfiguration motors) {
|
||||
checkCache(configuration);
|
||||
calculateStageCache(configuration);
|
||||
public double getRotationalInertia(FlightConfiguration config, MassCalcType type) {
|
||||
checkCache(config);
|
||||
calculateStageCache(config);
|
||||
|
||||
final Coordinate totalCG = getCG(configuration, motors);
|
||||
double totalInertia = 0;
|
||||
MassData structureData = MassData.ZERO_DATA;
|
||||
|
||||
// Stages
|
||||
for (AxialStage stage : configuration.getActiveStages()) {
|
||||
for (AxialStage stage : config.getActiveStages()) {
|
||||
int stageNumber = stage.getStageNumber();
|
||||
Coordinate stageCG = cgCache[stageNumber];
|
||||
|
||||
totalInertia += (rotationalInertiaCache[stageNumber] +
|
||||
stageCG.weight * (MathUtil.pow2(stageCG.y - totalCG.y) +
|
||||
MathUtil.pow2(stageCG.z - totalCG.z)));
|
||||
MassData stageData = cache.get(stageNumber);
|
||||
structureData = structureData.add(stageData);
|
||||
}
|
||||
|
||||
|
||||
// Motors
|
||||
if (motors != null) {
|
||||
for (MotorInstance motor : configuration.getActiveMotors()) {
|
||||
int stage = ((RocketComponent) motor.getMount()).getStageNumber();
|
||||
if (configuration.isStageActive(stage)) {
|
||||
Coordinate position = motor.getPosition();
|
||||
Coordinate cg = motor.getCG().add(position);
|
||||
|
||||
double inertia = motor.getRotationalInertia();
|
||||
totalInertia += inertia + cg.weight * (MathUtil.pow2(cg.y - totalCG.y) +
|
||||
MathUtil.pow2(cg.z - totalCG.z));
|
||||
}
|
||||
}
|
||||
MassData motorData = MassData.ZERO_DATA;
|
||||
if( MassCalcType.NO_MOTORS != type ){
|
||||
motorData = getMotorMassData(config, type);
|
||||
}
|
||||
|
||||
return totalInertia;
|
||||
MassData totalData = structureData.add( motorData);
|
||||
if(debug){
|
||||
System.err.println(String.format("==>> Combined MassData: %s", totalData.toDebug()));
|
||||
|
||||
}
|
||||
|
||||
return totalData.getRotationalInertia();
|
||||
}
|
||||
|
||||
|
||||
@ -235,13 +253,14 @@ public class MassCalculator implements Monitorable {
|
||||
* @param configuration the current motor instance configuration
|
||||
* @return the total mass of all motors
|
||||
*/
|
||||
public double getPropellantMass(FlightConfiguration configuration, MotorInstanceConfiguration motors) {
|
||||
public double getPropellantMass(FlightConfiguration configuration, MassCalcType calcType ){
|
||||
double mass = 0;
|
||||
|
||||
//throw new BugException("getPropellantMass is not yet implemented.... ");
|
||||
// add up the masses of all motors in the rocket
|
||||
if (motors != null) {
|
||||
for (MotorInstance motor : configuration.getActiveMotors()) {
|
||||
mass = mass + motor.getCG().weight - motor.getMotor().getEmptyCG().weight;
|
||||
if ( MassCalcType.NO_MOTORS != calcType ){
|
||||
for (MotorInstance curInstance : configuration.getActiveMotors()) {
|
||||
mass = mass + curInstance.getCG().weight - curInstance.getMotor().getEmptyCG().weight;
|
||||
}
|
||||
}
|
||||
return mass;
|
||||
@ -281,25 +300,23 @@ public class MassCalculator implements Monitorable {
|
||||
//////// Cache computations ////////
|
||||
|
||||
private void calculateStageCache(FlightConfiguration config) {
|
||||
if (cgCache == null) {
|
||||
ArrayList<AxialStage> stageList = new ArrayList<AxialStage>();
|
||||
stageList.addAll(config.getRocket().getStageList());
|
||||
int stageCount = stageList.size();
|
||||
|
||||
cgCache = new Coordinate[stageCount];
|
||||
longitudinalInertiaCache = new double[stageCount];
|
||||
rotationalInertiaCache = new double[stageCount];
|
||||
|
||||
for (int i = 0; i < stageCount; i++) {
|
||||
RocketComponent stage = stageList.get(i);
|
||||
MassData data = calculateAssemblyMassData(stage);
|
||||
cgCache[i] = stage.toAbsolute(data.getCG())[0];
|
||||
longitudinalInertiaCache[i] = data.getLongitudinalInertia();
|
||||
rotationalInertiaCache[i] = data.getRotationalInertia();
|
||||
int stageCount = config.getActiveStageCount();
|
||||
if(debug){
|
||||
System.err.println(">> Calculating CG 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -307,86 +324,114 @@ public class MassCalculator implements Monitorable {
|
||||
* The inertia is returned relative to the CG, and the CG is in the coordinates
|
||||
* of the specified component, not global coordinates.
|
||||
*/
|
||||
private MassData calculateAssemblyMassData(RocketComponent parent) {
|
||||
Coordinate parentCG = Coordinate.ZERO;
|
||||
double longitudinalInertia = 0.0;
|
||||
double rotationalInertia = 0.0;
|
||||
|
||||
// Calculate data for this component
|
||||
parentCG = parent.getComponentCG();
|
||||
if (parentCG.weight < MIN_MASS)
|
||||
parentCG = parentCG.setWeight(MIN_MASS);
|
||||
|
||||
|
||||
// Override only this component's data
|
||||
if (!parent.getOverrideSubcomponents()) {
|
||||
if (parent.isMassOverridden())
|
||||
parentCG = parentCG.setWeight(MathUtil.max(parent.getOverrideMass(), MIN_MASS));
|
||||
if (parent.isCGOverridden())
|
||||
parentCG = parentCG.setXYZ(parent.getOverrideCG());
|
||||
private MassData calculateAssemblyMassData(RocketComponent component) {
|
||||
return calculateAssemblyMassData(component, "....");
|
||||
}
|
||||
|
||||
longitudinalInertia = parent.getLongitudinalUnitInertia() * parentCG.weight;
|
||||
rotationalInertia = parent.getRotationalUnitInertia() * parentCG.weight;
|
||||
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());
|
||||
}
|
||||
|
||||
MassData childrenData = MassData.ZERO_DATA;
|
||||
// Combine data for subcomponents
|
||||
for (RocketComponent sibling : parent.getChildren()) {
|
||||
Coordinate combinedCG;
|
||||
double dx2, dr2;
|
||||
|
||||
// Compute data of sibling
|
||||
MassData siblingData = calculateAssemblyMassData(sibling);
|
||||
Coordinate[] siblingCGs = sibling.toRelative(siblingData.getCG(), parent);
|
||||
|
||||
for (Coordinate siblingCG : siblingCGs) {
|
||||
|
||||
// Compute CG of this + sibling
|
||||
combinedCG = parentCG.average(siblingCG);
|
||||
|
||||
// Add effect of this CG change to parent inertia
|
||||
dx2 = pow2(parentCG.x - combinedCG.x);
|
||||
longitudinalInertia += parentCG.weight * dx2;
|
||||
|
||||
dr2 = pow2(parentCG.y - combinedCG.y) + pow2(parentCG.z - combinedCG.z);
|
||||
rotationalInertia += parentCG.weight * dr2;
|
||||
|
||||
|
||||
// Add inertia of sibling
|
||||
longitudinalInertia += siblingData.getLongitudinalInertia();
|
||||
rotationalInertia += siblingData.getRotationalInertia();
|
||||
|
||||
// Add effect of sibling CG change
|
||||
dx2 = pow2(siblingData.getCG().x - combinedCG.x);
|
||||
longitudinalInertia += siblingData.getCG().weight * dx2;
|
||||
|
||||
dr2 = pow2(siblingData.getCG().y - combinedCG.y) + pow2(siblingData.getCG().z - combinedCG.z);
|
||||
rotationalInertia += siblingData.getCG().weight * dr2;
|
||||
|
||||
// Set combined CG
|
||||
parentCG = combinedCG;
|
||||
for (RocketComponent child : component.getChildren()) {
|
||||
if( child instanceof BoosterSet ){
|
||||
// this stage will be tallied separately... skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
// child data, relative to parent's reference frame
|
||||
MassData childData = calculateAssemblyMassData(child, indent+"....");
|
||||
|
||||
childrenData = childrenData.add( childData );
|
||||
}
|
||||
|
||||
|
||||
MassData resultantData = parentData; // default if not instanced
|
||||
// compensate for component-instancing propogating to children's data
|
||||
int instanceCount = component.getInstanceCount();
|
||||
boolean hasChildren = ( 0 < component.getChildCount());
|
||||
if (( 1 < instanceCount )&&( hasChildren )){
|
||||
if(( debug )){
|
||||
System.err.println(String.format("%s Found instanceable with %d children: %s (t= %s)",
|
||||
indent, component.getInstanceCount(), component.getName(), component.getClass().getSimpleName() ));
|
||||
}
|
||||
|
||||
final double curIxx = childrenData.getIxx(); // MOI about x-axis
|
||||
final double curIyy = childrenData.getIyy(); // MOI about y axis
|
||||
final double curIzz = childrenData.getIzz(); // MOI about z axis
|
||||
|
||||
Coordinate eachCM = childrenData.cm;
|
||||
MassData instAccumData = new MassData(); // accumulator for instance MassData
|
||||
Coordinate[] instanceLocations = ((Instanceable) component).getInstanceOffsets();
|
||||
for( Coordinate curOffset : instanceLocations ){
|
||||
if( debug){
|
||||
//System.err.println(String.format("%-32s: %s", indent+" inst Accum", instAccumData.toCMDebug() ));
|
||||
System.err.println(String.format("%-32s: %s", indent+" inst Accum", instAccumData.toDebug() ));
|
||||
}
|
||||
|
||||
Coordinate instanceCM = curOffset.add(eachCM);
|
||||
|
||||
MassData instanceData = new MassData( instanceCM, curIxx, curIyy, curIzz);
|
||||
|
||||
// 3) Project the template data to the new CM
|
||||
// and add to the total
|
||||
instAccumData = instAccumData.add( instanceData);
|
||||
}
|
||||
|
||||
childrenData = instAccumData;
|
||||
}
|
||||
|
||||
// combine the parent's and children's data
|
||||
resultantData = parentData.add( childrenData);
|
||||
|
||||
|
||||
// Override total data
|
||||
if (parent.getOverrideSubcomponents()) {
|
||||
if (parent.isMassOverridden()) {
|
||||
double oldMass = parentCG.weight;
|
||||
double newMass = MathUtil.max(parent.getOverrideMass(), MIN_MASS);
|
||||
longitudinalInertia = longitudinalInertia * newMass / oldMass;
|
||||
rotationalInertia = rotationalInertia * newMass / oldMass;
|
||||
parentCG = parentCG.setWeight(newMass);
|
||||
}
|
||||
if (parent.isCGOverridden()) {
|
||||
double oldx = parentCG.x;
|
||||
double newx = parent.getOverrideCGX();
|
||||
longitudinalInertia += parentCG.weight * pow2(oldx - newx);
|
||||
parentCG = parentCG.setX(newx);
|
||||
}
|
||||
// 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()));
|
||||
// }
|
||||
//
|
||||
// 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()));
|
||||
}
|
||||
|
||||
MassData parentData = new MassData(parentCG, longitudinalInertia, rotationalInertia, 0);
|
||||
return parentData;
|
||||
|
||||
return resultantData;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -418,9 +463,7 @@ public class MassCalculator implements Monitorable {
|
||||
* its execution.
|
||||
*/
|
||||
protected void voidMassCache() {
|
||||
this.cgCache = null;
|
||||
this.longitudinalInertiaCache = null;
|
||||
this.rotationalInertiaCache = null;
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
|
||||
|
294
core/src/net/sf/openrocket/masscalc/MassData.java
Normal file
294
core/src/net/sf/openrocket/masscalc/MassData.java
Normal file
@ -0,0 +1,294 @@
|
||||
package net.sf.openrocket.masscalc;
|
||||
|
||||
import static net.sf.openrocket.util.MathUtil.pow2;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
|
||||
/**
|
||||
* An immutable value object containing the mass data of a component, assembly or entire rocket.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
* @author Daniel Williams <equipoise@gmail.com>
|
||||
*/
|
||||
public class MassData {
|
||||
private static final double MIN_MASS = MathUtil.EPSILON;
|
||||
|
||||
/* ASSUME: a MassData instance implicitly describes a bodies w.r.t. a reference point
|
||||
* a) the cm locates the body from the reference point
|
||||
* b) each MOI is about the reference point.
|
||||
*/
|
||||
public final Coordinate cm;
|
||||
|
||||
// Moment of Inertias:
|
||||
// x-axis: through the rocket's nose
|
||||
// y,z axes: mutually perpendicular to each other and the x-axis. Because a rocket
|
||||
// is (mostly) axisymmetric, y-axis and z-axis placement is arbitrary.
|
||||
|
||||
// MOI about the Center of Mass
|
||||
private final InertiaMatrix I_cm;
|
||||
|
||||
// implements a simplified, diagonal MOI
|
||||
private class InertiaMatrix {
|
||||
public final double xx;
|
||||
public final double yy;
|
||||
public final double zz;
|
||||
|
||||
public InertiaMatrix( double Ixx, double Iyy, double Izz){
|
||||
if(( 0 > Ixx)||( 0 > Iyy)||( 0 > Izz)){
|
||||
throw new BugException(" attempted to initialize an InertiaMatrix with a negative inertia value.");
|
||||
}
|
||||
this.xx = Ixx;
|
||||
this.yy = Iyy;
|
||||
this.zz = Izz;
|
||||
}
|
||||
|
||||
public InertiaMatrix add( InertiaMatrix other){
|
||||
return new InertiaMatrix( this.xx + other.xx, this.yy + other.yy, this.zz + other.zz);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns a <b>copy</b> of this MassData translated to a new location via
|
||||
* a simplified model.
|
||||
*
|
||||
* Assuming rotations are independent, and occur perpendicular to the principal axes,
|
||||
* The above can be simplified to produce a diagonal newMOI in
|
||||
* the form of the parallel axis theorem:
|
||||
* [ oldMOI + m*d^2, ...]
|
||||
*
|
||||
* For the full version of the equations, see:
|
||||
* [1] https://en.wikipedia.org/wiki/Parallel_axis_theorem#Tensor_generalization
|
||||
* [2] http://www.kwon3d.com/theory/moi/triten.html
|
||||
*
|
||||
*
|
||||
* @param delta vector position from center of mass to desired reference location
|
||||
*
|
||||
* @return MassData the new MassData instance
|
||||
*/
|
||||
private InertiaMatrix translateInertia( final Coordinate delta, final double mass){
|
||||
double x2 = pow2(delta.x);
|
||||
double y2 = pow2(delta.y);
|
||||
double z2 = pow2(delta.z);
|
||||
|
||||
// See: Parallel Axis Theorem in the function comments.
|
||||
// I = I + m L^2; L = sqrt( y^2 + z^2);
|
||||
// ergo: I = I + m (y^2 + z^2);
|
||||
double newIxx = this.xx + mass*(y2 + z2);
|
||||
double newIyy = this.yy + mass*(x2 + z2);
|
||||
double newIzz = this.zz + mass*(x2 + y2);
|
||||
|
||||
// MOI about the reference point
|
||||
InertiaMatrix toReturn=new InertiaMatrix( newIxx, newIyy, newIzz);
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (Double.doubleToLongBits(this.xx) ^ Double.doubleToLongBits(this.yy) ^ Double.doubleToLongBits(this.xx) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!(obj instanceof InertiaMatrix))
|
||||
return false;
|
||||
|
||||
InertiaMatrix other = (InertiaMatrix) obj;
|
||||
return (MathUtil.equals(this.xx, other.xx) && MathUtil.equals(this.yy, other.yy) &&
|
||||
MathUtil.equals(this.zz, other.zz)) ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static final MassData ZERO_DATA = new MassData(Coordinate.ZERO, 0, 0, 0);
|
||||
|
||||
public MassData() {
|
||||
this.cm = Coordinate.ZERO;
|
||||
this.I_cm = new InertiaMatrix(0,0,0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a new instance of MassData which is a sum of this and the other.
|
||||
*
|
||||
* @param childData second mass data to combine with this
|
||||
*
|
||||
* @return MassData the new MassData instance
|
||||
*/
|
||||
public MassData add(MassData body2){
|
||||
MassData body1 = this;
|
||||
|
||||
// the combined Center of mass is defined to be 'point 3'
|
||||
Coordinate combinedCM = body1.cm.average( body2.cm );
|
||||
|
||||
// transform InertiaMatrix from it's previous frame to the common frame
|
||||
Coordinate delta1 = combinedCM.sub( body1.cm).setWeight(0);
|
||||
InertiaMatrix I1_at_3 = body1.I_cm.translateInertia(delta1, body1.getMass());
|
||||
|
||||
// transform InertiaMatrix from it's previous frame to the common frame
|
||||
Coordinate delta2 = combinedCM.sub( body2.cm).setWeight(0);
|
||||
InertiaMatrix I2_at_3 = body2.I_cm.translateInertia(delta2, body2.getMass());
|
||||
|
||||
// once both are in the same frame, simply add them
|
||||
InertiaMatrix combinedMOI = I1_at_3.add(I2_at_3);
|
||||
MassData sumData = new MassData( combinedCM, combinedMOI);
|
||||
|
||||
{ // vvvv DEVEL vvvv
|
||||
// System.err.println(" ?? body1: "+ body1.toDebug() );
|
||||
// System.err.println(" delta 1=>3: "+ delta1);
|
||||
// System.err.println(String.format(" >> 1@3: == [ %g, %g, %g ]",
|
||||
// I1_at_3.xx, I1_at_3.yy, I1_at_3.zz));
|
||||
//
|
||||
// System.err.println(" ?? body2: "+ body2.toDebug() );
|
||||
// System.err.println(" delta 2=>3: "+ delta2);
|
||||
// System.err.println(String.format(" >> 2@3: [ %g, %g, %g ]",
|
||||
// I2_at_3.xx, I2_at_3.yy, I2_at_3.zz));
|
||||
// System.err.println(" ?? asbly3: "+sumData.toDebug()+"\n");
|
||||
|
||||
// InertiaMatrix rev1 = It1.translateInertia(delta1.multiply(-1), body1.getMass());
|
||||
// System.err.println(String.format(" !!XX orig: %s\n", childDataChild.toDebug() ));
|
||||
// System.err.println(String.format("%s!!XX revr: %s\n", indent, reverse.toDebug() ));
|
||||
}
|
||||
|
||||
return sumData;
|
||||
}
|
||||
|
||||
private MassData(Coordinate newCM, InertiaMatrix newMOI){
|
||||
this.cm = newCM;
|
||||
this.I_cm = newMOI;
|
||||
}
|
||||
|
||||
public MassData( RocketComponent component ){
|
||||
//MassData parentData = new MassData( parentCM, parentIx, parentIt);
|
||||
|
||||
// Calculate data for this component
|
||||
Coordinate newCM = component.getComponentCG();
|
||||
double mass = newCM.weight;
|
||||
if ( mass < MIN_MASS){
|
||||
newCM = newCM .setWeight(MIN_MASS);
|
||||
mass = MIN_MASS;
|
||||
}
|
||||
this.cm = newCM;
|
||||
double Ix = component.getRotationalUnitInertia() * mass;
|
||||
double It = component.getLongitudinalUnitInertia() * mass;
|
||||
this.I_cm = new InertiaMatrix( Ix, It, It);
|
||||
}
|
||||
|
||||
public MassData(Coordinate newCM, double newIxx, double newIyy, double newIzz){
|
||||
if (newCM == null) {
|
||||
throw new IllegalArgumentException("CM is null");
|
||||
}
|
||||
|
||||
this.cm = newCM;
|
||||
this.I_cm = new InertiaMatrix(newIxx, newIyy, newIzz);
|
||||
}
|
||||
|
||||
public MassData(final Coordinate cg, final double rotationalInertia, final double longitudinalInertia) {
|
||||
if (cg == null) {
|
||||
throw new IllegalArgumentException("cg is null");
|
||||
}
|
||||
this.cm = cg;
|
||||
double newIxx = rotationalInertia;
|
||||
double newIyy = longitudinalInertia;
|
||||
double newIzz = longitudinalInertia;
|
||||
|
||||
this.I_cm = new InertiaMatrix( newIxx, newIyy, newIzz);
|
||||
}
|
||||
|
||||
public double getMass(){
|
||||
return cm.weight;
|
||||
}
|
||||
|
||||
public Coordinate getCG() {
|
||||
return cm;
|
||||
}
|
||||
|
||||
public Coordinate getCM() {
|
||||
return cm;
|
||||
}
|
||||
|
||||
public double getIyy(){
|
||||
return I_cm.yy;
|
||||
}
|
||||
public double getLongitudinalInertia() {
|
||||
return I_cm.yy;
|
||||
}
|
||||
|
||||
public double getIxx(){
|
||||
return I_cm.xx;
|
||||
}
|
||||
public double getRotationalInertia() {
|
||||
return I_cm.xx;
|
||||
}
|
||||
|
||||
public double getIzz(){
|
||||
return I_cm.zz;
|
||||
}
|
||||
|
||||
public double getPropellantMass(){
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance of MassData moved by the delta vector supplied.
|
||||
* This function is intended to move the REFERENCE POINT, not the CM, and will leave
|
||||
* the Inertia matrix completely unchanged.
|
||||
*
|
||||
* ASSUME: MassData implicity describe their respective bodies w.r.t 0,0,0)
|
||||
* a) the cm locates the body from the reference point
|
||||
* b) each MOI is about the reference point.
|
||||
*
|
||||
* @param delta vector from current reference point to new/desired reference point (mass is ignored)
|
||||
*
|
||||
* @return MassData a new MassData instance, locating the same CM from a different reference point.
|
||||
*/
|
||||
public MassData move( final Coordinate delta ){
|
||||
MassData body1 = this;
|
||||
|
||||
// don't change the mass, just move it.
|
||||
Coordinate newCM = body1.cm.add( delta );
|
||||
|
||||
MassData newData = new MassData( newCM, this.I_cm);
|
||||
|
||||
{ // DEVEL-DEBUG
|
||||
// System.err.println(" ?? body1: "+ body1.toDebug() );
|
||||
// System.err.println(" delta: "+ delta);
|
||||
// System.err.println(" ?? asbly3: "+newData.toDebug()+"\n");
|
||||
}
|
||||
return newData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!(obj instanceof MassData))
|
||||
return false;
|
||||
|
||||
MassData other = (MassData) obj;
|
||||
return (this.cm.equals(other.cm) && (this.I_cm.equals(other.I_cm)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (cm.hashCode() ^ I_cm.hashCode() );
|
||||
}
|
||||
|
||||
public String toCMDebug(){
|
||||
return String.format("cm= %6.4fg@[%g,%g,%g]", cm.weight, cm.x, cm.y, cm.z);
|
||||
}
|
||||
|
||||
public String toDebug(){
|
||||
return toCMDebug()+" " +
|
||||
String.format("I_cm=[ %g, %g, %g ]", I_cm.xx, I_cm.yy, I_cm.zz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MassData [cg=" + cm + ", longitudinalInertia=" + getIyy()
|
||||
+ ", rotationalInertia=" + getIxx() + "]";
|
||||
}
|
||||
|
||||
}
|
@ -76,6 +76,10 @@ public class MotorInstance implements FlightConfigurableParameter<MotorInstance>
|
||||
throw new UnsupportedOperationException("Retrieve a mount from an immutable no-motors instance");
|
||||
}
|
||||
|
||||
public Coordinate getOffset(){
|
||||
return this.position;
|
||||
}
|
||||
|
||||
public Coordinate getPosition() {
|
||||
return this.position;
|
||||
}
|
||||
|
@ -49,6 +49,10 @@ public final class MotorInstanceId {
|
||||
return componentId;
|
||||
}
|
||||
|
||||
public int getInstanceNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
@ -2,6 +2,7 @@ package net.sf.openrocket.motor;
|
||||
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.Inertia;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
@ -64,6 +65,17 @@ public class ThrustCurveMotorInstance extends MotorInstance {
|
||||
return stepCG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate getOffset( ){
|
||||
if( null == mount ){
|
||||
return Coordinate.NaN;
|
||||
}else{
|
||||
RocketComponent comp = (RocketComponent) mount;
|
||||
double delta_x = comp.getLength() + mount.getMotorOverhang() - this.motor.getLength();
|
||||
return new Coordinate(delta_x, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLongitudinalInertia() {
|
||||
return unitLongitudinalInertia * stepCG.weight;
|
||||
@ -94,6 +106,7 @@ public class ThrustCurveMotorInstance extends MotorInstance {
|
||||
}
|
||||
|
||||
this.motor = (ThrustCurveMotor)motor;
|
||||
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
@ -115,6 +128,7 @@ public class ThrustCurveMotorInstance extends MotorInstance {
|
||||
@Override
|
||||
public void setMount(final MotorMount _mount) {
|
||||
this.mount = _mount;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -222,5 +236,10 @@ public class ThrustCurveMotorInstance extends MotorInstance {
|
||||
return clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return this.id.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -383,6 +383,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
|
||||
}
|
||||
}
|
||||
|
||||
this.isActingMount=true;
|
||||
this.motors.set(fcid,newMotorInstance);
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,11 @@ public class BoosterSet extends AxialStage implements FlightConfigurableComponen
|
||||
return this.radialPosition_m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate[] getInstanceOffsets(){
|
||||
return this.shiftCoordinates(new Coordinate[]{Coordinate.ZERO});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate[] getLocations() {
|
||||
if (null == this.parent) {
|
||||
@ -211,12 +216,12 @@ public class BoosterSet extends AxialStage implements FlightConfigurableComponen
|
||||
buffer.append(String.format("%s %-24s (stage: %d)", prefix, this.getName(), this.getStageNumber()));
|
||||
buffer.append(String.format(" (len: %5.3f offset: %4.1f via: %s )\n", this.getLength(), this.getAxialOffset(), this.relativePosition.name()));
|
||||
|
||||
Coordinate[] relCoords = this.shiftCoordinates(new Coordinate[] { Coordinate.ZERO });
|
||||
Coordinate[] relCoords = this.shiftCoordinates(new Coordinate[] { this.getOffset() });
|
||||
Coordinate[] absCoords = this.getLocations();
|
||||
for (int instanceNumber = 0; instanceNumber < this.count; instanceNumber++) {
|
||||
Coordinate instanceRelativePosition = relCoords[instanceNumber];
|
||||
Coordinate instanceAbsolutePosition = absCoords[instanceNumber];
|
||||
buffer.append(String.format("%s [instance %2d of %2d] %28s %28s\n", prefix, instanceNumber, count,
|
||||
buffer.append(String.format("%s [%2d/%2d]; %28s; %28s;\n", prefix, instanceNumber+1, count,
|
||||
instanceRelativePosition, instanceAbsolutePosition));
|
||||
}
|
||||
|
||||
|
@ -99,6 +99,19 @@ public class CenteringRing extends RadiusRingComponent implements LineInstanceab
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate[] getInstanceOffsets(){
|
||||
Coordinate[] toReturn = new Coordinate[this.getInstanceCount()];
|
||||
toReturn[0] = Coordinate.ZERO;
|
||||
|
||||
for ( int index=1 ; index < this.getInstanceCount(); index++){
|
||||
toReturn[index] = new Coordinate(index*this.instanceSeparation,0,0,0);
|
||||
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstanceCount(){
|
||||
return this.instanceCount;
|
||||
|
@ -12,6 +12,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.sf.openrocket.motor.MotorInstance;
|
||||
import net.sf.openrocket.motor.MotorInstanceId;
|
||||
import net.sf.openrocket.util.ArrayList;
|
||||
import net.sf.openrocket.util.ChangeSource;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
@ -180,36 +181,46 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
|
||||
|
||||
public List<MotorInstance> getActiveMotors() {
|
||||
|
||||
ArrayList<MotorInstance> toReturn = new ArrayList<MotorInstance>();
|
||||
for ( RocketComponent comp : this.getActiveComponents() ){
|
||||
|
||||
// see planning notes...
|
||||
if ( comp instanceof MotorMount ){
|
||||
MotorMount mount = (MotorMount)comp;
|
||||
MotorInstance inst = mount.getMotorInstance(this.fcid);
|
||||
|
||||
// NYI: if clustered...
|
||||
// if( mount instanceof Clusterable ){
|
||||
// if( 1 < comp.getInstanceCount() ){
|
||||
// if comp is clustered, it will be clustered from the innerTube, no?
|
||||
//List<MotorInstance> instanceList = mount.getMotorInstance(this.fcid);
|
||||
// this merely accounts for instancing of this component:
|
||||
// int instancCount = comp.getInstanceCount();
|
||||
|
||||
// // vvvv DEVEL vvvv
|
||||
//
|
||||
// if(( mount.isMotorMount()) && ( MotorInstance.EMPTY_INSTANCE == inst)){
|
||||
// if( mount instanceof BodyTube){
|
||||
// MotorInstance bt_inst = ((BodyTube)mount).getMotorInstance(this.fcid);
|
||||
// log.error("Detected EMPTY_INSTANCE in config: "+this.fcid.key.substring(0,8)+", mount: \""+comp.getName()+"\"");
|
||||
// ((BodyTube)mount).printMotorDebug();
|
||||
// }
|
||||
// continue;
|
||||
// }
|
||||
// // ^^^^ DEVEL ^^^^
|
||||
// we account for instances this way because it counts *all* the instancing between here
|
||||
// and the rocket root.
|
||||
Coordinate[] instanceLocations= comp.getLocations();
|
||||
|
||||
// motors go inactive after burnout, so include this filter too
|
||||
if (inst.isActive()){
|
||||
toReturn.add(inst);
|
||||
// System.err.println(String.format(",,,,,,,, : %s (%s)",
|
||||
// inst.getMotor().getDigest(), inst.getMotorID() ));
|
||||
int instanceNumber = 0;
|
||||
for ( Coordinate curMountLocation : instanceLocations ){
|
||||
MotorInstance curInstance = inst.clone();
|
||||
curInstance.setID( new MotorInstanceId( comp.getName(), instanceNumber+1) );
|
||||
|
||||
// 1) mount location
|
||||
// == curMountLocation
|
||||
|
||||
// 2) motor location w/in mount: parent.refpoint -> motor.refpoint
|
||||
Coordinate curMotorOffset = curInstance.getOffset();
|
||||
curInstance.setPosition( curMountLocation.add(curMotorOffset) );
|
||||
toReturn.add( curInstance);
|
||||
|
||||
// vvvv DEVEL vvvv
|
||||
// System.err.println(String.format(",,,,,,,,[ %2d]: %s. (%s)",
|
||||
// instanceNumber, curInstance.getMotor().getDigest(), curInstance));
|
||||
// ^^^^ DEVEL ^^^^
|
||||
instanceNumber ++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -350,6 +361,9 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
}
|
||||
}
|
||||
|
||||
public String toShort() {
|
||||
return this.fcid.getShortKey();
|
||||
}
|
||||
|
||||
// DEBUG / DEVEL
|
||||
public String toDebug() {
|
||||
|
@ -32,7 +32,7 @@ public class InnerTube extends ThicknessRingComponent implements Clusterable, Ra
|
||||
private double clusterRotation = 0.0;
|
||||
|
||||
private double overhang = 0;
|
||||
private boolean isActing;
|
||||
private boolean isActingMount;
|
||||
private MotorConfigurationSet motors;
|
||||
|
||||
/**
|
||||
@ -215,6 +215,11 @@ public class InnerTube extends ThicknessRingComponent implements Clusterable, Ra
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate[] getInstanceOffsets(){
|
||||
return this.shiftCoordinates(new Coordinate[]{Coordinate.ZERO});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate[] getLocations(){
|
||||
if (null == this.parent) {
|
||||
@ -277,6 +282,7 @@ public class InnerTube extends ThicknessRingComponent implements Clusterable, Ra
|
||||
}
|
||||
}
|
||||
|
||||
this.isActingMount = true;
|
||||
this.motors.set(fcid,newMotorInstance);
|
||||
}
|
||||
|
||||
@ -293,15 +299,15 @@ public class InnerTube extends ThicknessRingComponent implements Clusterable, Ra
|
||||
|
||||
@Override
|
||||
public void setMotorMount(boolean _active){
|
||||
if (this.isActing == _active)
|
||||
if (this.isActingMount == _active)
|
||||
return;
|
||||
this.isActing = _active;
|
||||
this.isActingMount = _active;
|
||||
fireComponentChangeEvent(ComponentChangeEvent.MOTOR_CHANGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMotorMount(){
|
||||
return this.isActing;
|
||||
return this.isActingMount;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -382,29 +388,38 @@ public class InnerTube extends ThicknessRingComponent implements Clusterable, Ra
|
||||
buffer.append(String.format("%s %-24s (cluster: %s)", prefix, this.getName(), this.getPatternName()));
|
||||
buffer.append(String.format(" (len: %5.3f offset: %4.1f via: %s )\n", this.getLength(), this.getAxialOffset(), this.relativePosition.name()));
|
||||
|
||||
Coordinate[] relCoords = this.shiftCoordinates(new Coordinate[] { Coordinate.ZERO });
|
||||
Coordinate[] relCoords = this.shiftCoordinates(new Coordinate[] { this.getOffset() });
|
||||
Coordinate[] absCoords = this.getLocations();
|
||||
FlightConfigurationID curId = this.getRocket().getDefaultConfiguration().getFlightConfigurationID();
|
||||
int count = this.getInstanceCount();
|
||||
MotorInstance curInstance = this.motors.get(curId);
|
||||
if( curInstance.isEmpty() ){
|
||||
//if( curInstance.isEmpty() ){
|
||||
{
|
||||
// print just the tube locations
|
||||
buffer.append(prefix+" [X] This Instance doesn't have any motors... showing mount tubes only\n");
|
||||
|
||||
for (int instanceNumber = 0; instanceNumber < count; instanceNumber++) {
|
||||
Coordinate instanceRelativePosition = relCoords[instanceNumber];
|
||||
Coordinate instanceAbsolutePosition = absCoords[instanceNumber];
|
||||
buffer.append(String.format("%s [%2d / %2d] %28s %28s\n", prefix, instanceNumber, count,
|
||||
instanceRelativePosition, instanceAbsolutePosition));
|
||||
Coordinate tubeRelativePosition = relCoords[instanceNumber];
|
||||
Coordinate tubeAbsolutePosition = absCoords[instanceNumber];
|
||||
buffer.append(String.format("%s [%2d/%2d]; %28s; %28s;\n", prefix, instanceNumber, count,
|
||||
tubeRelativePosition, tubeAbsolutePosition));
|
||||
}
|
||||
}
|
||||
|
||||
if( curInstance.isEmpty() ){
|
||||
buffer.append(prefix+" [X] This Instance doesn't have any motors... showing mount tubes only\n");
|
||||
}else{
|
||||
// curInstance has a motor ...
|
||||
Motor curMotor = curInstance.getMotor();
|
||||
buffer.append(String.format("%s %-24s (in cluster: %s)\n", prefix, curMotor.getDesignation(), this.getPatternName()));
|
||||
double motorOffset = this.getLength() - curMotor.getLength();
|
||||
|
||||
buffer.append(String.format("%s %-24s Thrust: %f N; (Length: %f); \n",
|
||||
prefix, curMotor.getDesignation(), curMotor.getMaxThrustEstimate(), curMotor.getLength() ));
|
||||
for (int instanceNumber = 0; instanceNumber < count; instanceNumber++) {
|
||||
Coordinate instanceRelativePosition = relCoords[instanceNumber];
|
||||
Coordinate instanceAbsolutePosition = absCoords[instanceNumber];
|
||||
buffer.append(String.format("%s [%2d / %2d] %28s %28s\n", prefix, instanceNumber, count,
|
||||
instanceRelativePosition, instanceAbsolutePosition));
|
||||
Coordinate motorRelativePosition = new Coordinate(motorOffset, 0, 0);
|
||||
Coordinate tubeAbs = absCoords[instanceNumber];
|
||||
Coordinate motorAbsolutePosition = new Coordinate(tubeAbs.x+motorOffset,tubeAbs.y,tubeAbs.z);
|
||||
buffer.append(String.format("%s [%2d/%2d]; %28s; %28s;\n", prefix, instanceNumber, count,
|
||||
motorRelativePosition, motorAbsolutePosition));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,10 +10,21 @@ public interface Instanceable {
|
||||
*
|
||||
* Note: This is function has a concrete implementation in RocketComponent.java ... it is included here only as a reminder.
|
||||
*
|
||||
* @return coordinates of each instance of this component -- specifically the front center of each instance
|
||||
* @return coordinates of each instance of this component -- specifically the front center of each instance in global coordinates
|
||||
*/
|
||||
public Coordinate[] getLocations();
|
||||
|
||||
/**
|
||||
* Returns vector coordinates of each instance of this component relative to this component's reference point (typically front center)
|
||||
*
|
||||
* Note: <code> this.getOffsets().length == this.getInstanceCount() </code> should ALWAYS be true.
|
||||
* If getInstanceCount() returns anything besides 1 this function should be overridden as well.
|
||||
*
|
||||
*
|
||||
* @return coordinates location of each instance relative to <b>this</b> component's reference point.
|
||||
*/
|
||||
public Coordinate[] getInstanceOffsets();
|
||||
|
||||
/**
|
||||
* How many instances of this component are represented. This should generally be editable.
|
||||
* @param newCount number of instances to set
|
||||
|
@ -119,7 +119,17 @@ public abstract class LaunchButton extends ExternalComponent implements LineInst
|
||||
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate[] getInstanceOffsets(){
|
||||
Coordinate[] toReturn = new Coordinate[this.getInstanceCount()];
|
||||
toReturn[0] = Coordinate.ZERO;
|
||||
|
||||
for ( int index=1 ; index < this.getInstanceCount(); index++){
|
||||
toReturn[index] = new Coordinate(index*this.instanceSeparation,0,0,0);
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadFromPreset(ComponentPreset preset) {
|
||||
|
@ -142,6 +142,18 @@ public class LaunchLug extends ExternalComponent implements Coaxial, LineInstanc
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Coordinate[] getInstanceOffsets(){
|
||||
Coordinate[] toReturn = new Coordinate[this.getInstanceCount()];
|
||||
toReturn[0] = Coordinate.ZERO;
|
||||
|
||||
for ( int index=1 ; index < this.getInstanceCount(); index++){
|
||||
toReturn[index] = new Coordinate(index*this.instanceSeparation,0,0,0);
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Coordinate[] shiftCoordinates(Coordinate[] array) {
|
||||
array = super.shiftCoordinates(array);
|
||||
|
@ -76,6 +76,11 @@ public class PodSet extends ComponentAssembly implements RingInstanceable {
|
||||
return BodyComponent.class.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate[] getInstanceOffsets(){
|
||||
return shiftCoordinates(new Coordinate[]{Coordinate.ZERO});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinate[] getLocations() {
|
||||
if (null == this.parent) {
|
||||
|
@ -1083,9 +1083,13 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
|
||||
return this.position;
|
||||
}
|
||||
|
||||
// public Coordinate[] getOffset() {
|
||||
// return new Coordinate[]{this.position};
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated kept around as example code. instead use
|
||||
* @deprecated kept around as example code. instead use getLocations
|
||||
* @return
|
||||
*/
|
||||
private Coordinate getAbsoluteVector() {
|
||||
@ -2081,7 +2085,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
|
||||
}
|
||||
|
||||
public void toDebugTreeNode(final StringBuilder buffer, final String prefix) {
|
||||
buffer.append(String.format("%s %-24s %5.3f %24s %24s\n", prefix, this.getName(), this.getLength(),
|
||||
buffer.append(String.format("%s %-24s; %5.3f; %24s; %24s;\n", prefix, this.getName(), this.getLength(),
|
||||
this.getOffset(), this.getLocations()[0]));
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ public abstract class RocketUtils {
|
||||
return length;
|
||||
}
|
||||
|
||||
// get rid of this method.... we can sure come up with a better way to do this....
|
||||
public static Coordinate getCG(Rocket rocket, MassCalcType calcType) {
|
||||
MassCalculator massCalculator = new MassCalculator();
|
||||
Coordinate cg = massCalculator.getCG(rocket.getDefaultConfiguration(), calcType);
|
||||
|
@ -3,6 +3,8 @@ package net.sf.openrocket.simulation;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.masscalc.MassCalculator;
|
||||
import net.sf.openrocket.masscalc.MassData;
|
||||
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.motor.MotorInstance;
|
||||
import net.sf.openrocket.motor.MotorInstanceConfiguration;
|
||||
@ -124,10 +126,11 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
|
||||
}
|
||||
|
||||
MassCalculator calc = status.getSimulationConditions().getMassCalculator();
|
||||
cg = calc.getCG(status.getConfiguration(), status.getMotorConfiguration());
|
||||
longitudinalInertia = calc.getLongitudinalInertia(status.getConfiguration(), status.getMotorConfiguration());
|
||||
rotationalInertia = calc.getRotationalInertia(status.getConfiguration(), status.getMotorConfiguration());
|
||||
propellantMass = calc.getPropellantMass(status.getConfiguration(), status.getMotorConfiguration());
|
||||
// not sure if this is actually Launch mass or not...
|
||||
cg = calc.getCG(status.getConfiguration(), MassCalcType.LAUNCH_MASS);
|
||||
longitudinalInertia = calc.getLongitudinalInertia(status.getConfiguration(), MassCalcType.LAUNCH_MASS);
|
||||
rotationalInertia = calc.getRotationalInertia(status.getConfiguration(), MassCalcType.LAUNCH_MASS);
|
||||
propellantMass = calc.getPropellantMass(status.getConfiguration(), MassCalcType.LAUNCH_MASS);
|
||||
mass = new MassData(cg, longitudinalInertia, rotationalInertia, propellantMass);
|
||||
|
||||
// Call post-listener
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.sf.openrocket.simulation;
|
||||
|
||||
import net.sf.openrocket.masscalc.MassData;
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.sf.openrocket.simulation;
|
||||
|
||||
import net.sf.openrocket.masscalc.MassData;
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
@ -1,74 +0,0 @@
|
||||
package net.sf.openrocket.simulation;
|
||||
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
|
||||
/**
|
||||
* An immutable value object containing the mass data of a rocket.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public class MassData {
|
||||
|
||||
private final Coordinate cg;
|
||||
private final double longitudinalInertia;
|
||||
private final double rotationalInertia;
|
||||
private final double propellantMass;
|
||||
|
||||
|
||||
public MassData(Coordinate cg, double longitudinalInertia, double rotationalInertia, double propellantMass) {
|
||||
if (cg == null) {
|
||||
throw new IllegalArgumentException("cg is null");
|
||||
}
|
||||
this.cg = cg;
|
||||
this.longitudinalInertia = longitudinalInertia;
|
||||
this.rotationalInertia = rotationalInertia;
|
||||
this.propellantMass = propellantMass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Coordinate getCG() {
|
||||
return cg;
|
||||
}
|
||||
|
||||
public double getLongitudinalInertia() {
|
||||
return longitudinalInertia;
|
||||
}
|
||||
|
||||
public double getRotationalInertia() {
|
||||
return rotationalInertia;
|
||||
}
|
||||
|
||||
public double getPropellantMass() {
|
||||
return propellantMass;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!(obj instanceof MassData))
|
||||
return false;
|
||||
|
||||
MassData other = (MassData) obj;
|
||||
return (this.cg.equals(other.cg) && MathUtil.equals(this.longitudinalInertia, other.longitudinalInertia) &&
|
||||
MathUtil.equals(this.rotationalInertia, other.rotationalInertia)) && MathUtil.equals(this.propellantMass, other.propellantMass) ;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (cg.hashCode() ^ Double.doubleToLongBits(longitudinalInertia) ^ Double.doubleToLongBits(rotationalInertia) ^ Double.doubleToLongBits(propellantMass) );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MassData [cg=" + cg + ", longitudinalInertia=" + longitudinalInertia
|
||||
+ ", rotationalInertia=" + rotationalInertia + ", propellantMass="+propellantMass + "]";
|
||||
}
|
||||
|
||||
}
|
@ -10,6 +10,7 @@ import net.sf.openrocket.aerodynamics.AerodynamicForces;
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.masscalc.MassData;
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.simulation.exception.SimulationCalculationException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
|
@ -8,6 +8,7 @@ import javax.script.ScriptException;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.AerodynamicForces;
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.masscalc.MassData;
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.motor.MotorInstanceId;
|
||||
import net.sf.openrocket.motor.MotorInstance;
|
||||
@ -15,7 +16,6 @@ import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||
import net.sf.openrocket.simulation.AccelerationData;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.MassData;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationListenerException;
|
||||
|
@ -2,6 +2,7 @@ package net.sf.openrocket.simulation.listeners;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.AerodynamicForces;
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.masscalc.MassData;
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.motor.MotorInstanceId;
|
||||
import net.sf.openrocket.motor.MotorInstance;
|
||||
@ -9,7 +10,6 @@ import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||
import net.sf.openrocket.simulation.AccelerationData;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.MassData;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
|
@ -2,9 +2,9 @@ package net.sf.openrocket.simulation.listeners;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.AerodynamicForces;
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.masscalc.MassData;
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.simulation.AccelerationData;
|
||||
import net.sf.openrocket.simulation.MassData;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
@ -7,6 +7,7 @@ import org.slf4j.LoggerFactory;
|
||||
import net.sf.openrocket.aerodynamics.AerodynamicForces;
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.aerodynamics.Warning;
|
||||
import net.sf.openrocket.masscalc.MassData;
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.motor.MotorInstanceId;
|
||||
import net.sf.openrocket.motor.MotorInstance;
|
||||
@ -14,7 +15,6 @@ import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||
import net.sf.openrocket.simulation.AccelerationData;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.MassData;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
|
@ -12,15 +12,19 @@ import net.sf.openrocket.material.Material.Type;
|
||||
import net.sf.openrocket.motor.Manufacturer;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.MotorInstance;
|
||||
import net.sf.openrocket.motor.MotorInstanceId;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotorInstance;
|
||||
import net.sf.openrocket.preset.ComponentPreset;
|
||||
import net.sf.openrocket.preset.ComponentPresetFactory;
|
||||
import net.sf.openrocket.preset.InvalidComponentPresetException;
|
||||
import net.sf.openrocket.preset.TypedPropertyMap;
|
||||
import net.sf.openrocket.rocketcomponent.AxialStage;
|
||||
import net.sf.openrocket.rocketcomponent.BodyTube;
|
||||
import net.sf.openrocket.rocketcomponent.BoosterSet;
|
||||
import net.sf.openrocket.rocketcomponent.Bulkhead;
|
||||
import net.sf.openrocket.rocketcomponent.CenteringRing;
|
||||
import net.sf.openrocket.rocketcomponent.ClusterConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent;
|
||||
import net.sf.openrocket.rocketcomponent.ExternalComponent;
|
||||
@ -40,6 +44,7 @@ import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||
import net.sf.openrocket.rocketcomponent.ReferenceType;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.ShockCord;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent.Position;
|
||||
import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.Transition;
|
||||
@ -83,6 +88,66 @@ public class TestRockets {
|
||||
|
||||
}
|
||||
|
||||
private static MotorInstance generateMotorInstance_M1350_75mm(){
|
||||
// public ThrustCurveMotor(Manufacturer manufacturer, String designation, String description,
|
||||
// Motor.Type type, double[] delays, double diameter, double length,
|
||||
// double[] time, double[] thrust,
|
||||
// Coordinate[] cg, String digest);
|
||||
ThrustCurveMotor mtr = new ThrustCurveMotor(
|
||||
Manufacturer.getManufacturer("AeroTech"),"M1350", "Desc",
|
||||
Motor.Type.SINGLE, new double[] {}, 0.075, 0.622,
|
||||
new double[] { 0, 1, 2 }, new double[] { 0, 1357, 0 },
|
||||
new Coordinate[] {
|
||||
new Coordinate(.311, 0, 0, 4.808),new Coordinate(.311, 0, 0, 3.389),new Coordinate(.311, 0, 0, 1.970)},
|
||||
"digest M1350 test");
|
||||
return mtr.getNewInstance();
|
||||
}
|
||||
|
||||
private static MotorInstance generateMotorInstance_G77_29mm(){
|
||||
// public ThrustCurveMotor(Manufacturer manufacturer, String designation, String description,
|
||||
// Motor.Type type, double[] delays, double diameter, double length,
|
||||
// double[] time, double[] thrust,
|
||||
// Coordinate[] cg, String digest);
|
||||
ThrustCurveMotor mtr = new ThrustCurveMotor(
|
||||
Manufacturer.getManufacturer("AeroTech"),"G77", "Desc",
|
||||
Motor.Type.SINGLE, new double[] {4,7,10},0.029, 0.124,
|
||||
new double[] { 0, 1, 2 }, new double[] { 0, 1, 0 },
|
||||
new Coordinate[] {
|
||||
new Coordinate(.062, 0, 0, 0.123),new Coordinate(.062, 0, 0, .0935),new Coordinate(.062, 0, 0, 0.064)},
|
||||
"digest G77 test");
|
||||
return mtr.getNewInstance();
|
||||
}
|
||||
|
||||
//
|
||||
public static Rocket makeNoMotorRocket() {
|
||||
Rocket rocket;
|
||||
AxialStage stage;
|
||||
NoseCone nosecone;
|
||||
BodyTube bodytube;
|
||||
|
||||
|
||||
rocket = new Rocket();
|
||||
stage = new AxialStage();
|
||||
stage.setName("Stage1");
|
||||
// Stage construction
|
||||
rocket.addChild(stage);
|
||||
|
||||
nosecone = new NoseCone(Transition.Shape.ELLIPSOID, 0.105, 0.033);
|
||||
nosecone.setThickness(0.001);
|
||||
bodytube = new BodyTube(0.69, 0.033, 0.001);
|
||||
bodytube.setMotorMount(true);
|
||||
|
||||
TrapezoidFinSet finset = new TrapezoidFinSet(3, 0.495, 0.1, 0.3, 0.185);
|
||||
finset.setThickness(0.005);
|
||||
bodytube.addChild(finset);
|
||||
|
||||
// Component construction
|
||||
stage.addChild(nosecone);
|
||||
stage.addChild(bodytube);
|
||||
|
||||
|
||||
return rocket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new test rocket based on the value 'key'. The rocket utilizes most of the
|
||||
@ -544,6 +609,157 @@ public class TestRockets {
|
||||
return rocket;
|
||||
}
|
||||
|
||||
public static Rocket makeFalcon9Heavy() {
|
||||
Rocket rocket = new Rocket();
|
||||
rocket.setName("Falcon9H Scale Rocket");
|
||||
FlightConfiguration config = rocket.getDefaultConfiguration();
|
||||
|
||||
// ====== Payload Stage ======
|
||||
// ====== ====== ====== ======
|
||||
AxialStage payloadStage = new AxialStage();
|
||||
payloadStage.setName("Payload Fairing");
|
||||
rocket.addChild(payloadStage);
|
||||
|
||||
{
|
||||
NoseCone payloadFairingNoseCone = new NoseCone(Transition.Shape.POWER, 0.118, 0.052);
|
||||
payloadFairingNoseCone.setName("PL Fairing Nose");
|
||||
payloadFairingNoseCone.setThickness(0.001);
|
||||
payloadFairingNoseCone.setShapeParameter(0.5);
|
||||
//payloadFairingNoseCone.setLength(0.118);
|
||||
//payloadFairingNoseCone.setAftRadius(0.052);
|
||||
payloadFairingNoseCone.setAftShoulderRadius( 0.051 );
|
||||
payloadFairingNoseCone.setAftShoulderLength( 0.02 );
|
||||
payloadFairingNoseCone.setAftShoulderThickness( 0.001 );
|
||||
payloadFairingNoseCone.setAftShoulderCapped( false );
|
||||
payloadStage.addChild(payloadFairingNoseCone);
|
||||
|
||||
BodyTube payloadBody = new BodyTube(0.132, 0.052, 0.001);
|
||||
payloadBody.setName("PL Fairing Body");
|
||||
payloadStage.addChild(payloadBody);
|
||||
|
||||
Transition payloadFairingTail = new Transition();
|
||||
payloadFairingTail.setName("PL Fairing Transition");
|
||||
payloadFairingTail.setLength(0.014);
|
||||
payloadFairingTail.setThickness(0.002);
|
||||
payloadFairingTail.setForeRadiusAutomatic(true);
|
||||
payloadFairingTail.setAftRadiusAutomatic(true);
|
||||
payloadStage.addChild(payloadFairingTail);
|
||||
|
||||
BodyTube upperStageBody= new BodyTube(0.18, 0.0385, 0.001);
|
||||
upperStageBody.setName("Upper Stage Body ");
|
||||
payloadStage.addChild( upperStageBody);
|
||||
|
||||
{
|
||||
// Parachute
|
||||
Parachute upperChute= new Parachute();
|
||||
upperChute.setName("Parachute");
|
||||
upperChute.setRelativePosition(Position.MIDDLE);
|
||||
upperChute.setPositionValue(0.0);
|
||||
upperChute.setDiameter(0.3);
|
||||
upperChute.setLineCount(6);
|
||||
upperChute.setLineLength(0.3);
|
||||
upperStageBody.addChild( upperChute);
|
||||
|
||||
// Cord
|
||||
ShockCord cord = new ShockCord();
|
||||
cord.setName("Shock Cord");
|
||||
cord.setRelativePosition(Position.BOTTOM);
|
||||
cord.setPositionValue(0.0);
|
||||
cord.setCordLength(0.4);
|
||||
upperStageBody.addChild( cord);
|
||||
}
|
||||
|
||||
BodyTube interstage= new BodyTube(0.12, 0.0385, 0.001);
|
||||
interstage.setName("Interstage");
|
||||
payloadStage.addChild( interstage);
|
||||
}
|
||||
|
||||
// ====== Core Stage ======
|
||||
// ====== ====== ====== ======
|
||||
AxialStage coreStage = new AxialStage();
|
||||
coreStage.setName("Core Stage");
|
||||
rocket.addChild(coreStage);
|
||||
|
||||
{
|
||||
BodyTube coreBody = new BodyTube(0.8, 0.0385, 0.001);
|
||||
// 74 mm inner dia
|
||||
coreBody.setName("Core Stage Body");
|
||||
coreBody.setMotorMount(true);
|
||||
coreStage.addChild( coreBody);
|
||||
{
|
||||
MotorInstance motorInstance = TestRockets.generateMotorInstance_M1350_75mm();
|
||||
motorInstance.setID( new MotorInstanceId( coreBody.getName(), 1) );
|
||||
coreBody.setMotorMount( true);
|
||||
FlightConfigurationID motorConfigId = config.getFlightConfigurationID();
|
||||
coreBody.setMotorInstance( motorConfigId, motorInstance);
|
||||
}
|
||||
|
||||
TrapezoidFinSet coreFins = new TrapezoidFinSet();
|
||||
coreFins.setName("Core Fins");
|
||||
coreFins.setFinCount(4);
|
||||
coreFins.setRelativePosition(Position.BOTTOM);
|
||||
coreFins.setPositionValue(0.0);
|
||||
coreFins.setBaseRotation( Math.PI / 4);
|
||||
coreFins.setThickness(0.003);
|
||||
coreFins.setCrossSection(CrossSection.ROUNDED);
|
||||
coreFins.setRootChord(0.32);
|
||||
coreFins.setTipChord(0.12);
|
||||
coreFins.setHeight(0.12);
|
||||
coreFins.setSweep(0.18);
|
||||
coreBody.addChild(coreFins);
|
||||
}
|
||||
|
||||
// ====== Booster Stage Set ======
|
||||
// ====== ====== ====== ======
|
||||
BoosterSet boosterStage = new BoosterSet();
|
||||
boosterStage.setName("Booster Stage");
|
||||
coreStage.addChild( boosterStage);
|
||||
boosterStage.setRelativePositionMethod(Position.BOTTOM);
|
||||
boosterStage.setAxialOffset(0.0);
|
||||
boosterStage.setInstanceCount(2);
|
||||
boosterStage.setRadialOffset(0.075);
|
||||
|
||||
{
|
||||
NoseCone boosterCone = new NoseCone(Transition.Shape.POWER, 0.08, 0.0385);
|
||||
boosterCone.setShapeParameter(0.5);
|
||||
boosterCone.setName("Booster Nose");
|
||||
boosterCone.setThickness(0.002);
|
||||
//payloadFairingNoseCone.setLength(0.118);
|
||||
//payloadFairingNoseCone.setAftRadius(0.052);
|
||||
boosterCone.setAftShoulderRadius( 0.051 );
|
||||
boosterCone.setAftShoulderLength( 0.02 );
|
||||
boosterCone.setAftShoulderThickness( 0.001 );
|
||||
boosterCone.setAftShoulderCapped( false );
|
||||
boosterStage.addChild( boosterCone);
|
||||
|
||||
BodyTube boosterBody = new BodyTube(0.8, 0.0385, 0.001);
|
||||
boosterBody.setName("Booster Body");
|
||||
boosterBody.setOuterRadiusAutomatic(true);
|
||||
boosterStage.addChild( boosterBody);
|
||||
|
||||
{
|
||||
InnerTube boosterMotorTubes = new InnerTube();
|
||||
boosterMotorTubes.setName("Booster Motor Tubes");
|
||||
boosterMotorTubes.setLength(0.15);
|
||||
boosterMotorTubes.setOuterRadius(0.015); // => 29mm motors
|
||||
boosterMotorTubes.setThickness(0.0005);
|
||||
boosterMotorTubes.setClusterConfiguration( ClusterConfiguration.CONFIGURATIONS[5]); // 4-ring
|
||||
//boosterMotorTubes.setClusterConfiguration( ClusterConfiguration.CONFIGURATIONS[13]); // 9-star
|
||||
boosterMotorTubes.setClusterScale(1.0);
|
||||
boosterBody.addChild( boosterMotorTubes);
|
||||
|
||||
FlightConfigurationID motorConfigId = config.getFlightConfigurationID();
|
||||
MotorInstance motorInstance = TestRockets.generateMotorInstance_G77_29mm();
|
||||
motorInstance.setID( new MotorInstanceId( boosterMotorTubes.getName(), 1) );
|
||||
boosterMotorTubes.setMotorInstance( motorConfigId, motorInstance);
|
||||
boosterMotorTubes.setMotorOverhang(0.01234);
|
||||
}
|
||||
}
|
||||
|
||||
config.setAllStages(true);
|
||||
|
||||
return rocket;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new file version 1.00 rocket
|
||||
|
@ -2,176 +2,463 @@ package net.sf.openrocket.masscalc;
|
||||
|
||||
//import junit.framework.TestCase;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sf.openrocket.rocketcomponent.AxialStage;
|
||||
import net.sf.openrocket.rocketcomponent.BodyTube;
|
||||
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
|
||||
import net.sf.openrocket.motor.MotorInstance;
|
||||
import net.sf.openrocket.rocketcomponent.BoosterSet;
|
||||
import net.sf.openrocket.rocketcomponent.ClusterConfiguration;
|
||||
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.NoseCone;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.Transition;
|
||||
import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.TestRockets;
|
||||
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
|
||||
|
||||
public class MassCalculatorTest extends BaseTestCase {
|
||||
|
||||
// tolerance for compared double test results
|
||||
protected final double EPSILON = 0.000001;
|
||||
protected final double EPSILON = MathUtil.EPSILON;
|
||||
|
||||
protected final Coordinate ZERO = new Coordinate(0., 0., 0.);
|
||||
|
||||
public void test() {
|
||||
// fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public Rocket createTestRocket() {
|
||||
double tubeRadius = 0.1;
|
||||
// setup
|
||||
Rocket rocket = new Rocket();
|
||||
rocket.setName("Rocket");
|
||||
|
||||
AxialStage sustainer = new AxialStage();
|
||||
sustainer.setName("Sustainer stage");
|
||||
RocketComponent sustainerNose = new NoseCone(Transition.Shape.CONICAL, 0.2, tubeRadius);
|
||||
sustainerNose.setName("Sustainer Nosecone");
|
||||
sustainer.addChild(sustainerNose);
|
||||
RocketComponent sustainerBody = new BodyTube(0.3, tubeRadius, 0.001);
|
||||
sustainerBody.setName("Sustainer Body ");
|
||||
sustainer.addChild(sustainerBody);
|
||||
rocket.addChild(sustainer);
|
||||
|
||||
AxialStage core = new AxialStage();
|
||||
core.setName("Core stage");
|
||||
rocket.addChild(core);
|
||||
BodyTube coreBody = new BodyTube(0.6, tubeRadius, 0.001);
|
||||
coreBody.setName("Core Body ");
|
||||
core.addChild(coreBody);
|
||||
FinSet coreFins = new TrapezoidFinSet(4, 0.4, 0.2, 0.2, 0.4);
|
||||
coreFins.setName("Core Fins");
|
||||
coreBody.addChild(coreFins);
|
||||
|
||||
InnerTube motorCluster = new InnerTube();
|
||||
motorCluster.setName("Core Motor Cluster");
|
||||
// outdated. Just add an actual motor + motor instance
|
||||
//motorCluster.setMotorMount(true);
|
||||
motorCluster.setClusterConfiguration(ClusterConfiguration.CONFIGURATIONS[5]);
|
||||
coreBody.addChild(motorCluster);
|
||||
|
||||
return rocket;
|
||||
}
|
||||
|
||||
public BoosterSet createBooster() {
|
||||
double tubeRadius = 0.08;
|
||||
|
||||
BoosterSet booster = new BoosterSet();
|
||||
booster.setName("Booster Stage");
|
||||
RocketComponent boosterNose = new NoseCone(Transition.Shape.CONICAL, 0.2, tubeRadius);
|
||||
boosterNose.setName("Booster Nosecone");
|
||||
booster.addChild(boosterNose);
|
||||
RocketComponent boosterBody = new BodyTube(0.2, tubeRadius, 0.001);
|
||||
boosterBody.setName("Booster Body ");
|
||||
booster.addChild(boosterBody);
|
||||
Transition boosterTail = new Transition();
|
||||
boosterTail.setName("Booster Tail");
|
||||
boosterTail.setForeRadius(tubeRadius);
|
||||
boosterTail.setAftRadius(0.05);
|
||||
boosterTail.setLength(0.1);
|
||||
booster.addChild(boosterTail);
|
||||
|
||||
booster.setInstanceCount(3);
|
||||
booster.setRadialOffset(0.18);
|
||||
|
||||
return booster;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTestRocketMasses() {
|
||||
RocketComponent rocket = createTestRocket();
|
||||
String treeDump = rocket.toDebugTree();
|
||||
public void testRocketNoMotors() {
|
||||
Rocket rkt = TestRockets.makeNoMotorRocket();
|
||||
rkt.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
|
||||
|
||||
// String treeDump = rkt.toDebugTree();
|
||||
// System.err.println( treeDump);
|
||||
|
||||
// Validate Boosters
|
||||
MassCalculator mc = new MassCalculator();
|
||||
//mc.debug = true;
|
||||
Coordinate rocketCM = mc.getCM( rkt.getDefaultConfiguration(), MassCalcType.NO_MOTORS);
|
||||
|
||||
double expMass = 0.668984592;
|
||||
double expCMx = 0.558422219894;
|
||||
double calcMass = rocketCM.weight;
|
||||
Coordinate expCM = new Coordinate(expCMx,0,0, expMass);
|
||||
assertEquals(" Simple Motor Rocket mass incorrect: ", expMass, calcMass, EPSILON);
|
||||
assertEquals(" Delta Heavy Booster CM.x is incorrect: ", expCM.x, rocketCM.x, EPSILON);
|
||||
assertEquals(" Delta Heavy Booster CM.y is incorrect: ", expCM.y, rocketCM.y, EPSILON);
|
||||
assertEquals(" Delta Heavy Booster CM.z is incorrect: ", expCM.z, rocketCM.z, EPSILON);
|
||||
assertEquals(" Delta Heavy Booster CM is incorrect: ", expCM, rocketCM);
|
||||
|
||||
rocketCM = mc.getCM( rkt.getDefaultConfiguration(), MassCalcType.LAUNCH_MASS);
|
||||
assertEquals(" Delta Heavy Booster CM is incorrect: ", expCM, rocketCM);
|
||||
rocketCM = mc.getCM( rkt.getDefaultConfiguration(), MassCalcType.BURNOUT_MASS);
|
||||
assertEquals(" Delta Heavy Booster CM is incorrect: ", expCM, rocketCM);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTestComponentMasses() {
|
||||
Rocket rkt = TestRockets.makeFalcon9Heavy();
|
||||
rkt.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
|
||||
|
||||
double expMass;
|
||||
RocketComponent cc;
|
||||
double compMass;
|
||||
double calcMass;
|
||||
|
||||
expMass = 0.093417755;
|
||||
compMass = rocket.getChild(0).getChild(0).getComponentMass();
|
||||
assertEquals(" NoseCone mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
// ====== Payload Stage ======
|
||||
// ====== ====== ====== ======
|
||||
{
|
||||
expMass = 0.022549558353;
|
||||
cc= rkt.getChild(0).getChild(0);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals("P/L NoseCone mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
|
||||
expMass = 0.1275360953;
|
||||
compMass = rocket.getChild(0).getChild(1).getComponentMass();
|
||||
assertEquals(" Sustainer Body mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
expMass = 0.02904490372;
|
||||
cc= rkt.getChild(0).getChild(1);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals("P/L Body mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
|
||||
expMass = 0.255072190;
|
||||
compMass = rocket.getChild(1).getChild(0).getComponentMass();
|
||||
assertEquals(" Core Body mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
expMass = 0.007289284477103441;
|
||||
cc= rkt.getChild(0).getChild(2);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals("P/L Transition mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
|
||||
expMass = 0.9792000;
|
||||
compMass = rocket.getChild(1).getChild(0).getChild(0).getComponentMass();
|
||||
assertEquals(" Core Fins mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
expMass = 0.029224351500753608;
|
||||
cc= rkt.getChild(0).getChild(3);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals("P/L Upper Stage Body mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
{
|
||||
expMass = 0.0079759509252;
|
||||
cc= rkt.getChild(0).getChild(3).getChild(0);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals(cc.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
|
||||
InnerTube motorCluster = (InnerTube) rocket.getChild(1).getChild(0).getChild(1);
|
||||
expMass = 0.0055329;
|
||||
compMass = motorCluster.getComponentMass();
|
||||
assertEquals(" Core Motor Mount Tubes: mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
compMass = motorCluster.getMass();
|
||||
assertEquals(" Core Motor Mount Tubes: mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
expMass = 0.00072;
|
||||
cc= rkt.getChild(0).getChild(3).getChild(1);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals(cc.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
}
|
||||
|
||||
expMass = 490.061;
|
||||
// MassCalculator mc = new BasicMassCalculator();
|
||||
// calcMass = mc.getMass();
|
||||
calcMass = rocket.getMass();
|
||||
expMass = 0.01948290100050243;
|
||||
cc= rkt.getChild(0).getChild(4);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals(cc.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
}
|
||||
|
||||
assertEquals(" Simple Rocket Mass is incorrect: " + treeDump, expMass, calcMass, EPSILON);
|
||||
// ====== Core Stage ======
|
||||
// ====== ====== ======
|
||||
{
|
||||
expMass = 0.1298860066700161;
|
||||
cc= rkt.getChild(1).getChild(0);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals(cc.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
|
||||
expMass = 0.21326976;
|
||||
cc= rkt.getChild(1).getChild(0).getChild(0);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals(cc.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
}
|
||||
|
||||
|
||||
// ====== Booster Set Stage ======
|
||||
// ====== ====== ======
|
||||
BoosterSet boosters = (BoosterSet) rkt.getChild(1).getChild(1);
|
||||
{
|
||||
expMass = 0.01530561538;
|
||||
cc= boosters.getChild(0);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals(cc.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
|
||||
expMass = 0.08374229377;
|
||||
cc= boosters.getChild(1);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals(cc.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
|
||||
expMass = 0.018906104589303415;
|
||||
cc= boosters.getChild(1).getChild(0);
|
||||
compMass = cc.getComponentMass();
|
||||
assertEquals(cc.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTestRocketCG() {
|
||||
RocketComponent rocket = createTestRocket();
|
||||
String treeDump = rocket.toDebugTree();
|
||||
double expRelCGx;
|
||||
double expAbsCGx;
|
||||
double actualRelCGx;
|
||||
double actualAbsCGx;
|
||||
public void testTestComponentMOIs() {
|
||||
Rocket rkt = TestRockets.makeFalcon9Heavy();
|
||||
rkt.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
|
||||
|
||||
expRelCGx = 0.134068822;
|
||||
actualRelCGx = rocket.getChild(0).getChild(0).getComponentCG().x;
|
||||
assertEquals(" NoseCone CG calculated incorrectly: ", expRelCGx, actualRelCGx, EPSILON);
|
||||
double expInertia;
|
||||
RocketComponent cc;
|
||||
double compInertia;
|
||||
|
||||
expRelCGx = 0.15;
|
||||
actualRelCGx = rocket.getChild(0).getChild(1).getComponentCG().x;
|
||||
assertEquals(" Sustainer Body cg calculated incorrectly: ", expRelCGx, actualRelCGx, EPSILON);
|
||||
// ====== Payload Stage ======
|
||||
// ====== ====== ====== ======
|
||||
{
|
||||
expInertia = 3.1698055283e-5;
|
||||
cc= rkt.getChild(0).getChild(0);
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 1.79275e-5;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
|
||||
BodyTube coreBody = (BodyTube) rocket.getChild(1).getChild(0);
|
||||
expRelCGx = 0.3; // relative to parent
|
||||
actualRelCGx = coreBody.getComponentCG().x;
|
||||
assertEquals(" Core Body (relative) cg calculated incorrectly: ", expRelCGx, actualRelCGx, EPSILON);
|
||||
//expAbsCGx = 0.8;
|
||||
//actualAbsCGx = coreBody.getCG().x;
|
||||
//assertEquals(" Core Body (absolute) cg calculated incorrectly: ", expAbsCGx, actualAbsCGx, EPSILON);
|
||||
cc= rkt.getChild(0).getChild(1);
|
||||
expInertia = 7.70416e-5;
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 8.06940e-5;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
|
||||
FinSet coreFins = (FinSet) rocket.getChild(1).getChild(0).getChild(0);
|
||||
expRelCGx = 0.244444444; // relative to parent
|
||||
actualRelCGx = coreFins.getComponentCG().x;
|
||||
assertEquals(" Core Fins (relative) cg calculated incorrectly: ", expRelCGx, actualRelCGx, EPSILON);
|
||||
// expAbsCGx = 0.9444444444;
|
||||
// actualAbsCGx = coreBody.getCG().x;
|
||||
// assertEquals(" Core Fins (absolute) cg calculated incorrectly: ", expAbsCGx, actualAbsCGx, EPSILON);
|
||||
cc= rkt.getChild(0).getChild(2);
|
||||
expInertia = 1.43691e-5;
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 7.30265e-6;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
|
||||
expRelCGx = 10.061;
|
||||
// MassCalculator mc = new BasicMassCalculator();
|
||||
actualRelCGx = rocket.getCG().x;
|
||||
// mc.getCG(Configuration configuration, MotorInstanceConfiguration motors) {
|
||||
// calcMass = mc.getMass();
|
||||
cc= rkt.getChild(0).getChild(3);
|
||||
expInertia = 4.22073e-5;
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 0.0001;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
|
||||
assertEquals(" Simple Rocket CG is incorrect: " + treeDump, expRelCGx, actualRelCGx, EPSILON);
|
||||
{
|
||||
cc= rkt.getChild(0).getChild(3).getChild(0);
|
||||
expInertia = 6.23121e-7;
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 7.26975e-7;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
|
||||
cc= rkt.getChild(0).getChild(3).getChild(1);
|
||||
expInertia = 5.625e-8;
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 6.5625e-8;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
}
|
||||
|
||||
cc= rkt.getChild(0).getChild(4);
|
||||
expInertia = 2.81382e-5;
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 3.74486e-5;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
}
|
||||
|
||||
// ====== Core Stage ======
|
||||
// ====== ====== ======
|
||||
{
|
||||
cc= rkt.getChild(1).getChild(0);
|
||||
expInertia = 0.000187588;
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 0.00702105;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
|
||||
cc= rkt.getChild(1).getChild(0).getChild(0);
|
||||
expInertia = 0.00734753;
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 0.02160236691801411;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
}
|
||||
|
||||
|
||||
// ====== Booster Set Stage ======
|
||||
// ====== ====== ======
|
||||
BoosterSet boosters = (BoosterSet) rkt.getChild(1).getChild(1);
|
||||
{
|
||||
cc= boosters.getChild(0);
|
||||
expInertia = 5.20107e-6;
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 0;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
|
||||
cc= boosters.getChild(1);
|
||||
expInertia = 5.02872e-5;
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 0.00449140;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
|
||||
cc= boosters.getChild(1).getChild(0);
|
||||
expInertia = 4.11444e-6;
|
||||
compInertia = cc.getRotationalInertia();
|
||||
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
expInertia = 3.75062e-5;
|
||||
compInertia = cc.getLongitudinalInertia();
|
||||
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void testTestBoosterStructureCM() {
|
||||
Rocket rocket = TestRockets.makeFalcon9Heavy();
|
||||
rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
|
||||
|
||||
BoosterSet boosters = (BoosterSet) rocket.getChild(1).getChild(1);
|
||||
int boostNum = boosters.getStageNumber();
|
||||
|
||||
rocket.getDefaultConfiguration().setAllStages(false);
|
||||
rocket.getDefaultConfiguration().setOnlyStage( boostNum);
|
||||
// String treeDump = rocket.toDebugTree();
|
||||
// System.err.println( treeDump);
|
||||
|
||||
// Validate Boosters
|
||||
MassCalculator mc = new MassCalculator();
|
||||
Coordinate boosterSetCM = mc.getCM( rocket.getDefaultConfiguration(), MassCalcType.NO_MOTORS);
|
||||
|
||||
double expMass = 0.23590802751203407;
|
||||
double expCMx = 0.9615865040919498;
|
||||
double calcMass = boosterSetCM.weight;
|
||||
assertEquals(" Delta Heavy Booster Mass is incorrect: ", expMass, calcMass, EPSILON);
|
||||
|
||||
Coordinate expCM = new Coordinate(expCMx,0,0, expMass);
|
||||
assertEquals(" Delta Heavy Booster CM.x is incorrect: ", expCM.x, boosterSetCM.x, EPSILON);
|
||||
assertEquals(" Delta Heavy Booster CM.y is incorrect: ", expCM.y, boosterSetCM.y, EPSILON);
|
||||
assertEquals(" Delta Heavy Booster CM.z is incorrect: ", expCM.z, boosterSetCM.z, EPSILON);
|
||||
assertEquals(" Delta Heavy Booster CM is incorrect: ", expCM, boosterSetCM);
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testBoosterMotorCM() {
|
||||
// Rocket rocket = TestRockets.makeFalcon9Heavy();
|
||||
// FlightConfiguration defaultConfig = rocket.getDefaultConfiguration();
|
||||
// FlightConfigurationID fcid = defaultConfig.getFlightConfigurationID();
|
||||
// rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
|
||||
//
|
||||
// BoosterSet boosters = (BoosterSet) rocket.getChild(1).getChild(1);
|
||||
// int boostNum = boosters.getStageNumber();
|
||||
// rocket.getDefaultConfiguration().setAllStages(false);
|
||||
// rocket.getDefaultConfiguration().setOnlyStage( boostNum);
|
||||
//// String treeDump = rocket.toDebugTree();
|
||||
//// System.err.println( treeDump);
|
||||
//
|
||||
// // Validate Boosters
|
||||
// InnerTube inner = (InnerTube)boosters.getChild(1).getChild(0);
|
||||
// MassCalculator mc = new MassCalculator();
|
||||
//
|
||||
// double innerStartX = inner.getLocations()[0].x;
|
||||
// double innerLength = inner.getLength();
|
||||
// MotorInstance moto= inner.getMotorInstance(fcid);
|
||||
// double motorStart = innerStartX + innerLength + inner.getMotorOverhang() - moto.getMotor().getLength();
|
||||
//
|
||||
// int instanceCount = boosters.getInstanceCount()*inner.getInstanceCount();
|
||||
// assertEquals(" Total engine count is incorrect: ", 8, instanceCount);
|
||||
//
|
||||
// // LAUNCH
|
||||
// {
|
||||
//
|
||||
// Coordinate motorCM = mc.getMotorCG( rocket.getDefaultConfiguration(), MassCalcType.LAUNCH_MASS);
|
||||
//
|
||||
// double expMotorMass = 0.123*instanceCount;
|
||||
// double actMotorMass = motorCM.weight;
|
||||
// assertEquals(" Booster motor launch mass is incorrect: ", expMotorMass, actMotorMass, EPSILON);
|
||||
//
|
||||
// double expMotorCMX = motorStart + moto.getMotor().getLaunchCG().x;
|
||||
// Coordinate expMotorCM = new Coordinate(expMotorCMX, 0, 0, expMotorMass);
|
||||
// assertEquals(" Booster Motor CM.x is incorrect: ", expMotorCM.x, motorCM.x, EPSILON);
|
||||
// assertEquals(" Booster Motor CM.y is incorrect: ", expMotorCM.y, motorCM.y, EPSILON);
|
||||
// assertEquals(" Booster Motor CM.z is incorrect: ", expMotorCM.z, motorCM.z, EPSILON);
|
||||
// assertEquals(" Booster Motor CM is incorrect: ", expMotorCM, motorCM );
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // EMPTY / BURNOUT
|
||||
// {
|
||||
// Coordinate motorCM = mc.getMotorCG( rocket.getDefaultConfiguration(), MassCalcType.BURNOUT_MASS);
|
||||
//
|
||||
// double expMotorMass = 0.064*instanceCount;
|
||||
// double actMotorMass = motorCM.weight;
|
||||
// assertEquals(" Booster motor burnout mass is incorrect: ", expMotorMass, actMotorMass, EPSILON);
|
||||
//
|
||||
// // vvvv DEVEL vvvv
|
||||
//// System.err.println("\n ====== ====== ");
|
||||
//// System.err.println(String.format(" final position: %g = %g innerTube start", motorStart, innerStart ));
|
||||
//// System.err.println(String.format(" + %g innerTube Length ", innerLength ));
|
||||
//// System.err.println(String.format(" + %g overhang", inner.getMotorOverhang() ));
|
||||
//// System.err.println(String.format(" - %g motor length", moto.getMotor().getLength() ));
|
||||
//// double motorOffs = innerLength + inner.getMotorOverhang() - moto.getMotor().getLength();
|
||||
//// System.err.println(String.format(" [ %g motor Offset ]", motorOffs ));
|
||||
//// System.err.println(String.format(" [ %g motor CM ]", moto.getMotor().getEmptyCG().x));
|
||||
// // ^^^^ DEVEL ^^^^
|
||||
//
|
||||
// double expCMX = motorStart + moto.getMotor().getEmptyCG().x;
|
||||
// Coordinate expMotorCM = new Coordinate(expCMX, 0, 0, expMotorMass);
|
||||
// assertEquals(" Booster Motor CM.x is incorrect: ", expMotorCM.x, motorCM.x, EPSILON);
|
||||
// assertEquals(" Booster Motor CM.y is incorrect: ", expMotorCM.y, motorCM.y, EPSILON);
|
||||
// assertEquals(" Booster Motor CM.z is incorrect: ", expMotorCM.z, motorCM.z, EPSILON);
|
||||
// assertEquals(" Booster Motor CM is incorrect: ", expMotorCM, motorCM );
|
||||
// }
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testBoosterTotalCM() {
|
||||
Rocket rocket = TestRockets.makeFalcon9Heavy();
|
||||
rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
|
||||
|
||||
BoosterSet boosters = (BoosterSet) rocket.getChild(1).getChild(1);
|
||||
int boostNum = boosters.getStageNumber();
|
||||
rocket.getDefaultConfiguration().setAllStages(false);
|
||||
rocket.getDefaultConfiguration().setOnlyStage( boostNum);
|
||||
|
||||
//String treeDump = rocket.toDebugTree();
|
||||
//System.err.println( treeDump);
|
||||
{
|
||||
// Validate Booster Launch Mass
|
||||
MassCalculator mc = new MassCalculator();
|
||||
Coordinate boosterSetCM = mc.getCM( rocket.getDefaultConfiguration(), MassCalcType.LAUNCH_MASS);
|
||||
double calcTotalMass = boosterSetCM.weight;
|
||||
|
||||
double expTotalMass = 1.219908027512034;
|
||||
double expX = 1.2461238889997992;
|
||||
Coordinate expCM = new Coordinate(expX,0,0, expTotalMass);
|
||||
assertEquals(" Booster Launch Mass is incorrect: ", expTotalMass, calcTotalMass, EPSILON);
|
||||
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 Booster Burnout Mass
|
||||
MassCalculator mc = new MassCalculator();
|
||||
Coordinate boosterSetCM = mc.getCM( rocket.getDefaultConfiguration(), MassCalcType.BURNOUT_MASS);
|
||||
double calcTotalMass = boosterSetCM.weight;
|
||||
|
||||
double expTotalMass = 0.7479080275020341;
|
||||
assertEquals(" Booster Launch Mass is incorrect: ", expTotalMass, calcTotalMass, EPSILON);
|
||||
|
||||
double expX = 1.2030731351529202;
|
||||
Coordinate expCM = new Coordinate(expX,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);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTestBoosterStructureMOI() {
|
||||
Rocket rocket = TestRockets.makeFalcon9Heavy();
|
||||
rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
|
||||
FlightConfiguration defaultConfig = rocket.getDefaultConfiguration();
|
||||
|
||||
BoosterSet boosters = (BoosterSet) rocket.getChild(1).getChild(1);
|
||||
int boostNum = boosters.getStageNumber();
|
||||
|
||||
rocket.getDefaultConfiguration().setAllStages(false);
|
||||
rocket.getDefaultConfiguration().setOnlyStage( boostNum);
|
||||
// String treeDump = rocket.toDebugTree();
|
||||
// System.err.println( treeDump);
|
||||
|
||||
// Validate Boosters
|
||||
MassCalculator mc = new MassCalculator();
|
||||
//mc.debug = true;
|
||||
double expMOI_axial = .00144619;
|
||||
double boosterMOI_xx= mc.getRotationalInertia( defaultConfig, 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( defaultConfig, MassCalcType.NO_MOTORS);
|
||||
assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoosterTotalMOI() {
|
||||
Rocket rocket = TestRockets.makeFalcon9Heavy();
|
||||
FlightConfiguration defaultConfig = rocket.getDefaultConfiguration();
|
||||
rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
|
||||
|
||||
BoosterSet boosters = (BoosterSet) rocket.getChild(1).getChild(1);
|
||||
int boostNum = boosters.getStageNumber();
|
||||
|
||||
rocket.getDefaultConfiguration().setAllStages(false);
|
||||
rocket.getDefaultConfiguration().setOnlyStage( boostNum);
|
||||
//String treeDump = rocket.toDebugTree();
|
||||
//System.err.println( treeDump);
|
||||
|
||||
// Validate Boosters
|
||||
MassCalculator mc = new MassCalculator();
|
||||
mc.debug = true;
|
||||
double expMOI_axial = 0.00752743;
|
||||
double boosterMOI_xx= mc.getRotationalInertia( defaultConfig, MassCalcType.LAUNCH_MASS);
|
||||
|
||||
double expMOI_tr = 0.0436639;
|
||||
double boosterMOI_tr= mc.getLongitudinalInertia( defaultConfig, MassCalcType.LAUNCH_MASS);
|
||||
|
||||
assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON);
|
||||
assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON);
|
||||
}
|
||||
|
||||
}
|
||||
|
190
core/test/net/sf/openrocket/masscalc/MassDataTest.java
Normal file
190
core/test/net/sf/openrocket/masscalc/MassDataTest.java
Normal file
@ -0,0 +1,190 @@
|
||||
package net.sf.openrocket.masscalc;
|
||||
|
||||
//import junit.framework.TestCase;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
|
||||
|
||||
public class MassDataTest extends BaseTestCase {
|
||||
|
||||
// tolerance for compared double test results
|
||||
protected final double EPSILON = MathUtil.EPSILON;
|
||||
|
||||
protected final Coordinate ZERO = new Coordinate(0., 0., 0.);
|
||||
|
||||
@Test
|
||||
public void testTwoPointInline() {
|
||||
double m1 = 2.5;
|
||||
Coordinate r1 = new Coordinate(0,-40, 0, m1);
|
||||
double I1ax=28.7;
|
||||
double I1t = I1ax/2;
|
||||
MassData body1 = new MassData(r1, I1ax, I1t);
|
||||
|
||||
double m2 = 5.7;
|
||||
Coordinate r2 = new Coordinate(0, 32, 0, m2);
|
||||
double I2ax=20;
|
||||
double I2t = I2ax/2;
|
||||
MassData body2 = new MassData(r2, I2ax, I2t);
|
||||
|
||||
// point 3 is defined as the CM of bodies 1 and 2 combined.
|
||||
MassData asbly3 = body1.add(body2);
|
||||
|
||||
Coordinate cm3_expected = r1.average(r2);
|
||||
assertEquals(" Center of Mass calculated incorrectly: ", cm3_expected, asbly3.getCM() );
|
||||
|
||||
// these are a bit of a hack, and depend upon all the bodies being along the y=0, z=0 line.
|
||||
Coordinate delta13 = asbly3.getCM().sub( r1);
|
||||
Coordinate delta23 = asbly3.getCM().sub( r2);
|
||||
|
||||
double y13 = delta13.y;
|
||||
double dy_13_2 = MathUtil.pow2( y13); // hack
|
||||
double I13ax = I1ax + m1*dy_13_2;
|
||||
double I13zz = I1t + m1*dy_13_2;
|
||||
|
||||
double y23 = delta23.y;
|
||||
double dy_23_2 = MathUtil.pow2( y23); // hack
|
||||
double I23ax = I2ax + m2*dy_23_2 ;
|
||||
double I23zz = I2t + m2*dy_23_2 ;
|
||||
|
||||
double expI3xx = I13ax+I23ax;
|
||||
double expI3yy = I1t+I2t;
|
||||
double expI3zz = I13zz+I23zz;
|
||||
|
||||
assertEquals("x-axis MOI don't match: ", asbly3.getIxx(), expI3xx, EPSILON*10);
|
||||
|
||||
assertEquals("y-axis MOI don't match: ", asbly3.getIyy(), expI3yy, EPSILON*10);
|
||||
|
||||
assertEquals("z-axis MOI don't match: ", asbly3.getIzz(), expI3zz, EPSILON*10);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTwoPointGeneral() {
|
||||
boolean debug=false;
|
||||
double m1 = 2.5;
|
||||
Coordinate r1 = new Coordinate(0,-40, -10, m1);
|
||||
double I1xx=28.7;
|
||||
double I1t = I1xx/2;
|
||||
MassData body1 = new MassData(r1, I1xx, I1t);
|
||||
|
||||
double m2 = 5.7;
|
||||
Coordinate r2 = new Coordinate(0, 32, 15, m2);
|
||||
double I2xx=20;
|
||||
double I2t = I2xx/2;
|
||||
MassData body2 = new MassData(r2, I2xx, I2t);
|
||||
|
||||
// point 3 is defined as the CM of bodies 1 and 2 combined.
|
||||
MassData asbly3 = body1.add(body2);
|
||||
|
||||
Coordinate cm3_expected = r1.average(r2);
|
||||
// System.err.println(" @(1): "+ body1.toDebug());
|
||||
// System.err.println(" @(2): "+ body2.toDebug());
|
||||
// System.err.println(" @(3): "+ asbly3.toDebug());
|
||||
System.err.println(" Center of Mass: (3) expected: "+ cm3_expected);
|
||||
assertEquals(" Center of Mass calculated incorrectly: ", cm3_expected, asbly3.getCM() );
|
||||
|
||||
|
||||
if(debug){
|
||||
System.err.println(" Body 1: "+ body1.toDebug() );
|
||||
System.err.println(" Body 2: "+ body2.toDebug() );
|
||||
System.err.println(" Body 3: "+ asbly3.toDebug() );
|
||||
}
|
||||
|
||||
|
||||
// these are a bit of a hack, and depend upon all the bodies being along the y=0, z=0 line.
|
||||
Coordinate delta13 = asbly3.getCM().sub( r1);
|
||||
Coordinate delta23 = asbly3.getCM().sub( r2);
|
||||
double x2, y2, z2;
|
||||
|
||||
x2 = MathUtil.pow2( delta13.x);
|
||||
y2 = MathUtil.pow2( delta13.y);
|
||||
z2 = MathUtil.pow2( delta13.z);
|
||||
double I13xx = I1xx + m1*(y2+z2);
|
||||
double I13yy = I1t + m1*(x2+z2);
|
||||
double I13zz = I1t + m1*(x2+y2);
|
||||
|
||||
x2 = MathUtil.pow2( delta23.x);
|
||||
y2 = MathUtil.pow2( delta23.y);
|
||||
z2 = MathUtil.pow2( delta23.z);
|
||||
double I23xx = I2xx + m2*(y2+z2);
|
||||
double I23yy = I2t + m2*(x2+z2);
|
||||
double I23zz = I2t + m2*(x2+y2);
|
||||
|
||||
double expI3xx = I13xx + I23xx;
|
||||
assertEquals("x-axis MOI don't match: ", asbly3.getIxx(), expI3xx, EPSILON*10);
|
||||
|
||||
double expI3yy = I13yy + I23yy;
|
||||
assertEquals("y-axis MOI don't match: ", asbly3.getIyy(), expI3yy, EPSILON*10);
|
||||
|
||||
double expI3zz = I13zz + I23zz;
|
||||
assertEquals("z-axis MOI don't match: ", asbly3.getIzz(), expI3zz, EPSILON*10);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMassDataCompoundCalculations() {
|
||||
double m1 = 2.5;
|
||||
Coordinate r1 = new Coordinate(0,-40, 0, m1);
|
||||
double I1ax=28.7;
|
||||
double I1t = I1ax/2;
|
||||
MassData body1 = new MassData(r1, I1ax, I1t);
|
||||
|
||||
double m2 = m1;
|
||||
Coordinate r2 = new Coordinate(0, -2, 0, m2);
|
||||
double I2ax=28.7;
|
||||
double I2t = I2ax/2;
|
||||
MassData body2 = new MassData(r2, I2ax, I2t);
|
||||
|
||||
double m5 = 5.7;
|
||||
Coordinate r5 = new Coordinate(0, 32, 0, m5);
|
||||
double I5ax=20;
|
||||
double I5t = I5ax/2;
|
||||
MassData body5 = new MassData(r5, I5ax, I5t);
|
||||
|
||||
// point 3 is defined as the CM of bodies 1 and 2 combined.
|
||||
MassData asbly3 = body1.add(body2);
|
||||
|
||||
// point 4 is defined as the CM of bodies 1, 2 and 5 combined.
|
||||
MassData asbly4_indirect = asbly3.add(body5);
|
||||
Coordinate cm4_expected = r1.average(r2).average(r5);
|
||||
|
||||
//System.err.println(" Center of Mass: (3): "+ asbly3.toCMDebug() );
|
||||
//System.err.println(" MOI: (3): "+ asbly3.toIMDebug() );
|
||||
//System.err.println(" Center of Mass: indirect:"+ asbly4_indirect.getCM() );
|
||||
//System.err.println(" Center of Mass: (4) direct: "+ cm4_expected);
|
||||
assertEquals(" Center of Mass calculated incorrectly: ", cm4_expected, new Coordinate( 0, 7.233644859813085, 0, m1+m2+m5 ) );
|
||||
|
||||
// these are a bit of a hack, and depend upon all the bodies being along the y=0, z=0 line.
|
||||
double y4 = cm4_expected.y;
|
||||
double I14ax = I1ax + m1*MathUtil.pow2( Math.abs(body1.getCM().y - y4) );
|
||||
double I24ax = I2ax + m2*MathUtil.pow2( Math.abs(body2.getCM().y - y4) );
|
||||
double I54ax = I5ax + m5*MathUtil.pow2( Math.abs(body5.getCM().y - y4) );
|
||||
|
||||
double I14zz = I1t + m1*MathUtil.pow2( Math.abs(body1.getCM().y - y4) );
|
||||
double I24zz = I2t + m2*MathUtil.pow2( Math.abs(body2.getCM().y - y4) );
|
||||
// System.err.println(String.format(" I24yy: %8g = %6g + %3g*%g", I24zz, I2t, m2, MathUtil.pow2( Math.abs(body2.getCM().y - y4)) ));
|
||||
// System.err.println(String.format(" : delta y24: %8g = ||%g - %g||", Math.abs(body2.getCM().y - y4), body2.getCM().y, y4 ));
|
||||
double I54zz = I5t + m5*MathUtil.pow2( Math.abs(body5.getCM().y - y4) );
|
||||
|
||||
double I4xx = I14ax+I24ax+I54ax;
|
||||
double I4yy = I1t+I2t+I5t;
|
||||
double I4zz = I14zz+I24zz+I54zz;
|
||||
MassData asbly4_expected = new MassData( cm4_expected, I4xx, I4yy, I4zz);
|
||||
//System.err.println(String.format(" Ixx: direct: %12g", I4xx ));
|
||||
assertEquals("x-axis MOI don't match: ", asbly4_indirect.getIxx(), asbly4_expected.getIxx(), EPSILON*10);
|
||||
|
||||
//System.err.println(String.format(" Iyy: direct: %12g", I4yy ));
|
||||
assertEquals("y-axis MOI don't match: ", asbly4_indirect.getIyy(), asbly4_expected.getIyy(), EPSILON*10);
|
||||
|
||||
//System.err.println(String.format(" Izz: direct: %12g", I4zz));
|
||||
assertEquals("z-axis MOI don't match: ", asbly4_indirect.getIzz(), asbly4_expected.getIzz(), EPSILON*10);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user