diff --git a/.idea/artifacts/openrocket_jar.xml b/.idea/artifacts/openrocket_jar.xml
index 019f1369a..119b4f763 100644
--- a/.idea/artifacts/openrocket_jar.xml
+++ b/.idea/artifacts/openrocket_jar.xml
@@ -10,29 +10,16 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -45,6 +32,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/.classpath b/core/.classpath
index 18f644a33..163c5b1c5 100644
--- a/core/.classpath
+++ b/core/.classpath
@@ -16,7 +16,7 @@
-
+
diff --git a/core/OpenRocket Core.iml b/core/OpenRocket Core.iml
index f0cdceef9..8afb18531 100644
--- a/core/OpenRocket Core.iml
+++ b/core/OpenRocket Core.iml
@@ -80,15 +80,6 @@
-
-
-
-
-
-
-
-
-
@@ -127,5 +118,14 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/lib/slf4j-api-1.7.30.jar b/core/lib/slf4j-api-1.7.30.jar
new file mode 100644
index 000000000..29ac26fb8
Binary files /dev/null and b/core/lib/slf4j-api-1.7.30.jar differ
diff --git a/core/lib/slf4j-api-1.7.5.jar b/core/lib/slf4j-api-1.7.5.jar
deleted file mode 100644
index 8766455d8..000000000
Binary files a/core/lib/slf4j-api-1.7.5.jar and /dev/null differ
diff --git a/core/src/net/sf/openrocket/simulation/FlightData.java b/core/src/net/sf/openrocket/simulation/FlightData.java
index d0d3d79e4..e7f24f806 100644
--- a/core/src/net/sf/openrocket/simulation/FlightData.java
+++ b/core/src/net/sf/openrocket/simulation/FlightData.java
@@ -101,7 +101,7 @@ public class FlightData {
for (FlightDataBranch b : branches)
this.addBranch(b);
- calculateIntrestingValues();
+ calculateInterestingValues();
}
@@ -126,7 +126,7 @@ public class FlightData {
branches.add(branch);
if (branches.size() == 1) {
- calculateIntrestingValues();
+ calculateInterestingValues();
}
}
@@ -185,7 +185,7 @@ public class FlightData {
* Calculate the max. altitude/velocity/acceleration, time to apogee, flight time
* and ground hit velocity.
*/
- private void calculateIntrestingValues() {
+ private void calculateInterestingValues() {
if (branches.isEmpty())
return;
@@ -195,13 +195,7 @@ public class FlightData {
maxMachNumber = branch.getMaximum(FlightDataType.TYPE_MACH_NUMBER);
flightTime = branch.getLast(FlightDataType.TYPE_TIME);
- if (branch.getLast(FlightDataType.TYPE_ALTITUDE) < 10) {
- groundHitVelocity = branch.getLast(FlightDataType.TYPE_VELOCITY_TOTAL);
- } else {
- groundHitVelocity = Double.NaN;
- }
-
// Time to apogee
List time = branch.get(FlightDataType.TYPE_TIME);
List altitude = branch.get(FlightDataType.TYPE_ALTITUDE);
@@ -236,6 +230,10 @@ public class FlightData {
double t = event.getTime();
List velocity = branch.get(FlightDataType.TYPE_VELOCITY_TOTAL);
deploymentVelocity = MathUtil.interpolate( time, velocity, t);
+ } else if (event.getType() == FlightEvent.Type.GROUND_HIT) {
+ double t = event.getTime();
+ List velocity = branch.get(FlightDataType.TYPE_VELOCITY_TOTAL);
+ groundHitVelocity = MathUtil.interpolate( time, velocity, t);
}
}
diff --git a/core/test/net/sf/openrocket/simulation/TestFlightData.java b/core/test/net/sf/openrocket/simulation/TestFlightData.java
new file mode 100644
index 000000000..d9620d529
--- /dev/null
+++ b/core/test/net/sf/openrocket/simulation/TestFlightData.java
@@ -0,0 +1,285 @@
+package net.sf.openrocket.simulation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+
+/**
+ * Tests the FlightData object.
+ *
+ * @author Billy Olsen
+ */
+public class TestFlightData {
+
+ /**
+ * Test method for {@link net.sf.openrocket.simulation.FlightData#FlightData()}.
+ */
+ @Test
+ public void testFlightData() {
+ FlightData data = new FlightData();
+
+ WarningSet warnings = data.getWarningSet();
+ assertNotNull(warnings);
+ assertTrue(warnings.isEmpty());
+
+ assertEquals(0, data.getBranchCount());
+ assertEquals(Double.NaN, data.getDeploymentVelocity(), 0.00);
+ assertEquals(Double.NaN, data.getFlightTime(), 0.00);
+ assertEquals(Double.NaN, data.getGroundHitVelocity(), 0.00);
+ assertEquals(Double.NaN, data.getLaunchRodVelocity(), 0.00);
+ assertEquals(Double.NaN, data.getMaxAcceleration(), 0.00);
+ assertEquals(Double.NaN, data.getMaxAltitude(), 0.00);
+ assertEquals(Double.NaN, data.getMaxMachNumber(), 0.00);
+ assertEquals(Double.NaN, data.getTimeToApogee(), 0.00);
+
+ }
+
+ /**
+ * Tests flight data created from summary data.
+ */
+ @Test
+ public void testFlightDataFromSummaryData() {
+ double deploymentVelocity = 14.8;
+ double flightTime = 69.1;
+ double groundHitVelocity = 3.4;
+ double launchRodVelocity = 17.5;
+ double maxAcceleration = 156.2;
+ double maxVelocity = 105.9;
+ double maxAltitude = 355.1;
+ double maxMachNumber = 0.31;
+ double timeToApogee = 7.96;
+
+ FlightData data = new FlightData(maxAltitude, maxVelocity, maxAcceleration,
+ maxMachNumber, timeToApogee, flightTime,
+ groundHitVelocity, launchRodVelocity,
+ deploymentVelocity);
+
+ WarningSet warnings = data.getWarningSet();
+ assertNotNull(warnings);
+ assertTrue(warnings.isEmpty());
+
+ assertEquals(0, data.getBranchCount());
+ assertEquals(deploymentVelocity, data.getDeploymentVelocity(), 0.00);
+ assertEquals(flightTime, data.getFlightTime(), 0.00);
+ assertEquals(groundHitVelocity, data.getGroundHitVelocity(), 0.00);
+ assertEquals(launchRodVelocity, data.getLaunchRodVelocity(), 0.00);
+ assertEquals(maxAcceleration, data.getMaxAcceleration(), 0.00);
+ assertEquals(maxAltitude, data.getMaxAltitude(), 0.00);
+ assertEquals(maxMachNumber, data.getMaxMachNumber(), 0.00);
+ assertEquals(timeToApogee, data.getTimeToApogee(), 0.00);
+ }
+
+ /**
+ * Test method for {@link net.sf.openrocket.simulation.FlightData#FlightData(net.sf.openrocket.simulation.FlightDataBranch[])}.
+ */
+ @Test
+ public void testFlightDataFlightDataBranchArray() {
+ FlightData data = new FlightData(new FlightDataBranch("Test", FlightDataType.TYPE_TIME));
+
+ WarningSet warnings = data.getWarningSet();
+ assertNotNull(warnings);
+ assertTrue(warnings.isEmpty());
+
+ assertEquals(1, data.getBranchCount());
+
+ data = new FlightData(new FlightDataBranch("Test 1", FlightDataType.TYPE_TIME),
+ new FlightDataBranch("Test 2", FlightDataType.TYPE_TIME));
+
+ warnings = data.getWarningSet();
+ assertNotNull(warnings);
+ assertTrue(warnings.isEmpty());
+
+ assertEquals(2, data.getBranchCount());
+ }
+
+ private FlightDataBranch createFlightDataBranch(final String name, final FlightDataType dataType, final double[] values) {
+ final FlightDataBranch branch = new FlightDataBranch(name, dataType);
+ addDataPoints(branch, dataType, values);
+ return branch;
+ }
+
+ private void addDataPoints(final FlightDataBranch branch, final FlightDataType dataType, final double[] values) {
+ for (int i=0; i < values.length; i++) {
+ branch.addPoint();
+ branch.setValue(dataType, values[i]);
+ }
+ }
+
+ /**
+ * Test method for {@link net.sf.openrocket.simulation.FlightData#getMaxAltitude()}.
+ */
+ @Test
+ public void testGetMaxAltitudeCalculated() {
+ final double[] altitudes = new double[] {
+ 10.5,
+ 37.771,
+ 37.5,
+ 5.1,
+ 0.0
+ };
+ FlightDataBranch branch =
+ createFlightDataBranch("Test Max Alt", FlightDataType.TYPE_ALTITUDE, altitudes);
+
+ FlightData data = new FlightData(branch);
+
+ assertEquals(37.771, data.getMaxAltitude(), 0.000);
+ }
+
+ /**
+ * Test method for {@link net.sf.openrocket.simulation.FlightData#getMaxVelocity()}.
+ */
+ @Test
+ public void testGetMaxVelocityCalculated() {
+ final double[] velocities = new double[] {
+ 10.5,
+ 23.7,
+ 35.5,
+ 30.1,
+ 0.0
+ };
+ FlightDataBranch branch =
+ createFlightDataBranch("Test Max Velocity", FlightDataType.TYPE_VELOCITY_TOTAL, velocities);
+
+ FlightData data = new FlightData(branch);
+
+ assertEquals(35.5, data.getMaxVelocity(), 0.000);
+ }
+
+ /**
+ * Test method for {@link net.sf.openrocket.simulation.FlightData#getMaxMachNumber()}.
+ */
+ @Test
+ public void testGetMaxMachNumberCalculated() {
+ final double[] machs = new double[] {
+ 0.1,
+ 0.2,
+ 0.333,
+ 0.3,
+ 0.1
+ };
+ FlightDataBranch branch =
+ createFlightDataBranch("Test Max Mach", FlightDataType.TYPE_MACH_NUMBER, machs);
+
+ FlightData data = new FlightData(branch);
+
+ assertEquals(0.333, data.getMaxMachNumber(), 0.000);
+ }
+
+ /**
+ * Test method for {@link net.sf.openrocket.simulation.FlightData#getFlightTime()}.
+ */
+ @Test
+ public void testGetFlightTime() {
+ final double[] times = new double[] {
+ 1.0,
+ 5.0,
+ 15.0,
+ 20.1,
+ 30.2
+ };
+ FlightDataBranch branch =
+ createFlightDataBranch("Test Flight Time", FlightDataType.TYPE_TIME, times);
+
+ // Flight time is calculated as the last time entry
+ FlightData data = new FlightData(branch);
+ assertEquals(30.2, data.getFlightTime(), 0.000);
+ }
+
+ /**
+ * Test method for {@link net.sf.openrocket.simulation.FlightData#getGroundHitVelocity()}.
+ */
+ @Test
+ public void testGetGroundHitVelocity() {
+ /*
+ * Setup a flight profile where there is data logged for every second.
+ * The time events will start at 1, rather than 0 so the time into the
+ * data log is the index + 1.0 seconds.
+ *
+ * i.e. at second 1, the velocity becomes 1.2 m/s, the altitude of the
+ * rocket is 1.2, and the LiftOff event is saved.
+ *
+ * This loosely approximates a flight of about 21 seconds where it
+ * flies up quickly and comes down quickly. The actual flight profile
+ * is not as important as the fact that a profile exists.
+ *
+ * Each array is stored as
+ */
+ final double[] velocities = new double[] {
+ // launch to burn out
+ 1.2, 15.0, 31.1, 45.6,
+ // burn out to apogee
+ 33.6, 21.2, 14.1, 6.8, 0.0,
+ // apogee to ejection charge
+ 2.4, 4.7, 8.6, 9.23,
+ // ejection charge to parachute deployment
+ 7.1, 6.2, 6.2, 6.2, 6.2,
+ // parachute deployment to ground hit
+ 6.2, 0.0, 0.0
+ };
+ final double[] altitudes = new double[] {
+ // launch to burn out
+ 1.2, 16.2, 47.3, 92.9,
+ // burn out to apogee
+ 126.5, 147.7, 161.8, 168.6, 168.6,
+ // apogee to ejection charge
+ 166.2, 161.5, 152.9, 143.67,
+ // ejection charge to parachute deployment
+ 136.57, 113.81, 91.05, 68.29, 45.53,
+ // parachute deployment to ground hit
+ 22.77, 0.0, 0.0
+ };
+ final FlightEvent.Type[] eventTypes = new FlightEvent.Type[] {
+ // launch to burn out
+ FlightEvent.Type.LIFTOFF, FlightEvent.Type.LAUNCHROD,
+ FlightEvent.Type.ALTITUDE, FlightEvent.Type.BURNOUT,
+
+ // burn out to apogee
+ FlightEvent.Type.ALTITUDE, FlightEvent.Type.ALTITUDE,
+ FlightEvent.Type.ALTITUDE, FlightEvent.Type.ALTITUDE,
+ FlightEvent.Type.APOGEE,
+
+ // apogee to ejection charge
+ FlightEvent.Type.ALTITUDE, FlightEvent.Type.ALTITUDE,
+ FlightEvent.Type.ALTITUDE, FlightEvent.Type.EJECTION_CHARGE,
+
+ // ejection charge to parachute deployment
+ FlightEvent.Type.ALTITUDE, FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT,
+ FlightEvent.Type.ALTITUDE, FlightEvent.Type.ALTITUDE,
+ FlightEvent.Type.ALTITUDE,
+
+ // parachute deployment to ground hit
+ FlightEvent.Type.GROUND_HIT, FlightEvent.Type.ALTITUDE,
+ FlightEvent.Type.SIMULATION_END
+ };
+
+ assertEquals(velocities.length, eventTypes.length);
+ assertEquals(velocities.length, altitudes.length);
+
+ // This flight data branch only needs to record for time,
+ // altitude and velocity.
+ FlightDataBranch branch =
+ new FlightDataBranch("Ground Hit Velocities",
+ FlightDataType.TYPE_TIME,
+ FlightDataType.TYPE_ALTITUDE,
+ FlightDataType.TYPE_VELOCITY_TOTAL);
+
+ for (int i = 0; i < velocities.length; i++) {
+ branch.addPoint();
+ // the data entries are 1 second ahead of the index
+ double time = i + 1.0;
+ branch.setValue(FlightDataType.TYPE_TIME, time);
+ branch.setValue(FlightDataType.TYPE_ALTITUDE, altitudes[i]);
+ branch.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, velocities[i]);
+ branch.addEvent(new FlightEvent(eventTypes[i], time));
+ }
+
+ FlightData data = new FlightData(branch);
+
+ assertEquals(6.2, data.getGroundHitVelocity(), 0.000);
+ }
+
+}
diff --git a/lib-test/OpenRocket Test Libraries.iml b/lib-test/OpenRocket Test Libraries.iml
index c32cfac6d..7202ad899 100644
--- a/lib-test/OpenRocket Test Libraries.iml
+++ b/lib-test/OpenRocket Test Libraries.iml
@@ -70,5 +70,14 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/swing/.classpath b/swing/.classpath
index 169795d31..2de126122 100644
--- a/swing/.classpath
+++ b/swing/.classpath
@@ -14,7 +14,7 @@
-
+
diff --git a/swing/OpenRocket Swing.iml b/swing/OpenRocket Swing.iml
index 408d6a61b..7ac4a7284 100644
--- a/swing/OpenRocket Swing.iml
+++ b/swing/OpenRocket Swing.iml
@@ -71,15 +71,6 @@
-
-
-
-
-
-
-
-
-
@@ -174,5 +165,14 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/swing/build.xml b/swing/build.xml
index 1b44cf8d8..f790ac82e 100644
--- a/swing/build.xml
+++ b/swing/build.xml
@@ -109,12 +109,11 @@
-
+
-
-
+
diff --git a/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java b/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java
index 6fb903c53..b9df44786 100644
--- a/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java
+++ b/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java
@@ -770,6 +770,8 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat
try {
setMethod.invoke(source, v / multiplier);
+ // Make sure to notify all the listeners that have registered
+ fireStateChanged();
} catch (IllegalArgumentException e) {
throw new BugException("Unable to invoke setMethod of " + this, e);
} catch (IllegalAccessException e) {