From 28689825a463a15b2a7164e858be0b20ff11392c Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Fri, 12 Feb 2016 12:29:36 -0500 Subject: [PATCH] [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 ( ) - 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) --- .../openrocket/masscalc/MassCalculator.java | 45 ++- core/src/net/sf/openrocket/motor/Motor.java | 18 +- .../openrocket/motor/MotorConfiguration.java | 26 +- .../sf/openrocket/motor/ThrustCurveMotor.java | 255 ++++++++++++++--- .../motor/ThrustCurveMotorPlaceholder.java | 40 ++- .../motor/ThrustCurveMotorState.java | 257 ------------------ .../rocketcomponent/FlightConfiguration.java | 5 +- .../simulation/AbstractSimulationStepper.java | 16 +- .../BasicEventSimulationEngine.java | 31 ++- .../simulation/MotorSimulation.java | 160 +++++++++++ .../sf/openrocket/simulation/MotorState.java | 118 ++++++-- .../simulation/SimulationStatus.java | 54 ++-- .../impl/ScriptingSimulationListener.java | 4 +- .../listeners/AbstractSimulationListener.java | 4 +- .../listeners/SimulationEventListener.java | 4 +- .../listeners/SimulationListenerHelper.java | 4 +- .../listeners/example/DampingMoment.java | 2 +- .../net/sf/openrocket/utils/MotorCheck.java | 8 +- .../net/sf/openrocket/utils/MotorCompare.java | 4 +- .../sf/openrocket/utils/MotorCorrelation.java | 25 +- .../masscalc/MassCalculatorTest.java | 4 +- .../motor/ThrustCurveMotorTest.java | 139 +++++++--- .../database/MotorDatabaseLoader.java | 6 +- .../gui/dialogs/motor/MotorChooserDialog.java | 3 +- .../thrustcurve/MotorInformationPanel.java | 22 +- .../thrustcurve/ThrustCurveMotorColumns.java | 4 +- .../sf/openrocket/gui/print/DesignReport.java | 2 +- 27 files changed, 754 insertions(+), 506 deletions(-) delete mode 100644 core/src/net/sf/openrocket/motor/ThrustCurveMotorState.java create mode 100644 core/src/net/sf/openrocket/simulation/MotorSimulation.java diff --git a/core/src/net/sf/openrocket/masscalc/MassCalculator.java b/core/src/net/sf/openrocket/masscalc/MassCalculator.java index 040861910..514bd56d5 100644 --- a/core/src/net/sf/openrocket/masscalc/MassCalculator.java +++ b/core/src/net/sf/openrocket/masscalc/MassCalculator.java @@ -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 ); diff --git a/core/src/net/sf/openrocket/motor/Motor.java b/core/src/net/sf/openrocket/motor/Motor.java index 1b735da6a..5ff556a36 100644 --- a/core/src/net/sf/openrocket/motor/Motor.java +++ b/core/src/net/sf/openrocket/motor/Motor.java @@ -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 double getLaunchCGx(); - public Coordinate getEmptyCG(); + 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. diff --git a/core/src/net/sf/openrocket/motor/MotorConfiguration.java b/core/src/net/sf/openrocket/motor/MotorConfiguration.java index ea79e0d51..217a216df 100644 --- a/core/src/net/sf/openrocket/motor/MotorConfiguration.java +++ b/core/src/net/sf/openrocket/motor/MotorConfiguration.java @@ -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; @@ -15,7 +14,8 @@ import net.sf.openrocket.util.Inertia; public class MotorConfiguration implements FlightConfigurableParameter { 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, 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") private static final Logger log = LoggerFactory.getLogger(ThrustCurveMotor.class); @@ -45,13 +42,18 @@ public class ThrustCurveMotor implements Motor, Comparable, 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, 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, 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, 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, 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, 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, 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, Se } public double getCutOffTime() { - return this.time[this.time.length - 1]; + return time[time.length - 1]; } /** diff --git a/core/src/net/sf/openrocket/motor/ThrustCurveMotorPlaceholder.java b/core/src/net/sf/openrocket/motor/ThrustCurveMotorPlaceholder.java index 27d0346ff..da8a48db0 100644 --- a/core/src/net/sf/openrocket/motor/ThrustCurveMotorPlaceholder.java +++ b/core/src/net/sf/openrocket/motor/ThrustCurveMotorPlaceholder.java @@ -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; + } + } diff --git a/core/src/net/sf/openrocket/motor/ThrustCurveMotorState.java b/core/src/net/sf/openrocket/motor/ThrustCurveMotorState.java deleted file mode 100644 index bbb0f063e..000000000 --- a/core/src/net/sf/openrocket/motor/ThrustCurveMotorState.java +++ /dev/null @@ -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(); - } - -} diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java index 00ca73533..00ee75a0d 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java @@ -528,12 +528,11 @@ public class FlightConfiguration implements FlightConfigurableParameter activeMotorList = status.getActiveMotors(); - for (MotorState currentMotorInstance : activeMotorList ) { + Collection activeMotorList = status.getMotors(); + for (MotorSimulation currentMotorState : activeMotorList ) { if ( !stepMotors ) { - currentMotorInstance = currentMotorInstance.clone(); + currentMotorState = currentMotorState.clone(); } - // old: transplanted from MotorInstanceConfiguration - double instanceTime = currentTime - currentMotorInstance.getIgnitionTime(); - if (instanceTime >= 0) { - currentMotorInstance.step(instanceTime, acceleration, atmosphericConditions); - } - - // old: from here - thrust += currentMotorInstance.getThrust(); + + thrust += currentMotorState.getThrust( currentTime, atmosphericConditions ); } // Post-listeners diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index 6319b5574..be63f9628 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -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())); diff --git a/core/src/net/sf/openrocket/simulation/MotorSimulation.java b/core/src/net/sf/openrocket/simulation/MotorSimulation.java new file mode 100644 index 000000000..4dc4f798b --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/MotorSimulation.java @@ -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(); + } + +} diff --git a/core/src/net/sf/openrocket/simulation/MotorState.java b/core/src/net/sf/openrocket/simulation/MotorState.java index 53df42cbf..b4c8b925d 100644 --- a/core/src/net/sf/openrocket/simulation/MotorState.java +++ b/core/src/net/sf/openrocket/simulation/MotorState.java @@ -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 void step(double nextTime, double acceleration, AtmosphericConditions cond); - public double getThrust(); - public boolean isActive(); - - public double getIgnitionTime(); - public void setIgnitionTime( double ignitionTime ); +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 setMount(MotorMount mount); - public MotorMount getMount(); - - public void setId(MotorInstanceId id); - public MotorInstanceId getID(); + private final static int SEQUENCE_NUMBER_END = 10; // arbitrary number - public IgnitionEvent getIgnitionEvent(); - public void setIgnitionEvent( IgnitionEvent event ); + private final String name; + private final String description; + private final int sequenceNumber; + private final MotorState nextState; - public double getIgnitionDelay(); - public void setIgnitionDelay( double delay ); + 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 double getEjectionDelay(); - public void setEjectionDelay( double delay); + /** + * Return a short name of this motor type. + * @return a short name of the motor type. + */ + public String getName() { + return this.name; + } - public MotorState clone(); + /* + * + * @Return a MotorState enum telling what state should follow this one. + */ + public MotorState getNext( ){ + return this.nextState; + } + + /** + * Return a long description of this motor type. + * @return a description of the motor type. + */ + public String getDescription() { + return this.description; + } + + /** + * 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; + } } diff --git a/core/src/net/sf/openrocket/simulation/SimulationStatus.java b/core/src/net/sf/openrocket/simulation/SimulationStatus.java index 5160a99cb..5f0d1eea5 100644 --- a/core/src/net/sf/openrocket/simulation/SimulationStatus.java +++ b/core/src/net/sf/openrocket/simulation/SimulationStatus.java @@ -49,9 +49,9 @@ public class SimulationStatus implements Monitorable { private double effectiveLaunchRodLength; // Set of burnt out motors - Set motorBurntOut = new HashSet(); + Set spentMotors = new HashSet(); - List motorState = new ArrayList(); + List motorStateList = new ArrayList(); /** 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,21 +224,25 @@ public class SimulationStatus implements Monitorable { this.configuration = configuration; } - public Collection getAllMotors() { - return motorState; + public Collection getMotors() { + return motorStateList; } - public Collection getActiveMotors() { - List activeList = new ArrayList(); - for( MotorState inst : this.motorState ){ - if( inst.isActive() ){ - activeList.add( inst ); - } - } - - return activeList; + public Collection getAllMotors() { + return motorStateList; } +// public Collection getActiveMotors() { +// List activeList = new ArrayList(); +// 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); } diff --git a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingSimulationListener.java b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingSimulationListener.java index 0cc613708..099aa4368 100644 --- a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingSimulationListener.java +++ b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingSimulationListener.java @@ -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); } diff --git a/core/src/net/sf/openrocket/simulation/listeners/AbstractSimulationListener.java b/core/src/net/sf/openrocket/simulation/listeners/AbstractSimulationListener.java index bc875c0c6..c5c547683 100644 --- a/core/src/net/sf/openrocket/simulation/listeners/AbstractSimulationListener.java +++ b/core/src/net/sf/openrocket/simulation/listeners/AbstractSimulationListener.java @@ -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; } diff --git a/core/src/net/sf/openrocket/simulation/listeners/SimulationEventListener.java b/core/src/net/sf/openrocket/simulation/listeners/SimulationEventListener.java index c725309ec..94be38660 100644 --- a/core/src/net/sf/openrocket/simulation/listeners/SimulationEventListener.java +++ b/core/src/net/sf/openrocket/simulation/listeners/SimulationEventListener.java @@ -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 true to ignite the motor, false to abort ignition */ public boolean motorIgnition(SimulationStatus status, MotorInstanceId motorId, MotorMount mount, - MotorState instance) throws SimulationException; + MotorSimulation instance) throws SimulationException; /** diff --git a/core/src/net/sf/openrocket/simulation/listeners/SimulationListenerHelper.java b/core/src/net/sf/openrocket/simulation/listeners/SimulationListenerHelper.java index bd004c743..0548f95d9 100644 --- a/core/src/net/sf/openrocket/simulation/listeners/SimulationListenerHelper.java +++ b/core/src/net/sf/openrocket/simulation/listeners/SimulationListenerHelper.java @@ -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 true to handle the event normally, false 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 diff --git a/core/src/net/sf/openrocket/simulation/listeners/example/DampingMoment.java b/core/src/net/sf/openrocket/simulation/listeners/example/DampingMoment.java index c7f91dfeb..90437a2d0 100644 --- a/core/src/net/sf/openrocket/simulation/listeners/example/DampingMoment.java +++ b/core/src/net/sf/openrocket/simulation/listeners/example/DampingMoment.java @@ -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; } diff --git a/core/src/net/sf/openrocket/utils/MotorCheck.java b/core/src/net/sf/openrocket/utils/MotorCheck.java index 5710486d8..4fcade4b2 100644 --- a/core/src/net/sf/openrocket/utils/MotorCheck.java +++ b/core/src/net/sf/openrocket/utils/MotorCheck.java @@ -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"); diff --git a/core/src/net/sf/openrocket/utils/MotorCompare.java b/core/src/net/sf/openrocket/utils/MotorCompare.java index 8c4390804..e9d353d0d 100644 --- a/core/src/net/sf/openrocket/utils/MotorCompare.java +++ b/core/src/net/sf/openrocket/utils/MotorCompare.java @@ -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); diff --git a/core/src/net/sf/openrocket/utils/MotorCorrelation.java b/core/src/net/sf/openrocket/utils/MotorCorrelation.java index e1c5eb846..fc9c42a1f 100644 --- a/core/src/net/sf/openrocket/utils/MotorCorrelation.java +++ b/core/src/net/sf/openrocket/utils/MotorCorrelation.java @@ -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; @@ -60,30 +58,23 @@ public class MotorCorrelation { * @param motor2 the second motor. * @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(); - + public static double crossCorrelation(Motor motor1, Motor motor2) { 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); diff --git a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java index 3b05b5fac..458362f0a 100644 --- a/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java +++ b/core/test/net/sf/openrocket/masscalc/MassCalculatorTest.java @@ -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(); diff --git a/core/test/net/sf/openrocket/motor/ThrustCurveMotorTest.java b/core/test/net/sf/openrocket/motor/ThrustCurveMotorTest.java index e16ac3a6f..4dd256e0e 100644 --- a/core/test/net/sf/openrocket/motor/ThrustCurveMotorTest.java +++ b/core/test/net/sf/openrocket/motor/ThrustCurveMotorTest.java @@ -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"), @@ -38,38 +35,118 @@ public class ThrustCurveMotorTest { assertEquals(Motor.Type.RELOAD, motor.getMotorType()); } + + @Test + public void testTimeIndexingNegative(){ + // attempt to retrieve for a time before the motor ignites + assertEquals( 0.0, motor.getPseudoIndex( -1 ), 0.001 ); + } + + @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 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 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 ); } - 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); + // 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); +// } } diff --git a/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java b/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java index 97f36f60b..0fe27ef69 100644 --- a/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java +++ b/swing/src/net/sf/openrocket/database/MotorDatabaseLoader.java @@ -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. diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java index ee108fac5..613c9359f 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java @@ -21,9 +21,8 @@ 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; diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java index 698728a5b..08c2d43eb 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/MotorInformationPanel.java @@ -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 { @@ -230,7 +230,7 @@ class MotorInformationPanel extends JPanel { this.selectedMotorSet = motors; this.selectedMotor = selectedMotor; - + // Update thrust curve data double impulse = selectedMotor.getTotalImpulseEstimate(); MotorClass mc = MotorClass.getMotorClass(impulse); @@ -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()); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorColumns.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorColumns.java index 47d931dd9..ac6ca8329 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorColumns.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorColumns.java @@ -151,11 +151,11 @@ enum ThrustCurveMotorColumns { UnitGroup.UNITS_IMPULSE.getDefaultUnit() .toStringUnit(m.getTotalImpulseEstimate()) + "
"); tip += (trans.get("TCurveMotor.ttip.launchMass") + " " + - UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(m.getLaunchCG().weight) + + UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(m.getLaunchMass()) + "
"); tip += (trans.get("TCurveMotor.ttip.emptyMass") + " " + UnitGroup.UNITS_MASS.getDefaultUnit() - .toStringUnit(m.getEmptyCG().weight)); + .toStringUnit(m.getBurnoutMass())); return tip; } diff --git a/swing/src/net/sf/openrocket/gui/print/DesignReport.java b/swing/src/net/sf/openrocket/gui/print/DesignReport.java index 1b1dc79c6..88a7dcce4 100644 --- a/swing/src/net/sf/openrocket/gui/print/DesignReport.java +++ b/swing/src/net/sf/openrocket/gui/print/DesignReport.java @@ -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));