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;
55 * Create a FlightData object with no content. The resulting object is mutable.
63 * Construct a FlightData object with no data branches but the specified
64 * summary information. The resulting object is mutable.
66 * @param maxAltitude maximum altitude.
67 * @param maxVelocity maximum velocity.
68 * @param maxAcceleration maximum acceleration.
69 * @param maxMachNumber maximum Mach number.
70 * @param timeToApogee time to apogee.
71 * @param flightTime total flight time.
72 * @param groundHitVelocity ground hit velocity.
73 * @param launchRodVelocity TODO
75 public FlightData(double maxAltitude, double maxVelocity, double maxAcceleration,
76 double maxMachNumber, double timeToApogee, double flightTime,
77 double groundHitVelocity, double launchRodVelocity) {
78 this.maxAltitude = maxAltitude;
79 this.maxVelocity = maxVelocity;
80 this.maxAcceleration = maxAcceleration;
81 this.maxMachNumber = maxMachNumber;
82 this.timeToApogee = timeToApogee;
83 this.flightTime = flightTime;
84 this.groundHitVelocity = groundHitVelocity;
85 this.launchRodVelocity = launchRodVelocity;
90 * Create a FlightData object with the specified branches. The resulting object is mutable.
92 * @param branches the branches.
94 public FlightData(FlightDataBranch... branches) {
97 for (FlightDataBranch b : branches)
100 calculateIntrestingValues();
107 * Returns the warning set associated with this object. This WarningSet cannot be
108 * set, so simulations must use this warning set to store their warnings.
109 * The returned WarningSet should not be modified otherwise.
111 * @return the warnings generated during this simulation.
113 public WarningSet getWarningSet() {
118 public void addBranch(FlightDataBranch branch) {
122 branches.add(branch);
124 if (branches.size() == 1) {
125 calculateIntrestingValues();
129 public int getBranchCount() {
130 return branches.size();
133 public FlightDataBranch getBranch(int n) {
134 return branches.get(n);
139 public double getMaxAltitude() {
143 public double getMaxVelocity() {
148 * NOTE: This value only takes into account flight phase.
150 public double getMaxAcceleration() {
151 return maxAcceleration;
154 public double getMaxMachNumber() {
155 return maxMachNumber;
158 public double getTimeToApogee() {
162 public double getFlightTime() {
166 public double getGroundHitVelocity() {
167 return groundHitVelocity;
170 public double getLaunchRodVelocity() {
171 return launchRodVelocity;
177 * Calculate the max. altitude/velocity/acceleration, time to apogee, flight time
178 * and ground hit velocity.
180 private void calculateIntrestingValues() {
181 if (branches.isEmpty())
184 FlightDataBranch branch = branches.get(0);
185 maxAltitude = branch.getMaximum(FlightDataType.TYPE_ALTITUDE);
186 maxVelocity = branch.getMaximum(FlightDataType.TYPE_VELOCITY_TOTAL);
187 maxMachNumber = branch.getMaximum(FlightDataType.TYPE_MACH_NUMBER);
189 flightTime = branch.getLast(FlightDataType.TYPE_TIME);
190 if (branch.getLast(FlightDataType.TYPE_ALTITUDE) < 10) {
191 groundHitVelocity = branch.getLast(FlightDataType.TYPE_VELOCITY_TOTAL);
193 groundHitVelocity = Double.NaN;
198 List<Double> time = branch.get(FlightDataType.TYPE_TIME);
199 List<Double> altitude = branch.get(FlightDataType.TYPE_ALTITUDE);
201 if (time == null || altitude == null) {
202 timeToApogee = Double.NaN;
203 maxAcceleration = Double.NaN;
207 for (Double alt : altitude) {
209 if (MathUtil.equals(alt, maxAltitude))
215 if (index < time.size())
216 timeToApogee = time.get(index);
218 timeToApogee = Double.NaN;
221 // Launch rod velocity
222 eventloop: for (FlightEvent event : branch.getEvents()) {
223 if (event.getType() == FlightEvent.Type.LAUNCHROD) {
224 double t = event.getTime();
225 List<Double> velocity = branch.get(FlightDataType.TYPE_VELOCITY_TOTAL);
226 if (velocity == null) {
229 for (int i = 0; i < velocity.size(); i++) {
230 if (time.get(i) >= t) {
231 launchRodVelocity = velocity.get(i);
238 // Max. acceleration (must be after apogee time)
239 if (branch.get(FlightDataType.TYPE_ACCELERATION_TOTAL) != null) {
240 maxAcceleration = calculateMaxAcceleration();
242 maxAcceleration = Double.NaN;
245 log.debug("Computed flight values:" +
246 " maxAltitude=" + maxAltitude +
247 " maxVelocity=" + maxVelocity +
248 " maxAcceleration=" + maxAcceleration +
249 " maxMachNumber=" + maxMachNumber +
250 " timeToApogee=" + timeToApogee +
251 " flightTime=" + flightTime +
252 " groundHitVelocity=" + groundHitVelocity +
253 " launchRodVelocity=" + launchRodVelocity);
257 public void immute() {
260 for (FlightDataBranch b : branches) {
266 public boolean isMutable() {
267 return mutable.isMutable();
273 * Find the maximum acceleration before apogee.
275 private double calculateMaxAcceleration() {
277 // End check at first recovery device deployment
278 double endTime = Double.MAX_VALUE;
280 FlightDataBranch branch = this.getBranch(0);
281 for (FlightEvent event : branch.getEvents()) {
282 if (event.getType() == FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT) {
283 if (event.getTime() < endTime) {
284 endTime = event.getTime();
289 List<Double> time = branch.get(FlightDataType.TYPE_TIME);
290 List<Double> acceleration = branch.get(FlightDataType.TYPE_ACCELERATION_TOTAL);
292 if (time == null || acceleration == null) {
298 for (int i = 0; i < time.size(); i++) {
299 if (time.get(i) >= endTime) {
302 double a = acceleration.get(i);