create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / simulation / FlightData.java
1 package net.sf.openrocket.simulation;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
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;
11
12 /**
13  * A collection of various flight data.  This is the result of a simulation, or importing
14  * data into the software.  The data includes:
15  * <ul>
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
19  * </ul> 
20  * <p>
21  * A FlightData object can be made immutable by calling {@link #immute()}.
22  * 
23  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
24  */
25 public class FlightData {
26         private static final LogHelper log = Application.getLogger();
27         
28         /**
29          * An immutable FlightData object with NaN data.
30          */
31         public static final FlightData NaN_DATA;
32         static {
33                 FlightData data = new FlightData();
34                 data.immute();
35                 NaN_DATA = data;
36         }
37         
38         private Mutable mutable = new Mutable();
39         
40         private final ArrayList<FlightDataBranch> branches = new ArrayList<FlightDataBranch>();
41         
42         private final WarningSet warnings = new WarningSet();
43         
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;
53         
54         
55         /**
56          * Create a FlightData object with no content.  The resulting object is mutable.
57          */
58         public FlightData() {
59                 
60         }
61         
62         
63         /**
64          * Construct a FlightData object with no data branches but the specified
65          * summary information.  The resulting object is mutable.
66          * 
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
76          */
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;
89         }
90         
91         
92         /**
93          * Create a FlightData object with the specified branches.  The resulting object is mutable.
94          * 
95          * @param branches      the branches.
96          */
97         public FlightData(FlightDataBranch... branches) {
98                 this();
99                 
100                 for (FlightDataBranch b : branches)
101                         this.addBranch(b);
102                 
103                 calculateIntrestingValues();
104         }
105         
106         
107
108
109         /**
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.
113          * 
114          * @return      the warnings generated during this simulation.
115          */
116         public WarningSet getWarningSet() {
117                 return warnings;
118         }
119         
120         
121         public void addBranch(FlightDataBranch branch) {
122                 mutable.check();
123                 
124                 branch.immute();
125                 branches.add(branch);
126                 
127                 if (branches.size() == 1) {
128                         calculateIntrestingValues();
129                 }
130         }
131         
132         public int getBranchCount() {
133                 return branches.size();
134         }
135         
136         public FlightDataBranch getBranch(int n) {
137                 return branches.get(n);
138         }
139         
140         
141
142         public double getMaxAltitude() {
143                 return maxAltitude;
144         }
145         
146         public double getMaxVelocity() {
147                 return maxVelocity;
148         }
149         
150         /**
151          * NOTE:  This value only takes into account flight phase.
152          */
153         public double getMaxAcceleration() {
154                 return maxAcceleration;
155         }
156         
157         public double getMaxMachNumber() {
158                 return maxMachNumber;
159         }
160         
161         public double getTimeToApogee() {
162                 return timeToApogee;
163         }
164         
165         public double getFlightTime() {
166                 return flightTime;
167         }
168         
169         public double getGroundHitVelocity() {
170                 return groundHitVelocity;
171         }
172         
173         public double getLaunchRodVelocity() {
174                 return launchRodVelocity;
175         }
176         
177
178         public double getDeploymentVelocity() {
179                 return deploymentVelocity;
180         }
181
182
183         /**
184          * Calculate the max. altitude/velocity/acceleration, time to apogee, flight time
185          * and ground hit velocity.
186          */
187         private void calculateIntrestingValues() {
188                 if (branches.isEmpty())
189                         return;
190                 
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);
195                 
196                 flightTime = branch.getLast(FlightDataType.TYPE_TIME);
197                 if (branch.getLast(FlightDataType.TYPE_ALTITUDE) < 10) {
198                         groundHitVelocity = branch.getLast(FlightDataType.TYPE_VELOCITY_TOTAL);
199                 } else {
200                         groundHitVelocity = Double.NaN;
201                 }
202                 
203
204                 // Time to apogee
205                 List<Double> time = branch.get(FlightDataType.TYPE_TIME);
206                 List<Double> altitude = branch.get(FlightDataType.TYPE_ALTITUDE);
207                 
208                 if (time == null || altitude == null) {
209                         timeToApogee = Double.NaN;
210                         maxAcceleration = Double.NaN;
211                         return;
212                 }
213                 int index = 0;
214                 for (Double alt : altitude) {
215                         if (alt != null) {
216                                 if (MathUtil.equals(alt, maxAltitude))
217                                         break;
218                         }
219                         
220                         index++;
221                 }
222                 if (index < time.size())
223                         timeToApogee = time.get(index);
224                 else
225                         timeToApogee = Double.NaN;
226                 
227
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);
238                         }
239                 }
240                 
241                 // Max. acceleration (must be after apogee time)
242                 if (branch.get(FlightDataType.TYPE_ACCELERATION_TOTAL) != null) {
243                         maxAcceleration = calculateMaxAcceleration();
244                 } else {
245                         maxAcceleration = Double.NaN;
246                 }
247                 
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);
257         }
258         
259         
260         public void immute() {
261                 mutable.immute();
262                 warnings.immute();
263                 for (FlightDataBranch b : branches) {
264                         b.immute();
265                 }
266         }
267         
268         
269         public boolean isMutable() {
270                 return mutable.isMutable();
271         }
272         
273         
274
275         /**
276          * Find the maximum acceleration before apogee.
277          */
278         private double calculateMaxAcceleration() {
279                 
280                 // End check at first recovery device deployment
281                 double endTime = Double.MAX_VALUE;
282                 
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();
288                                 }
289                         }
290                 }
291                 
292                 List<Double> time = branch.get(FlightDataType.TYPE_TIME);
293                 List<Double> acceleration = branch.get(FlightDataType.TYPE_ACCELERATION_TOTAL);
294                 
295                 if (time == null || acceleration == null) {
296                         return Double.NaN;
297                 }
298                 
299                 double max = 0;
300                 
301                 for (int i = 0; i < time.size(); i++) {
302                         if (time.get(i) >= endTime) {
303                                 break;
304                         }
305                         double a = acceleration.get(i);
306                         if (a > max)
307                                 max = a;
308                 }
309                 
310                 return max;
311         }
312 }