Use actual burn time for determination of burnout event. Compute
average thrust while simulating.
This commit is contained in:
parent
7e68a01780
commit
a5b083ade7
@ -1,5 +1,6 @@
|
||||
package net.sf.openrocket.masscalc;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -8,13 +9,14 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.MotorConfiguration;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.rocketcomponent.AxialStage;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.Instanceable;
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
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;
|
||||
@ -95,9 +97,9 @@ public class MassCalculator implements Monitorable {
|
||||
* 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 MassData dryData = null;
|
||||
// private MassData launchData = null;
|
||||
// private Vector< MassData> motorData = new Vector<MassData>();
|
||||
|
||||
// this turns on copious amounts of debug. Recommend leaving this false
|
||||
// until reaching code that causes troublesome conditions.
|
||||
@ -163,20 +165,20 @@ public class MassCalculator implements Monitorable {
|
||||
return MassData.ZERO_DATA;
|
||||
}
|
||||
|
||||
// // vvvv DEVEL vvvv
|
||||
// //String massFormat = " [%2s]: %-16s %6s x %6s = %6s += %6s @ (%s, %s, %s )";
|
||||
// String inertiaFormat = " [%2s](%2s): %-16s %6s %6s";
|
||||
// if( debug){
|
||||
// System.err.println("====== ====== getMotorMassData( config:"+config.toDebug()+", type: "+type.name()+") ====== ====== ====== ====== ====== ======");
|
||||
// //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 ^^^^
|
||||
// // vvvv DEVEL vvvv
|
||||
// //String massFormat = " [%2s]: %-16s %6s x %6s = %6s += %6s @ (%s, %s, %s )";
|
||||
// String inertiaFormat = " [%2s](%2s): %-16s %6s %6s";
|
||||
// if( debug){
|
||||
// System.err.println("====== ====== getMotorMassData( config:"+config.toDebug()+", type: "+type.name()+") ====== ====== ====== ====== ====== ======");
|
||||
// //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 allMotorData = MassData.ZERO_DATA;
|
||||
//int motorIndex = 0;
|
||||
for (MotorConfiguration mtrConfig : config.getActiveMotors() ) {
|
||||
ThrustCurveMotor mtr = (ThrustCurveMotor) mtrConfig.getMotor();
|
||||
Motor mtr = (Motor) mtrConfig.getMotor();
|
||||
|
||||
MotorMount mount = mtrConfig.getMount();
|
||||
RocketComponent mountComp = (RocketComponent)mount;
|
||||
@ -191,10 +193,10 @@ public class MassCalculator implements Monitorable {
|
||||
Coordinate curMotorCM = localCM.setX( localCM.x + locations[0].x + motorXPosition );
|
||||
|
||||
// alternate version:
|
||||
// double Ir = inst.getRotationalInertia();
|
||||
// double It = inst.getLongitudinalInertia();
|
||||
// +
|
||||
// + Coordinate curMotorCM = type.getCG(inst);
|
||||
// double Ir = inst.getRotationalInertia();
|
||||
// double It = inst.getLongitudinalInertia();
|
||||
// +
|
||||
// + Coordinate curMotorCM = type.getCG(inst);
|
||||
|
||||
double motorMass = curMotorCM.weight;
|
||||
double Ir_single = mtrConfig.getUnitRotationalInertia()*motorMass;
|
||||
@ -330,6 +332,25 @@ public class MassCalculator implements Monitorable {
|
||||
return mass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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(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().getMassAtMotorTime(motorTime) - curConfig.getMotor().getBurnoutMass())*instanceCount;
|
||||
}
|
||||
return mass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute an analysis of the per-component CG's of the provided configuration.
|
||||
* The returned map will contain an entry for each physical rocket component (not stages)
|
||||
|
@ -121,6 +121,8 @@ public interface Motor extends Cloneable {
|
||||
// there's a second (non-trivial) type of motor to support...
|
||||
public double getThrustAtMotorTime( final double motorTimeDelta );
|
||||
|
||||
public double getAverageThrust( final double startTime, final double endTime );
|
||||
|
||||
public double getLaunchCGx();
|
||||
|
||||
public double getBurnoutCGx();
|
||||
@ -149,4 +151,10 @@ public interface Motor extends Cloneable {
|
||||
*/
|
||||
public double getTotalImpulseEstimate();
|
||||
|
||||
|
||||
double getMassAtMotorTime(final double motorTime);
|
||||
|
||||
|
||||
double getBurnTime();
|
||||
|
||||
}
|
||||
|
@ -44,9 +44,9 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
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 Coordinate[] cg; /// @deprecated, but required b/c the motor database is serialized java classes.
|
||||
private final Coordinate[] cg;
|
||||
private double maxThrust;
|
||||
private double burnTime;
|
||||
private double burnTimeEstimate;
|
||||
private double averageThrust;
|
||||
private double totalImpulse;
|
||||
|
||||
@ -74,7 +74,7 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
// this.cgx = Arrays.copyOf(m.cgx, m.cgx.length);
|
||||
// this.mass = Arrays.copyOf(m.mass, m.mass.length);
|
||||
this.maxThrust = m.maxThrust;
|
||||
this.burnTime = m.burnTime;
|
||||
this.burnTimeEstimate = m.burnTimeEstimate;
|
||||
this.averageThrust = m.averageThrust;
|
||||
this.totalImpulse = m.totalImpulse;
|
||||
|
||||
@ -254,6 +254,51 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAverageThrust( final double startTime, final double endTime ) {
|
||||
|
||||
int timeIndex = 0;
|
||||
|
||||
while( timeIndex < time.length-2 && startTime > time[timeIndex+1] ) {
|
||||
timeIndex++;
|
||||
}
|
||||
|
||||
if ( timeIndex == time.length ) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if ( endTime <= time[timeIndex+1] ) {
|
||||
// we are completely within this time slice so the computation of the average is pretty easy:
|
||||
double avgImpulse = MathUtil.map(startTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]);
|
||||
avgImpulse += MathUtil.map(endTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]);
|
||||
avgImpulse /= 2.0;
|
||||
return avgImpulse;
|
||||
}
|
||||
|
||||
// portion from startTime through time[timeIndex]
|
||||
double avgImpulse = 0.0;
|
||||
// For numeric stability.
|
||||
if( time[timeIndex+1] - startTime > 0.001 ) {
|
||||
avgImpulse = (MathUtil.map(startTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]) + thrust[timeIndex+1])
|
||||
/ 2.0 * (time[timeIndex+1] - startTime);
|
||||
}
|
||||
|
||||
// Now add the whole steps;
|
||||
timeIndex++;
|
||||
while( timeIndex < time.length -1 && endTime >= time[timeIndex+1] ) {
|
||||
avgImpulse += (thrust[timeIndex] + thrust[timeIndex+1]) / 2.0 * (time[timeIndex+1]-time[timeIndex]);
|
||||
timeIndex++;
|
||||
}
|
||||
|
||||
// Now add the bit after the last time index
|
||||
if ( timeIndex < time.length -1 ) {
|
||||
double endInstImpulse = MathUtil.map( endTime, time[timeIndex], time[timeIndex+1], thrust[timeIndex], thrust[timeIndex+1]);
|
||||
avgImpulse += (thrust[timeIndex] + endInstImpulse) / 2.0 * (endTime - time[timeIndex]);
|
||||
}
|
||||
|
||||
return avgImpulse / (endTime - startTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getThrustAtMotorTime( final double searchTime ){
|
||||
double pseudoIndex = getPseudoIndex( searchTime );
|
||||
@ -355,7 +400,7 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThrustCurveMotor clone() {
|
||||
public Motor clone() {
|
||||
return new ThrustCurveMotor(this);
|
||||
}
|
||||
|
||||
@ -379,6 +424,12 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
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...
|
||||
private static double interpolateValueAtIndex( final double[] values, final double pseudoIndex ){
|
||||
final double SNAP_TOLERANCE = 0.0001;
|
||||
|
||||
@ -388,11 +439,11 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
final int upperIndex= lowerIndex+1;
|
||||
|
||||
// if the pseudo
|
||||
if( SNAP_TOLERANCE > (1-lowerFrac) ){
|
||||
if( SNAP_TOLERANCE > lowerFrac ){ // 1-lowerFrac = 1-(1-upperFrac) = upperFrac ?!?
|
||||
// index ~= int ... therefore:
|
||||
return values[ (int) pseudoIndex ];
|
||||
return values[ lowerIndex ];
|
||||
}else if( SNAP_TOLERANCE > upperFrac ){
|
||||
return values[ (int)upperIndex ];
|
||||
return values[ upperIndex ];
|
||||
}
|
||||
|
||||
final double lowerValue = values[lowerIndex];
|
||||
@ -410,6 +461,13 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
return interpolateValueAtIndex( this.time, index );
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getMassAtMotorTime( final double motorTime ) {
|
||||
double pseudoIndex = getPseudoIndex(motorTime);
|
||||
Coordinate cg = getCGAtIndex( pseudoIndex );
|
||||
return cg.weight;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Coordinate getCGAtIndex( final double pseudoIndex ){
|
||||
final double SNAP_TOLERANCE = 0.0001;
|
||||
@ -431,7 +489,7 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
final Coordinate upperValue = cg[upperIndex].multiply(upperFrac);
|
||||
|
||||
// return simple linear interpolation
|
||||
return lowerValue.add( upperValue );
|
||||
return lowerValue.average( upperValue );
|
||||
}
|
||||
|
||||
|
||||
@ -461,7 +519,7 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
|
||||
@Override
|
||||
public double getBurnTimeEstimate() {
|
||||
return burnTime;
|
||||
return burnTimeEstimate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -541,7 +599,7 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
|
||||
|
||||
// Burn time
|
||||
burnTime = Math.max(burnEnd - burnStart, 0);
|
||||
burnTimeEstimate = Math.max(burnEnd - burnStart, 0);
|
||||
|
||||
|
||||
// Total impulse and average thrust
|
||||
@ -567,8 +625,8 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
}
|
||||
}
|
||||
|
||||
if (burnTime > 0) {
|
||||
averageThrust /= burnTime;
|
||||
if (burnTimeEstimate > 0) {
|
||||
averageThrust /= burnTimeEstimate;
|
||||
} else {
|
||||
averageThrust = 0;
|
||||
}
|
||||
|
@ -198,4 +198,20 @@ public class ThrustCurveMotorPlaceholder implements Motor {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getAverageThrust(double startTime, double endTime) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getMassAtMotorTime(final double motorTime) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBurnTime() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
|
||||
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.getConfiguration(), MassCalcType.LAUNCH_MASS);
|
||||
propellantMass = calc.getPropellantMass(status);
|
||||
mass.setPropellantMass( propellantMass );
|
||||
|
||||
// Call post-listener
|
||||
@ -172,7 +172,7 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
|
||||
final double currentTime = status.getSimulationTime() + timestep;
|
||||
Collection<MotorClusterState> activeMotorList = status.getMotors();
|
||||
for (MotorClusterState currentMotorState : activeMotorList ) {
|
||||
thrust += currentMotorState.getThrust( currentTime, atmosphericConditions );
|
||||
thrust += currentMotorState.getAverageThrust( status.getSimulationTime(), currentTime );
|
||||
}
|
||||
|
||||
// Post-listeners
|
||||
|
@ -252,8 +252,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
FlightEvent event;
|
||||
|
||||
log.trace("HandleEvents: current branch = " + currentStatus.getFlightData().getBranchName());
|
||||
log.trace("EventQueue = " + currentStatus.getEventQueue().toString());
|
||||
for (event = nextEvent(); event != null; event = nextEvent()) {
|
||||
log.trace("EventQueue = " + currentStatus.getEventQueue().toString());
|
||||
|
||||
// Ignore events for components that are no longer attached to the rocket
|
||||
if (event.getSource() != null && event.getSource().getParent() != null &&
|
||||
@ -347,7 +347,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
}
|
||||
|
||||
// and queue up the burnout for this motor, as well.
|
||||
double duration = motorState.getMotor().getBurnTimeEstimate();
|
||||
// double duration = motorState.getMotor().getBurnTimeEstimate();
|
||||
double duration = motorState.getBurnTime();
|
||||
double burnout = currentStatus.getSimulationTime() + duration;
|
||||
addEvent(new FlightEvent(FlightEvent.Type.BURNOUT, burnout,
|
||||
event.getSource(), motorState ));
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.sf.openrocket.simulation;
|
||||
|
||||
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
|
||||
import net.sf.openrocket.motor.IgnitionEvent;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.MotorConfiguration;
|
||||
@ -52,12 +51,15 @@ public class MotorClusterState {
|
||||
|
||||
public void burnOut( final double _burnOutTime ){
|
||||
if( ThrustState.THRUSTING == currentState ){
|
||||
this.ignitionTime = _burnOutTime;
|
||||
this.cutoffTime = _burnOutTime;
|
||||
this.currentState = this.currentState.getNext();
|
||||
// }else{
|
||||
// System.err.println("!! Attempted to turn off motor state "+toDescription()+" with current status="
|
||||
// +this.currentState.getName()+" ... Ignoring.");
|
||||
}
|
||||
if( !this.hasEjectionCharge() ) {
|
||||
this.currentState = ThrustState.SPENT;
|
||||
}
|
||||
}
|
||||
|
||||
public void expend( final double _ejectionTime ){
|
||||
@ -70,6 +72,9 @@ public class MotorClusterState {
|
||||
}
|
||||
}
|
||||
|
||||
public double getBurnTime( ) {
|
||||
return motor.getBurnTime();
|
||||
}
|
||||
/**
|
||||
* Alias for "burnOut(double)"
|
||||
*/
|
||||
@ -105,10 +110,18 @@ public class MotorClusterState {
|
||||
return _simulationTime - this.getIgnitionTime();
|
||||
}
|
||||
|
||||
public double getThrust( final double simulationTime, final AtmosphericConditions cond){
|
||||
if( this.currentState.isThrusting() ){
|
||||
double motorTime = this.getMotorTime( simulationTime);
|
||||
return this.motorCount * motor.getThrustAtMotorTime( motorTime );
|
||||
/**
|
||||
* Compute the average thrust over an interval.
|
||||
*
|
||||
* @param simulationTime
|
||||
* @param cond
|
||||
* @return
|
||||
*/
|
||||
public double getAverageThrust( final double startTime, final double endTime) {
|
||||
if( this.currentState.isThrusting() ) {
|
||||
double motorStartTime = this.getMotorTime( startTime );
|
||||
double motorEndTime = this.getMotorTime(endTime);
|
||||
return this.motorCount * motor.getAverageThrust( motorStartTime, motorEndTime );
|
||||
}else{
|
||||
return 0.0;
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import java.util.List;
|
||||
import net.sf.openrocket.file.motor.GeneralMotorLoader;
|
||||
import net.sf.openrocket.file.motor.MotorLoader;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
|
||||
@ -116,7 +115,7 @@ public class MotorCorrelation {
|
||||
// Output motor digests
|
||||
final int count = motors.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
System.out.println(files.get(i) + ": " + ((ThrustCurveMotor) motors.get(i)).getDigest());
|
||||
System.out.println(files.get(i) + ": " + ((Motor) motors.get(i)).getDigest());
|
||||
}
|
||||
|
||||
// Cross-correlate every pair
|
||||
|
@ -46,7 +46,7 @@ public class MotorDigester {
|
||||
continue;
|
||||
}
|
||||
|
||||
String digest = ((ThrustCurveMotor) m).getDigest();
|
||||
String digest = ((Motor) m).getDigest();
|
||||
if (printFileNames) {
|
||||
System.out.print(file + ": ");
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@ -63,7 +62,7 @@ public class TestMotorLoader {
|
||||
|
||||
String[] d = new String[digests.length];
|
||||
for (int i = 0; i < motors.size(); i++) {
|
||||
d[i] = ((ThrustCurveMotor) motors.get(i)).getDigest();
|
||||
d[i] = ((Motor) motors.get(i)).getDigest();
|
||||
}
|
||||
|
||||
Arrays.sort(digests);
|
||||
|
Loading…
x
Reference in New Issue
Block a user