[bugfix] Overhauled MassCalculator Methods.

- Rocket total mass is split into 'dryMass' and 'propellantMass'
    -- each mass type has a corresponding calculation method in MassCalculator
  - Calculating Inertias and Center-of-Mass FOR MOTORS assume:
    - time-invariant x-coordinate
    - time-decreasing density
  - mass correctly tracks propellant usage, as measured by thrustCurves
  - elimated MassCalcType enum: was not actually solving a problem.
     - simply use motorTime: 0 for launch Double.MAX_VALUE for burnout
     - NO_MOTORS is represented by a configuration without attached motors
       -- try:  "rocket.setSelectedConfiguration( rocket.getEmptyConfiguration())"
     - 'dry mass' vs 'total mass': 90% of the time, a caller wanted 'total mass'
     - total_mass = dry_mass + propellant_mass
     - mass @ simulation time wasn't represented by the enum, had existing overloads anyway
  - get vs calculate methods:
    -- gets revalidate the cache, then retreive specific information
    -- calculate simple calculate the desired information, and ignore the cache.
       --- I'm particularly confident about cache reliability:
           particularly as it doesn't account for changing time during simulation.
  - reduced / simplified debugging messages
This commit is contained in:
Daniel_M_Williams 2016-07-01 16:26:02 -04:00
parent 56463b02fc
commit 04c0914d0a
32 changed files with 904 additions and 644 deletions

View File

@ -94,8 +94,8 @@ public class RocksimSaver extends RocketSaver {
MassCalculator massCalc = new MassCalculator();
final FlightConfiguration configuration = rocket.getSelectedConfiguration();
final double cg = massCalc.getCG(configuration, MassCalculator.MassCalcType.NO_MOTORS).x *
final FlightConfiguration configuration = rocket.getEmptyConfiguration();
final double cg = massCalc.getRocketSpentMassData(configuration).getCM().x *
RocksimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH;
int stageCount = rocket.getStageCount();

View File

@ -4,9 +4,6 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorConfiguration;
import net.sf.openrocket.rocketcomponent.AxialStage;
@ -17,74 +14,27 @@ import net.sf.openrocket.rocketcomponent.ParallelStage;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.simulation.MotorClusterState;
import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Monitorable;
public class MassCalculator implements Monitorable {
// public static enum MassCalcType {
// NO_MOTORS( Double.NaN),
// LAUNCH_MASS(0.),
// BURNOUT_MASS(Double.MAX_VALUE);
//
// public final double motorTime;
//
// MassCalcType( final double _motorTime ){
// this.motorTime = _motorTime; }
//
// };
public static enum MassCalcType {
NO_MOTORS {
@Override
public double getCGx(Motor motor) {
return 0;
}
@Override
public double getMass(Motor motor) {
return 0;
}
},
LAUNCH_MASS {
@Override
public double getCGx(Motor motor) {
return motor.getLaunchCGx();
}
@Override
public double getMass(Motor motor) {
return motor.getLaunchMass();
}
},
BURNOUT_MASS {
@Override
public double getCGx(Motor motor) {
return motor.getBurnoutCGx();
}
@Override
public double getMass(Motor motor) {
return motor.getBurnoutMass();
}
};
public abstract double getMass(Motor motor);
public abstract double getCGx(Motor motor);
/**
* Compute the cg contribution of the motor relative to the rocket's coordinates
*
* @param motorConfig
* @return
*/
public Coordinate getCG(MotorConfiguration motorConfig) {
Motor mtr = motorConfig.getMotor();
double mass = getMass(mtr);
Coordinate cg = motorConfig.getPosition().add( getCGx(mtr), 0, 0, mass);
RocketComponent motorMount = (RocketComponent) motorConfig.getMount();
Coordinate totalCM = new Coordinate();
for (Coordinate cord : motorMount.toAbsolute(cg) ) {
totalCM = totalCM.average(cord);
}
return totalCM;
}
public Coordinate getCM( Motor motor ){
return new Coordinate( getCGx(motor), 0, 0, getMass(motor));
}
}
//private static final Logger log = LoggerFactory.getLogger(MassCalculator.class);
private static final Logger log = LoggerFactory.getLogger(MassCalculator.class);
public boolean debug=false;
public static final double MIN_MASS = 0.001 * MathUtil.EPSILON;
@ -96,14 +46,10 @@ public class MassCalculator implements Monitorable {
* Cached data. All CG data is in absolute coordinates. All moments of inertia
* are relative to their respective CG.
*/
private HashMap< Integer, MassData> cache = new HashMap<Integer, MassData >();
// private MassData dryData = null;
// private MassData launchData = null;
// private Vector< MassData> motorData = new Vector<MassData>();
private HashMap< Integer, MassData> stageMassCache = new HashMap<Integer, MassData >();
private MassData rocketSpentMassCache;
private MassData propellantMassCache;
// this turns on copious amounts of debug. Recommend leaving this false
// until reaching code that causes troublesome conditions.
public boolean debug = false;
////////////////// Constructors ///////////////////
public MassCalculator() {
@ -111,60 +57,189 @@ public class MassCalculator implements Monitorable {
////////////////// Mass property calculations ///////////////////
/**
* Return the CG of the rocket with the specified motor status (no motors,
* ignition, burnout).
*
* @param configuration the rocket configuration
* @param type the state of the motors (none, launch mass, burnout mass)
* @return the CG of the configuration
*/
public Coordinate getCG(FlightConfiguration configuration, MassCalcType type) {
return getCM( configuration, type);
public MassData getRocketSpentMassData( final FlightConfiguration config ){
revalidateCache( config);
return this.rocketSpentMassCache;
}
public Coordinate getCM(FlightConfiguration config, MassCalcType type) {
checkCache(config);
calculateStageCache(config);
public MassData getRocketLaunchMassData( final FlightConfiguration config ){
revalidateCache( config);
return rocketSpentMassCache.add( propellantMassCache );
}
/** calculates the massdata for all propellant in the rocket given the simulation status.
*
* @param status CurrentSimulation status to calculate data with
* @return combined mass data for all propellant
*/
public MassData getPropellantMassData( final SimulationStatus status ){
revalidateCache( status );
// 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);
return propellantMassCache;
}
/** calculates the massdata @ launch for all propellant in the rocket
*
* @param status CurrentSimulation status to calculate data with
* @return combined mass data for all propellant
*/
protected MassData calculatePropellantMassData( final FlightConfiguration config ){
MassData allPropellantData = MassData.ZERO_DATA;
if(debug){// vvvv DEVEL vvvv
System.err.println("====== ====== calculatePropellantMassData( config: "+config.toDebug()+" ) ====== ====== ====== ====== ====== ======");
//String massFormat = " [%2s]: %-16s %6s x %6s = %6s += %6s @ (%s, %s, %s )";
//System.err.println(String.format(massFormat, " #", "<Designation>","Mass","Count","Config","Sum", "x","y","z"));
String inertiaFormat = " [%2s](%2s): %-16s %6s %6s";
System.err.println(String.format(inertiaFormat, " #","ct", "<Designation>","I_ax","I_tr"));
}// ^^^^ DEVEL ^^^^
Collection<MotorConfiguration> activeMotorList = config.getActiveMotors();
for (MotorConfiguration mtrConfig : activeMotorList ) {
MassData curMotorConfigData = calculateClusterPropellantData( mtrConfig, Motor.PSEUDO_TIME_LAUNCH );
allPropellantData = allPropellantData.add( curMotorConfigData );
}
return allPropellantData;
}
/** calculates the massdata @ launch for all propellant in the rocket
*
* @param status CurrentSimulation status to calculate data with
* @return combined mass data for all propellant
*/
protected MassData calculatePropellantMassData( final SimulationStatus status ){
MassData allPropellantData = MassData.ZERO_DATA;
if(debug){// vvvv DEVEL vvvv
System.err.println("====== ====== calculatePropellantMassData( status.config: "+status.getConfiguration().toDebug()+" ) ====== ====== ====== ====== ====== ======");
//String massFormat = " [%2s]: %-16s %6s x %6s = %6s += %6s @ (%s, %s, %s )";
//System.err.println(String.format(massFormat, " #", "<Designation>","Mass","Count","Config","Sum", "x","y","z"));
String inertiaFormat = " [%2s](%2s): %-16s %6s %6s";
System.err.println(String.format(inertiaFormat, " #","ct", "<Designation>","I_ax","I_tr"));
}// ^^^^ DEVEL ^^^^
Coordinate totalCM=null;
if( MassCalcType.NO_MOTORS == type ){
totalCM = dryCM;
}else{
MassData motorData = getMotorMassData(config, type);
Coordinate motorCM = motorData.getCM();
totalCM = dryCM.average(motorCM);
Collection<MotorClusterState> motorStates = status.getActiveMotors();
for (MotorClusterState state: motorStates) {
final double motorTime = state.getMotorTime( status.getSimulationTime() );
MassData clusterPropData = calculateClusterPropellantData( state.getConfig(), motorTime );
allPropellantData = allPropellantData.add( clusterPropData);
}
return allPropellantData;
}
// helper method to calculate the propellant mass data for a given motor cluster( i.e. MotorConfiguration)
private MassData calculateClusterPropellantData( final MotorConfiguration mtrConfig, final double motorTime ){
final Motor mtr = mtrConfig.getMotor();
final MotorMount mnt = mtrConfig.getMount();
final int instanceCount = mnt.getInstanceCount();
return totalCM;
// location of mount, w/in entire rocket
final Coordinate[] locations = mnt.getLocations();
final double motorXPosition = mtrConfig.getX(); // location of motor from mount
final double propMassEach = mtr.getPropellantMass( motorTime );
final double propCMxEach = mtr.getCMx( motorTime); // CoM from beginning of motor
// coordinates in rocket frame; Ir, It about CoM.
final Coordinate curClusterCM = new Coordinate( locations[0].x + motorXPosition + propCMxEach, 0, 0, propMassEach*instanceCount);
final double unitRotationalInertiaEach = mtrConfig.getUnitRotationalInertia();
final double unitLongitudinalInertiaEach = mtrConfig.getUnitLongitudinalInertia();
double Ir=unitRotationalInertiaEach*instanceCount*propMassEach;
double It=unitLongitudinalInertiaEach*instanceCount*propMassEach;
if(debug){
System.err.println(String.format(" Motor: %-16s ( %2dx): m: %6.4f l: %6.4f od: %6.4f I_xx_u: %6.4g I_yy_u: %6.4g",
mtr.getDesignation(), instanceCount, propMassEach, mtr.getLength(), mtr.getDiameter(), unitRotationalInertiaEach, unitLongitudinalInertiaEach));
}// ^^^^ DEVEL ^^^^
if( 1 < instanceCount ){
// if not on rocket centerline, then add an offset factor, according to the parallel axis theorem:
for( Coordinate coord : locations ){
double distance = Math.hypot( coord.y, coord.z);
Ir += propMassEach*Math.pow( distance, 2);
}
}
if(debug){
System.err.println(String.format(" :cluster: m: %6.4f Ixx: %6.4g Iyy: %6.4g", curClusterCM.weight, Ir, It));
}
return new MassData( curClusterCM, Ir, It);
}
/**
* Compute the CM of all motors, given a configuration and type
* Calculates mass data of the rocket burnout mass
*
* @param configuration the rocket configuration
* @param motors the motor configuration
* I.O.W., this mass data is invariant during thrust (see also: calculatePropellantMassData(...) )
*
* @param configuration a given rocket configuration
* @return the CG of the configuration
*/
public MassData getMotorMassData(FlightConfiguration config, MassCalcType type) {
if( MassCalcType.NO_MOTORS == type ){
return MassData.ZERO_DATA;
}
protected MassData calculateBurnoutMassData( final FlightConfiguration config) {
if(debug){// vvvv DEVEL vvvv
//String massFormat = " [%2s]: %-16s %6s x %6s = %6s += %6s @ (%s, %s, %s )";
String inertiaFormat = " [%2s](%2s): %-16s %6s %6s";
System.err.println("====== ====== getMotorMassData( config:"+config.toDebug()+" ) ====== ====== ====== ====== ====== ======");
//System.err.println(String.format(massFormat, " #", "<Designation>","Mass","Count","Config","Sum", "x","y","z"));
System.err.println(String.format(inertiaFormat, " #","ct", "<Designation>","I_ax","I_tr"));
}// ^^^^ DEVEL ^^^^
MassData exceptMotorsMassData = calculateStageData( config);
if(debug){// vvvv DEVEL vvvv
System.err.println(" exc motors stage data: "+exceptMotorsMassData );
System.err.println(" ====== ====== ^^^^ stage data ^^^^ ====== ======\n");
System.err.println(" ====== ====== vvvv motor spent mass data vvvv ====== ======\n");
}// ^^^^ DEVEL ^^^^
MassData motorMassData = calculateMotorBurnoutMassData( config);
if(debug){ // vvvv DEVEL vvvv
System.err.println(" exc motors stage data: "+motorMassData);
System.err.println(" ====== ====== ^^^^ motor spent mass data ^^^^ ====== ======\n\n");
} // ^^^^ DEVEL ^^^^
return exceptMotorsMassData.add( motorMassData );
}
private MassData calculateStageData( final FlightConfiguration config ){
MassData componentData = MassData.ZERO_DATA;
// Stages
for (AxialStage stage : config.getActiveStages()) {
int stageNumber = stage.getStageNumber();
MassData stageData = this.calculateAssemblyMassData( stage );
stageMassCache.put(stageNumber, stageData);
componentData = componentData.add(stageData);
}
return componentData;
}
/**
* Compute the burnout mass properties all motors, given a configuration
*
* Will calculate data for:MassCalcType.BURNOUT_MASS
*
* @param configuration the rocket configuration
* @return the MassData struct of the motors at burnout
*/
private MassData calculateMotorBurnoutMassData(FlightConfiguration config) {
// // vvvv DEVEL vvvv
// //String massFormat = " [%2s]: %-16s %6s x %6s = %6s += %6s @ (%s, %s, %s )";
// String inertiaFormat = " [%2s](%2s): %-16s %6s %6s";
@ -176,163 +251,64 @@ public class MassCalculator implements Monitorable {
// // ^^^^ DEVEL ^^^^
MassData allMotorData = MassData.ZERO_DATA;
//int motorIndex = 0;
for (MotorConfiguration mtrConfig : config.getActiveMotors() ) {
Motor mtr = (Motor) mtrConfig.getMotor();
MotorMount mount = mtrConfig.getMount();
RocketComponent mountComp = (RocketComponent)mount;
Coordinate[] locations = mountComp.getLocations(); // location of mount, w/in entire rocket
// use 'mount.getLocations()' because:
// 1) includes ALL clustering sources!
// 2) location of mount, w/in entire rocket
// 3) Note: mount.getInstanceCount() ONLY indicates instancing of the mount's cluster, not parent components (such as stages)
Coordinate[] locations = mount.getLocations();
int instanceCount = locations.length;
double motorXPosition = mtrConfig.getX(); // location of motor from mount
final double burnoutMassEach = mtr.getBurnoutMass();
final double burnoutCMx = mtr.getBurnoutCGx(); // CoM from beginning of motor
Coordinate localCM = type.getCM( mtr ); // CoM from beginning of motor
localCM = localCM.setWeight( localCM.weight * instanceCount);
// a *bit* hacky :P
Coordinate curMotorCM = localCM.setX( localCM.x + locations[0].x + motorXPosition );
final Coordinate clusterCM = new Coordinate( locations[0].x + motorXPosition + burnoutCMx, 0, 0, burnoutMassEach*instanceCount);
// alternate version:
// double Ir = inst.getRotationalInertia();
// double It = inst.getLongitudinalInertia();
// +
// + Coordinate curMotorCM = type.getCG(inst);
final double unitRotationalInertia = mtrConfig.getUnitRotationalInertia();
final double unitLongitudinalInertia = mtrConfig.getUnitLongitudinalInertia();
if(debug){// vv DEBUG
System.err.println(String.format(" Processing f/mount: %s [%8s] (ct: %d)(w/spent mass = %g)", mtrConfig.getMount(), mtr.getDesignation(), instanceCount, mtr.getBurnoutMass()));
double eachIxx = unitRotationalInertia*burnoutMassEach;
double eachIyy = unitLongitudinalInertia*burnoutMassEach;
System.err.println(String.format("(MOI: [%8g, %8g])", eachIxx, eachIyy));
} // ^^ DEBUG
double motorMass = curMotorCM.weight;
double Ir_single = mtrConfig.getUnitRotationalInertia()*motorMass;
double It_single = mtrConfig.getUnitLongitudinalInertia()*motorMass;
double Ir=0;
double It=0;
if( 1 == instanceCount ){
Ir=Ir_single;
It=It_single;
}else{
It = It_single * instanceCount;
double Ir=(unitRotationalInertia*burnoutMassEach)*instanceCount;
double It=(unitLongitudinalInertia*burnoutMassEach)*instanceCount;
if( 1 < instanceCount ){
if(debug){// vv DEBUG
System.err.println(String.format(" Instanced. %d motors in a %s", instanceCount, mount.getClusterConfiguration().getXMLName()));
System.err.println(String.format(" I_long: %6g * %6g * %d = %6g ", unitLongitudinalInertia, burnoutMassEach, instanceCount, It));
System.err.println(String.format(" I_rot_base: %6g * %6g * %d = %6g ", unitRotationalInertia, burnoutMassEach, instanceCount, Ir));
} // ^^ DEBUG
Ir = Ir_single*instanceCount;
// these need more complex instancing code...
for( Coordinate coord : locations ){
double distance = Math.hypot( coord.y, coord.z);
Ir += motorMass*Math.pow( distance, 2);
double distance_squared = ((coord.y*coord.y) + (coord.z*coord.z));
double instance_correction = burnoutMassEach*distance_squared;
Ir += instance_correction;
}
if(debug){// vv DEBUG
System.err.println(String.format(" I_rot: %6g ", Ir));
} // ^^ DEBUG
}
MassData configData = new MassData( curMotorCM, Ir, It);
MassData configData = new MassData( clusterCM, Ir, It);
allMotorData = allMotorData.add( configData );
// BEGIN DEVEL
//if( debug){
// // Inertia
// System.err.println(String.format( inertiaFormat, motorIndex, instanceCount, mtr.getDesignation(), Ir, It));
// // mass only
//double singleMass = type.getCG( mtr ).weight;
//System.err.println(String.format( massFormat, motorIndex, mtr.getDesignation(),
// singleMass, instanceCount, curMotorCM.weight, allMotorData.getMass(),curMotorCM.x, curMotorCM.y, curMotorCM.z ));
//}
//motorIndex++;
// END DEVEL
}
return allMotorData;
}
/**
* Return the longitudinal inertia of the rocket with the specified motor instance
* 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 config, MassCalcType type) {
checkCache(config);
calculateStageCache(config);
MassData structureData = MassData.ZERO_DATA;
// Stages
for (AxialStage stage : config.getActiveStages()) {
int stageNumber = stage.getStageNumber();
MassData stageData = cache.get(stageNumber);
structureData = structureData.add(stageData);
}
MassData motorData = MassData.ZERO_DATA;
if( MassCalcType.NO_MOTORS != type ){
motorData = getMotorMassData(config, type);
}
MassData totalData = structureData.add( motorData);
if(debug){
System.err.println(String.format(" >> Structural MassData: %s", structureData.toDebug()));
System.err.println(String.format(" >> Motor MassData: %s", motorData.toDebug()));
System.err.println(String.format("==>> Combined MassData: %s", totalData.toDebug()));
}
return totalData.getLongitudinalInertia();
}
/**
* Compute the rotational inertia of the provided configuration with specified motors.
*
* @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 config, MassCalcType type) {
checkCache(config);
calculateStageCache(config);
MassData structureData = MassData.ZERO_DATA;
// Stages
for (AxialStage stage : config.getActiveStages()) {
int stageNumber = stage.getStageNumber();
MassData stageData = cache.get(stageNumber);
structureData = structureData.add(stageData);
}
MassData motorData = MassData.ZERO_DATA;
if( MassCalcType.NO_MOTORS != type ){
motorData = getMotorMassData(config, type);
}
MassData totalData = structureData.add( motorData);
if(debug){
System.err.println(String.format(" >> Structural MassData: %s", structureData.toDebug()));
System.err.println(String.format(" >> Motor MassData: %s", motorData.toDebug()));
System.err.println(String.format("==>> Combined MassData: %s", totalData.toDebug()));
}
return totalData.getRotationalInertia();
}
/**
* Return the total mass of the motors
*
* @param motors the motor configuration
* @param configuration the current motor instance configuration
* @return the total mass of all 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 ( MassCalcType.NO_MOTORS != calcType ){
for (MotorConfiguration curConfig : configuration.getActiveMotors()) {
int instanceCount = curConfig.getMount().getInstanceCount();
mass = mass + curConfig.getPropellantMass()*instanceCount;
}
}
return mass;
}
/**
* Return the total mass of the motors
*
@ -341,14 +317,7 @@ public class MassCalculator implements Monitorable {
* @return the total mass of all motors
*/
public double getPropellantMass(SimulationStatus status ){
double mass = 0;
Collection<MotorClusterState> activeMotorList = status.getMotors();
for (MotorClusterState curConfig : activeMotorList ) {
int instanceCount = curConfig.getMount().getInstanceCount();
double motorTime = curConfig.getMotorTime(status.getSimulationTime());
mass += (curConfig.getMotor().getTotalMass(motorTime) - curConfig.getMotor().getBurnoutMass())*instanceCount;
}
return mass;
return (getPropellantMassData( status )).getCM().weight;
}
/**
@ -362,43 +331,28 @@ public class MassCalculator implements Monitorable {
* @param type the state of the motors (none, launch mass, burnout mass)
* @return a map from each rocket component to its corresponding CG.
*/
public Map<RocketComponent, Coordinate> getCGAnalysis(FlightConfiguration configuration, MassCalcType type) {
checkCache(configuration);
calculateStageCache(configuration);
public Map<RocketComponent, Coordinate> getCGAnalysis(FlightConfiguration configuration) {
revalidateCache(configuration);
Map<RocketComponent, Coordinate> map = new HashMap<RocketComponent, Coordinate>();
Coordinate rocketCG = Coordinate.ZERO;
for (RocketComponent comp : configuration.getActiveComponents()) {
Coordinate[] cgs = comp.toAbsolute(comp.getCG());
Coordinate totalCG = Coordinate.NUL;
Coordinate stageCG = Coordinate.NUL;
for (Coordinate cg : cgs) {
totalCG = totalCG.average(cg);
stageCG = stageCG.average(cg);
}
map.put(comp, totalCG);
map.put(comp, stageCG);
rocketCG.average( stageCG);
}
map.put(configuration.getRocket(), getCG(configuration, type));
map.put(configuration.getRocket(), rocketCG );
return map;
}
//////// Cache computations ////////
private void calculateStageCache(FlightConfiguration config) {
int stageCount = config.getActiveStageCount();
if(debug){
System.err.println(">> Calculating massData cache for config: "+config.toDebug()+" with "+stageCount+" stages");
}
if( 0 < stageCount ){
for( AxialStage curStage : config.getActiveStages()){
int index = curStage.getStageNumber();
MassData stageData = calculateAssemblyMassData( curStage);
cache.put(index, stageData);
}
}
}
/**
* Returns the mass and inertia data for this component and all subcomponents.
@ -423,9 +377,9 @@ public class MassCalculator implements Monitorable {
}
// default if not instanced (instance count == 1)
MassData resultantData = new MassData( compCM, compIx, compIt);
MassData assemblyData = new MassData( compCM, compIx, compIt);
if( debug && (MIN_MASS < compCM.weight)){
if( debug && ( MIN_MASS < compCM.weight)){
System.err.println(String.format("%-32s: %s ",indent+"ea["+ component.getName()+"]", compCM ));
if( component.isMassOverridden() && component.isMassOverridden() && component.getOverrideSubcomponents()){
System.err.println(indent+" ?["+ component.isMassOverridden()+"]["+
@ -447,20 +401,20 @@ public class MassCalculator implements Monitorable {
childrenData = childrenData.add( childData );
}
resultantData = resultantData.add( childrenData);
assemblyData = assemblyData.add( childrenData);
// if instanced, adjust children's data too.
if ( 1 < component.getInstanceCount() ){
if( debug ){
if(debug){// vv DEBUG
System.err.println(String.format("%s Found instanceable with %d children: %s (t= %s)",
indent, component.getInstanceCount(), component.getName(), component.getClass().getSimpleName() ));
}
}// ^^ DEBUG
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 templateCM = resultantData.cm;
Coordinate templateCM = assemblyData.cm;
MassData instAccumData = new MassData(); // accumulator for instance MassData
Coordinate[] instanceLocations = ((Instanceable) component).getInstanceOffsets();
for( Coordinate curOffset : instanceLocations ){
@ -472,55 +426,77 @@ public class MassCalculator implements Monitorable {
instAccumData = instAccumData.add( instanceData);
}
resultantData = instAccumData;
assemblyData = instAccumData;
if( debug && (MIN_MASS < compCM.weight)){
System.err.println(String.format("%-32s: %s ", indent+"x"+component.getInstanceCount()+"["+component.getName()+"][asbly]", resultantData.toDebug()));
System.err.println(String.format("%-32s: %s ", indent+"x"+component.getInstanceCount()+"["+component.getName()+"][asbly]", assemblyData.toDebug()));
}
}
// move to parent's reference point
resultantData = resultantData.move( component.getOffset() );
assemblyData = assemblyData.move( component.getOffset() );
if( component instanceof ParallelStage ){
// hacky correction for the fact Booster Stages aren't direct subchildren to the rocket
resultantData = resultantData.move( component.getParent().getOffset() );
assemblyData = assemblyData.move( component.getParent().getOffset() );
}
// Override total data
if (component.getOverrideSubcomponents()) {
if( debug){
System.err.println(String.format("%-32s: %s ", indent+"vv["+component.getName()+"][asbly]", resultantData.toDebug()));
}
if(debug){// vv DEBUG
System.err.println(String.format("%-32s: %s ", indent+"vv["+component.getName()+"][asbly]", assemblyData.toDebug()));
}// ^^ DEBUG
if (component.isMassOverridden()) {
double oldMass = resultantData.getMass();
double oldMass = assemblyData.getMass();
double newMass = MathUtil.max(component.getOverrideMass(), MIN_MASS);
Coordinate newCM = resultantData.getCM().setWeight(newMass);
Coordinate newCM = assemblyData.getCM().setWeight(newMass);
double newIxx = resultantData.getIxx() * newMass / oldMass;
double newIyy = resultantData.getIyy() * newMass / oldMass;
double newIzz = resultantData.getIzz() * newMass / oldMass;
double newIxx = assemblyData.getIxx() * newMass / oldMass;
double newIyy = assemblyData.getIyy() * newMass / oldMass;
double newIzz = assemblyData.getIzz() * newMass / oldMass;
resultantData = new MassData( newCM, newIxx, newIyy, newIzz );
assemblyData = new MassData( newCM, newIxx, newIyy, newIzz );
}
if (component.isCGOverridden()) {
double oldx = resultantData.getCM().x;
double oldx = assemblyData.getCM().x;
double newx = component.getOverrideCGX();
Coordinate delta = new Coordinate(newx-oldx, 0, 0);
if(debug){
if(debug){// vv DEBUG
System.err.println(String.format("%-32s: x: %g => %g (%g)", indent+" 88", oldx, newx, delta.x));
}
resultantData = resultantData.move( delta );
}// ^^ DEBUG
assemblyData = assemblyData.move( delta );
}
}
if( debug){
System.err.println(String.format("%-32s: %s ", indent+"<<["+component.getName()+"][asbly]", resultantData.toDebug()));
if(debug){// vv DEBUG
System.err.println(String.format("%-32s: %s ", indent+"<<["+component.getName()+"][asbly]", assemblyData.toDebug()));
}// ^^ DEBUG
return assemblyData;
}
/// nooooot quite done, yet.
public void revalidateCache( final SimulationStatus status ){
//if( ... check what? the config may not have changed, but if the time has, we want to recalculate the cache!
rocketSpentMassCache = calculateBurnoutMassData( status.getConfiguration() );
propellantMassCache = calculatePropellantMassData( status);
//}
}
public void revalidateCache( final FlightConfiguration config ){
checkCache( config);
if( null == rocketSpentMassCache ){
rocketSpentMassCache = calculateBurnoutMassData(config);
}
if( null == propellantMassCache ){
propellantMassCache = calculatePropellantMassData( config);
}
return resultantData;
}
/**
@ -534,13 +510,16 @@ public class MassCalculator implements Monitorable {
*
* @param configuration the configuration of the current call
*/
protected final void checkCache(FlightConfiguration configuration) {
protected final boolean checkCache(FlightConfiguration configuration) {
//System.err.println("?? Checking the cache ... ");
if (rocketMassModID != configuration.getRocket().getMassModID() ||
rocketTreeModID != configuration.getRocket().getTreeModID()) {
rocketMassModID = configuration.getRocket().getMassModID();
rocketTreeModID = configuration.getRocket().getTreeModID();
voidMassCache();
return false;
}
return true;
}
/**
@ -551,11 +530,12 @@ public class MassCalculator implements Monitorable {
* its execution.
*/
protected void voidMassCache() {
this.cache.clear();
this.stageMassCache.clear();
this.rocketSpentMassCache=null;
this.propellantMassCache=null;
}
@Override
public int getModID() {
return 0;

View File

@ -1,6 +1,7 @@
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;
@ -227,16 +228,6 @@ public class MassData {
return I_cm.zz;
}
// this is a tacked-on hack.
double m_p = Double.NaN;
public void setPropellantMass( final double _mp){
this.m_p = _mp;
}
public double getPropellantMass(){
return this.m_p;
}
/**
* 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
@ -293,8 +284,8 @@ public class MassData {
@Override
public String toString() {
return "MassData [cg=" + cm + ", longitudinalInertia=" + getIyy()
+ ", rotationalInertia=" + getIxx() + "]";
return "MassData [cg=" + cm
+ ", rotationalInertia=" + getIxx() + ", longitudinalInertia=" + getIyy() + "]";
}
}

View File

@ -45,7 +45,11 @@ public interface Motor extends Cloneable {
}
}
public static final double PSEUDO_TIME_EMPTY = Double.NaN;
public static final double PSEUDO_TIME_LAUNCH = 0.0;
public static final double PSEUDO_TIME_BURNOUT = Double.MAX_VALUE;
/**
* Ejection charge delay value signifying a "plugged" motor with no ejection charge.
* The value is that of <code>Double.POSITIVE_INFINITY</code>.
@ -118,7 +122,7 @@ public interface Motor extends Cloneable {
public Motor clone();
public double getAverageThrust( final double startTime, final double endTime );
public double getLaunchCGx();
public double getBurnoutCGx();
@ -169,11 +173,18 @@ public interface Motor extends Cloneable {
*/
public double getTotalMass( final double motorTime);
public double getPropellantMass( final Double motorTime);
/** Return the mass at a given time
*
* @param motorTime time (in seconds) since motor ignition
* @return
*/
public double getCGx( final double motorTime);
public double getCMx( final double motorTime);
public double getUnitIxx();
public double getUnitIyy();
public double getUnitIzz();
}

View File

@ -43,8 +43,8 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
private final double length;
private final double[] time;
private final double[] thrust;
// private final double[] cgx; // cannot add without rebuilding the motor database ... automatically on every user's install.
// private final double[] mass; // cannot add without rebuilding the motor database ... on every user's install.
// private final double[] cgx; // cannot add without rebuilding the motor database ... *automatically on every user's install*
// private final double[] mass; // cannot add without rebuilding the motor database ... <see above>
private final Coordinate[] cg;
private double maxThrust;
private double burnTimeEstimate;
@ -317,7 +317,7 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
}
@Override
public double getCGx( final double motorTime ){
public double getCMx( final double motorTime ){
double pseudoIndex = getPseudoIndex( motorTime );
return this.interpolateCenterOfMassAtIndex( pseudoIndex).x;
}
@ -378,6 +378,21 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
public double getUnitRotationalInertia() {
return this.unitRotationalInertia;
}
@Override
public double getUnitIxx() {
return this.unitRotationalInertia;
}
@Override
public double getUnitIyy() {
return this.unitLongitudinalInertia;
}
@Override
public double getUnitIzz(){
return this.unitLongitudinalInertia;
}
@Override
public String getDesignation() {
@ -428,21 +443,19 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
@Override
public double getBurnoutMass() {
return cg[cg.length-1].weight; //mass[mass.length - 1];
}
}
@Override
public double getBurnTime() {
return time[time.length-1];
}
// FIXME - there seems to be some numeric problems in here...
// simple linear interpolation... not sample density for anything more complex
private static double interpolateAtIndex( final double[] values, final double pseudoIndex ){
final double SNAP_TOLERANCE = 0.0001;
final int lowerIndex = (int)pseudoIndex;
final int upperIndex= lowerIndex+1;
//
final double lowerFrac = pseudoIndex - ((double) lowerIndex);
final double upperFrac = 1-lowerFrac;
@ -482,6 +495,17 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
return interpolateCenterOfMassAtIndex( pseudoIndex).weight;
}
public double getPropellantMass(){
return (getLaunchMass() - getBurnoutMass());
}
@Override
public double getPropellantMass( final Double motorTime){
final double pseudoIndex = getPseudoIndex( motorTime);
final double totalMass = interpolateCenterOfMassAtIndex( pseudoIndex).weight;
return totalMass - this.getBurnoutMass();
}
protected Coordinate interpolateCenterOfMassAtIndex( final double pseudoIndex ){
final double SNAP_TOLERANCE = 0.0001;
@ -692,6 +716,5 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
return value;
}
}

View File

@ -192,7 +192,6 @@ public class ThrustCurveMotorPlaceholder implements Motor {
return emptyMass;
}
@Override
public double getThrust(double pseudoIndex) {
return 0;
@ -209,13 +208,32 @@ public class ThrustCurveMotorPlaceholder implements Motor {
}
@Override
public double getCGx(double pseudoIndex) {
public double getPropellantMass( final Double motorTime){
return 0.;
}
@Override
public double getCMx(double pseudoIndex) {
return 0;
}
@Override
public double getBurnTime() {
return 0;
}
@Override
public double getUnitIxx() {
return 0.;
}
@Override
public double getUnitIyy() {
return 0.;
}
@Override
public double getUnitIzz(){
return 0.;
}
}

View File

@ -5,7 +5,6 @@ import net.sf.openrocket.aerodynamics.BarrowmanCalculator;
import net.sf.openrocket.aerodynamics.FlightConditions;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.optimization.rocketoptimization.SimulationDomain;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.RocketComponent;
@ -74,7 +73,7 @@ public class StabilityDomain implements SimulationDomain {
// TODO: HIGH: This re-calculates the worst theta value every time
cp = aerodynamicCalculator.getWorstCP(configuration, conditions, null);
cg = massCalculator.getCG(configuration, MassCalcType.LAUNCH_MASS);
cg = massCalculator.getRocketLaunchMassData(configuration).getCM();
if (cp.weight > 0.000001)
cpx = cp.x;

View File

@ -9,7 +9,6 @@ import net.sf.openrocket.aerodynamics.FlightConditions;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.optimization.general.OptimizationException;
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
@ -66,7 +65,8 @@ public class StabilityParameter implements OptimizableParameter {
conditions.setRollRate(0);
cp = aerodynamicCalculator.getWorstCP(configuration, conditions, null);
cg = massCalculator.getCG(configuration, MassCalcType.LAUNCH_MASS);
// worst case CM is also
cg = massCalculator.getRocketLaunchMassData(configuration).getCM();
if (cp.weight > 0.000001)
cpx = cp.x;

View File

@ -164,6 +164,26 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
return null;
}
@Override
public void toDebugTreeNode(final StringBuilder buffer, final String indent) {
Coordinate[] relCoords = this.getInstanceOffsets();
Coordinate[] absCoords = this.getLocations();
if( 1 == getInstanceCount()){
buffer.append(String.format("%-40s| %5.3f; %24s; %24s;", indent+this.getName()+" (# "+this.getStageNumber()+")",
this.getLength(), this.getOffset(), this.getLocations()[0]));
buffer.append(String.format("len: %6.4f )(offset: %4.1f via: %s )\n", this.getLength(), this.getAxialOffset(), this.relativePosition.name()));
}else{
buffer.append(String.format("%-40s|(len: %6.4f )(offset: %4.1f via: %s)\n", (indent+this.getName()+"(# "+this.getStageNumber()+")"), this.getLength(), this.getAxialOffset(), this.relativePosition.name()));
for (int instanceNumber = 0; instanceNumber < this.getInstanceCount(); instanceNumber++) {
Coordinate instanceRelativePosition = relCoords[instanceNumber];
Coordinate instanceAbsolutePosition = absCoords[instanceNumber];
final String prefix = String.format("%s [%2d/%2d]", indent, instanceNumber+1, getInstanceCount());
buffer.append(String.format("%-40s| %5.3f; %24s; %24s;\n", prefix, this.getLength(), instanceRelativePosition, instanceAbsolutePosition));
}
}
}
}

View File

@ -472,4 +472,9 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
copy.motors = new MotorConfigurationSet( this.motors, copy );
return copy;
}
@Override
public ClusterConfiguration getClusterConfiguration() {
return ClusterConfiguration.SINGLE;
}
}

View File

@ -546,7 +546,9 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
motors.size(), getName(), getId().toShortKey(), this.instanceNumber));
for( MotorConfiguration curConfig : this.motors.values() ){
buf.append(" "+curConfig.toDebugDetail()+"\n");
boolean active=this.isStageActive( curConfig.getMount().getStage().getStageNumber());
String activeString = (active?"active":" ");
buf.append(" "+"("+activeString+")"+curConfig.toDebugDetail()+"\n");
}
buf.append("\n");
return buf.toString();

View File

@ -406,42 +406,5 @@ public class InnerTube extends ThicknessRingComponent implements Clusterable, Ra
public String toMotorDebug( ){
return this.motors.toDebug();
}
@Override
public void toDebugTreeNode(final StringBuilder buffer, final String prefix) {
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.getInstanceOffsets();
Coordinate[] absCoords = this.getLocations();
FlightConfigurationId curId = this.getRocket().getSelectedConfiguration().getFlightConfigurationID();
final int intanceCount = this.getInstanceCount();
MotorConfiguration curInstance = this.motors.get(curId);
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 < intanceCount; instanceNumber++) {
Coordinate tubeRelativePosition = relCoords[instanceNumber];
Coordinate tubeAbsolutePosition = absCoords[instanceNumber];
buffer.append(String.format("%s [%2d/%2d]; %28s; %28s;\n", prefix, instanceNumber+1, intanceCount,
tubeRelativePosition, tubeAbsolutePosition));
}
}else{
// curInstance has a motor ...
Motor curMotor = curInstance.getMotor();
final 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 < intanceCount; instanceNumber++) {
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+1, intanceCount,
motorRelativePosition, motorAbsolutePosition));
}
}
}
}

View File

@ -54,6 +54,13 @@ public interface MotorMount extends ChangeSource, FlightConfigurableComponent {
*/
public int getInstanceCount();
/**
* Get the current cluster configuration.
* @return The current cluster configuration.
*/
public ClusterConfiguration getClusterConfiguration();
/**
* Get the length of this motor mount. Synonymous with the RocketComponent method.
*
@ -63,11 +70,16 @@ public interface MotorMount extends ChangeSource, FlightConfigurableComponent {
// duplicate of RocketComponent
public String getID();
// duplicate of RocketComponent
public String getDebugName();
// duplicate of RocketComponent
public AxialStage getStage();
// duplicate of RocketComponent
public Coordinate[] getLocations();
/**
*
* @param fcid id for which to return the motor (null retrieves the default)

View File

@ -219,23 +219,7 @@ public class ParallelStage extends AxialStage implements FlightConfigurableCompo
this.angularPosition_rad = MathUtil.reduce180( angle_rad);
fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
}
@Override
public void toDebugTreeNode(final StringBuilder buffer, final String prefix) {
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.getInstanceOffsets();
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 [%2d/%2d]; %28s; %28s;\n", prefix, instanceNumber+1, count,
instanceRelativePosition, instanceAbsolutePosition));
}
}
@Override
protected void update() {
super.update();

View File

@ -659,28 +659,27 @@ public class Rocket extends RocketComponent {
return false;
}
/**
* Return a flight configuration. If the supplied id does not have a specific instance, the default is returned.
*
* @param fcid the flight configuration id
* @return FlightConfiguration instance
*/
public FlightConfiguration createFlightConfiguration( final FlightConfigurationId fcid) {
public FlightConfigurationId createFlightConfiguration( final FlightConfigurationId fcid) {
checkState();
if( null == fcid ){
// fall-through to the default case...
// creating a FlightConfiguration( null ) just allocates a fresh new FCID
// fall-through to the default case:
// ...creating a FlightConfiguration( null ) just allocates a fresh new FCID
}else if( fcid.hasError() ){
return configSet.getDefault();
return configSet.getDefault().getFlightConfigurationID();
}else if( configSet.containsId(fcid)){
return this.getFlightConfiguration(fcid);
return fcid;
}
FlightConfiguration nextConfig = new FlightConfiguration(this, fcid);
this.configSet.set(nextConfig.getId(), nextConfig);
fireComponentChangeEvent(ComponentChangeEvent.TREE_CHANGE);
return nextConfig;
return nextConfig.getFlightConfigurationID();
}
@ -850,5 +849,9 @@ public class Rocket extends RocketComponent {
}
return buf.toString();
}
public FlightConfiguration getEmptyConfiguration() {
return this.configSet.getDefault();
}
}

View File

@ -13,6 +13,8 @@ import org.slf4j.LoggerFactory;
import net.sf.openrocket.appearance.Appearance;
import net.sf.openrocket.appearance.Decal;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorConfiguration;
import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.ArrayList;
@ -2118,22 +2120,80 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
public String toDebugTree() {
StringBuilder buffer = new StringBuilder();
buffer.append("\n ====== ====== ====== ====== ====== ====== ====== ====== ====== ====== ====== ======\n");
buffer.append(" [Name] [Length] [Rel Pos] [Abs Pos] \n");
this.dumpTreeHelper(buffer, "");
buffer.append(" [Name] [Length] [Rel Pos] [Abs Pos] \n");
this.toDebugTreeHelper(buffer, "");
return buffer.toString();
}
public void toDebugTreeNode(final StringBuilder buffer, final String prefix) {
buffer.append(String.format("%-40s; %5.3f; %24s; %24s;\n", prefix+" "+this.getName(),
this.getLength(), this.getOffset(), this.getLocations()[0]));
}
public void dumpTreeHelper(StringBuilder buffer, final String prefix) {
this.toDebugTreeNode(buffer, prefix);
public void toDebugTreeHelper(StringBuilder buffer, final String indent) {
this.toDebugTreeNode(buffer, indent);
Iterator<RocketComponent> iterator = this.children.iterator();
while (iterator.hasNext()) {
iterator.next().dumpTreeHelper(buffer, prefix + "....");
iterator.next().toDebugTreeHelper(buffer, indent + "....");
}
}
// this method is in need of some refactoring...
// eventually, combine the stage-instance debug code into here...
public void toDebugTreeNode(final StringBuilder buffer, final String indent) {
final String prefix = String.format("%s%s", indent, this.getName());
// 1) instanced vs non-instanced
if( 1 == getInstanceCount() ){
// un-instanced RocketComponents (usual case)
buffer.append(String.format("%-40s| %5.3f; %24s; %24s; ", prefix, this.getLength(), this.getOffset(), this.getLocations()[0]));
buffer.append(String.format("(offset: %4.1f via: %s )\n", this.getAxialOffset(), this.relativePosition.name()));
}else if( this instanceof Clusterable ){
// instanced components -- think motor clusters or booster stage clusters
final String patternName = ((Clusterable)this).getPatternName();
buffer.append(String.format("%-40s (cluster: %s )", prefix, patternName));
buffer.append(String.format("(offset: %4.1f via: %s )\n", this.getAxialOffset(), this.relativePosition.name()));
for (int instanceNumber = 0; instanceNumber < this.getInstanceCount(); instanceNumber++) {
final String instancePrefix = String.format("%s [%2d/%2d]", indent, instanceNumber+1, getInstanceCount());
buffer.append(String.format("%-40s| %5.3f; %24s; %24s;\n", instancePrefix, getLength(), getOffset(), getLocations()[0]));
}
}else{
throw new IllegalStateException("This is a developer error! If you implement an instanced class, please subclass the Clusterable interface.");
}
// 2) if this is an ACTING motor mount:
if(( this instanceof MotorMount ) &&( ((MotorMount)this).isMotorMount())){
// because InnerTube and BodyTube don't really share a common ancestor besides RocketComponent
// ... and it's easier to have all this code in one place...
toDebugMountNode( buffer, indent);
}
}
public void toDebugMountNode(final StringBuilder buffer, final String indent) {
MotorMount mnt = (MotorMount)this;
Coordinate[] relCoords = this.getInstanceOffsets();
Coordinate[] absCoords = this.getLocations();
FlightConfigurationId curId = this.getRocket().getSelectedConfiguration().getFlightConfigurationID();
final int intanceCount = this.getInstanceCount();
MotorConfiguration curInstance = mnt.getMotorConfig( curId);
if( curInstance.isEmpty() ){
// print just the tube locations
buffer.append(indent+" [X] This Instance doesn't have any motors for the active configuration.\n");
}else{
// curInstance has a motor ...
Motor curMotor = curInstance.getMotor();
final double motorOffset = this.getLength() - curMotor.getLength();
buffer.append(String.format("%-40sThrust: %f N; \n",
indent+" Mounted: "+curMotor.getDesignation(), curMotor.getMaxThrustEstimate() ));
Coordinate motorRelativePosition = new Coordinate(motorOffset, 0, 0);
Coordinate tubeAbs = absCoords[0];
Coordinate motorAbsolutePosition = new Coordinate(tubeAbs.x+motorOffset,tubeAbs.y,tubeAbs.z);
buffer.append(String.format("%-40s| %5.3f; %24s; %24s;\n", indent, curMotor.getLength(), motorRelativePosition, motorAbsolutePosition));
}
}
}

View File

@ -2,8 +2,6 @@ package net.sf.openrocket.rocketcomponent;
import java.util.Collection;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.util.Coordinate;
public abstract class RocketUtils {
@ -24,11 +22,5 @@ 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.getSelectedConfiguration(), calcType);
return cg;
}
}

View File

@ -3,7 +3,6 @@ package net.sf.openrocket.simulation;
import java.util.Collection;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.masscalc.MassData;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.simulation.exception.SimulationException;
@ -111,10 +110,8 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
* @return the mass data to use
* @throws SimulationException if a listener throws SimulationException
*/
protected MassData calculateMassData(SimulationStatus status) throws SimulationException {
protected MassData calculateDryMassData(SimulationStatus status) throws SimulationException {
MassData mass;
Coordinate cg;
double longitudinalInertia, rotationalInertia, propellantMass;
// Call pre-listener
mass = SimulationListenerHelper.firePreMassCalculation(status);
@ -123,13 +120,34 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
}
MassCalculator calc = status.getSimulationConditions().getMassCalculator();
// 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);
mass = new MassData(cg, rotationalInertia, longitudinalInertia);
propellantMass = calc.getPropellantMass(status);
mass.setPropellantMass( propellantMass );
mass = calc.getRocketSpentMassData( status.getConfiguration() );
// Call post-listener
mass = SimulationListenerHelper.firePostMassCalculation(status, mass);
checkNaN(mass.getCG());
checkNaN(mass.getLongitudinalInertia());
checkNaN(mass.getRotationalInertia());
return mass;
}
protected MassData calculatePropellantMassData(SimulationStatus status) throws SimulationException {
MassData mass;
// Call pre-listener
mass = SimulationListenerHelper.firePreMassCalculation(status);
if (mass != null) {
return mass;
}
MassCalculator calc = status.getSimulationConditions().getMassCalculator();
mass = calc.getPropellantMassData( status );
// Call post-listener
mass = SimulationListenerHelper.firePostMassCalculation(status, mass);
@ -137,7 +155,6 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
checkNaN(mass.getCG());
checkNaN(mass.getLongitudinalInertia());
checkNaN(mass.getRotationalInertia());
checkNaN(mass.getPropellantMass());
return mass;
}

View File

@ -39,7 +39,7 @@ public class BasicLandingStepper extends AbstractSimulationStepper {
// Compute drag force
double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2());
double dragForce = totalCD * dynP * refArea;
MassData massData = calculateMassData(status);
MassData massData = calculateDryMassData(status);
double mass = massData.getCG().weight;

View File

@ -35,7 +35,8 @@ public class BasicTumbleStepper extends AbstractSimulationStepper {
// Compute drag force
double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2());
double dragForce = tumbleDrag * dynP;
MassData massData = calculateMassData(status);
// n.b. this is consntant, and could be calculated once at the beginning of this simulation branch...
MassData massData = calculateDryMassData(status);
double mass = massData.getCG().weight;

View File

@ -82,6 +82,10 @@ public class MotorClusterState {
burnOut( _cutoffTime );
}
public MotorConfiguration getConfig(){
return config;
}
public double getEjectionDelay() {
return config.getEjectionDelay();
}
@ -93,6 +97,10 @@ public class MotorClusterState {
public double getPropellantMass(){
return (motor.getLaunchMass() - motor.getBurnoutMass());
}
public double getPropellantMass( final double motorTime ){
return (motor.getPropellantMass( motorTime) - motor.getBurnoutMass());
}
public MotorMount getMount(){
return config.getMount();

View File

@ -327,8 +327,10 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
calculateForces(status, store);
// Calculate mass data
store.massData = calculateMassData(status);
MassData dryMassData = calculateDryMassData(status);
store.propellantMassData = calculatePropellantMassData(status);
store.rocketMassData = dryMassData.add( store.propellantMassData );
// Calculate the forces from the aerodynamic coefficients
@ -345,9 +347,9 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
double forceZ = store.thrustForce - store.dragForce;
store.linearAcceleration = new Coordinate(-fN / store.massData.getCG().weight,
-fSide / store.massData.getCG().weight,
forceZ / store.massData.getCG().weight);
store.linearAcceleration = new Coordinate(-fN / store.rocketMassData.getCG().weight,
-fSide / store.rocketMassData.getCG().weight,
forceZ / store.rocketMassData.getCG().weight);
store.linearAcceleration = store.thetaRotation.rotateZ(store.linearAcceleration);
@ -376,8 +378,8 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
} else {
// Shift moments to CG
double Cm = store.forces.getCm() - store.forces.getCN() * store.massData.getCG().x / refLength;
double Cyaw = store.forces.getCyaw() - store.forces.getCside() * store.massData.getCG().x / refLength;
double Cm = store.forces.getCm() - store.forces.getCN() * store.rocketMassData.getCG().x / refLength;
double Cyaw = store.forces.getCyaw() - store.forces.getCside() * store.rocketMassData.getCG().x / refLength;
// Compute moments
double momX = -Cyaw * dynP * refArea * refLength;
@ -385,9 +387,9 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
double momZ = store.forces.getCroll() * dynP * refArea * refLength;
// Compute acceleration in rocket coordinates
store.angularAcceleration = new Coordinate(momX / store.massData.getLongitudinalInertia(),
momY / store.massData.getLongitudinalInertia(),
momZ / store.massData.getRotationalInertia());
store.angularAcceleration = new Coordinate(momX / store.rocketMassData.getLongitudinalInertia(),
momY / store.rocketMassData.getLongitudinalInertia(),
momZ / store.rocketMassData.getRotationalInertia());
store.rollAcceleration = store.angularAcceleration.z;
// TODO: LOW: This should be hypot, but does it matter?
@ -595,24 +597,30 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
data.setValue(FlightDataType.TYPE_MACH_NUMBER, store.flightConditions.getMach());
}
if (store.massData != null) {
data.setValue(FlightDataType.TYPE_CG_LOCATION, store.massData.getCG().x);
if (store.rocketMassData != null) {
data.setValue(FlightDataType.TYPE_CG_LOCATION, store.rocketMassData.getCG().x);
}
if (status.isLaunchRodCleared()) {
// Don't include CP and stability with huge launch AOA
if (store.forces != null) {
data.setValue(FlightDataType.TYPE_CP_LOCATION, store.forces.getCP().x);
}
if (store.forces != null && store.flightConditions != null && store.massData != null) {
if (store.forces != null && store.flightConditions != null && store.rocketMassData != null) {
data.setValue(FlightDataType.TYPE_STABILITY,
(store.forces.getCP().x - store.massData.getCG().x) / store.flightConditions.getRefLength());
(store.forces.getCP().x - store.rocketMassData.getCG().x) / store.flightConditions.getRefLength());
}
}
if (store.massData != null) {
data.setValue(FlightDataType.TYPE_MASS, store.massData.getCG().weight);
data.setValue(FlightDataType.TYPE_PROPELLANT_MASS, store.massData.getPropellantMass());
data.setValue(FlightDataType.TYPE_LONGITUDINAL_INERTIA, store.massData.getLongitudinalInertia());
data.setValue(FlightDataType.TYPE_ROTATIONAL_INERTIA, store.massData.getRotationalInertia());
if( null != store.propellantMassData ){
data.setValue(FlightDataType.TYPE_PROPELLANT_MASS, store.propellantMassData.getCG().weight);
//data.setValue(FlightDataType.TYPE_PROPELLANT_LONGITUDINAL_INERTIA, store.propellantMassData.getLongitudinalInertia());
//data.setValue(FlightDataType.TYPE_PROPELLANT_ROTATIONAL_INERTIA, store.propellantMassData.getRotationalInertia());
}
if (store.rocketMassData != null) {
// N.B.: These refer to total mass
data.setValue(FlightDataType.TYPE_MASS, store.rocketMassData.getCG().weight);
data.setValue(FlightDataType.TYPE_LONGITUDINAL_INERTIA, store.rocketMassData.getLongitudinalInertia());
data.setValue(FlightDataType.TYPE_ROTATIONAL_INERTIA, store.rocketMassData.getRotationalInertia());
}
data.setValue(FlightDataType.TYPE_THRUST_FORCE, store.thrustForce);
@ -620,11 +628,11 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
data.setValue(FlightDataType.TYPE_GRAVITY, store.gravity);
if (status.isLaunchRodCleared() && store.forces != null) {
if (store.massData != null && store.flightConditions != null) {
if (store.rocketMassData != null && store.flightConditions != null) {
data.setValue(FlightDataType.TYPE_PITCH_MOMENT_COEFF,
store.forces.getCm() - store.forces.getCN() * store.massData.getCG().x / store.flightConditions.getRefLength());
store.forces.getCm() - store.forces.getCN() * store.rocketMassData.getCG().x / store.flightConditions.getRefLength());
data.setValue(FlightDataType.TYPE_YAW_MOMENT_COEFF,
store.forces.getCyaw() - store.forces.getCside() * store.massData.getCG().x / store.flightConditions.getRefLength());
store.forces.getCyaw() - store.forces.getCside() * store.rocketMassData.getCG().x / store.flightConditions.getRefLength());
}
data.setValue(FlightDataType.TYPE_NORMAL_FORCE_COEFF, store.forces.getCN());
data.setValue(FlightDataType.TYPE_SIDE_FORCE_COEFF, store.forces.getCside());
@ -707,7 +715,9 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
public double longitudinalAcceleration = Double.NaN;
public MassData massData;
public MassData rocketMassData;
public MassData propellantMassData;
public Coordinate coriolisAcceleration;

View File

@ -87,8 +87,7 @@ public class SimulationStatus implements Monitorable {
private int modID = 0;
private int modIDadd = 0;
public SimulationStatus(FlightConfiguration configuration,
SimulationConditions simulationConditions) {
public SimulationStatus(FlightConfiguration configuration, SimulationConditions simulationConditions) {
this.simulationConditions = simulationConditions;
this.configuration = configuration;

View File

@ -526,7 +526,7 @@ public class SimulationListenerHelper {
/**
* Fire postMassCalculation event.
*
* @return the aerodynamic forces to use.
* @return the resultant mass data
*/
public static MassData firePostMassCalculation(SimulationStatus status, MassData mass) throws SimulationException {
MassData m;

View File

@ -188,7 +188,6 @@ public class TestRockets {
NoseCone nosecone;
BodyTube bodytube;
rocket = new Rocket();
stage = new AxialStage();
stage.setName("Stage1");
@ -371,23 +370,25 @@ public class TestRockets {
return values[rnd.nextInt(values.length)];
}
public final static String ESTES_ALPHA_III_FCID_1="test_config #1: A8-0";
public final static String ESTES_ALPHA_III_FCID_2="test_config #2: B4-3";
public final static String ESTES_ALPHA_III_FCID_3="test_config #3: C6-3";
public final static String ESTES_ALPHA_III_FCID_4="test_config #4: C6-5";
public final static String ESTES_ALPHA_III_FCID_5="test_config #5: C6-7";
// This is a Estes Alpha III
// http://www.rocketreviews.com/alpha-iii---estes-221256.html
// It is picked as a standard, simple, validation rocket.
// This function is used for unit, integration tests, DO NOT CHANGE (without updating tests).
public static final Rocket makeEstesAlphaIII(){
Rocket rocket = new Rocket();
FlightConfigurationId fcid[] = new FlightConfigurationId[5];
fcid[0] = new FlightConfigurationId();
rocket.createFlightConfiguration(fcid[0]);
fcid[1] = new FlightConfigurationId();
rocket.createFlightConfiguration(fcid[1]);
fcid[2] = new FlightConfigurationId();
rocket.createFlightConfiguration(fcid[2]);
fcid[3] = new FlightConfigurationId();
rocket.createFlightConfiguration(fcid[3]);
fcid[4] = new FlightConfigurationId();
rocket.createFlightConfiguration(fcid[4]);
FlightConfigurationId fcid[] = new FlightConfigurationId[5];
fcid[0] = rocket.createFlightConfiguration( new FlightConfigurationId( ESTES_ALPHA_III_FCID_1 ));
fcid[1] = rocket.createFlightConfiguration( new FlightConfigurationId( ESTES_ALPHA_III_FCID_2 ));
fcid[2] = rocket.createFlightConfiguration( new FlightConfigurationId( ESTES_ALPHA_III_FCID_3 ));
fcid[3] = rocket.createFlightConfiguration( new FlightConfigurationId( ESTES_ALPHA_III_FCID_4 ));
fcid[4] = rocket.createFlightConfiguration( new FlightConfigurationId( ESTES_ALPHA_III_FCID_5 ));
rocket.setName("Estes Alpha III / Code Verification Rocket");
AxialStage stage = new AxialStage();
@ -515,8 +516,9 @@ public class TestRockets {
bodytube.setMaterial(material);
finset.setMaterial(material);
rocket.setSelectedConfiguration( fcid[0] );
rocket.getSelectedConfiguration().setAllStages();
// preserve default default configuration of rocket -- to test what the default is set to upon initialization.
rocket.enableEvents();
return rocket;
}
@ -1018,13 +1020,15 @@ public class TestRockets {
return rocket;
}
public final static String FALCON_9_FCID_1="test_config #1: [ M1350, G77]";
// This function is used for unit, integration tests, DO NOT CHANGE (without updating tests).
public static Rocket makeFalcon9Heavy() {
Rocket rocket = new Rocket();
rocket.setName("Falcon9H Scale Rocket");
FlightConfiguration selConfig = rocket.createFlightConfiguration(null);
FlightConfigurationId selFCID = selConfig.getFlightConfigurationID();
FlightConfigurationId selFCID = rocket.createFlightConfiguration( new FlightConfigurationId( FALCON_9_FCID_1 ));
rocket.setSelectedConfiguration(selFCID);
// ====== Payload Stage ======
@ -1104,7 +1108,7 @@ public class TestRockets {
Motor mtr = TestRockets.generateMotor_M1350_75mm();
motorConfig.setMotor( mtr);
coreBody.setMotorMount( true);
FlightConfigurationId motorConfigId = selConfig.getFlightConfigurationID();
FlightConfigurationId motorConfigId = selFCID;
coreBody.setMotorConfig( motorConfig, motorConfigId);
}
@ -1162,7 +1166,7 @@ public class TestRockets {
boosterMotorTubes.setClusterScale(1.0);
boosterBody.addChild( boosterMotorTubes);
FlightConfigurationId motorConfigId = selConfig.getFlightConfigurationID();
FlightConfigurationId motorConfigId = selFCID;
MotorConfiguration motorConfig = new MotorConfiguration( boosterMotorTubes, selFCID);
Motor mtr = TestRockets.generateMotor_G77_29mm();
motorConfig.setMotor(mtr);
@ -1174,7 +1178,7 @@ public class TestRockets {
rocket.enableEvents();
rocket.setSelectedConfiguration( selFCID);
selConfig.setAllStages();
rocket.getFlightConfiguration( selFCID).setAllStages();
return rocket;
}

View File

@ -1,16 +1,18 @@
package net.sf.openrocket.masscalc;
//import junit.framework.TestCase;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
import net.sf.openrocket.rocketcomponent.InnerTube;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.ParallelStage;
import net.sf.openrocket.rocketcomponent.Rocket;
@ -22,19 +24,27 @@ import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
public class MassCalculatorTest extends BaseTestCase {
// tolerance for compared double test results
protected final double EPSILON = 0.000001;
private static final double EPSILON = 0.000001;
private static final double G77_MASS_LAUNCH = 0.123;
private static final double G77_MASS_SPENT = 0.064;
private static final double M1350_MASS_LAUNCH = 4.808;
private static final double M1350_MASS_SPENT = 1.970;
private static final double BOOSTER_SET_NO_MOTORS_MASS = 0.4555128227852;
private static final double BOOSTER_SET_NO_MOTORS_CMX = 1.246297525;
private static final double BOOSTER_SET_SPENT_MASS = BOOSTER_SET_NO_MOTORS_MASS + G77_MASS_SPENT*8;
private final double BOOSTER_NOSE_MASS_EA = 0.0222459863653;
private final double BOOSTER_BODY_MASS_EA = 0.129886006;
private final double BOOSTER_MMT_MASS_EA = 0.01890610458;
private final double BOOSTER_TOTAL_DRY_MASS_EACH = 0.4555128227852;
private final double BOOSTER_TOTAL_DRY_CMX = 1.246297525;
private final double G77_MASS_LAUNCH = 0.123;
@Test
public void testRocketNoMotors() {
Rocket rkt = TestRockets.makeNoMotorRocket();
FlightConfiguration config = rkt.getEmptyConfiguration();
config.setAllStages();
rkt.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
// String treeDump = rkt.toDebugTree();
@ -42,23 +52,21 @@ public class MassCalculatorTest extends BaseTestCase {
// Validate Boosters
MassCalculator mc = new MassCalculator();
//mc.debug = true;
Coordinate rocketCM = mc.getCM( rkt.getSelectedConfiguration(), MassCalcType.NO_MOTORS);
// any config will do, beceause the rocket literally has no defined motors.
Coordinate rocketCM = mc.getRocketSpentMassData( config).getCM( );
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);
assertEquals("Simple Motor Rocket mass incorrect: ", expMass, calcMass, EPSILON);
assertEquals("Simple Rocket CM.x is incorrect: ", expCM.x, rocketCM.x, EPSILON);
assertEquals("Simple Rocket CM.y is incorrect: ", expCM.y, rocketCM.y, EPSILON);
assertEquals("Simple Rocket CM.z is incorrect: ", expCM.z, rocketCM.z, EPSILON);
assertEquals("Simple Rocket CM is incorrect: ", expCM, rocketCM);
rocketCM = mc.getCM( rkt.getSelectedConfiguration(), MassCalcType.LAUNCH_MASS);
assertEquals(" Delta Heavy Booster CM is incorrect: ", expCM, rocketCM);
rocketCM = mc.getCM( rkt.getSelectedConfiguration(), MassCalcType.BURNOUT_MASS);
assertEquals(" Delta Heavy Booster CM is incorrect: ", expCM, rocketCM);
rocketCM = mc.getRocketLaunchMassData(config).getCM( );
assertEquals("Simple Rocket w/no Motors: CM is incorrect: ", expCM, rocketCM);
}
@Test
@ -129,18 +137,18 @@ public class MassCalculatorTest extends BaseTestCase {
// ====== ====== ======
ParallelStage boosters = (ParallelStage) rkt.getChild(1).getChild(1);
{
expMass = BOOSTER_NOSE_MASS_EA;
expMass = 0.0222459863653;
// think of the casts as an assert that ( child instanceof NoseCone) == true
NoseCone nose = (NoseCone) boosters.getChild(0);
compMass = nose.getComponentMass();
assertEquals( nose.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON);
expMass = BOOSTER_BODY_MASS_EA;
expMass = 0.129886006;
BodyTube body = (BodyTube) boosters.getChild(1);
compMass = body.getComponentMass();
assertEquals( body.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON);
expMass = BOOSTER_MMT_MASS_EA;
expMass = 0.01890610458;
InnerTube mmt = (InnerTube)boosters.getChild(1).getChild(0);
compMass = mmt.getComponentMass();
assertEquals( mmt.getName()+" mass calculated incorrectly: ", expMass, compMass, EPSILON);
@ -149,8 +157,12 @@ public class MassCalculatorTest extends BaseTestCase {
@Test
public void testComponentMOIs() {
Rocket rkt = TestRockets.makeFalcon9Heavy();
rkt.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
Rocket rocket = TestRockets.makeFalcon9Heavy();
rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
FlightConfiguration emptyConfig = rocket.getEmptyConfiguration();
rocket.setSelectedConfiguration( emptyConfig.getFlightConfigurationID() );
double expInertia;
RocketComponent cc;
@ -160,14 +172,14 @@ public class MassCalculatorTest extends BaseTestCase {
// ====== ====== ====== ======
{
expInertia = 3.1698055283e-5;
cc= rkt.getChild(0).getChild(0);
cc= rocket.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);
cc= rkt.getChild(0).getChild(1);
cc= rocket.getChild(0).getChild(1);
expInertia = 7.70416e-5;
compInertia = cc.getRotationalInertia();
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
@ -175,7 +187,7 @@ public class MassCalculatorTest extends BaseTestCase {
compInertia = cc.getLongitudinalInertia();
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
cc= rkt.getChild(0).getChild(2);
cc= rocket.getChild(0).getChild(2);
expInertia = 1.43691e-5;
compInertia = cc.getRotationalInertia();
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
@ -183,7 +195,7 @@ public class MassCalculatorTest extends BaseTestCase {
compInertia = cc.getLongitudinalInertia();
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
cc= rkt.getChild(0).getChild(3);
cc= rocket.getChild(0).getChild(3);
expInertia = 4.22073e-5;
compInertia = cc.getRotationalInertia();
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
@ -192,7 +204,7 @@ public class MassCalculatorTest extends BaseTestCase {
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
{
cc= rkt.getChild(0).getChild(3).getChild(0);
cc= rocket.getChild(0).getChild(3).getChild(0);
expInertia = 6.23121e-7;
compInertia = cc.getRotationalInertia();
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
@ -200,7 +212,7 @@ public class MassCalculatorTest extends BaseTestCase {
compInertia = cc.getLongitudinalInertia();
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
cc= rkt.getChild(0).getChild(3).getChild(1);
cc= rocket.getChild(0).getChild(3).getChild(1);
expInertia = 5.625e-8;
compInertia = cc.getRotationalInertia();
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
@ -209,7 +221,7 @@ public class MassCalculatorTest extends BaseTestCase {
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
}
cc= rkt.getChild(0).getChild(4);
cc= rocket.getChild(0).getChild(4);
expInertia = 2.81382e-5;
compInertia = cc.getRotationalInertia();
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
@ -221,7 +233,7 @@ public class MassCalculatorTest extends BaseTestCase {
// ====== Core Stage ======
// ====== ====== ======
{
cc= rkt.getChild(1).getChild(0);
cc= rocket.getChild(1).getChild(0);
expInertia = 0.000187588;
compInertia = cc.getRotationalInertia();
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
@ -229,7 +241,7 @@ public class MassCalculatorTest extends BaseTestCase {
compInertia = cc.getLongitudinalInertia();
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
cc= rkt.getChild(1).getChild(0).getChild(0);
cc= rocket.getChild(1).getChild(0).getChild(0);
expInertia = 0.00734753;
compInertia = cc.getRotationalInertia();
assertEquals(cc.getName()+" Rotational MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
@ -241,7 +253,7 @@ public class MassCalculatorTest extends BaseTestCase {
// ====== Booster Set Stage ======
// ====== ====== ======
ParallelStage boosters = (ParallelStage) rkt.getChild(1).getChild(1);
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
{
cc= boosters.getChild(0);
expInertia = 1.82665797857e-5;
@ -268,6 +280,127 @@ public class MassCalculatorTest extends BaseTestCase {
assertEquals(cc.getName()+" Longitudinal MOI calculated incorrectly: ", expInertia, compInertia, EPSILON);
}
}
@Test
public void testPropellantMasses() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
FlightConfiguration config = rocket.getFlightConfiguration( new FlightConfigurationId( TestRockets.FALCON_9_FCID_1) );
config.setAllStages();
MassCalculator calc = new MassCalculator();
{ // test core stage motors
AxialStage core = (AxialStage) rocket.getChild(1);
final int coreNum = core.getStageNumber();
config.setOnlyStage( coreNum);
MassData corePropInertia = calc.calculatePropellantMassData(config);
final Coordinate actCM= corePropInertia.getCM();
final double actCorePropMass = corePropInertia.getMass();
final MotorMount mnt = (MotorMount)core.getChild(0);
final Motor coreMotor = mnt.getMotorConfig( config.getFlightConfigurationID()).getMotor();
final double expCorePropMassEach = M1350_MASS_LAUNCH - M1350_MASS_SPENT;
final double coreMotorCount = 1.;
final double expCorePropMass = expCorePropMassEach * coreMotorCount;
final Coordinate expCM = new Coordinate( 1.053, 0, 0, expCorePropMass);
assertEquals(core.getName()+" => "+coreMotor.getDesignation()+" propellant mass is incorrect: ", expCorePropMass, actCorePropMass, EPSILON);
assertEquals(core.getName()+" => "+coreMotor.getDesignation()+" propellant CoM x is incorrect: ", expCM.x, actCM.x, EPSILON);
assertEquals(core.getName()+" => "+coreMotor.getDesignation()+" propellant CoM y is incorrect: ", expCM.y, actCM.y, EPSILON);
assertEquals(core.getName()+" => "+coreMotor.getDesignation()+" propellant CoM z is incorrect: ", expCM.z, actCM.z, EPSILON);
}
{ // test booster stage motors
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
final int boostNum = boosters.getStageNumber();
config.setOnlyStage( boostNum);
MassData boosterPropInertia = calc.calculatePropellantMassData(config);
final Coordinate actCM= boosterPropInertia.getCM();
final double actBoosterPropMass = boosterPropInertia.getMass();
final MotorMount mnt = (MotorMount)boosters.getChild(1).getChild(0);
final Motor boosterMotor = mnt.getMotorConfig( config.getFlightConfigurationID()).getMotor();
final double expBoosterPropMassEach = G77_MASS_LAUNCH - G77_MASS_SPENT;
final double boosterSetMotorCount = 8.; /// it's a double merely to prevent type-casting issues
final double expBoosterPropMass = expBoosterPropMassEach * boosterSetMotorCount;
final Coordinate expCM = new Coordinate( 1.31434, 0, 0, expBoosterPropMass);
assertEquals( boosters.getName()+" => "+boosterMotor.getDesignation()+" propellant mass is incorrect: ", expBoosterPropMass, actBoosterPropMass, EPSILON);
assertEquals( boosters.getName()+" => "+boosterMotor.getDesignation()+" propellant CoM x is incorrect: ", expCM.x, actCM.x, EPSILON);
assertEquals( boosters.getName()+" => "+boosterMotor.getDesignation()+" propellant CoM y is incorrect: ", expCM.y, actCM.y, EPSILON);
assertEquals( boosters.getName()+" => "+boosterMotor.getDesignation()+" propellant CoM z is incorrect: ", expCM.z, actCM.z, EPSILON);
}
}
@Test
public void testPropellantMOIs() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
FlightConfiguration config = rocket.getFlightConfiguration( new FlightConfigurationId( TestRockets.FALCON_9_FCID_1) );
{ // test core stage motors
AxialStage core = (AxialStage) rocket.getChild(1);
final int coreNum = core.getStageNumber();
config.setOnlyStage( coreNum);
MassCalculator calc = new MassCalculator();
MassData corePropInertia = calc.calculatePropellantMassData(config);
final double actCorePropMass = corePropInertia.getMass();
final MotorMount mnt = (MotorMount)core.getChild(0);
final Motor coreMotor = mnt.getMotorConfig( config.getFlightConfigurationID()).getMotor();
// validated against a specific motor/radius/length
final double expIxxEach = 0.00199546875;
final double expIyyEach = 0.092495800375;
final double actIxxEach = coreMotor.getUnitIxx()*actCorePropMass;
final double actIyyEach = coreMotor.getUnitIyy()*actCorePropMass;
final double coreMotorCount = mnt.getInstanceCount();
final double actCorePropIxx = actIxxEach*coreMotorCount;
final double actCorePropIyy = actIyyEach*coreMotorCount;
assertEquals(core.getName()+" propellant axial MOI is incorrect: ", expIxxEach, actCorePropIxx, EPSILON);
assertEquals(core.getName()+" propellant longitudinal MOI is incorrect: ", expIyyEach, actCorePropIyy, EPSILON);
}
{ // test booster stage motors
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
final int boostNum = boosters.getStageNumber();
config.setOnlyStage( boostNum);
MassCalculator calc = new MassCalculator();
MassData boosterPropInertia = calc.calculatePropellantMassData(config);
final double actBoosterPropMass = boosterPropInertia.getMass();
final MotorMount mnt = (MotorMount)boosters.getChild(1).getChild(0);
final Motor boosterMotor = mnt.getMotorConfig( config.getFlightConfigurationID()).getMotor();
final double expBrIxxEach = 3.96952E-4;
final double expBrIyyEach = 0.005036790;
final double actIxxEach = boosterMotor.getUnitIxx()*actBoosterPropMass;
final double actIyyEach = boosterMotor.getUnitIyy()*actBoosterPropMass;
final int boosterMotorCount = mnt.getInstanceCount();
assertThat( boosters.getName()+" booster motor count is not as expected! ", boosterMotorCount, equalTo(8));
final double actBoosterPropIxx = actIxxEach*boosterMotorCount;
final double actBoosterPropIyy = actIyyEach*boosterMotorCount;
assertEquals(boosters.getName()+" propellant axial MOI is incorrect: ", expBrIxxEach, actBoosterPropIxx, EPSILON);
assertEquals(boosters.getName()+" propellant longitudinal MOI is incorrect: ", expBrIyyEach, actBoosterPropIyy, EPSILON);
}
}
@Test
public void testBoosterStructureCM() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
@ -276,22 +409,24 @@ public class MassCalculatorTest extends BaseTestCase {
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
int boostNum = boosters.getStageNumber();
rocket.getSelectedConfiguration().setOnlyStage( boostNum);
FlightConfiguration config = rocket.getEmptyConfiguration();
config.setOnlyStage( boostNum);
// Validate Boosters
MassCalculator mc = new MassCalculator();
Coordinate boosterSetCM = mc.getCM( rocket.getSelectedConfiguration(), MassCalcType.NO_MOTORS);
double expMass = BOOSTER_TOTAL_DRY_MASS_EACH;
double expCMx = BOOSTER_TOTAL_DRY_CMX;
double calcMass = boosterSetCM.weight;
MassData md = mc.calculateBurnoutMassData( config);
Coordinate actCM = md.getCM();
double expMass = BOOSTER_SET_NO_MOTORS_MASS;
double expCMx = BOOSTER_SET_NO_MOTORS_CMX;
double calcMass = actCM.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);
assertEquals(" Delta Heavy Booster CM.x is incorrect: ", expCM.x, md.getCM().x, EPSILON);
assertEquals(" Delta Heavy Booster CM.y is incorrect: ", expCM.y, md.getCM().y, EPSILON);
assertEquals(" Delta Heavy Booster CM.z is incorrect: ", expCM.z, md.getCM().z, EPSILON);
assertEquals(" Delta Heavy Booster CM is incorrect: ", expCM, md.getCM() );
}
@ -300,7 +435,8 @@ public class MassCalculatorTest extends BaseTestCase {
Rocket rocket = TestRockets.makeEstesAlphaIII();
InnerTube mmt = (InnerTube) rocket.getChild(0).getChild(1).getChild(2);
FlightConfigurationId fcid = rocket.getSelectedConfiguration().getId();
FlightConfiguration config = rocket.getFlightConfigurationByIndex(0, false);
FlightConfigurationId fcid = config.getFlightConfigurationID();
Motor activeMotor = mmt.getMotorConfig( fcid ).getMotor();
String desig = activeMotor.getDesignation();
@ -311,153 +447,149 @@ public class MassCalculatorTest extends BaseTestCase {
// Validate Booster Launch Mass
MassCalculator mc = new MassCalculator();
double actPropMass = mc.getPropellantMass( rocket.getSelectedConfiguration(), MassCalcType.LAUNCH_MASS);
MassData propMassData = mc.calculatePropellantMassData( config);
double actPropMass = propMassData.getCM().weight;
double expPropMass = expLaunchMass - expSpentMass;
assertEquals(" Motor Mass "+desig+" is incorrect: ", expPropMass, actPropMass, EPSILON);
}
@Test
public void testBoosterMotorMass() {
public void testBoosterPropellantInertia() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
int boostNum = boosters.getStageNumber();
FlightConfiguration currentConfig = rocket.getSelectedConfiguration();
currentConfig.setOnlyStage( boostNum);
// String treeDump = rocket.toDebugTree();
// System.err.println( treeDump);
FlightConfiguration config = rocket.getFlightConfiguration( new FlightConfigurationId( TestRockets.FALCON_9_FCID_1));
config.setOnlyStage( boostNum);
InnerTube mmt = (InnerTube) boosters.getChild(1).getChild(0);
{
InnerTube mmt = (InnerTube) boosters.getChild(1).getChild(0);
double expX = (.564 + 0.8 - 0.150 );
double actX = mmt.getLocations()[0].x;
assertEquals(" Booster motor mount tubes located incorrectly: ", expX, actX, EPSILON);
}
{
// Validate Booster Propellant Mass
Motor mtr = mmt.getMotorConfig( config.getId()).getMotor();
double propMassEach = mtr.getLaunchMass()-mtr.getBurnoutMass();
MassCalculator mc = new MassCalculator();
MassData propMotorData = mc.calculatePropellantMassData( config );
Coordinate propCM = propMotorData.getCM();
Coordinate expPropCM = new Coordinate(1.31434, 0, 0, propMassEach*2*4);
assertEquals(" Booster Prop Mass is incorrect: ", expPropCM.weight, propCM.weight, EPSILON);
assertEquals(" Booster Prop CM.x is incorrect: ", expPropCM.x, propCM.x, EPSILON);
assertEquals(" Booster Prop CM.y is incorrect: ", expPropCM.y, propCM.y, EPSILON);
assertEquals(" Booster Prop CM.z is incorrect: ", expPropCM.z, propCM.z, EPSILON);
assertEquals(" Booster Prop CM is incorrect: ", expPropCM, propCM);
}
}
@Test
public void testBoosterSpentCM(){
Rocket rocket = TestRockets.makeFalcon9Heavy();
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
int boostNum = boosters.getStageNumber();
FlightConfiguration config = rocket.getFlightConfiguration( new FlightConfigurationId( TestRockets.FALCON_9_FCID_1));
config.setOnlyStage( boostNum);
{
// Validate Booster Launch Mass
MassCalculator mc = new MassCalculator();
MassData launchMotorData = mc.getMotorMassData( rocket.getSelectedConfiguration(), MassCalcType.LAUNCH_MASS);
Coordinate launchCM = launchMotorData.getCM();
// 1.214 = beginning of engine mmt
// 1.364-.062 = middle of engine: 1.302
Coordinate expLaunchCM = new Coordinate(1.31434, 0, 0, 0.123*2*4);
MassData launchData = mc.calculateBurnoutMassData( config );
Coordinate launchCM = launchData.getCM();
double expLaunchCMx = 1.2823050552779347;
double expLaunchMass = BOOSTER_SET_SPENT_MASS;
Coordinate expLaunchCM = new Coordinate( expLaunchCMx, 0, 0, expLaunchMass);
assertEquals(" Booster Launch Mass is incorrect: ", expLaunchCM.weight, launchCM.weight, EPSILON);
assertEquals(" Booster Launch CM.x is incorrect: ", expLaunchCM.x, launchCM.x, EPSILON);
assertEquals(" Booster Launch CM.y is incorrect: ", expLaunchCM.y, launchCM.y, EPSILON);
assertEquals(" Booster Launch CM.z is incorrect: ", expLaunchCM.z, launchCM.z, EPSILON);
assertEquals(" Booster Launch CM is incorrect: ", expLaunchCM, launchCM);
MassData spentMotorData = mc.getMotorMassData( rocket.getSelectedConfiguration(), MassCalcType.BURNOUT_MASS);
Coordinate spentCM = spentMotorData.getCM();
Coordinate expSpentCM = new Coordinate(1.31434, 0, 0, 0.064*2*4);
assertEquals(" Booster Spent Mass is incorrect: ", expSpentCM.weight, spentCM.weight, EPSILON);
assertEquals(" Booster Launch CM.x is incorrect: ", expSpentCM.x, spentCM.x, EPSILON);
assertEquals(" Booster Launch CM.y is incorrect: ", expSpentCM.y, spentCM.y, EPSILON);
assertEquals(" Booster Launch CM.z is incorrect: ", expSpentCM.z, spentCM.z, EPSILON);
assertEquals(" Booster Launch CM is incorrect: ", expSpentCM, spentCM);
}
}
@Test
public void testBoosterTotalCM() {
public void testBoosterLaunchCM() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
int boostNum = boosters.getStageNumber();
rocket.getSelectedConfiguration().setOnlyStage( boostNum);
FlightConfiguration config = rocket.getFlightConfiguration( new FlightConfigurationId( TestRockets.FALCON_9_FCID_1));
config.setOnlyStage( boostNum);
{
// Validate Booster Launch Mass
MassCalculator mc = new MassCalculator();
Coordinate boosterSetCM = mc.getCM( rocket.getSelectedConfiguration(), MassCalcType.LAUNCH_MASS);
double calcTotalMass = boosterSetCM.weight;
Coordinate boosterSetLaunchCM = mc.getRocketLaunchMassData( rocket.getSelectedConfiguration()).getCM();
double calcTotalMass = boosterSetLaunchCM.weight;
double expTotalMass = BOOSTER_TOTAL_DRY_MASS_EACH + 8*G77_MASS_LAUNCH;
double expTotalMass = BOOSTER_SET_NO_MOTORS_MASS + 2*4*G77_MASS_LAUNCH;
assertEquals(" Booster Launch Mass is incorrect: ", expTotalMass, calcTotalMass, EPSILON);
double expX = 1.292808951;
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);
}
{
// Validate Booster Burnout Mass
MassCalculator mc = new MassCalculator();
//mc.debug = true;
Coordinate boosterSetCM = mc.getCM( rocket.getSelectedConfiguration(), MassCalcType.BURNOUT_MASS);
double calcTotalMass = boosterSetCM.weight;
double expTotalMass = BOOSTER_TOTAL_DRY_MASS_EACH + 8*0.064;
assertEquals(" Booster Burnout Mass is incorrect: ", expTotalMass, calcTotalMass, EPSILON);
double expX = 1.282305055;
Coordinate expCM = new Coordinate(expX,0,0, expTotalMass);
assertEquals(" Booster Burnout CM.x is incorrect: ", expCM.x, boosterSetCM.x, EPSILON);
assertEquals(" Booster Burnout CM.y is incorrect: ", expCM.y, boosterSetCM.y, EPSILON);
assertEquals(" Booster Burnout CM.z is incorrect: ", expCM.z, boosterSetCM.z, EPSILON);
assertEquals(" Booster Burnout CM is incorrect: ", expCM, boosterSetCM);
assertEquals(" Booster Launch CM.x is incorrect: ", expCM.x, boosterSetLaunchCM.x, EPSILON);
assertEquals(" Booster Launch CM.y is incorrect: ", expCM.y, boosterSetLaunchCM.y, EPSILON);
assertEquals(" Booster Launch CM.z is incorrect: ", expCM.z, boosterSetLaunchCM.z, EPSILON);
assertEquals(" Booster Launch CM is incorrect: ", expCM, boosterSetLaunchCM);
}
}
@Test
public void testBoosterStructureMOI() {
public void testBoosterSpentMOIs() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
FlightConfiguration defaultConfig = rocket.getSelectedConfiguration();
FlightConfiguration config = rocket.getFlightConfiguration( new FlightConfigurationId( TestRockets.FALCON_9_FCID_1));
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
int boostNum = boosters.getStageNumber();
rocket.getSelectedConfiguration().setOnlyStage( boostNum);
config.setOnlyStage( boostNum);
// Validate Boosters
MassCalculator mc = new MassCalculator();
double expMOI_axial = .00304203;
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.129566277;
double boosterMOI_tr= mc.getLongitudinalInertia( defaultConfig, MassCalcType.NO_MOTORS);
MassData spent = mc.calculateBurnoutMassData( config);
double expMOIRotational = .0062065449;
double boosterMOIRotational = spent.getRotationalInertia();
assertEquals(" Booster x-axis MOI is incorrect: ", expMOIRotational, boosterMOIRotational, EPSILON);
double expMOI_tr = 0.13136525;
double boosterMOI_tr= spent.getLongitudinalInertia();
assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON);
}
@Test
public void testBoosterTotalMOI() {
public void testBoosterLaunchMOIs() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
FlightConfiguration defaultConfig = rocket.getSelectedConfiguration();
rocket.setName("TestRocket:F9H:Total_MOI");
FlightConfiguration config = rocket.getFlightConfiguration( new FlightConfigurationId( TestRockets.FALCON_9_FCID_1));
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
int boostNum = boosters.getStageNumber();
rocket.getSelectedConfiguration().setOnlyStage( boostNum);
config.setOnlyStage( boostNum);
// Validate Boosters
MassCalculator mc = new MassCalculator();
final double expMOI_axial = 0.0516919744;
final double boosterMOI_xx= mc.getRotationalInertia( defaultConfig, MassCalcType.LAUNCH_MASS);
final double expMOI_tr = 0.141508294;
final double boosterMOI_tr= mc.getLongitudinalInertia( defaultConfig, MassCalcType.LAUNCH_MASS);
final MassData launch= mc.getRocketLaunchMassData( config);
final double expIxx = 0.00912327349;
final double actIxx= launch.getRotationalInertia();
final double expIyy = 0.132320;
final double actIyy= launch.getLongitudinalInertia();
assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON);
assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON);
assertEquals(" Booster x-axis MOI is incorrect: ", expIxx, actIxx, EPSILON);
assertEquals(" Booster transverse MOI is incorrect: ", expIyy, actIyy, EPSILON);
}
@Test
public void testStageMassOverride() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
FlightConfiguration config = rocket.getSelectedConfiguration();
rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
FlightConfiguration config = rocket.getEmptyConfiguration();
rocket.setSelectedConfiguration( config.getId() );
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
int boostNum = boosters.getStageNumber();
config.setOnlyStage( boostNum);
@ -471,7 +603,9 @@ public class MassCalculatorTest extends BaseTestCase {
{
// Validate Mass
MassCalculator mc = new MassCalculator();
Coordinate boosterSetCM = mc.getCM( rocket.getSelectedConfiguration(), MassCalcType.NO_MOTORS);
MassData burnout = mc.calculateBurnoutMassData( config);
Coordinate boosterSetCM = burnout.getCM();
double calcTotalMass = boosterSetCM.weight;
double expTotalMass = overrideMass;
@ -486,11 +620,11 @@ public class MassCalculatorTest extends BaseTestCase {
// Validate MOI
double expMOI_axial = .00333912717;
double boosterMOI_xx= mc.getRotationalInertia( config, MassCalcType.NO_MOTORS);
double boosterMOI_xx= burnout.getRotationalInertia();
assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON);
double expMOI_tr = 0.142220231;
double boosterMOI_tr= mc.getLongitudinalInertia( config, MassCalcType.NO_MOTORS);
double boosterMOI_tr= burnout.getLongitudinalInertia();
assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON);
}
@ -499,9 +633,11 @@ public class MassCalculatorTest extends BaseTestCase {
@Test
public void testComponentMassOverride() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
FlightConfiguration config = rocket.getSelectedConfiguration();
rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
FlightConfiguration config = rocket.getEmptyConfiguration();
rocket.setSelectedConfiguration( config.getId() );
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
int boostNum = boosters.getStageNumber();
config.setOnlyStage( boostNum);
@ -521,7 +657,8 @@ public class MassCalculatorTest extends BaseTestCase {
{
// Validate Mass
MassCalculator mc = new MassCalculator();
Coordinate boosterSetCM = mc.getCM( rocket.getSelectedConfiguration(), MassCalcType.NO_MOTORS);
MassData burnout = mc.calculateBurnoutMassData( config);
Coordinate boosterSetCM = burnout.getCM();
double calcTotalMass = boosterSetCM.weight;
double expTotalMass = 4.368;
@ -536,11 +673,11 @@ public class MassCalculatorTest extends BaseTestCase {
// Validate MOI
double expMOI_axial = 0.0257485;
double boosterMOI_xx= mc.getRotationalInertia( config, MassCalcType.NO_MOTORS);
double boosterMOI_xx= burnout.getRotationalInertia();
assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON);
double expMOI_tr = 1.633216231;
double boosterMOI_tr= mc.getLongitudinalInertia( config, MassCalcType.NO_MOTORS);
double boosterMOI_tr= burnout.getLongitudinalInertia();
assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON);
}
@ -549,9 +686,10 @@ public class MassCalculatorTest extends BaseTestCase {
@Test
public void testCMOverride() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
FlightConfiguration config = rocket.getSelectedConfiguration();
rocket.setName("TestRocket."+Thread.currentThread().getStackTrace()[1].getMethodName());
FlightConfiguration config = rocket.getEmptyConfiguration();
rocket.setSelectedConfiguration( config.getId() );
ParallelStage boosters = (ParallelStage) rocket.getChild(1).getChild(1);
int boostNum = boosters.getStageNumber();
config.setOnlyStage( boostNum);
@ -571,10 +709,11 @@ public class MassCalculatorTest extends BaseTestCase {
{
// Validate Mass
MassCalculator mc = new MassCalculator();
//mc.debug = true;
Coordinate boosterSetCM = mc.getCM( rocket.getSelectedConfiguration(), MassCalcType.NO_MOTORS);
double expMass = BOOSTER_TOTAL_DRY_MASS_EACH;
MassData burnout = mc.calculateBurnoutMassData( config);
Coordinate boosterSetCM = burnout.getCM();
double expMass = BOOSTER_SET_NO_MOTORS_MASS;
double calcTotalMass = boosterSetCM.weight;
assertEquals(" Booster Launch Mass is incorrect: ", expMass, calcTotalMass, EPSILON);
@ -587,11 +726,11 @@ public class MassCalculatorTest extends BaseTestCase {
// Validate MOI
double expMOI_axial = 0.00304203;
double boosterMOI_xx= mc.getRotationalInertia( config, MassCalcType.NO_MOTORS);
double boosterMOI_xx= burnout.getRotationalInertia();
assertEquals(" Booster x-axis MOI is incorrect: ", expMOI_axial, boosterMOI_xx, EPSILON);
double expMOI_tr = 0.1893499746;
double boosterMOI_tr= mc.getLongitudinalInertia( config, MassCalcType.NO_MOTORS);
double boosterMOI_tr= burnout.getLongitudinalInertia();
assertEquals(" Booster transverse MOI is incorrect: ", expMOI_tr, boosterMOI_tr, EPSILON);
}

View File

@ -84,12 +84,12 @@ public class ThrustCurveMotorTest {
public void testVerifyMotorA8_3CG(){
final ThrustCurveMotor mtr = motorEstesA8_3;
final double actCGx0p041 = mtr.getCGx(0.041);
final double actCGx0p041 = mtr.getCMx(0.041);
assertEquals( 0.0352, actCGx0p041, 0.001 );
final double actMass0p041 = mtr.getTotalMass( 0.041 );
assertEquals( 0.016335, actMass0p041, 0.001 );
final double actCGx0p206 = mtr.getCGx( 0.206 );
final double actCGx0p206 = mtr.getCMx( 0.206 );
assertEquals( 0.0362, actCGx0p206, 0.001 );
final double actMass0p206 = mtr.getTotalMass( 0.206 );
assertEquals( 0.015285, actMass0p206, 0.001 );

View File

@ -142,6 +142,20 @@ public class FlightConfigurationTest extends BaseTestCase {
config.setAllStages();
}
/**
* Single stage rocket specific configuration tests
*/
@Test
public void testDefaultConfigurationIsEmpty() {
Rocket r1 = TestRockets.makeEstesAlphaIII();
// don't change the configuration:
FlightConfiguration defaultConfig = r1.getSelectedConfiguration();
assertThat( "Empty configuration has motors! it should be empty!", r1.getEmptyConfiguration().getActiveMotors().size(), equalTo(0));
assertThat( "Default configuration is not the empty configuration. It should be!", defaultConfig.getActiveMotors().size(), equalTo(0));
}
@Test
public void testCreateConfigurationNullId() {
@ -155,7 +169,7 @@ public class FlightConfigurationTest extends BaseTestCase {
assertThat("number of loaded configuration counts doesn't actually match.", actualConfigCount, equalTo(expectedConfigCount));
// create with
rkt.createFlightConfiguration(null);
rkt.createFlightConfiguration( (FlightConfigurationId)null);
expectedConfigCount = 6;
actualConfigCount = rkt.getConfigurationCount();
assertThat("createFlightConfiguration with null: doesn't actually work.", actualConfigCount, equalTo(expectedConfigCount));

View File

@ -55,7 +55,6 @@ import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.motor.MotorConfiguration;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.FinSet;
@ -540,7 +539,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
Map<RocketComponent, AerodynamicForces> aeroData =
aerodynamicCalculator.getForceAnalysis(configuration, conditions, set);
Map<RocketComponent, Coordinate> massData =
massCalculator.getCGAnalysis(configuration, MassCalcType.LAUNCH_MASS);
massCalculator.getCGAnalysis(configuration);
cgData.clear();
@ -579,7 +578,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
cgData.add(data);
data[0] = motorConfig.getMotor().getDesignation();
data[1] = MassCalcType.LAUNCH_MASS.getCG(motorConfig);
data[1] = motorConfig.getMotor().getLaunchMass();
}
forces = aeroData.get(rkt);

View File

@ -28,7 +28,6 @@ import net.sf.openrocket.gui.figureelements.FigureElement;
import net.sf.openrocket.gui.figureelements.RocketInfo;
import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
@ -329,7 +328,11 @@ public class DesignReport {
MassCalculator massCalc = new MassCalculator();
FlightConfiguration config = rocket.createFlightConfiguration(motorId);
if( !motorId.hasError() ){
throw new IllegalStateException("Attempted to add motor data with an invalid fcid");
}
rocket.createFlightConfiguration(motorId);
FlightConfiguration config = rocket.getFlightConfiguration( motorId);
int totalMotorCount = 0;
double totalPropMass = 0;
@ -346,7 +349,7 @@ public class DesignReport {
config.clearAllStages();
config.setOnlyStage(stage);
stage++;
stageMass = massCalc.getCG(config, MassCalcType.LAUNCH_MASS).weight;
stageMass = massCalc.getCGAnalysis( config).get(stage).weight;
// Calculate total thrust-to-weight from only lowest stage motors
totalTTW = 0;
topBorder = true;

View File

@ -38,9 +38,9 @@ import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.ConfigurationModel;
import net.sf.openrocket.gui.components.StageSelector;
import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.components.ConfigurationModel;
import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
import net.sf.openrocket.gui.figure3d.RocketFigure3d;
import net.sf.openrocket.gui.figureelements.CGCaret;
@ -52,7 +52,6 @@ import net.sf.openrocket.gui.simulation.SimulationWorker;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
@ -599,7 +598,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
}
extraText.setTheta(cpTheta);
cg = massCalculator.getCG(curConfig, MassCalcType.LAUNCH_MASS);
cg = massCalculator.getRocketLaunchMassData( curConfig).getCG();
if (cp.weight > MassCalculator.MIN_MASS){
@ -644,7 +643,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
extraText.setLength(length);
extraText.setDiameter(diameter);
extraText.setMass(cg.weight);
extraText.setMassWithoutMotors( massCalculator.getCG( getSelectedConfiguration(), MassCalcType.NO_MOTORS ).weight );
extraText.setMassWithoutMotors( massCalculator.getRocketSpentMassData( curConfig.getRocket().getEmptyConfiguration() ).getMass() );
extraText.setWarnings(warnings);
if (figure.getType() == RocketPanel.VIEW_TYPE.SideView && length > 0) {

View File

@ -13,6 +13,20 @@ import java.io.InputStream;
import javax.swing.Action;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.util.Modules;
import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
import net.sf.openrocket.aerodynamics.BarrowmanCalculator;
import net.sf.openrocket.aerodynamics.FlightConditions;
@ -29,31 +43,21 @@ import net.sf.openrocket.gui.main.UndoRedoAction;
import net.sf.openrocket.l10n.DebugTranslator;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.plugin.PluginModule;
import net.sf.openrocket.rocketcomponent.*;
import net.sf.openrocket.rocketcomponent.EngineBlock;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.utils.CoreServicesModule;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.util.Modules;
/**
* This class contains various integration tests that simulate user actions that
* might be performed.
@ -325,7 +329,7 @@ public class IntegrationTest {
private void checkCgCp(double cgx, double mass, double cpx, double cna) {
Coordinate cg, cp;
cg = massCalc.getCG(config, MassCalcType.LAUNCH_MASS);
cg = massCalc.getRocketLaunchMassData(config).getCG();
assertEquals(cgx, cg.x, 0.001);
assertEquals(mass, cg.weight, 0.0005);