moving to core/
[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         
53         
54         /**
55          * Create a FlightData object with no content.  The resulting object is mutable.
56          */
57         public FlightData() {
58                 
59         }
60         
61         
62         /**
63          * Construct a FlightData object with no data branches but the specified
64          * summary information.  The resulting object is mutable.
65          * 
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
74          */
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;
86         }
87         
88         
89         /**
90          * Create a FlightData object with the specified branches.  The resulting object is mutable.
91          * 
92          * @param branches      the branches.
93          */
94         public FlightData(FlightDataBranch... branches) {
95                 this();
96                 
97                 for (FlightDataBranch b : branches)
98                         this.addBranch(b);
99                 
100                 calculateIntrestingValues();
101         }
102         
103         
104
105
106         /**
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.
110          * 
111          * @return      the warnings generated during this simulation.
112          */
113         public WarningSet getWarningSet() {
114                 return warnings;
115         }
116         
117         
118         public void addBranch(FlightDataBranch branch) {
119                 mutable.check();
120                 
121                 branch.immute();
122                 branches.add(branch);
123                 
124                 if (branches.size() == 1) {
125                         calculateIntrestingValues();
126                 }
127         }
128         
129         public int getBranchCount() {
130                 return branches.size();
131         }
132         
133         public FlightDataBranch getBranch(int n) {
134                 return branches.get(n);
135         }
136         
137         
138
139         public double getMaxAltitude() {
140                 return maxAltitude;
141         }
142         
143         public double getMaxVelocity() {
144                 return maxVelocity;
145         }
146         
147         /**
148          * NOTE:  This value only takes into account flight phase.
149          */
150         public double getMaxAcceleration() {
151                 return maxAcceleration;
152         }
153         
154         public double getMaxMachNumber() {
155                 return maxMachNumber;
156         }
157         
158         public double getTimeToApogee() {
159                 return timeToApogee;
160         }
161         
162         public double getFlightTime() {
163                 return flightTime;
164         }
165         
166         public double getGroundHitVelocity() {
167                 return groundHitVelocity;
168         }
169         
170         public double getLaunchRodVelocity() {
171                 return launchRodVelocity;
172         }
173         
174         
175
176         /**
177          * Calculate the max. altitude/velocity/acceleration, time to apogee, flight time
178          * and ground hit velocity.
179          */
180         private void calculateIntrestingValues() {
181                 if (branches.isEmpty())
182                         return;
183                 
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);
188                 
189                 flightTime = branch.getLast(FlightDataType.TYPE_TIME);
190                 if (branch.getLast(FlightDataType.TYPE_ALTITUDE) < 10) {
191                         groundHitVelocity = branch.getLast(FlightDataType.TYPE_VELOCITY_TOTAL);
192                 } else {
193                         groundHitVelocity = Double.NaN;
194                 }
195                 
196
197                 // Time to apogee
198                 List<Double> time = branch.get(FlightDataType.TYPE_TIME);
199                 List<Double> altitude = branch.get(FlightDataType.TYPE_ALTITUDE);
200                 
201                 if (time == null || altitude == null) {
202                         timeToApogee = Double.NaN;
203                         maxAcceleration = Double.NaN;
204                         return;
205                 }
206                 int index = 0;
207                 for (Double alt : altitude) {
208                         if (alt != null) {
209                                 if (MathUtil.equals(alt, maxAltitude))
210                                         break;
211                         }
212                         
213                         index++;
214                 }
215                 if (index < time.size())
216                         timeToApogee = time.get(index);
217                 else
218                         timeToApogee = Double.NaN;
219                 
220
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) {
227                                         break;
228                                 }
229                                 for (int i = 0; i < velocity.size(); i++) {
230                                         if (time.get(i) >= t) {
231                                                 launchRodVelocity = velocity.get(i);
232                                                 break eventloop;
233                                         }
234                                 }
235                         }
236                 }
237                 
238                 // Max. acceleration (must be after apogee time)
239                 if (branch.get(FlightDataType.TYPE_ACCELERATION_TOTAL) != null) {
240                         maxAcceleration = calculateMaxAcceleration();
241                 } else {
242                         maxAcceleration = Double.NaN;
243                 }
244                 
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);
254         }
255         
256         
257         public void immute() {
258                 mutable.immute();
259                 warnings.immute();
260                 for (FlightDataBranch b : branches) {
261                         b.immute();
262                 }
263         }
264         
265         
266         public boolean isMutable() {
267                 return mutable.isMutable();
268         }
269         
270         
271
272         /**
273          * Find the maximum acceleration before apogee.
274          */
275         private double calculateMaxAcceleration() {
276                 
277                 // End check at first recovery device deployment
278                 double endTime = Double.MAX_VALUE;
279                 
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();
285                                 }
286                         }
287                 }
288                 
289                 List<Double> time = branch.get(FlightDataType.TYPE_TIME);
290                 List<Double> acceleration = branch.get(FlightDataType.TYPE_ACCELERATION_TOTAL);
291                 
292                 if (time == null || acceleration == null) {
293                         return Double.NaN;
294                 }
295                 
296                 double max = 0;
297                 
298                 for (int i = 0; i < time.size(); i++) {
299                         if (time.get(i) >= endTime) {
300                                 break;
301                         }
302                         double a = acceleration.get(i);
303                         if (a > max)
304                                 max = a;
305                 }
306                 
307                 return max;
308         }
309 }