Persist the Velocity at launch rod clearance in ork file. Added to column in SimulationPanel. Added computation of Velocity at deployment in FlightData. Persist Velocity at deployment in ork file and added to SimulationPanel and DesignReport.
Added l10n message keys for new column in SimulationPanel. Added function MathUtil.interpolate which does linear interpolation of intermediate values. This is used in FlightData for computing Velocity at deployment and Velocity at launch rod clearance.
This commit is contained in:
parent
c0f100e3a5
commit
c3cfb2e781
@ -39,3 +39,5 @@ The following file format versions exist:
|
||||
1.3: Introduced with OpenRocket 1.1.9. Adds the <launchlongitude> and
|
||||
<geodeticmethod> parameters to the simulation conditions element.
|
||||
|
||||
1.4: Introduced with OpenRocket 1.1.10. Adds the launchrodvelocity and
|
||||
deploymentvelocity attributes to <flightdata> element.
|
@ -378,6 +378,8 @@ simpanel.dlg.lbl.DeleteSim2 = <html><i>This operation cannot be undone.</i>
|
||||
simpanel.dlg.lbl.DeleteSim3 = Delete simulations
|
||||
simpanel.col.Name = Name
|
||||
simpanel.col.Motors = Motors
|
||||
simpanel.col.Velocityoffrod = Velocity off rod
|
||||
simpanel.col.Velocityatdeploy = Velocity at deployment
|
||||
simpanel.col.Apogee = Apogee
|
||||
simpanel.col.Maxvelocity = Max. velocity
|
||||
simpanel.col.Maxacceleration = Max. acceleration
|
||||
|
@ -1517,6 +1517,8 @@ class FlightDataHandler extends ElementHandler {
|
||||
double timeToApogee = Double.NaN;
|
||||
double flightTime = Double.NaN;
|
||||
double groundHitVelocity = Double.NaN;
|
||||
double launchRodVelocity = Double.NaN;
|
||||
double deploymentVelocity = Double.NaN;
|
||||
|
||||
try {
|
||||
maxAltitude = DocumentConfig.stringToDouble(attributes.get("maxaltitude"));
|
||||
@ -1547,10 +1549,18 @@ class FlightDataHandler extends ElementHandler {
|
||||
DocumentConfig.stringToDouble(attributes.get("groundhitvelocity"));
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
try {
|
||||
launchRodVelocity = DocumentConfig.stringToDouble(attributes.get("launchrodvelocity"));
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
try {
|
||||
deploymentVelocity = DocumentConfig.stringToDouble(attributes.get("deploymentvelocity"));
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
|
||||
// TODO: HIGH: Store and load launchRodVelocity
|
||||
data = new FlightData(maxAltitude, maxVelocity, maxAcceleration, maxMach,
|
||||
timeToApogee, flightTime, groundHitVelocity, Double.NaN);
|
||||
timeToApogee, flightTime, groundHitVelocity, launchRodVelocity, deploymentVelocity);
|
||||
}
|
||||
|
||||
data.getWarningSet().addAll(warningSet);
|
||||
|
@ -360,6 +360,10 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
str += " flighttime=\"" + TextUtil.doubleToString(data.getFlightTime()) + "\"";
|
||||
if (!Double.isNaN(data.getGroundHitVelocity()))
|
||||
str += " groundhitvelocity=\"" + TextUtil.doubleToString(data.getGroundHitVelocity()) + "\"";
|
||||
if (!Double.isNaN(data.getLaunchRodVelocity()))
|
||||
str += " launchrodvelocity=\"" + TextUtil.doubleToString(data.getLaunchRodVelocity()) + "\"";
|
||||
if (!Double.isNaN(data.getDeploymentVelocity()))
|
||||
str += " deploymentvelocity=\"" + TextUtil.doubleToString(data.getDeploymentVelocity()) + "\"";
|
||||
str += ">";
|
||||
writeln(str);
|
||||
indent++;
|
||||
|
@ -311,6 +311,22 @@ public class SimulationPanel extends JPanel {
|
||||
}
|
||||
},
|
||||
|
||||
//// Launch rod velocity
|
||||
new Column(trans.get("simpanel.col.Velocityoffrod")) {
|
||||
@Override
|
||||
public Object getValueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
return UnitGroup.UNITS_VELOCITY.getDefaultUnit().toStringUnit(
|
||||
data.getLaunchRodVelocity());
|
||||
}
|
||||
},
|
||||
|
||||
//// Apogee
|
||||
new Column(trans.get("simpanel.col.Apogee")) {
|
||||
@Override
|
||||
@ -327,6 +343,22 @@ public class SimulationPanel extends JPanel {
|
||||
}
|
||||
},
|
||||
|
||||
//// Velocity at deployment
|
||||
new Column(trans.get("simpanel.col.Velocityatdeploy")) {
|
||||
@Override
|
||||
public Object getValueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
return UnitGroup.UNITS_VELOCITY.getDefaultUnit().toStringUnit(
|
||||
data.getDeploymentVelocity());
|
||||
}
|
||||
},
|
||||
|
||||
//// Maximum velocity
|
||||
new Column(trans.get("simpanel.col.Maxvelocity")) {
|
||||
@Override
|
||||
|
@ -117,6 +117,7 @@ public class DesignReport {
|
||||
private static final String TIME_TO_APOGEE = "Time to Apogee";
|
||||
private static final String VELOCITY_OFF_PAD = "Velocity off Pad";
|
||||
private static final String MAX_VELOCITY = "Max Velocity";
|
||||
private static final String DEPLOYMENT_VELOCITY = "Velocity at Deployment";
|
||||
private static final String LANDING_VELOCITY = "Landing Velocity";
|
||||
private static final String ROCKET_DESIGN = "Rocket Design";
|
||||
private static final double GRAVITY_CONSTANT = 9.80665d;
|
||||
@ -469,6 +470,9 @@ public class DesignReport {
|
||||
labelTable.addCell(ITextHelper.createCell(MAX_VELOCITY, 2, 2));
|
||||
labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getMaxVelocity()), 2, 2));
|
||||
|
||||
labelTable.addCell(ITextHelper.createCell(DEPLOYMENT_VELOCITY, 2,2));
|
||||
labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getDeploymentVelocity()),2,2));
|
||||
|
||||
labelTable.addCell(ITextHelper.createCell(LANDING_VELOCITY, 2, 2));
|
||||
labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getGroundHitVelocity()), 2, 2));
|
||||
|
||||
|
@ -49,6 +49,7 @@ public class FlightData {
|
||||
private double flightTime = Double.NaN;
|
||||
private double groundHitVelocity = Double.NaN;
|
||||
private double launchRodVelocity = Double.NaN;
|
||||
private double deploymentVelocity = Double.NaN;
|
||||
|
||||
|
||||
/**
|
||||
@ -70,11 +71,12 @@ public class FlightData {
|
||||
* @param timeToApogee time to apogee.
|
||||
* @param flightTime total flight time.
|
||||
* @param groundHitVelocity ground hit velocity.
|
||||
* @param launchRodVelocity TODO
|
||||
* @param launchRodVelocity velocity at launch rod clearance
|
||||
* @param deploymentVelocity velocity at deployment
|
||||
*/
|
||||
public FlightData(double maxAltitude, double maxVelocity, double maxAcceleration,
|
||||
double maxMachNumber, double timeToApogee, double flightTime,
|
||||
double groundHitVelocity, double launchRodVelocity) {
|
||||
double groundHitVelocity, double launchRodVelocity, double deploymentVelocity) {
|
||||
this.maxAltitude = maxAltitude;
|
||||
this.maxVelocity = maxVelocity;
|
||||
this.maxAcceleration = maxAcceleration;
|
||||
@ -83,6 +85,7 @@ public class FlightData {
|
||||
this.flightTime = flightTime;
|
||||
this.groundHitVelocity = groundHitVelocity;
|
||||
this.launchRodVelocity = launchRodVelocity;
|
||||
this.deploymentVelocity = deploymentVelocity;
|
||||
}
|
||||
|
||||
|
||||
@ -171,7 +174,11 @@ public class FlightData {
|
||||
return launchRodVelocity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public double getDeploymentVelocity() {
|
||||
return deploymentVelocity;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the max. altitude/velocity/acceleration, time to apogee, flight time
|
||||
@ -223,15 +230,11 @@ public class FlightData {
|
||||
if (event.getType() == FlightEvent.Type.LAUNCHROD) {
|
||||
double t = event.getTime();
|
||||
List<Double> velocity = branch.get(FlightDataType.TYPE_VELOCITY_TOTAL);
|
||||
if (velocity == null) {
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < velocity.size(); i++) {
|
||||
if (time.get(i) >= t) {
|
||||
launchRodVelocity = velocity.get(i);
|
||||
break eventloop;
|
||||
}
|
||||
}
|
||||
launchRodVelocity = MathUtil.interpolate( time, velocity, t);
|
||||
} else if ( event.getType() == FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT) {
|
||||
double t = event.getTime();
|
||||
List<Double> velocity = branch.get(FlightDataType.TYPE_VELOCITY_TOTAL);
|
||||
deploymentVelocity = MathUtil.interpolate( time, velocity, t);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,9 +11,9 @@ import net.sf.openrocket.startup.Application;
|
||||
|
||||
public class MathUtil {
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
|
||||
public static final double EPSILON = 0.00000001; // 10mm^3 in m^3
|
||||
|
||||
|
||||
/**
|
||||
* The square of x (x^2). On Sun's JRE using this method is as fast as typing x*x.
|
||||
* @param x x
|
||||
@ -22,7 +22,7 @@ public class MathUtil {
|
||||
public static double pow2(double x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The cube of x (x^3).
|
||||
* @param x x
|
||||
@ -31,11 +31,11 @@ public class MathUtil {
|
||||
public static double pow3(double x) {
|
||||
return x * x * x;
|
||||
}
|
||||
|
||||
|
||||
public static double pow4(double x) {
|
||||
return (x * x) * (x * x);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamps the value x to the range min - max.
|
||||
* @param x Original value.
|
||||
@ -50,7 +50,7 @@ public class MathUtil {
|
||||
return max;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
public static float clamp(float x, float min, float max) {
|
||||
if (x < min)
|
||||
return min;
|
||||
@ -58,7 +58,7 @@ public class MathUtil {
|
||||
return max;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
public static int clamp(int x, int min, int max) {
|
||||
if (x < min)
|
||||
return min;
|
||||
@ -66,8 +66,8 @@ public class MathUtil {
|
||||
return max;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Maps a value from one value range to another.
|
||||
*
|
||||
@ -90,8 +90,8 @@ public class MathUtil {
|
||||
}
|
||||
return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Maps a coordinate from one value range to another.
|
||||
*
|
||||
@ -115,8 +115,8 @@ public class MathUtil {
|
||||
double a = (value - fromMin) / (fromMax - fromMin);
|
||||
return toMax.multiply(a).add(toMin.multiply(1 - a));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compute the minimum of two values. This is performed by direct comparison.
|
||||
* However, if one of the values is NaN and the other is not, the non-NaN value is
|
||||
@ -127,7 +127,7 @@ public class MathUtil {
|
||||
return x;
|
||||
return (x < y) ? x : y;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute the maximum of two values. This is performed by direct comparison.
|
||||
* However, if one of the values is NaN and the other is not, the non-NaN value is
|
||||
@ -138,7 +138,7 @@ public class MathUtil {
|
||||
return y;
|
||||
return (x < y) ? y : x;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute the minimum of three values. This is performed by direct comparison.
|
||||
* However, if one of the values is NaN and the other is not, the non-NaN value is
|
||||
@ -151,8 +151,8 @@ public class MathUtil {
|
||||
return min(y, z);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compute the minimum of three values. This is performed by direct comparison.
|
||||
@ -162,8 +162,8 @@ public class MathUtil {
|
||||
public static double min(double w, double x, double y, double z) {
|
||||
return min(min(w, x), min(y, z));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compute the maximum of three values. This is performed by direct comparison.
|
||||
* However, if one of the values is NaN and the other is not, the non-NaN value is
|
||||
@ -176,7 +176,7 @@ public class MathUtil {
|
||||
return max(y, z);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the hypotenuse <code>sqrt(x^2+y^2)</code>. This method is SIGNIFICANTLY
|
||||
* faster than <code>Math.hypot(x,y)</code>.
|
||||
@ -184,7 +184,7 @@ public class MathUtil {
|
||||
public static double hypot(double x, double y) {
|
||||
return Math.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reduce the angle x to the range 0 - 2*PI.
|
||||
* @param x Original angle.
|
||||
@ -194,7 +194,7 @@ public class MathUtil {
|
||||
double d = Math.floor(x / (2 * Math.PI));
|
||||
return x - d * 2 * Math.PI;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reduce the angle x to the range -PI - PI.
|
||||
*
|
||||
@ -207,8 +207,8 @@ public class MathUtil {
|
||||
double d = Math.rint(x / (2 * Math.PI));
|
||||
return x - d * 2 * Math.PI;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the square root of a value. If the value is negative, zero is returned.
|
||||
* This is safer in cases where rounding errors might make a value slightly negative.
|
||||
@ -225,20 +225,20 @@ public class MathUtil {
|
||||
}
|
||||
return Math.sqrt(d);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static boolean equals(double a, double b) {
|
||||
double absb = Math.abs(b);
|
||||
|
||||
|
||||
if (absb < EPSILON / 2) {
|
||||
// Near zero
|
||||
return Math.abs(a) < EPSILON / 2;
|
||||
}
|
||||
return Math.abs(a - b) < EPSILON * absb;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the sign of the number. This corresponds to Math.signum, but ignores
|
||||
* the special cases of zero and NaN. The value returned for those is arbitrary.
|
||||
@ -251,20 +251,20 @@ public class MathUtil {
|
||||
public static double sign(double x) {
|
||||
return (x < 0) ? -1.0 : 1.0;
|
||||
}
|
||||
|
||||
|
||||
/* Math.abs() is about 3x as fast as this:
|
||||
|
||||
|
||||
public static double abs(double x) {
|
||||
return (x<0) ? -x : x;
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
public static double average(Collection<? extends Number> values) {
|
||||
if (values.isEmpty()) {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
|
||||
double avg = 0.0;
|
||||
int count = 0;
|
||||
for (Number n : values) {
|
||||
@ -273,12 +273,12 @@ public class MathUtil {
|
||||
}
|
||||
return avg / count;
|
||||
}
|
||||
|
||||
|
||||
public static double stddev(Collection<? extends Number> values) {
|
||||
if (values.size() < 2) {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
|
||||
double avg = average(values);
|
||||
double stddev = 0.0;
|
||||
int count = 0;
|
||||
@ -289,12 +289,12 @@ public class MathUtil {
|
||||
stddev = Math.sqrt(stddev / (count - 1));
|
||||
return stddev;
|
||||
}
|
||||
|
||||
|
||||
public static double median(Collection<? extends Number> values) {
|
||||
if (values.isEmpty()) {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
|
||||
List<Number> sorted = new ArrayList<Number>(values);
|
||||
Collections.sort(sorted, new Comparator<Number>() {
|
||||
@Override
|
||||
@ -302,7 +302,7 @@ public class MathUtil {
|
||||
return Double.compare(o1.doubleValue(), o2.doubleValue());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
int n = sorted.size();
|
||||
if (n % 2 == 0) {
|
||||
return (sorted.get(n / 2).doubleValue() + sorted.get(n / 2 - 1).doubleValue()) / 2;
|
||||
@ -310,5 +310,58 @@ public class MathUtil {
|
||||
return sorted.get(n / 2).doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use interpolation to determine the value of the function at point t.
|
||||
* Current implementation uses simple linear interpolation. The domain
|
||||
* and range lists must include the same number of values, t must be within
|
||||
* the domain, and the domain list must be sorted.
|
||||
*
|
||||
* @param domain list containing domain samples
|
||||
* @param range list of corresponding range samples
|
||||
* @param t domain value at which to interpolate
|
||||
* @return returns Double.NaN if either list is null or empty or different size, or if t is outsize the domain.
|
||||
*/
|
||||
public static double interpolate( List<Double> domain, List<Double> range, double t ) {
|
||||
|
||||
if ( domain == null || range == null || domain.size() != range.size() ) {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
int length = domain.size();
|
||||
if ( length <= 1 || t < domain.get(0) || t > domain.get( length-1 ) ) {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
// Look for the index of the right end point.
|
||||
int right = 1;
|
||||
while( t > domain.get(right) ) {
|
||||
right ++;
|
||||
}
|
||||
int left = right -1;
|
||||
|
||||
// Points are:
|
||||
|
||||
double deltax = domain.get(right) - domain.get(left);
|
||||
double deltay = range.get(right) - range.get(left);
|
||||
|
||||
// For numerical stability, if deltax is small,
|
||||
if ( Math.abs(deltax) < EPSILON ) {
|
||||
if ( deltay < -1.0 * EPSILON ) {
|
||||
// return neg infinity if deltay is negative
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
else if ( deltay > EPSILON ) {
|
||||
// return infinity if deltay is large
|
||||
return Double.POSITIVE_INFINITY;
|
||||
} else {
|
||||
// otherwise return 0
|
||||
return 0.0d;
|
||||
}
|
||||
}
|
||||
|
||||
return range.get(left) + ( t - domain.get(left) ) * deltay / deltax;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -216,4 +216,64 @@ public class MathUtilTest {
|
||||
assertEquals(5.43, MathUtil.median(doubles), EPS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterpolate() {
|
||||
double v;
|
||||
List<Double> x;
|
||||
List<Double> y;
|
||||
|
||||
x = new ArrayList<Double>();
|
||||
y = new ArrayList<Double>();
|
||||
y.add(1.0);
|
||||
|
||||
v= MathUtil.interpolate(null, y, 0.0);
|
||||
assertEquals("Failed to test for domain null", Double.NaN, v, EPS);
|
||||
|
||||
v = MathUtil.interpolate(x, y, 0.0);
|
||||
assertEquals("Failed to test for empty domain", Double.NaN, v, EPS);
|
||||
|
||||
x = new ArrayList<Double>();
|
||||
x.add(1.0);
|
||||
y = new ArrayList<Double>();
|
||||
|
||||
v = MathUtil.interpolate(x, null, 0.0);
|
||||
assertEquals("Failed to test for range null", Double.NaN, v, EPS);
|
||||
|
||||
v = MathUtil.interpolate(x, y, 0.0);
|
||||
assertEquals("Failed to test for empty range", Double.NaN, v, EPS);
|
||||
|
||||
x = new ArrayList<Double>();
|
||||
x.add(1.0);
|
||||
x.add(2.0);
|
||||
y = new ArrayList<Double>();
|
||||
y.add(15.0);
|
||||
y.add(17.0);
|
||||
|
||||
v = MathUtil.interpolate(x,y,0.0);
|
||||
assertEquals("Failed to test t out of domain", Double.NaN, v, EPS);
|
||||
|
||||
v = MathUtil.interpolate(x,y,5.0);
|
||||
assertEquals("Failed to test t out of domain", Double.NaN, v, EPS);
|
||||
|
||||
v = MathUtil.interpolate(x,y,1.0);
|
||||
assertEquals("Failed to calculate left endpoint", 15.0, v, EPS);
|
||||
v = MathUtil.interpolate(x,y,2.0);
|
||||
assertEquals("Failed to calculate right endpoint", 17.0, v, EPS);
|
||||
v = MathUtil.interpolate(x,y,1.5);
|
||||
assertEquals("Failed to calculate center", 16.0, v, EPS);
|
||||
|
||||
x = new ArrayList<Double>();
|
||||
x.add(0.25);
|
||||
x.add(0.5);
|
||||
x.add(1.0);
|
||||
x.add(2.0);
|
||||
y = new ArrayList<Double>();
|
||||
y.add(0.0);
|
||||
y.add(0.0);
|
||||
y.add(15.0);
|
||||
y.add(17.0);
|
||||
v = MathUtil.interpolate(x,y,1.5);
|
||||
assertEquals("Failed to calculate center with longer list", 16.0, v, EPS);
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user