1 package net.sf.openrocket.simulation;
3 import java.util.ArrayList;
6 import net.sf.openrocket.aerodynamics.WarningSet;
7 import net.sf.openrocket.logging.LogHelper;
8 import net.sf.openrocket.startup.Application;
9 import net.sf.openrocket.util.MathUtil;
10 import net.sf.openrocket.util.Mutable;
13 * A collection of various flight data. This is the result of a simulation, or importing
14 * data into the software. The data includes:
16 * <li>A number of generally interesting values of a simulation, such as max. altitude and velocity
17 * <li>A number (or zero) of flight data branches containing the actual data
18 * <li>A WarningSet including warnings that occurred during simulation
21 * A FlightData object can be made immutable by calling {@link #immute()}.
23 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
25 public class FlightData {
26 private static final LogHelper log = Application.getLogger();
29 * An immutable FlightData object with NaN data.
31 public static final FlightData NaN_DATA;
33 FlightData data = new FlightData();
38 private Mutable mutable = new Mutable();
40 private final ArrayList<FlightDataBranch> branches = new ArrayList<FlightDataBranch>();
42 private final WarningSet warnings = new WarningSet();
44 private double maxAltitude = Double.NaN;
45 private double maxVelocity = Double.NaN;
46 private double maxAcceleration = Double.NaN;
47 private double maxMachNumber = Double.NaN;
48 private double timeToApogee = Double.NaN;
49 private double flightTime = Double.NaN;
50 private double groundHitVelocity = Double.NaN;
51 private double launchRodVelocity = Double.NaN;
52 private double deploymentVelocity = Double.NaN;
56 * Create a FlightData object with no content. The resulting object is mutable.
64 * Construct a FlightData object with no data branches but the specified
65 * summary information. The resulting object is mutable.
67 * @param maxAltitude maximum altitude.
68 * @param maxVelocity maximum velocity.
69 * @param maxAcceleration maximum acceleration.
70 * @param maxMachNumber maximum Mach number.
71 * @param timeToApogee time to apogee.
72 * @param flightTime total flight time.
73 * @param groundHitVelocity ground hit velocity.
74 * @param launchRodVelocity velocity at launch rod clearance
75 * @param deploymentVelocity velocity at deployment
77 public FlightData(double maxAltitude, double maxVelocity, double maxAcceleration,
78 double maxMachNumber, double timeToApogee, double flightTime,
79 double groundHitVelocity, double launchRodVelocity, double deploymentVelocity) {
80 this.maxAltitude = maxAltitude;
81 this.maxVelocity = maxVelocity;
82 this.maxAcceleration = maxAcceleration;
83 this.maxMachNumber = maxMachNumber;
84 this.timeToApogee = timeToApogee;
85 this.flightTime = flightTime;
86 this.groundHitVelocity = groundHitVelocity;
87 this.launchRodVelocity = launchRodVelocity;
88 this.deploymentVelocity = deploymentVelocity;
93 * Create a FlightData object with the specified branches. The resulting object is mutable.
95 * @param branches the branches.
97 public FlightData(FlightDataBranch... branches) {
100 for (FlightDataBranch b : branches)
103 calculateIntrestingValues();
110 * Returns the warning set associated with this object. This WarningSet cannot be
111 * set, so simulations must use this warning set to store their warnings.
112 * The returned WarningSet should not be modified otherwise.
114 * @return the warnings generated during this simulation.
116 public WarningSet getWarningSet() {
121 public void addBranch(FlightDataBranch branch) {
125 branches.add(branch);
127 if (branches.size() == 1) {
128 calculateIntrestingValues();
132 public int getBranchCount() {
133 return branches.size();
136 public FlightDataBranch getBranch(int n) {
137 return branches.get(n);
142 public double getMaxAltitude() {
146 public double getMaxVelocity() {
151 * NOTE: This value only takes into account flight phase.
153 public double getMaxAcceleration() {
154 return maxAcceleration;
157 public double getMaxMachNumber() {
158 return maxMachNumber;
161 public double getTimeToApogee() {
165 public double getFlightTime() {
169 public double getGroundHitVelocity() {
170 return groundHitVelocity;
173 public double getLaunchRodVelocity() {
174 return launchRodVelocity;
178 public double getDeploymentVelocity() {
179 return deploymentVelocity;
184 * Calculate the max. altitude/velocity/acceleration, time to apogee, flight time
185 * and ground hit velocity.
187 private void calculateIntrestingValues() {
188 if (branches.isEmpty())
191 FlightDataBranch branch = branches.get(0);
192 maxAltitude = branch.getMaximum(FlightDataType.TYPE_ALTITUDE);
193 maxVelocity = branch.getMaximum(FlightDataType.TYPE_VELOCITY_TOTAL);
194 maxMachNumber = branch.getMaximum(FlightDataType.TYPE_MACH_NUMBER);
196 flightTime = branch.getLast(FlightDataType.TYPE_TIME);
197 if (branch.getLast(FlightDataType.TYPE_ALTITUDE) < 10) {
198 groundHitVelocity = branch.getLast(FlightDataType.TYPE_VELOCITY_TOTAL);
200 groundHitVelocity = Double.NaN;
205 List<Double> time = branch.get(FlightDataType.TYPE_TIME);
206 List<Double> altitude = branch.get(FlightDataType.TYPE_ALTITUDE);
208 if (time == null || altitude == null) {
209 timeToApogee = Double.NaN;
210 maxAcceleration = Double.NaN;
214 for (Double alt : altitude) {
216 if (MathUtil.equals(alt, maxAltitude))
222 if (index < time.size())
223 timeToApogee = time.get(index);
225 timeToApogee = Double.NaN;
228 // Launch rod velocity
229 eventloop: for (FlightEvent event : branch.getEvents()) {
230 if (event.getType() == FlightEvent.Type.LAUNCHROD) {
231 double t = event.getTime();
232 List<Double> velocity = branch.get(FlightDataType.TYPE_VELOCITY_TOTAL);
233 launchRodVelocity = MathUtil.interpolate( time, velocity, t);
234 } else if ( event.getType() == FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT) {
235 double t = event.getTime();
236 List<Double> velocity = branch.get(FlightDataType.TYPE_VELOCITY_TOTAL);
237 deploymentVelocity = MathUtil.interpolate( time, velocity, t);
241 // Max. acceleration (must be after apogee time)
242 if (branch.get(FlightDataType.TYPE_ACCELERATION_TOTAL) != null) {
243 maxAcceleration = calculateMaxAcceleration();
245 maxAcceleration = Double.NaN;
248 log.debug("Computed flight values:" +
249 " maxAltitude=" + maxAltitude +
250 " maxVelocity=" + maxVelocity +
251 " maxAcceleration=" + maxAcceleration +
252 " maxMachNumber=" + maxMachNumber +
253 " timeToApogee=" + timeToApogee +
254 " flightTime=" + flightTime +
255 " groundHitVelocity=" + groundHitVelocity +
256 " launchRodVelocity=" + launchRodVelocity);
260 public void immute() {
263 for (FlightDataBranch b : branches) {
269 public boolean isMutable() {
270 return mutable.isMutable();
276 * Find the maximum acceleration before apogee.
278 private double calculateMaxAcceleration() {
280 // End check at first recovery device deployment
281 double endTime = Double.MAX_VALUE;
283 FlightDataBranch branch = this.getBranch(0);
284 for (FlightEvent event : branch.getEvents()) {
285 if (event.getType() == FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT) {
286 if (event.getTime() < endTime) {
287 endTime = event.getTime();
292 List<Double> time = branch.get(FlightDataType.TYPE_TIME);
293 List<Double> acceleration = branch.get(FlightDataType.TYPE_ACCELERATION_TOTAL);
295 if (time == null || acceleration == null) {
301 for (int i = 0; i < time.size(); i++) {
302 if (time.get(i) >= endTime) {
305 double a = acceleration.get(i);