diff --git a/core/src/net/sf/openrocket/database/motor/ThrustCurveMotorSet.java b/core/src/net/sf/openrocket/database/motor/ThrustCurveMotorSet.java index ed77d3a47..bbdc628b1 100644 --- a/core/src/net/sf/openrocket/database/motor/ThrustCurveMotorSet.java +++ b/core/src/net/sf/openrocket/database/motor/ThrustCurveMotorSet.java @@ -271,8 +271,8 @@ public class ThrustCurveMotorSet implements Comparable { } // 2. Number of data points (more is better) - if (o1.getTimePoints().length != o2.getTimePoints().length) { - return o2.getTimePoints().length - o1.getTimePoints().length; + if (o1.getSampleSize() != o2.getSampleSize()) { + return o2.getSampleSize() - o1.getSampleSize(); } // 3. Comment length (longer is better) diff --git a/core/src/net/sf/openrocket/document/OpenRocketDocument.java b/core/src/net/sf/openrocket/document/OpenRocketDocument.java index 101d2e34a..4feb45cac 100644 --- a/core/src/net/sf/openrocket/document/OpenRocketDocument.java +++ b/core/src/net/sf/openrocket/document/OpenRocketDocument.java @@ -415,7 +415,7 @@ public class OpenRocketDocument implements ComponentChangeListener { * Clear the undo history. */ public void clearUndo() { - log.info("Clearing undo history of " + this); + //log.info("Clearing undo history of " + this); undoHistory.clear(); undoDescription.clear(); diff --git a/core/src/net/sf/openrocket/masscalc/MassCalculator.java b/core/src/net/sf/openrocket/masscalc/MassCalculator.java index 82bf37079..ea589a460 100644 --- a/core/src/net/sf/openrocket/masscalc/MassCalculator.java +++ b/core/src/net/sf/openrocket/masscalc/MassCalculator.java @@ -346,7 +346,7 @@ public class MassCalculator implements Monitorable { for (MotorClusterState curConfig : activeMotorList ) { int instanceCount = curConfig.getMount().getInstanceCount(); double motorTime = curConfig.getMotorTime(status.getSimulationTime()); - mass += (curConfig.getMotor().getMassAtMotorTime(motorTime) - curConfig.getMotor().getBurnoutMass())*instanceCount; + mass += (curConfig.getMotor().getTotalMass(motorTime) - curConfig.getMotor().getBurnoutMass())*instanceCount; } return mass; } @@ -539,7 +539,6 @@ public class MassCalculator implements Monitorable { rocketTreeModID != configuration.getRocket().getTreeModID()) { rocketMassModID = configuration.getRocket().getMassModID(); rocketTreeModID = configuration.getRocket().getTreeModID(); - log.debug("Voiding the mass cache"); voidMassCache(); } } diff --git a/core/src/net/sf/openrocket/motor/Motor.java b/core/src/net/sf/openrocket/motor/Motor.java index 73f376258..9fbd4fbc8 100644 --- a/core/src/net/sf/openrocket/motor/Motor.java +++ b/core/src/net/sf/openrocket/motor/Motor.java @@ -116,10 +116,6 @@ public interface Motor extends Cloneable { public String getDigest(); public Motor clone(); - - // 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 getAverageThrust( final double startTime, final double endTime ); @@ -152,9 +148,32 @@ public interface Motor extends Cloneable { public double getTotalImpulseEstimate(); - double getMassAtMotorTime(final double motorTime); - - double getBurnTime(); + + /** + * Return the thrust at a time offset from motor ignition + * + * 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... + * + * @param motorTime time (in seconds) since motor ignition + * @return thrust (double, in Newtons) at given time + */ + public double getThrust( final double motorTime); + + /** + * Return the mass at a time offset from motor ignition + * + * @param motorTime time (in seconds) since motor ignition + */ + public double getTotalMass( final double motorTime); + + /** Return the mass at a given time + * + * @param motorTime time (in seconds) since motor ignition + * @return + */ + public double getCGx( final double motorTime); + } diff --git a/core/src/net/sf/openrocket/motor/MotorDigest.java b/core/src/net/sf/openrocket/motor/MotorDigest.java index 92f01d3c0..52593975e 100644 --- a/core/src/net/sf/openrocket/motor/MotorDigest.java +++ b/core/src/net/sf/openrocket/motor/MotorDigest.java @@ -143,9 +143,9 @@ public class MotorDigest { MotorDigest motorDigest = new MotorDigest(); motorDigest.update(DataType.TIME_ARRAY, m.getTimePoints()); - Coordinate[] cg = m.getCGPoints(); - double[] cgx = new double[cg.length]; - double[] mass = new double[cg.length]; + final Coordinate[] cg = m.getCGPoints(); + final double[] cgx = new double[cg.length]; + final double[] mass = new double[cg.length]; for (int i = 0; i < cg.length; i++) { cgx[i] = cg[i].x; mass[i] = cg[i].weight; diff --git a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java index 2a175f7d6..a0586de69 100644 --- a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java +++ b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java @@ -14,7 +14,8 @@ import net.sf.openrocket.util.Inertia; import net.sf.openrocket.util.MathUtil; public class ThrustCurveMotor implements Motor, Comparable, Serializable { - // NECESSARY field, for this class -- this class is serialized in the motor database, and loaded directly. + // NECESSARY field, for this class -- this class is serialized in the motor database.... + // and loaded directly-- bypassing the existing class constructors. private static final long serialVersionUID = -1490333207132694479L; @SuppressWarnings("unused") @@ -54,6 +55,7 @@ public class ThrustCurveMotor implements Motor, Comparable, Se private final double unitLongitudinalInertia; + /** * Deep copy constructor. * Constructs a new ThrustCurveMotor from an existing ThrustCurveMotor. @@ -212,7 +214,7 @@ public class ThrustCurveMotor implements Motor, Comparable, Se * @param is time after motor ignition, in seconds * @return a pseudo index to this motor's data. */ - public double getPseudoIndex( final double motorTime ){ + protected double getPseudoIndex( final double motorTime ){ final double SNAP_DISTANCE = 0.001; if( time.length == 0 ){ @@ -230,7 +232,7 @@ public class ThrustCurveMotor implements Motor, Comparable, Se } lowerBoundIndex = upperBoundIndex-1; if( upperBoundIndex == time.length ){ - return time.length - 1; + return lowerBoundIndex; // == time.length-1; } if ( SNAP_DISTANCE > Math.abs( motorTime - time[lowerBoundIndex])){ @@ -253,7 +255,7 @@ public class ThrustCurveMotor implements Motor, Comparable, Se return lowerBoundIndex + indexFraction; } } - + @Override public double getAverageThrust( final double startTime, final double endTime ) { @@ -298,13 +300,20 @@ public class ThrustCurveMotor implements Motor, Comparable, Se return avgImpulse / (endTime - startTime); } - + @Override - public double getThrustAtMotorTime( final double searchTime ){ - double pseudoIndex = getPseudoIndex( searchTime ); - return getThrustAtIndex( pseudoIndex ); + public double getThrust( final double motorTime ){ + double pseudoIndex = getPseudoIndex( motorTime ); + return ThrustCurveMotor.interpolateAtIndex( thrust, pseudoIndex); } + @Override + public double getCGx( final double motorTime ){ + double pseudoIndex = getPseudoIndex( motorTime ); + return this.interpolateCenterOfMassAtIndex( pseudoIndex).x; + } + + /** * Returns the array of thrust points for this thrust curve. @@ -323,7 +332,7 @@ public class ThrustCurveMotor implements Motor, Comparable, Se // } public Coordinate[] getCGPoints(){ - return cg.clone(); + return cg; } // /** @@ -361,16 +370,16 @@ public class ThrustCurveMotor implements Motor, Comparable, Se return this.unitRotationalInertia; } - public double getIxxAtTime( final double searchTime) { - final double index = getPseudoIndex( searchTime); + public double getIxx( final double searchTime) { + final double pseudoIndex = getPseudoIndex( searchTime); //return this.unitLongitudinalInertia * this.getMassAtIndex( index); - return this.unitLongitudinalInertia * this.getCGAtIndex( index).weight; + return this.unitLongitudinalInertia * interpolateCenterOfMassAtIndex( pseudoIndex).weight; } - public double getIyyAtTime( final double searchTime) { - final double index = getPseudoIndex( searchTime); + public double getIyy( final double searchTime) { + final double pseudoIndex = getPseudoIndex( searchTime); //return this.unitRotationalInertia * this.getMassAtIndex( index); - return this.unitRotationalInertia * this.getCGAtIndex( index).weight; + return this.unitRotationalInertia * interpolateCenterOfMassAtIndex( pseudoIndex).weight; } @Override @@ -430,16 +439,18 @@ public class ThrustCurveMotor implements Motor, Comparable, Se } // FIXME - there seems to be some numeric problems in here... - private static double interpolateValueAtIndex( final double[] values, final double pseudoIndex ){ + // simple linear interpolation... not sample density for anything more complex + private static double interpolateAtIndex( final double[] values, final double pseudoIndex ){ final double SNAP_TOLERANCE = 0.0001; + // assumes that pseudoIndex > 1 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 > lowerFrac ){ // 1-lowerFrac = 1-(1-upperFrac) = upperFrac ?!? + if( SNAP_TOLERANCE > lowerFrac ){ // index ~= int ... therefore: return values[ lowerIndex ]; }else if( SNAP_TOLERANCE > upperFrac ){ @@ -453,23 +464,27 @@ public class ThrustCurveMotor implements Motor, Comparable, Se 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 ); + + /** + * for testing. In practice, the return value should generally match the parameter value, (except for error conditions) + * + * @ignore javadoc ignore + * @param motorTime + * @return the time at requested time + */ + public double getTime( final double motorTime ){ + final double pseudoIndex = getPseudoIndex( motorTime); + final double foundTime = ThrustCurveMotor.interpolateAtIndex( this.time, pseudoIndex); + return foundTime; } @Override - public double getMassAtMotorTime( final double motorTime ) { - double pseudoIndex = getPseudoIndex(motorTime); - Coordinate cg = getCGAtIndex( pseudoIndex ); - return cg.weight; + public double getTotalMass( final double motorTime){ + final double pseudoIndex = getPseudoIndex( motorTime); + return interpolateCenterOfMassAtIndex( pseudoIndex).weight; } - @Deprecated - public Coordinate getCGAtIndex( final double pseudoIndex ){ + protected Coordinate interpolateCenterOfMassAtIndex( final double pseudoIndex ){ final double SNAP_TOLERANCE = 0.0001; final double upperFrac = pseudoIndex%1; @@ -477,9 +492,8 @@ public class ThrustCurveMotor implements Motor, Comparable, Se final int lowerIndex = (int)pseudoIndex; final int upperIndex= lowerIndex+1; - // if the pseudo + // if the pseudo index is close to an integer: if( SNAP_TOLERANCE > (1-lowerFrac) ){ - // index ~= int ... therefore: return cg[ (int) pseudoIndex ]; }else if( SNAP_TOLERANCE > upperFrac ){ return cg[ (int)upperIndex ]; @@ -492,27 +506,6 @@ public class ThrustCurveMotor implements Motor, Comparable, Se return lowerValue.average( 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(); - - // Coordinate interpolateCG( ... ) - - // public int getDataSize() { return this.time.length; } @@ -664,8 +657,14 @@ public class ThrustCurveMotor implements Motor, Comparable, Se return "" + delay; } - - + /** + * This is the number of data points of measured thrust, CGx, mass, time. + * + * @return return the size of the data arrays + */ + public int getSampleSize(){ + return time.length; + } diff --git a/core/src/net/sf/openrocket/motor/ThrustCurveMotorPlaceholder.java b/core/src/net/sf/openrocket/motor/ThrustCurveMotorPlaceholder.java index 748e7c1bf..5c5e72143 100644 --- a/core/src/net/sf/openrocket/motor/ThrustCurveMotorPlaceholder.java +++ b/core/src/net/sf/openrocket/motor/ThrustCurveMotorPlaceholder.java @@ -194,24 +194,28 @@ public class ThrustCurveMotorPlaceholder implements Motor { @Override - public double getThrustAtMotorTime(double pseudoIndex) { + public double getThrust(double pseudoIndex) { return 0; } - @Override public double getAverageThrust(double startTime, double endTime) { return 0; } @Override - public double getMassAtMotorTime(final double motorTime) { + public double getTotalMass(final double motorTime) { + return 0; + } + + @Override + public double getCGx(double pseudoIndex) { return 0; } @Override - public double getBurnTime() { - return 0; + public double getBurnTime() { + return 0; } } diff --git a/core/src/net/sf/openrocket/simulation/AbstractSimulationStepper.java b/core/src/net/sf/openrocket/simulation/AbstractSimulationStepper.java index 3699dcddf..2e5b82641 100644 --- a/core/src/net/sf/openrocket/simulation/AbstractSimulationStepper.java +++ b/core/src/net/sf/openrocket/simulation/AbstractSimulationStepper.java @@ -157,7 +157,7 @@ public abstract class AbstractSimulationStepper implements SimulationStepper { * @param stepMotors whether to step the motors forward or work on a clone object * @return the average thrust during the time step. */ - protected double calculateThrust(SimulationStatus status, double timestep, + protected double calculateAvrageThrust(SimulationStatus status, double timestep, double acceleration, AtmosphericConditions atmosphericConditions, boolean stepMotors) throws SimulationException { double thrust; @@ -173,6 +173,7 @@ public abstract class AbstractSimulationStepper implements SimulationStepper { Collection activeMotorList = status.getMotors(); for (MotorClusterState currentMotorState : activeMotorList ) { thrust += currentMotorState.getAverageThrust( status.getSimulationTime(), currentTime ); + //thrust += currentMotorState.getThrust( currentTime ); } // Post-listeners diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index 580730e7e..7e48062e5 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -1,7 +1,6 @@ package net.sf.openrocket.simulation; import java.util.ArrayDeque; -import java.util.Collection; import java.util.Deque; import org.slf4j.Logger; @@ -9,7 +8,6 @@ import org.slf4j.LoggerFactory; import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.motor.MotorConfiguration; import net.sf.openrocket.motor.MotorConfigurationId; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; @@ -378,10 +376,11 @@ public class BasicEventSimulationEngine implements SimulationEngine { // Add ejection charge event MotorClusterState motorState = (MotorClusterState) event.getData(); motorState.burnOut( event.getTime() ); - + AxialStage stage = motorState.getMount().getStage(); - log.debug( " adding EJECTION_CHARGE event for stage "+stage.getStageNumber()+": "+stage.getName()); - log.debug( " .... for motor "+motorState.getMotor().getDesignation()); + //log.debug( " adding EJECTION_CHARGE event for motor "+motorState.getMotor().getDesignation()+" on stage "+stage.getStageNumber()+": "+stage.getName()); + log.debug( " detected Motor Burnout for motor "+motorState.getMotor().getDesignation()+"@ "+event.getTime()+" on stage "+stage.getStageNumber()+": "+stage.getName()); + double delay = motorState.getEjectionDelay(); if ( motorState.hasEjectionCharge() ){ diff --git a/core/src/net/sf/openrocket/simulation/MotorClusterState.java b/core/src/net/sf/openrocket/simulation/MotorClusterState.java index ac855046d..776700965 100644 --- a/core/src/net/sf/openrocket/simulation/MotorClusterState.java +++ b/core/src/net/sf/openrocket/simulation/MotorClusterState.java @@ -117,11 +117,28 @@ public class MotorClusterState { * @param cond * @return */ - public double getAverageThrust( final double startTime, final double endTime) { + public double getAverageThrust( final double startSimulationTime, final double endSimulationTime) { if( this.currentState.isThrusting() ) { - double motorStartTime = this.getMotorTime( startTime ); - double motorEndTime = this.getMotorTime(endTime); + double motorStartTime = this.getMotorTime( startSimulationTime); + double motorEndTime = this.getMotorTime( endSimulationTime); return this.motorCount * motor.getAverageThrust( motorStartTime, motorEndTime ); + }else{ + return 0.00; + } + } + + /** + * Compute the average thrust over an interval. + * + * @param simulationTime + * @param cond + * @return + */ + public double getThrust( final double simulationTime){ + if( this.currentState.isThrusting() ){ + double motorTime = this.getMotorTime( simulationTime); + return this.motorCount * motor.getThrust( motorTime ); + }else{ return 0.0; } diff --git a/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java b/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java index 0a4d9ec58..364996de1 100644 --- a/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java +++ b/core/src/net/sf/openrocket/simulation/RK4SimulationStepper.java @@ -111,7 +111,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { /* * Compute the initial thrust estimate. This is used for the first time step computation. */ - store.thrustForce = calculateThrust(status, store.timestep, status.getPreviousAcceleration(), + store.thrustForce = calculateAvrageThrust(status, store.timestep, status.getPreviousAcceleration(), status.getPreviousAtmosphericConditions(), false); @@ -180,7 +180,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { * diminished by it affecting only 1/6th of the total, so it's an acceptable error. */ double thrustEstimate = store.thrustForce; - store.thrustForce = calculateThrust(status, store.timestep, store.longitudinalAcceleration, + store.thrustForce = calculateAvrageThrust(status, store.timestep, store.longitudinalAcceleration, store.atmosphericConditions, true); log.trace("Thrust at time " + store.timestep + " thrustForce = " + store.thrustForce); double thrustDiff = Math.abs(store.thrustForce - thrustEstimate); @@ -246,9 +246,6 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { //// Sum all together, y(n+1) = y(n) + h*(k1 + 2*k2 + 2*k3 + k4)/6 - - - Coordinate deltaV, deltaP, deltaR, deltaO; deltaV = k2.a.add(k3.a).multiply(2).add(k1.a).add(k4.a).multiply(store.timestep / 6); deltaP = k2.v.add(k3.v).multiply(2).add(k1.v).add(k4.v).multiply(store.timestep / 6); @@ -256,7 +253,6 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { deltaO = k2.rv.add(k3.rv).multiply(2).add(k1.rv).add(k4.rv).multiply(store.timestep / 6); - status.setRocketVelocity(status.getRocketVelocity().add(deltaV)); status.setRocketPosition(status.getRocketPosition().add(deltaP)); status.setRocketRotationVelocity(status.getRocketRotationVelocity().add(deltaR)); diff --git a/core/src/net/sf/openrocket/utils/MotorCheck.java b/core/src/net/sf/openrocket/utils/MotorCheck.java index 4fcade4b2..de3bd7957 100644 --- a/core/src/net/sf/openrocket/utils/MotorCheck.java +++ b/core/src/net/sf/openrocket/utils/MotorCheck.java @@ -72,7 +72,7 @@ public class MotorCheck { ok = false; } - int points = ((ThrustCurveMotor) m).getTimePoints().length; + int points = ((ThrustCurveMotor) m).getSampleSize(); if (points < WARN_POINTS) { System.out.println("WARNING: Only " + points + " data points"); ok = false; diff --git a/core/src/net/sf/openrocket/utils/MotorCompare.java b/core/src/net/sf/openrocket/utils/MotorCompare.java index e9d353d0d..350640b29 100644 --- a/core/src/net/sf/openrocket/utils/MotorCompare.java +++ b/core/src/net/sf/openrocket/utils/MotorCompare.java @@ -256,8 +256,8 @@ public class MotorCompare { maxPoints = 0; System.out.printf("Points :"); for (Motor m : motors) { - System.out.printf("\t%d", ((ThrustCurveMotor) m).getTimePoints().length); - maxPoints = Math.max(maxPoints, ((ThrustCurveMotor) m).getTimePoints().length); + System.out.printf("\t%d", ((ThrustCurveMotor) m).getSampleSize()); + maxPoints = Math.max(maxPoints, ((ThrustCurveMotor) m).getSampleSize()); } System.out.println(); @@ -318,7 +318,7 @@ public class MotorCompare { ThrustCurveMotor m = motors.get(i); if (m.getStandardDelays().length == maxDelays) goodness[i] += 1000; - if (((ThrustCurveMotor) m).getTimePoints().length == maxPoints) + if (((ThrustCurveMotor) m).getSampleSize() == maxPoints) goodness[i] += 100; if (m.getDescription().length() == maxCommentLen) goodness[i] += 10; @@ -333,7 +333,7 @@ public class MotorCompare { // Verify enough points - int pts = ((ThrustCurveMotor) motors.get(best)).getTimePoints().length; + int pts = ((ThrustCurveMotor) motors.get(best)).getSampleSize(); if (pts < MIN_POINTS) { System.out.println("WARNING: Best has only " + pts + " data points"); } diff --git a/core/src/net/sf/openrocket/utils/MotorCorrelation.java b/core/src/net/sf/openrocket/utils/MotorCorrelation.java index 23642f766..009a6d45b 100644 --- a/core/src/net/sf/openrocket/utils/MotorCorrelation.java +++ b/core/src/net/sf/openrocket/utils/MotorCorrelation.java @@ -64,8 +64,8 @@ public class MotorCorrelation { double cross = 0; for (t = 0; t < 1000; t += 0.01) { - double thrust1 = motor1.getThrustAtMotorTime( t); - double thrust2 = motor2.getThrustAtMotorTime( t); + double thrust1 = motor1.getThrust( t); + double thrust2 = motor2.getThrust( t); if ( thrust1 < 0 || thrust2 < 0) { throw new BugException("Negative thrust, t1=" + thrust1 + " t2=" + thrust2); diff --git a/core/test/net/sf/openrocket/motor/ThrustCurveMotorTest.java b/core/test/net/sf/openrocket/motor/ThrustCurveMotorTest.java index 96d8a8d45..0565c6768 100644 --- a/core/test/net/sf/openrocket/motor/ThrustCurveMotorTest.java +++ b/core/test/net/sf/openrocket/motor/ThrustCurveMotorTest.java @@ -5,6 +5,8 @@ import static org.junit.Assert.assertEquals; import org.junit.Test; import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.Pair; + public class ThrustCurveMotorTest { @@ -13,7 +15,7 @@ public class ThrustCurveMotorTest { private final double radius = 0.025; private final double length = 0.10; - private final ThrustCurveMotor motor = + private final ThrustCurveMotor motorX6 = new ThrustCurveMotor(Manufacturer.getManufacturer("foo"), "X6", "Description of X6", Motor.Type.RELOAD, new double[] {0, 2, Motor.PLUGGED_DELAY}, radius*2, length, @@ -26,127 +28,149 @@ public class ThrustCurveMotorTest { new Coordinate(0.03,0,0,0.03) }, "digestA"); + + private final double radiusA8 = 0.018; + private final double lengthA8 = 0.10; + private final ThrustCurveMotor motorEstesA8_3 = + new ThrustCurveMotor(Manufacturer.getManufacturer("Estes"), + "A8-3", "A8 Test Motor", Motor.Type.SINGLE, + new double[] {0, 2, Motor.PLUGGED_DELAY}, radiusA8*2, lengthA8, + + new double[] { // time (sec) + 0, 0.041, 0.084, 0.127, + 0.166, 0.192, 0.206, 0.226, + 0.236, 0.247, 0.261, 0.277, + 0.306, 0.351, 0.405, 0.467, + 0.532, 0.589, 0.632, 0.652, + 0.668, 0.684, 0.703, 0.73}, + new double[] { // thrust (N) + 0, 0.512, 2.115, 4.358, + 6.794, 8.588, 9.294, 9.73, + 8.845, 7.179, 5.063, 3.717, + 3.205, 2.884, 2.499, 2.371, + 2.307, 2.371, 2.371, 2.243, + 1.794, 1.153, 0.448, 0}, + new Coordinate[] { /// ( m, m, m, kg) + new Coordinate(0.0350, 0, 0, 0.016350),new Coordinate(0.0352, 0, 0, 0.016335),new Coordinate(0.0354, 0, 0, 0.016255),new Coordinate(0.0356, 0, 0, 0.016057), + new Coordinate(0.0358, 0, 0, 0.015748),new Coordinate(0.0360, 0, 0, 0.015463),new Coordinate(0.0362, 0, 0, 0.015285),new Coordinate(0.0364, 0, 0, 0.015014), + new Coordinate(0.0366, 0, 0, 0.014882),new Coordinate(0.0368, 0, 0, 0.014757),new Coordinate(0.0370, 0, 0, 0.014635),new Coordinate(0.0372, 0, 0, 0.014535), + new Coordinate(0.0374, 0, 0, 0.014393),new Coordinate(0.0376, 0, 0, 0.014198),new Coordinate(0.0378, 0, 0, 0.013991),new Coordinate(0.0380, 0, 0, 0.013776), + new Coordinate(0.0382, 0, 0, 0.013560),new Coordinate(0.0384, 0, 0, 0.013370),new Coordinate(0.0386, 0, 0, 0.013225),new Coordinate(0.0388, 0, 0, 0.013160), + new Coordinate(0.0390, 0, 0, 0.013114),new Coordinate(0.0392, 0, 0, 0.013080),new Coordinate(0.0394, 0, 0, 0.013059),new Coordinate(0.0396, 0, 0, 0.013050) + }, "digestA8-3"); + + + @Test + public void testVerifyMotorA8_3Times(){ + final ThrustCurveMotor mtr = motorEstesA8_3; + + assertEquals( 0.041, mtr.getTime( 0.041), 0.001 ); + + assertEquals( 0.206, mtr.getTime( 0.206), 0.001 ); + } + + @Test + public void testVerifyMotorA8_3Thrusts(){ + final ThrustCurveMotor mtr = motorEstesA8_3; + + assertEquals( 0.512, mtr.getThrust( 0.041), 0.001 ); + + assertEquals( 9.294, mtr.getThrust( 0.206), 0.001 ); + } + + + @Test + public void testVerifyMotorA8_3CG(){ + final ThrustCurveMotor mtr = motorEstesA8_3; + + final double actCGx0p041 = mtr.getCGx(0.041); + assertEquals( 0.0352, actCGx0p041, 0.001 ); + final double actMass0p041 = mtr.getTotalMass( 0.041 ); + assertEquals( 0.016335, actMass0p041, 0.001 ); + + final double actCGx0p206 = mtr.getCGx( 0.206 ); + assertEquals( 0.0362, actCGx0p206, 0.001 ); + final double actMass0p206 = mtr.getTotalMass( 0.206 ); + assertEquals( 0.015285, actMass0p206, 0.001 ); + } + + private class TestPair extends Pair{ + private TestPair(){ super( 0., 0.);} + + public TestPair( Double u, Double v){ + super(u,v); + } + } + + @Test + public void testThrustInterpolation(){ + final ThrustCurveMotor mtr = motorEstesA8_3; + + Pair testPairs[] = new TestPair[]{ + new TestPair(0.512, 0.041), + new TestPair(2.115, 0.084), + new TestPair( 1.220, 0.060), + new TestPair( 1.593, 0.070), + new TestPair( 1.965, 0.080), + new TestPair( 2.428, 0.090), + }; + + for( Pair testCase : testPairs ){ + final double time = testCase.getV(); + final double expThrust = testCase.getU(); + } + } + @Test public void testMotorData() { - - assertEquals("X6", motor.getDesignation()); - assertEquals("X6-5", motor.getDesignation(5.0)); - assertEquals("Description of X6", motor.getDescription()); - assertEquals(Motor.Type.RELOAD, motor.getMotorType()); - + assertEquals("X6", motorX6.getDesignation()); + assertEquals("X6-5", motorX6.getDesignation(5.0)); + assertEquals("Description of X6", motorX6.getDescription()); + assertEquals(Motor.Type.RELOAD, motorX6.getMotorType()); } @Test public void testTimeIndexingNegative(){ + final ThrustCurveMotor mtr = motorX6; // attempt to retrieve for a time before the motor ignites - assertEquals( 0.0, motor.getPseudoIndex( -1 ), 0.001 ); + assertEquals( 0.0, mtr.getTime( -1 ), 0.00000001 ); + } + + @Test + public void testTimeIndexingPastBurnout(){ + final ThrustCurveMotor mtr = motorX6; + + // attempt to retrieve for a time after the motor finishes + // should retrieve the last time value. In this case: 4.0 + assertEquals( 4.0, mtr.getTime( Double.MAX_VALUE ), 0.00000001 ); + assertEquals( 4.0, mtr.getTime( 20.0 ), 0.00000001 ); + } + + + @Test + public void testTimeIndexingAtBurnout(){ + // attempt to retrieve for a time after motor cutoff + assertEquals( 4.0, motorX6.getTime( 4.0), 0.00001 ); } @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 ThrustCurveMotor mtr = motorX6; - final double searchTime = 0.2; - assertEquals( searchTime, motor.getMotorTimeAtIndex( motor.getPseudoIndex(searchTime)), 0.001 ); + final double[] timeList = { 0.2, 0.441, 0.512, 1., 2., 3}; + + for( double searchTime : timeList ){ + assertEquals( searchTime, mtr.getTime(searchTime), 0.00001); + } } @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 ); + assertEquals( 2.0, motorX6.getThrust( 1 ), 0.001 ); + assertEquals( 2.5, motorX6.getThrust( 2 ), 0.001 ); + assertEquals( 3.0, motorX6.getThrust( 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); -// } } 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 08c2d43eb..657eddc85 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 @@ -249,7 +249,7 @@ class MotorInformationPanel extends JPanel { selectedMotor.getLaunchMass())); emptyMassLabel.setText(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit( selectedMotor.getBurnoutMass())); - dataPointsLabel.setText("" + (selectedMotor.getTimePoints().length - 1)); + dataPointsLabel.setText("" + (selectedMotor.getSampleSize() - 1)); if (digestLabel != null) { digestLabel.setText(selectedMotor.getDigest()); }