[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:
parent
56463b02fc
commit
04c0914d0a
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -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.;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 );
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user