[Refactor ] Refactored motor state in preparation for debugging the simulation itself

- Created MotorState as an enum describing discrete states a motor may be in
- moved ThrustCurveMotorState info back into MotorSimulation
  -- MotorSimulation will be used by the simulation code.
  -- tracks simulation-time info, such as event times, and current state ( <MotorState> )
- MotorConfiguration no longer have any knowledge about their simulation info
- moved functionality (BUT NOT STATE) into ThrustCurveMotor
  -- can query about thrust(t), mass(t), cgx(t)
This commit is contained in:
Daniel_M_Williams 2016-02-12 12:29:36 -05:00 committed by Daniel_M_Williams
parent 438f58c438
commit 28689825a4
27 changed files with 754 additions and 506 deletions

View File

@ -25,25 +25,37 @@ public class MassCalculator implements Monitorable {
public static enum MassCalcType {
NO_MOTORS {
@Override
public Coordinate getCG(Motor motor) {
return Coordinate.NUL;
public double getCGx(Motor motor) {
return 0;
}
@Override
public double getMass(Motor motor) {
return 0;
}
},
LAUNCH_MASS {
@Override
public Coordinate getCG(Motor motor) {
return motor.getLaunchCG();
public double getCGx(Motor motor) {
return motor.getLaunchCGx();
}
@Override
public double getMass(Motor motor) {
return motor.getLaunchMass();
}
},
BURNOUT_MASS {
@Override
public Coordinate getCG(Motor motor) {
return motor.getEmptyCG();
public double getCGx(Motor motor) {
return motor.getBurnoutCGx();
}
@Override
public double getMass(Motor motor) {
return motor.getBurnoutMass();
}
};
public abstract Coordinate getCG(Motor motor);
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
@ -52,16 +64,21 @@ public class MassCalculator implements Monitorable {
* @return
*/
public Coordinate getCG(MotorConfiguration motorConfig) {
Coordinate cg = getCG(motorConfig.getMotor());
cg = cg.add(motorConfig.getPosition());
Motor mtr = motorConfig.getMotor();
double mass = getMass(mtr);
Coordinate cg = motorConfig.getPosition().add( getCGx(mtr), 0, 0, mass);
RocketComponent motorMount = (RocketComponent) motorConfig.getMount();
Coordinate totalCG = new Coordinate();
Coordinate totalCM = new Coordinate();
for (Coordinate cord : motorMount.toAbsolute(cg) ) {
totalCG = totalCG.average(cord);
totalCM = totalCM.average(cord);
}
return totalCG;
return totalCM;
}
public Coordinate getCM( Motor motor ){
return new Coordinate( getCGx(motor), 0, 0, getMass(motor));
}
}
@ -168,7 +185,7 @@ public class MassCalculator implements Monitorable {
double motorXPosition = mtrConfig.getX(); // location of motor from mount
Coordinate localCM = type.getCG( mtr ); // 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 );

View File

@ -1,9 +1,6 @@
package net.sf.openrocket.motor;
import net.sf.openrocket.simulation.MotorState;
import net.sf.openrocket.util.Coordinate;
public interface Motor {
public interface Motor extends Cloneable {
/**
* Enum of rocket motor types.
@ -118,12 +115,19 @@ public interface Motor {
public String getDigest();
public MotorState getNewInstance();
public Motor clone();
public Coordinate getLaunchCG();
// this is probably a badly-designed way to expose the thrust, but it's not worth worrying about until
// there's a second (non-trivial) type of motor to support...
public double getThrustAtMotorTime( final double motorTimeDelta );
public Coordinate getEmptyCG();
public double getLaunchCGx();
public double getBurnoutCGx();
public double getLaunchMass();
public double getBurnoutMass();
/**
* Return an estimate of the burn time of this motor, or NaN if an estimate is unavailable.

View File

@ -4,7 +4,6 @@ import net.sf.openrocket.rocketcomponent.FlightConfigurableParameter;
import net.sf.openrocket.rocketcomponent.IgnitionEvent;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.simulation.MotorState;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Inertia;
@ -16,6 +15,7 @@ public class MotorConfiguration implements FlightConfigurableParameter<MotorConf
public static final String EMPTY_DESCRIPTION = "Empty Configuration";
// set at config time
protected MotorMount mount = null;
protected Motor motor = null;
protected double ejectionDelay = 0.0;
@ -41,30 +41,10 @@ public class MotorConfiguration implements FlightConfigurableParameter<MotorConf
modID++;
}
public MotorState getSimulationState() {
MotorState state = motor.getNewInstance();
state.setEjectionDelay( this.ejectionDelay );
if( ignitionOveride ) {
state.setIgnitionEvent( this.ignitionEvent );
state.setIgnitionDelay( this.ignitionDelay );
} else {
MotorConfiguration defInstance = mount.getDefaultMotorInstance();
state.setIgnitionEvent( defInstance.ignitionEvent );
state.setIgnitionDelay( defInstance.ignitionDelay );
}
state.setMount( mount );
state.setId( id );
return state;
}
public boolean hasIgnitionOverride() {
return ignitionOveride;
}
public boolean isActive() {
return motor != null;
}
public String getDescription(){
if( motor == null ){
return EMPTY_DESCRIPTION;
@ -164,7 +144,7 @@ public class MotorConfiguration implements FlightConfigurableParameter<MotorConf
public double getPropellantMass(){
if ( motor != null ) {
return (motor.getLaunchCG().weight - motor.getEmptyCG().weight);
return (motor.getLaunchMass() - motor.getBurnoutMass());
}
return 0.0;
}

View File

@ -8,16 +8,13 @@ import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sf.openrocket.util.ArrayUtils;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Inertia;
import net.sf.openrocket.util.MathUtil;
public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Serializable {
/**
*
*/
// NECESSARY field, for this class -- this class is serialized in the motor database, and loaded directly.
private static final long serialVersionUID = -1490333207132694479L;
@SuppressWarnings("unused")
@ -45,13 +42,18 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
private final double length;
private final double[] time;
private final double[] thrust;
private final Coordinate[] cg;
// 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 double maxThrust;
private double burnTime;
private double averageThrust;
private double totalImpulse;
private final double unitRotationalInertia;
private final double unitLongitudinalInertia;
/**
* Deep copy constructor.
* Constructs a new ThrustCurveMotor from an existing ThrustCurveMotor.
@ -63,19 +65,22 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
this.designation = m.designation;
this.description = m.description;
this.type = m.type;
this.delays = ArrayUtils.copyOf(m.delays, m.delays.length);
this.delays = Arrays.copyOf(m.delays, m.delays.length);
this.diameter = m.diameter;
this.length = m.length;
this.time = ArrayUtils.copyOf(m.time, m.time.length);
this.thrust = ArrayUtils.copyOf(m.thrust, m.thrust.length);
this.cg = new Coordinate[m.cg.length];
for (int i = 0; i < cg.length; i++) {
this.cg[i] = m.cg[i].clone();
}
this.time = Arrays.copyOf(m.time, m.time.length);
this.thrust = Arrays.copyOf(m.thrust, m.thrust.length);
this.cg = m.getCGPoints().clone();
// 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.averageThrust = m.averageThrust;
this.totalImpulse = m.totalImpulse;
this.unitRotationalInertia = m.unitRotationalInertia;
this.unitLongitudinalInertia = m.unitLongitudinalInertia;
}
/**
@ -162,8 +167,19 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
this.time = time.clone();
this.thrust = thrust.clone();
this.cg = cg.clone();
// this.cgx = new double[ cg.length];
// this.mass = new double[ cg.length];
// for (int cgIndex = 0; cgIndex < cg.length; ++cgIndex){
// this.cgx[cgIndex] = cg[cgIndex].x;
// this.mass[cgIndex] = cg[cgIndex].weight;
// }
unitRotationalInertia = Inertia.filledCylinderRotational( this.diameter / 2);
unitLongitudinalInertia = Inertia.filledCylinderLongitudinal( this.diameter / 2, this.length);
computeStatistics();
// This constructor is not called upon serialized data constructor.
//System.err.println("loading motor: "+designation);
}
@ -186,6 +202,65 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
return time.clone();
}
/*
* find the index to data that corresponds to the given time:
*
* Pseudo Index is two parts:
* integer : simple index into the array
* fractional part: [0,1]: weighting to interpolate between the given index and the next index.
*
* @param is time after motor ignition, in seconds
* @return a pseudo index to this motor's data.
*/
public double getPseudoIndex( final double motorTime ){
final double SNAP_DISTANCE = 0.001;
if( time.length == 0 ){
return Double.NaN;
}
if( 0 > motorTime ){
return 0.0;
}
int lowerBoundIndex=0;
int upperBoundIndex=0;
while( ( upperBoundIndex < time.length ) && ( motorTime >= time[upperBoundIndex] )){
++upperBoundIndex;
}
lowerBoundIndex = upperBoundIndex-1;
if( upperBoundIndex == time.length ){
return time.length - 1;
}
if ( SNAP_DISTANCE > Math.abs( motorTime - time[lowerBoundIndex])){
return lowerBoundIndex;
}
double lowerBoundTime = time[lowerBoundIndex];
double upperBoundTime = time[upperBoundIndex];
double timeFraction = motorTime - lowerBoundTime;
double indexFraction = ( timeFraction ) / ( upperBoundTime - lowerBoundTime );
if( indexFraction < SNAP_DISTANCE ){
// round down to last index
return lowerBoundIndex;
}else if( indexFraction > (1-SNAP_DISTANCE)){
// round up to next index
return ++lowerBoundIndex;
}else{
// general case
return lowerBoundIndex + indexFraction;
}
}
@Override
public double getThrustAtMotorTime( final double searchTime ){
double pseudoIndex = getPseudoIndex( searchTime );
return getThrustAtIndex( pseudoIndex );
}
/**
* Returns the array of thrust points for this thrust curve.
* @return an array of thrust samples
@ -194,14 +269,26 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
return thrust.clone();
}
/**
* Returns the array of CG points for this thrust curve.
* @return an array of CG samples
*/
public Coordinate[] getCGPoints() {
// /**
// * Returns the array of CG points for this thrust curve.
// * @return an array of CG samples
// */
// public double[] getCGxPoints() {
// return cgx;
// }
public Coordinate[] getCGPoints(){
return cg.clone();
}
// /**
// * Returns the array of Mass values for this thrust curve.
// * @return an array of Masses
// */
// public double[] getMassPoints() {
// return mass;
// }
/**
* Return a list of standard delays defined for this motor.
* @return a list of standard delays
@ -221,6 +308,25 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
return type;
}
public double getUnitLongitudinalInertia() {
return this.unitLongitudinalInertia;
}
public double getUnitRotationalInertia() {
return this.unitRotationalInertia;
}
public double getIxxAtTime( final double searchTime) {
final double index = getPseudoIndex( searchTime);
//return this.unitLongitudinalInertia * this.getMassAtIndex( index);
return this.unitLongitudinalInertia * this.getCGAtIndex( index).weight;
}
public double getIyyAtTime( final double searchTime) {
final double index = getPseudoIndex( searchTime);
//return this.unitRotationalInertia * this.getMassAtIndex( index);
return this.unitRotationalInertia * this.getCGAtIndex( index).weight;
}
@Override
public String getDesignation() {
@ -248,29 +354,104 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
return length;
}
@Override
public ThrustCurveMotorState getNewInstance() {
return new ThrustCurveMotorState(this);
}
@Override
public Coordinate getLaunchCG() {
return cg[0];
public ThrustCurveMotor clone() {
return new ThrustCurveMotor(this);
}
@Override
public Coordinate getEmptyCG() {
return cg[cg.length - 1];
public double getLaunchCGx() {
return cg[0].x;//cgx[0];
}
// Coordinate getCG(int index)
// double getThrust( int index)
// double getTime( int index)
@Override
public double getBurnoutCGx() {
return cg[cg.length - 1].x;// cgx[ cg.length - 1];
}
@Override
public double getLaunchMass() {
return cg[0].weight;//mass[0];
}
@Override
public double getBurnoutMass() {
return cg[cg.length-1].weight; //mass[mass.length - 1];
}
private static double interpolateValueAtIndex( final double[] values, final double pseudoIndex ){
final double SNAP_TOLERANCE = 0.0001;
final double upperFrac = pseudoIndex%1;
final double lowerFrac = 1-upperFrac;
final int lowerIndex = (int)pseudoIndex;
final int upperIndex= lowerIndex+1;
// if the pseudo
if( SNAP_TOLERANCE > (1-lowerFrac) ){
// index ~= int ... therefore:
return values[ (int) pseudoIndex ];
}else if( SNAP_TOLERANCE > upperFrac ){
return values[ (int)upperIndex ];
}
final double lowerValue = values[lowerIndex];
final double upperValue = values[upperIndex];
// return simple linear interpolation
return ( lowerValue*lowerFrac + upperValue*upperFrac );
}
public double getThrustAtIndex( final double pseudoIndex ){
return interpolateValueAtIndex( this.thrust, pseudoIndex );
}
public double getMotorTimeAtIndex( final double index ){
return interpolateValueAtIndex( this.time, index );
}
@Deprecated
public Coordinate getCGAtIndex( final double pseudoIndex ){
final double SNAP_TOLERANCE = 0.0001;
final double upperFrac = pseudoIndex%1;
final double lowerFrac = 1-upperFrac;
final int lowerIndex = (int)pseudoIndex;
final int upperIndex= lowerIndex+1;
// if the pseudo
if( SNAP_TOLERANCE > (1-lowerFrac) ){
// index ~= int ... therefore:
return cg[ (int) pseudoIndex ];
}else if( SNAP_TOLERANCE > upperFrac ){
return cg[ (int)upperIndex ];
}
final Coordinate lowerValue = cg[lowerIndex].multiply(lowerFrac);
final Coordinate upperValue = cg[upperIndex].multiply(upperFrac);
// return simple linear interpolation
return lowerValue.add( upperValue );
}
// public double getCGxAtIndex( final double index){
// //return interpolateValueAtIndex( this.cgx, index );
//
// }
//
// public double getMassAtIndex( final double index){
// //return interpolateValueAtIndex( this.mass, index );
//
// }
// int getCutoffIndex();
// double getCutoffTime()
// int getCutoffIndex();
// double interpolateThrust(...)
// Coordinate interpolateCG( ... )
//
@ -304,7 +485,7 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
}
public double getCutOffTime() {
return this.time[this.time.length - 1];
return time[time.length - 1];
}
/**

View File

@ -1,8 +1,6 @@
package net.sf.openrocket.motor;
import net.sf.openrocket.simulation.MotorState;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
public class ThrustCurveMotorPlaceholder implements Motor {
@ -75,20 +73,10 @@ public class ThrustCurveMotorPlaceholder implements Motor {
}
@Override
public MotorState getNewInstance() {
public Motor clone() {
throw new BugException("Called getInstance on PlaceholderMotor");
}
@Override
public Coordinate getLaunchCG() {
return new Coordinate(length / 2, 0, 0, launchMass);
}
@Override
public Coordinate getEmptyCG() {
return new Coordinate(length / 2, 0, 0, emptyMass);
}
@Override
public double getBurnTimeEstimate() {
return Double.NaN;
@ -184,4 +172,30 @@ public class ThrustCurveMotorPlaceholder implements Motor {
+ ", designation=" + designation + "]";
}
@Override
public double getLaunchCGx() {
return length / 2;
}
@Override
public double getBurnoutCGx() {
return length / 2;
}
@Override
public double getLaunchMass() {
return launchMass;
}
@Override
public double getBurnoutMass() {
return emptyMass;
}
@Override
public double getThrustAtMotorTime(double pseudoIndex) {
return 0;
}
}

View File

@ -1,257 +0,0 @@
package net.sf.openrocket.motor;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.rocketcomponent.IgnitionEvent;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.simulation.MotorState;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Inertia;
import net.sf.openrocket.util.MathUtil;
public class ThrustCurveMotorState implements MotorState {
// private static final Logger log = LoggerFactory.getLogger(ThrustCurveMotorInstance.class);
private int timeIndex = -1;
protected MotorMount mount = null;
protected MotorInstanceId id = null;
private double ignitionTime = -1;
private double ignitionDelay;
private IgnitionEvent ignitionEvent;
private double ejectionDelay;
protected ThrustCurveMotor motor = null;
// Previous time step value
private double prevTime = Double.NaN;
// Average thrust during previous step
private double stepThrust = Double.NaN;
// Instantaneous thrust at current time point
private double instThrust = Double.NaN;
// Average CG during previous step
private Coordinate stepCG = Coordinate.ZERO;
// Instantaneous CG at current time point
private Coordinate instCG = Coordinate.ZERO;
private final double unitRotationalInertia;
private final double unitLongitudinalInertia;
// // please use the Motor Constructor below, instead.
// @SuppressWarnings("unused")
// private ThrustCurveMotorInstance() {
// unitRotationalInertia = Double.NaN;
// unitLongitudinalInertia = Double.NaN;
// }
public ThrustCurveMotorState(final ThrustCurveMotor source) {
//log.debug( Creating motor instance of " + ThrustCurveMotor.this);
this.motor = source;
this.reset();
unitRotationalInertia = Inertia.filledCylinderRotational(source.getDiameter() / 2);
unitLongitudinalInertia = Inertia.filledCylinderLongitudinal(source.getDiameter() / 2, source.getLength());
}
@Override
public ThrustCurveMotorState clone() {
try {
return (ThrustCurveMotorState) super.clone();
} catch (CloneNotSupportedException e) {
throw new BugException("CloneNotSupportedException", e);
}
}
@Override
public double getIgnitionTime() {
return ignitionTime;
}
@Override
public void setIgnitionTime(double ignitionTime) {
this.ignitionTime = ignitionTime;
}
@Override
public MotorMount getMount() {
return mount;
}
@Override
public void setMount(MotorMount mount) {
this.mount = mount;
}
@Override
public IgnitionEvent getIgnitionEvent() {
return ignitionEvent;
}
@Override
public void setIgnitionEvent(IgnitionEvent event) {
this.ignitionEvent = event;
}
@Override
public double getIgnitionDelay() {
return ignitionDelay;
}
@Override
public void setIgnitionDelay(double delay) {
this.ignitionDelay = delay;
}
@Override
public double getEjectionDelay() {
return ejectionDelay;
}
@Override
public void setEjectionDelay(double delay) {
this.ejectionDelay = delay;
}
@Override
public void setId(MotorInstanceId id) {
this.id = id;
}
@Override
public MotorInstanceId getID() {
return id;
}
public double getTime() {
return prevTime;
}
public Coordinate getCG() {
return stepCG;
}
public Coordinate getCM() {
return stepCG;
}
public double getPropellantMass(){
return (motor.getLaunchCG().weight - motor.getEmptyCG().weight);
}
public double getLongitudinalInertia() {
return unitLongitudinalInertia * stepCG.weight;
}
public double getRotationalInertia() {
return unitRotationalInertia * stepCG.weight;
}
@Override
public double getThrust() {
return stepThrust;
}
@Override
public boolean isActive() {
return prevTime < motor.getCutOffTime();
}
public Motor getMotor(){
return this.motor;
}
public boolean isEmpty(){
return false;
}
@Override
public void step(double nextTime, double acceleration, AtmosphericConditions cond) {
if (MathUtil.equals(prevTime, nextTime)) {
return;
}
double[] time = motor.getTimePoints();
double[] thrust = motor.getThrustPoints();
Coordinate[] cg = motor.getCGPoints();
if (timeIndex >= (motor.getDataSize() - 1)) {
// Thrust has ended
prevTime = nextTime;
stepThrust = 0;
instThrust = 0;
stepCG = motor.getEmptyCG();
return;
}
// Compute average & instantaneous thrust
if (nextTime < time[timeIndex + 1]) {
// Time step between time points
double nextF = MathUtil.map(nextTime, time[timeIndex], time[timeIndex + 1],
thrust[timeIndex], thrust[timeIndex + 1]);
stepThrust = (instThrust + nextF) / 2;
instThrust = nextF;
} else {
// Portion of previous step
stepThrust = (instThrust + thrust[timeIndex + 1]) / 2 * (time[timeIndex + 1] - prevTime);
// Whole steps
timeIndex++;
while ((timeIndex < time.length - 1) && (nextTime >= time[timeIndex + 1])) {
stepThrust += (thrust[timeIndex] + thrust[timeIndex + 1]) / 2 *
(time[timeIndex + 1] - time[timeIndex]);
timeIndex++;
}
// End step
if (timeIndex < time.length - 1) {
instThrust = MathUtil.map(nextTime, time[timeIndex], time[timeIndex + 1],
thrust[timeIndex], thrust[timeIndex + 1]);
stepThrust += (thrust[timeIndex] + instThrust) / 2 *
(nextTime - time[timeIndex]);
} else {
// Thrust ended during this step
instThrust = 0;
}
stepThrust /= (nextTime - prevTime);
}
// Compute average and instantaneous CG (simple average between points)
Coordinate nextCG;
if (timeIndex < time.length - 1) {
nextCG = MathUtil.map(nextTime, time[timeIndex], time[timeIndex + 1],
cg[timeIndex], cg[timeIndex + 1]);
} else {
nextCG = cg[cg.length - 1];
}
stepCG = instCG.add(nextCG).multiply(0.5);
instCG = nextCG;
// Update time
prevTime = nextTime;
}
public void reset(){
timeIndex = 0;
prevTime = 0;
instThrust = 0;
stepThrust = 0;
instCG = motor.getLaunchCG();
stepCG = instCG;
}
@Override
public String toString(){
return this.motor.getDesignation();
}
}

View File

@ -528,12 +528,11 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
StringBuilder buf = new StringBuilder();
buf.append(String.format("\nDumping %2d Motors for configuration %s: (#: %s)\n", this.motors.size(), this, this.instanceNumber));
final String fmt = " ..[%-8s] <%6s> %-12s %-20s\n";
buf.append(String.format(fmt, "Motor Id", "?active", "Mtr Desig","Mount"));
buf.append(String.format(fmt, "Motor Id", "Mtr Desig","Mount"));
for( MotorConfiguration curConfig : this.motors.values() ){
MotorMount mount = curConfig.getMount();
String motorId = curConfig.getID().toShortKey();
String activeDescr = (curConfig.isActive()? "active": "inactv");
String motorDesig;
if( curConfig.isEmpty() ){
motorDesig = "(empty)";
@ -542,7 +541,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
}
String mountName = ((RocketComponent)mount).getName();
buf.append(String.format( fmt, motorId, activeDescr, motorDesig, mountName));
buf.append(String.format( fmt, motorId, motorDesig, mountName));
}
buf.append("\n");
return buf.toString();

View File

@ -170,19 +170,13 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
thrust = 0;
final double currentTime = status.getSimulationTime() + timestep;
Collection<MotorState> activeMotorList = status.getActiveMotors();
for (MotorState currentMotorInstance : activeMotorList ) {
Collection<MotorSimulation> activeMotorList = status.getMotors();
for (MotorSimulation currentMotorState : activeMotorList ) {
if ( !stepMotors ) {
currentMotorInstance = currentMotorInstance.clone();
}
// old: transplanted from MotorInstanceConfiguration
double instanceTime = currentTime - currentMotorInstance.getIgnitionTime();
if (instanceTime >= 0) {
currentMotorInstance.step(instanceTime, acceleration, atmosphericConditions);
currentMotorState = currentMotorState.clone();
}
// old: from here
thrust += currentMotorInstance.getThrust();
thrust += currentMotorState.getThrust( currentTime, atmosphericConditions );
}
// Post-listeners

View File

@ -194,11 +194,19 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Check for burnt out motors
for( MotorState motor : currentStatus.getAllMotors()){
MotorInstanceId motorId = motor.getID();
if (!motor.isActive() && currentStatus.addBurntOutMotor(motorId)) {
for( MotorSimulation state : currentStatus.getAllMotors()){
state.isFinishedThrusting( );
MotorInstanceId motorId = state.getID();
if ( state.isFinishedThrusting()){
// TODO : implement me!
//currentStatus.moveBurntOutMotor( motorId);
currentStatus.addBurntOutMotor(motorId);
addEvent(new FlightEvent(FlightEvent.Type.BURNOUT, currentStatus.getSimulationTime(),
(RocketComponent) motor.getMount(), motorId));
(RocketComponent) state.getMount(), motorId));
}
}
@ -275,7 +283,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
if (event.getType() == FlightEvent.Type.IGNITION) {
MotorMount mount = (MotorMount) event.getSource();
MotorInstanceId motorId = (MotorInstanceId) event.getData();
MotorState instance = currentStatus.getMotor(motorId);
MotorSimulation instance = currentStatus.getMotor(motorId);
if (!SimulationListenerHelper.fireMotorIgnition(currentStatus, motorId, mount, instance)) {
continue;
}
@ -288,7 +296,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
}
// Check for motor ignition events, add ignition events to queue
for (MotorState inst : currentStatus.getActiveMotors() ){
for (MotorSimulation inst : currentStatus.getAllMotors() ){
IgnitionEvent ignitionEvent = inst.getIgnitionEvent();
MotorMount mount = inst.getMount();
RocketComponent component = (RocketComponent) mount;
@ -296,7 +304,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
if (ignitionEvent.isActivationEvent(event, component)) {
double ignitionDelay = inst.getIgnitionDelay();
// TODO: this event seems to get enque'd multiple times -- more than necessary...
//System.err.println("Queing ignition of mtr:"+inst.getMotor().getDesignation()+" @"+currentStatus.getSimulationTime());
//System.err.println("Queueing ignition of mtr:"+inst.getMotor().getDesignation()+" @"+currentStatus.getSimulationTime());
addEvent(new FlightEvent(FlightEvent.Type.IGNITION,
currentStatus.getSimulationTime() + ignitionDelay,
component, inst.getID() ));
@ -341,8 +349,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
case IGNITION: {
// Ignite the motor
MotorInstanceId motorId = (MotorInstanceId) event.getData();
MotorState inst = currentStatus.getMotor( motorId);
inst.setIgnitionTime(event.getTime());
MotorSimulation inst = currentStatus.getMotor( motorId);
inst.ignite( event.getTime());
//System.err.println("Igniting motor: "+inst.getMotor().getDesignation()+" @"+currentStatus.getSimulationTime());
currentStatus.setMotorIgnited(true);
@ -370,10 +378,11 @@ public class BasicEventSimulationEngine implements SimulationEngine {
if (!currentStatus.isLiftoff()) {
throw new SimulationLaunchException(trans.get("BasicEventSimulationEngine.error.earlyMotorBurnout"));
}
// Add ejection charge event
MotorInstanceId motorId = (MotorInstanceId) event.getData();
MotorState motor = currentStatus.getMotor( motorId);
double delay = motor.getEjectionDelay();
MotorSimulation state = currentStatus.getMotor( motorId);
double delay = state.getEjectionDelay();
if (delay != Motor.PLUGGED) {
addEvent(new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, currentStatus.getSimulationTime() + delay,
event.getSource(), event.getData()));

View File

@ -0,0 +1,160 @@
package net.sf.openrocket.simulation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorConfiguration;
import net.sf.openrocket.motor.MotorInstanceId;
import net.sf.openrocket.rocketcomponent.IgnitionEvent;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.util.BugException;
public class MotorSimulation {
private static final Logger log = LoggerFactory.getLogger(MotorSimulation.class);
// for reference: set at initialization ONLY.
final protected Motor motor;
final protected MotorConfiguration config;
final protected double thrustDuration;
// for state:
protected double ignitionTime = Double.NaN;
protected double cutoffTime = Double.NaN;
protected double ejectionTime = Double.NaN;
protected MotorState currentState = MotorState.PREFLIGHT;
public MotorSimulation(final MotorConfiguration _config) {
log.debug(" Creating motor instance of " + _config.getDescription());
this.config = _config;
this.motor = _config.getMotor();
thrustDuration = this.motor.getBurnTimeEstimate();
this.resetToPreflight();
}
@Override
public MotorSimulation clone() {
try {
return (MotorSimulation) super.clone();
} catch (CloneNotSupportedException e) {
throw new BugException("CloneNotSupportedException", e);
}
}
public void arm( final double _armTime ){
if( MotorState.PREFLIGHT == currentState ){
log.info( "igniting motor: "+this.toString()+" at t="+_armTime);
//this.ignitionTime = _ignitionTime;
this.currentState = this.currentState.getNext();
}else{
throw new IllegalStateException("Attempted to arm a motor with status="+this.currentState.getName());
}
}
public boolean isFinishedThrusting(){
return currentState.isAfter( MotorState.THRUSTING );
}
public double getIgnitionTime() {
return ignitionTime;
}
public IgnitionEvent getIgnitionEvent() {
return config.getIgnitionEvent();
}
public void ignite( final double _ignitionTime ){
if( MotorState.ARMED == currentState ){
log.info( "igniting motor: "+this.toString()+" at t="+_ignitionTime);
this.ignitionTime = _ignitionTime;
this.currentState = this.currentState.getNext();
}else{
throw new IllegalStateException("Attempted to ignite a motor state with status="+this.currentState.getName());
}
}
public void burnOut( final double _burnOutTime ){
if( MotorState.THRUSTING == currentState ){
log.info( "igniting motor: "+this.toString()+" at t="+_burnOutTime);
this.ignitionTime = _burnOutTime;
this.currentState = this.currentState.getNext();
}else{
throw new IllegalStateException("Attempted to stop thrust (burn-out) a motor state with status="+this.currentState.getName());
}
}
public void fireEjectionCharge( final double _ejectionTime ){
if( MotorState.DELAYING == currentState ){
log.info( "igniting motor: "+this.toString()+" at t="+_ejectionTime);
this.ejectionTime = _ejectionTime;
this.currentState = this.currentState.getNext();
}else{
throw new IllegalStateException("Attempted to fire an ejection charge in motor state: "+this.currentState.getName());
}
}
/*
* Alias for "burnOut(double)"
*/
public void cutOff( final double _cutoffTime ){
burnOut( _cutoffTime );
}
public double getIgnitionDelay() {
return config.getEjectionDelay();
}
public double getEjectionDelay() {
return config.getEjectionDelay();
}
public MotorInstanceId getID() {
return config.getID();
}
public double getPropellantMass(){
return (motor.getLaunchMass() - motor.getBurnoutMass());
}
// public boolean isActive( ) {
// return this.currentState.isActive();
// }
public MotorMount getMount(){
return config.getMount();
}
public Motor getMotor(){
return this.motor;
}
double getCutOffTime(){
return this.cutoffTime;
}
public double getThrust( final double simTime, final AtmosphericConditions cond){
if( this.currentState.isThrusting() ){
return motor.getThrustAtMotorTime( simTime - this.getIgnitionTime());
}else{
return 0.0;
}
}
public void resetToPreflight(){
ignitionTime = Double.NaN;
cutoffTime = Double.NaN;
ejectionTime = Double.NaN;
currentState = MotorState.PREFLIGHT;
}
@Override
public String toString(){
return this.motor.getDesignation();
}
}

View File

@ -1,34 +1,104 @@
package net.sf.openrocket.simulation;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.motor.MotorInstanceId;
import net.sf.openrocket.rocketcomponent.IgnitionEvent;
import net.sf.openrocket.rocketcomponent.MotorMount;
public interface MotorState extends Cloneable {
public enum MotorState {
FINISHED("Finished with sequence.", "Finished Producing thrust.", null)
,DELAYING("Delaying", " After Burnout, but before ejection", FINISHED){
@Override
public boolean needsSimulation(){ return true;}
}
,THRUSTING("Thrusting", "Currently Producing thrust", DELAYING){
@Override
public boolean isThrusting(){ return true; }
@Override
public boolean needsSimulation(){ return true;}
}
,ARMED("Armed", "Armed, but not yet lit.", FINISHED)
,PREFLIGHT("Pre-Launch", "Safed and inactive, prior to launch.", FINISHED)
;
public void step(double nextTime, double acceleration, AtmosphericConditions cond);
public double getThrust();
public boolean isActive();
private final static int SEQUENCE_NUMBER_END = 10; // arbitrary number
public double getIgnitionTime();
public void setIgnitionTime( double ignitionTime );
private final String name;
private final String description;
private final int sequenceNumber;
private final MotorState nextState;
public void setMount(MotorMount mount);
public MotorMount getMount();
MotorState( final String name, final String description, final MotorState _nextState) {
this.name = name;
this.description = description;
if( null == _nextState ){
this.sequenceNumber = SEQUENCE_NUMBER_END;
this.nextState = null;
}else{
this.sequenceNumber = -1 + _nextState.getSequenceNumber() ;
this.nextState = _nextState;
}
}
public void setId(MotorInstanceId id);
public MotorInstanceId getID();
/**
* Return a short name of this motor type.
* @return a short name of the motor type.
*/
public String getName() {
return this.name;
}
public IgnitionEvent getIgnitionEvent();
public void setIgnitionEvent( IgnitionEvent event );
public double getIgnitionDelay();
public void setIgnitionDelay( double delay );
/*
*
* @Return a MotorState enum telling what state should follow this one.
*/
public MotorState getNext( ){
return this.nextState;
}
public double getEjectionDelay();
public void setEjectionDelay( double delay);
/**
* Return a long description of this motor type.
* @return a description of the motor type.
*/
public String getDescription() {
return this.description;
}
public MotorState clone();
/**
* Sequence numbers have no intrinsic meaning, but their sequence (and relative value)
* indicate which states occur before other states.
* @see isAfter(MotorState)
* @see isBefore(MotorState)
* @return integer indicating this state's place in the allowable sequence
*/
public int getSequenceNumber(){
return this.sequenceNumber;
}
public boolean isAfter( final MotorState other ){
return this.getSequenceNumber() > other.getSequenceNumber();
}
public boolean isBefore( final MotorState other ){
return this.getSequenceNumber() < other.getSequenceNumber();
}
/*
* If this motor is in a state which produces thrust
*/
public boolean isThrusting(){
return false;
}
/**
* This flag determines whether the motor has its state updated, and updates of cg and thrust updated.
* A motor instance will always receive events -- which may affect the simulation yes/no state
*
* @return should this motor be simulated
*/
public boolean needsSimulation(){
return false;
}
@Override
public String toString() {
return name;
}
}

View File

@ -49,9 +49,9 @@ public class SimulationStatus implements Monitorable {
private double effectiveLaunchRodLength;
// Set of burnt out motors
Set<MotorInstanceId> motorBurntOut = new HashSet<MotorInstanceId>();
Set<MotorInstanceId> spentMotors = new HashSet<MotorInstanceId>();
List<MotorState> motorState = new ArrayList<MotorState>();
List<MotorSimulation> motorStateList = new ArrayList<MotorSimulation>();
/** Nanosecond time when the simulation was started. */
private long simulationStartWallTime = Long.MIN_VALUE;
@ -148,11 +148,10 @@ public class SimulationStatus implements Monitorable {
this.launchRodCleared = false;
this.apogeeReached = false;
for( MotorConfiguration motorInstance : this.configuration.getActiveMotors() ) {
this.motorState.add( motorInstance.getSimulationState() );
for( MotorConfiguration motorConfig : this.configuration.getActiveMotors() ) {
this.motorStateList.add( new MotorSimulation( motorConfig) );
}
this.warnings = new WarningSet();
}
/**
@ -185,14 +184,14 @@ public class SimulationStatus implements Monitorable {
this.launchRodCleared = orig.launchRodCleared;
this.apogeeReached = orig.apogeeReached;
this.tumbling = orig.tumbling;
this.motorBurntOut = orig.motorBurntOut;
this.spentMotors = orig.spentMotors;
this.deployedRecoveryDevices.clear();
this.deployedRecoveryDevices.addAll(orig.deployedRecoveryDevices);
// FIXME - is this right?
this.motorState.clear();
this.motorState.addAll(orig.motorState);
this.motorStateList.clear();
this.motorStateList.addAll(orig.motorStateList);
this.eventQueue.clear();
this.eventQueue.addAll(orig.eventQueue);
@ -225,20 +224,24 @@ public class SimulationStatus implements Monitorable {
this.configuration = configuration;
}
public Collection<MotorState> getAllMotors() {
return motorState;
public Collection<MotorSimulation> getMotors() {
return motorStateList;
}
public Collection<MotorState> getActiveMotors() {
List<MotorState> activeList = new ArrayList<MotorState>();
for( MotorState inst : this.motorState ){
if( inst.isActive() ){
activeList.add( inst );
}
public Collection<MotorSimulation> getAllMotors() {
return motorStateList;
}
return activeList;
}
// public Collection<MotorInstance> getActiveMotors() {
// List<MotorInstance> activeList = new ArrayList<MotorInstance>();
// for( MotorInstance inst : this.motorStateList ){
// if( inst.isActive() ){
// activeList.add( inst );
// }
// }
//
// return activeList;
// }
public FlightConfiguration getConfiguration() {
return configuration;
@ -260,8 +263,8 @@ public class SimulationStatus implements Monitorable {
return flightData;
}
public MotorState getMotor( final MotorInstanceId motorId ){
for( MotorState state : motorState ) {
public MotorSimulation getMotor( final MotorInstanceId motorId ){
for( MotorSimulation state : motorStateList ) {
if ( motorId.equals(state.getID() )) {
return state;
}
@ -310,8 +313,15 @@ public class SimulationStatus implements Monitorable {
}
public boolean moveBurntOutMotor( final MotorInstanceId motor) {
// get motor from normal list
// remove motor from 'normal' list
// add to spent list
return false;
}
public boolean addBurntOutMotor(MotorInstanceId motor) {
return motorBurntOut.add(motor);
return spentMotors.add(motor);
}

View File

@ -18,7 +18,7 @@ import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.simulation.AccelerationData;
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.simulation.MotorState;
import net.sf.openrocket.simulation.MotorSimulation;
import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.simulation.exception.SimulationListenerException;
@ -105,7 +105,7 @@ public class ScriptingSimulationListener implements SimulationListener, Simulati
}
@Override
public boolean motorIgnition(SimulationStatus status, MotorInstanceId motorId, MotorMount mount, MotorState instance) throws SimulationException {
public boolean motorIgnition(SimulationStatus status, MotorInstanceId motorId, MotorMount mount, MotorSimulation instance) throws SimulationException {
return invoke(Boolean.class, true, "motorIgnition", status, motorId, mount, instance);
}

View File

@ -9,7 +9,7 @@ import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.simulation.AccelerationData;
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.simulation.MotorState;
import net.sf.openrocket.simulation.MotorSimulation;
import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.util.BugException;
@ -72,7 +72,7 @@ public class AbstractSimulationListener implements SimulationListener, Simulatio
}
@Override
public boolean motorIgnition(SimulationStatus status, MotorInstanceId motorId, MotorMount mount, MotorState instance) throws SimulationException {
public boolean motorIgnition(SimulationStatus status, MotorInstanceId motorId, MotorMount mount, MotorSimulation instance) throws SimulationException {
return true;
}

View File

@ -4,7 +4,7 @@ import net.sf.openrocket.motor.MotorInstanceId;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.simulation.MotorState;
import net.sf.openrocket.simulation.MotorSimulation;
import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.simulation.exception.SimulationException;
@ -44,7 +44,7 @@ public interface SimulationEventListener {
* @return <code>true</code> to ignite the motor, <code>false</code> to abort ignition
*/
public boolean motorIgnition(SimulationStatus status, MotorInstanceId motorId, MotorMount mount,
MotorState instance) throws SimulationException;
MotorSimulation instance) throws SimulationException;
/**

View File

@ -14,7 +14,7 @@ import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.simulation.AccelerationData;
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.simulation.MotorState;
import net.sf.openrocket.simulation.MotorSimulation;
import net.sf.openrocket.simulation.SimulationStatus;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.util.Coordinate;
@ -168,7 +168,7 @@ public class SimulationListenerHelper {
* @return <code>true</code> to handle the event normally, <code>false</code> to skip event.
*/
public static boolean fireMotorIgnition(SimulationStatus status, MotorInstanceId motorId, MotorMount mount,
MotorState instance) throws SimulationException {
MotorSimulation instance) throws SimulationException {
boolean b;
int modID = status.getModID(); // Contains also motor instance

View File

@ -70,7 +70,7 @@ public class DampingMoment extends AbstractSimulationListener {
FlightConfiguration config = status.getConfiguration();
for (MotorConfiguration inst : config.getActiveMotors()) {
double x_position= inst.getX();
double x = x_position + inst.getMotor().getLaunchCG().x;
double x = x_position + inst.getMotor().getLaunchCGx();
if (x > nozzleDistance) {
nozzleDistance = x;
}

View File

@ -55,10 +55,10 @@ public class MotorCheck {
// sum += m.getTotalTime();
sum += m.getDiameter();
sum += m.getLength();
sum += m.getEmptyCG().weight;
sum += m.getEmptyCG().x;
sum += m.getLaunchCG().weight;
sum += m.getLaunchCG().x;
sum += m.getBurnoutMass();
sum += m.getBurnoutCGx();
sum += m.getLaunchMass();
sum += m.getLaunchCGx();
sum += m.getMaxThrustEstimate();
if (Double.isInfinite(sum) || Double.isNaN(sum)) {
System.out.println("ERROR: Invalid motor values");

View File

@ -211,7 +211,7 @@ public class MotorCompare {
min = Double.MAX_VALUE;
System.out.printf("Init mass :");
for (Motor m : motors) {
double f = m.getLaunchCG().weight;
double f = m.getLaunchMass();
System.out.printf("\t%.2f", f * 1000);
max = Math.max(max, f);
min = Math.min(min, f);
@ -229,7 +229,7 @@ public class MotorCompare {
min = Double.MAX_VALUE;
System.out.printf("Empty mass :");
for (Motor m : motors) {
double f = m.getEmptyCG().weight;
double f = m.getBurnoutMass();
System.out.printf("\t%.2f", f * 1000);
max = Math.max(max, f);
min = Math.min(min, f);

View File

@ -8,10 +8,8 @@ import java.util.List;
import net.sf.openrocket.file.motor.GeneralMotorLoader;
import net.sf.openrocket.file.motor.MotorLoader;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.simulation.MotorState;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.MathUtil;
@ -61,29 +59,22 @@ public class MotorCorrelation {
* @return the scaled cross-correlation of the two thrust curves.
*/
public static double crossCorrelation(Motor motor1, Motor motor2) {
MotorState m1 = motor1.getNewInstance();
MotorState m2 = motor2.getNewInstance();
AtmosphericConditions cond = new AtmosphericConditions();
double t;
double auto1 = 0;
double auto2 = 0;
double cross = 0;
for (t = 0; t < 1000; t += 0.01) {
m1.step(t, 0, cond);
m2.step(t, 0, cond);
double t1 = m1.getThrust();
double t2 = m2.getThrust();
double thrust1 = motor1.getThrustAtMotorTime( t);
double thrust2 = motor2.getThrustAtMotorTime( t);
if (t1 < 0 || t2 < 0) {
throw new BugException("Negative thrust, t1=" + t1 + " t2=" + t2);
if ( thrust1 < 0 || thrust2 < 0) {
throw new BugException("Negative thrust, t1=" + thrust1 + " t2=" + thrust2);
}
auto1 += t1 * t1;
auto2 += t2 * t2;
cross += t1 * t2;
auto1 += thrust1 * thrust1;
auto2 += thrust2 * thrust2;
cross += thrust1 * thrust2;
}
double auto = Math.max(auto1, auto2);

View File

@ -304,8 +304,8 @@ public class MassCalculatorTest extends BaseTestCase {
double expLaunchMass = 0.0164; // kg
double expSpentMass = 0.0131; // kg
assertEquals(" Motor Mass "+desig+" is incorrect: ", expLaunchMass, activeMotor.getLaunchCG().weight, EPSILON);
assertEquals(" Motor Mass "+desig+" is incorrect: ", expSpentMass, activeMotor.getEmptyCG().weight, EPSILON);
assertEquals(" Motor Mass "+desig+" is incorrect: ", expLaunchMass, activeMotor.getLaunchMass(), EPSILON);
assertEquals(" Motor Mass "+desig+" is incorrect: ", expSpentMass, activeMotor.getBurnoutMass(), EPSILON);
// Validate Booster Launch Mass
MassCalculator mc = new MassCalculator();

View File

@ -5,16 +5,13 @@ import static org.junit.Assert.assertEquals;
import org.junit.Test;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Inertia;
public class ThrustCurveMotorTest {
private final double EPS = 0.000001;
// private final double EPSILON = 0.000001;
private final double radius = 0.025;
private final double length = 0.10;
private final double longitudinal = Inertia.filledCylinderLongitudinal(radius, length);
private final double rotational = Inertia.filledCylinderRotational(radius);
private final ThrustCurveMotor motor =
new ThrustCurveMotor(Manufacturer.getManufacturer("foo"),
@ -40,36 +37,116 @@ public class ThrustCurveMotorTest {
}
@Test
public void testInstance() {
ThrustCurveMotorState instance = motor.getNewInstance();
verify(instance, 0, 0.05, 0.02);
instance.step(0.0, 0, null);
verify(instance, 0, 0.05, 0.02);
instance.step(0.5, 0, null);
verify(instance, 0.5, 0.05, 0.02);
instance.step(1.5, 0, null);
verify(instance, (1.5 + 2.125)/2, 0.05, 0.02);
instance.step(2.5, 0, null);
verify(instance, (2.125 + 2.875)/2, 0.05, 0.02);
instance.step(3.0, 0, null);
verify(instance, (2+3.0/4 + 3)/2, 0.05, 0.02);
instance.step(3.5, 0, null);
verify(instance, (1.5 + 3)/2, 0.045, 0.0225);
instance.step(4.5, 0, null);
// mass and cg is simply average of the end points
verify(instance, 1.5/4, 0.035, 0.0275);
instance.step(5.0, 0, null);
verify(instance, 0, 0.03, 0.03);
public void testTimeIndexingNegative(){
// attempt to retrieve for a time before the motor ignites
assertEquals( 0.0, motor.getPseudoIndex( -1 ), 0.001 );
}
private void verify(ThrustCurveMotorState instance, double thrust, double mass, double cgx) {
assertEquals("Testing thrust", thrust, instance.getThrust(), EPS);
assertEquals("Testing mass", mass, instance.getCG().weight, EPS);
assertEquals("Testing cg x", cgx, instance.getCG().x, EPS);
assertEquals("Testing longitudinal inertia", mass*longitudinal, instance.getLongitudinalInertia(), EPS);
assertEquals("Testing rotational inertia", mass*rotational, instance.getRotationalInertia(), EPS);
@Test
public void testTimeRetrieval(){
// attempt to retrieve an integer index:
assertEquals( 0.0, motor.getMotorTimeAtIndex( 0 ), 0.001 );
assertEquals( 1.0, motor.getMotorTimeAtIndex( 1 ), 0.001 );
assertEquals( 4.0, motor.getMotorTimeAtIndex( 3 ), 0.001 );
final double searchTime = 0.2;
assertEquals( searchTime, motor.getMotorTimeAtIndex( motor.getPseudoIndex(searchTime)), 0.001 );
}
@Test
public void testThrustRetrieval(){
// attempt to retrieve an integer index:
assertEquals( 2.0, motor.getThrustAtIndex( 1 ), 0.001 );
assertEquals( 3.0, motor.getThrustAtIndex( 2 ), 0.001 );
assertEquals( 0.0, motor.getThrustAtIndex( 3 ), 0.001 );
}
// using better interface
// @Test
// public void testCGRetrievalByDouble(){
// final double actCGx0 = motor.getCGxAtIndex( 0.0 );
// assertEquals( 0.02, actCGx0, 0.001 );
// final double actMass0 = motor.getMassAtIndex( 0.0 );
// assertEquals( 0.05, actMass0, 0.001 );
//
// final double actCGx25 = motor.getCGxAtIndex( 2.5 );
// assertEquals( 0.025, actCGx25, 0.001 );
// final double actMass25 = motor.getMassAtIndex( 2.5 );
// assertEquals( 0.04, actMass25, 0.001 );
// }
// deprecated version.
// delete this method upon change to new function signatures
@Test
public void testCGRetrieval(){
final double actCGx0 = motor.getCGAtIndex( 0.0 ).x;
assertEquals( 0.02, actCGx0, 0.001 );
final double actMass0 = motor.getCGAtIndex( 0.0 ).weight;
assertEquals( 0.05, actMass0, 0.001 );
final double actCGx25 = motor.getCGAtIndex( 2.5 ).x;
assertEquals( 0.025, actCGx25, 0.001 );
final double actMass25 = motor.getCGAtIndex( 2.5 ).weight;
assertEquals( 0.04, actMass25, 0.001 );
}
@Test
public void testTimeIndexingPastEnd(){
// attempt to retrieve for a time after motor cutoff
assertEquals( 3.0, motor.getPseudoIndex( 5.0), 0.001 );
}
@Test
public void testTimeIndexingAtEnd(){
// attempt to retrieve for a time just at motor cutoff
assertEquals( 3.0, motor.getPseudoIndex( 4.0), 0.001 );
}
@Test
public void testTimeIndexingDuring(){
// attempt to retrieve for a generic time during the motor's burn
assertEquals( 1.6, motor.getPseudoIndex( 2.2), 0.001 );
}
@Test
public void testTimeIndexingSnapUp(){
// attempt to retrieve for a generic time during the motor's burn
assertEquals( 3.0, motor.getPseudoIndex( 3.9999), 0.001 );
}
@Test
public void testTimeIndexingSnapDown(){
// attempt to retrieve for a generic time during the motor's burn
assertEquals( 2.0, motor.getPseudoIndex( 3.0001), 0.001 );
}
// @Test
// public void testInstance() {
// ThrustCurveMotorState instance = motor.getNewInstance();
//
// verify(instance, 0, 0.05, 0.02);
// instance.step(0.0, null);
// verify(instance, 0, 0.05, 0.02);
// instance.step(0.5, null);
// verify(instance, 0.5, 0.05, 0.02);
// instance.step(1.5, null);
// verify(instance, (1.5 + 2.125)/2, 0.05, 0.02);
// instance.step(2.5, null);
// verify(instance, (2.125 + 2.875)/2, 0.05, 0.02);
// instance.step(3.0, null);
// verify(instance, (2+3.0/4 + 3)/2, 0.05, 0.02);
// instance.step(3.5, null);
// verify(instance, (1.5 + 3)/2, 0.045, 0.0225);
// instance.step(4.5, null);
// // mass and cg is simply average of the end points
// verify(instance, 1.5/4, 0.035, 0.0275);
// instance.step(5.0, null);
// verify(instance, 0, 0.03, 0.03);
// }
//
// private void verify(ThrustCurveMotorState instance, double thrust, double mass, double cgx) {
// assertEquals("Testing thrust", thrust, instance.getThrust(), EPS);
// assertEquals("Testing mass", mass, instance.getCG().weight, EPS);
// assertEquals("Testing cg x", cgx, instance.getCG().x, EPS);
// assertEquals("Testing longitudinal inertia", mass*longitudinal, instance.getMotor().getLongitudinalInertia(), EPS);
// assertEquals("Testing rotational inertia", mass*rotational, instance.getMotor().getRotationalInertia(), EPS);
// }
}

View File

@ -8,6 +8,9 @@ import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sf.openrocket.database.motor.ThrustCurveMotorSetDatabase;
import net.sf.openrocket.file.iterator.DirectoryIterator;
import net.sf.openrocket.file.iterator.FileIterator;
@ -20,9 +23,6 @@ import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An asynchronous database loader that loads the internal thrust curves
* and external user-supplied thrust curves to a ThrustCurveMotorSetDatabase.

View File

@ -21,10 +21,9 @@ import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.startup.Application;
@SuppressWarnings("serial")
public class MotorChooserDialog extends JDialog implements CloseableDialog {
private static final long serialVersionUID = 6903386330489783515L;
private final ThrustCurveMotorSelectionPanel selectionPanel;
private boolean okClicked = false;

View File

@ -16,14 +16,6 @@ import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.Icons;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
@ -34,6 +26,14 @@ import org.jfree.chart.title.TextTitle;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.Icons;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup;
@SuppressWarnings("serial")
class MotorInformationPanel extends JPanel {
@ -246,9 +246,9 @@ class MotorInformationPanel extends JPanel {
burnTimeLabel.setText(UnitGroup.UNITS_SHORT_TIME.getDefaultUnit().toStringUnit(
selectedMotor.getBurnTimeEstimate()));
launchMassLabel.setText(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(
selectedMotor.getLaunchCG().weight));
selectedMotor.getLaunchMass()));
emptyMassLabel.setText(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(
selectedMotor.getEmptyCG().weight));
selectedMotor.getBurnoutMass()));
dataPointsLabel.setText("" + (selectedMotor.getTimePoints().length - 1));
if (digestLabel != null) {
digestLabel.setText(selectedMotor.getDigest());

View File

@ -151,11 +151,11 @@ enum ThrustCurveMotorColumns {
UnitGroup.UNITS_IMPULSE.getDefaultUnit()
.toStringUnit(m.getTotalImpulseEstimate()) + "<br>");
tip += (trans.get("TCurveMotor.ttip.launchMass") + " " +
UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(m.getLaunchCG().weight) +
UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(m.getLaunchMass()) +
"<br>");
tip += (trans.get("TCurveMotor.ttip.emptyMass") + " " +
UnitGroup.UNITS_MASS.getDefaultUnit()
.toStringUnit(m.getEmptyCG().weight));
.toStringUnit(m.getBurnoutMass()));
return tip;
}

View File

@ -388,7 +388,7 @@ public class DesignReport {
motorTable.addCell(ITextHelper.createCell(
ttwFormat.format(ttw) + ":1", border));
double propMass = (motor.getLaunchCG().weight - motor.getEmptyCG().weight);
double propMass = (motor.getLaunchMass() - motor.getBurnoutMass());
motorTable.addCell(ITextHelper.createCell(
UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(propMass), border));