bug fixes and rocket optimization
[debian/openrocket] / src / net / sf / openrocket / simulation / GUISimulationConditions.java
1 package net.sf.openrocket.simulation;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import javax.swing.event.ChangeEvent;
7 import javax.swing.event.ChangeListener;
8
9 import net.sf.openrocket.aerodynamics.BarrowmanCalculator;
10 import net.sf.openrocket.masscalc.BasicMassCalculator;
11 import net.sf.openrocket.models.atmosphere.AtmosphericModel;
12 import net.sf.openrocket.models.atmosphere.ExtendedISAModel;
13 import net.sf.openrocket.models.gravity.BasicGravityModel;
14 import net.sf.openrocket.models.wind.PinkNoiseWindModel;
15 import net.sf.openrocket.rocketcomponent.Rocket;
16 import net.sf.openrocket.util.BugException;
17 import net.sf.openrocket.util.ChangeSource;
18 import net.sf.openrocket.util.MathUtil;
19
20 // TODO: HIGH: Move somewhere else and clean up
21 @Deprecated
22 public class GUISimulationConditions implements ChangeSource, Cloneable {
23         
24         public static final double MAX_LAUNCH_ROD_ANGLE = Math.PI / 3;
25         
26         /**
27          * The ISA standard atmosphere.
28          */
29         private static final AtmosphericModel ISA_ATMOSPHERIC_MODEL = new ExtendedISAModel();
30         
31
32         private final Rocket rocket;
33         private String motorID = null;
34         
35
36         /*
37          * NOTE:  When adding/modifying parameters, they must also be added to the
38          * equals and copyFrom methods!!
39          */
40
41         // TODO: HIGH: Fetch default values from Prefs!
42         
43         private double launchRodLength = 1;
44         
45         /** Launch rod angle > 0, radians from vertical */
46         private double launchRodAngle = 0;
47         
48         /** Launch rod direction, 0 = upwind, PI = downwind. */
49         private double launchRodDirection = 0;
50         
51
52         private double windAverage = 2.0;
53         private double windTurbulence = 0.1;
54         
55         private double launchAltitude = 0;
56         private double launchLatitude = 45;
57         
58         private boolean useISA = true;
59         private double launchTemperature = ExtendedISAModel.STANDARD_TEMPERATURE;
60         private double launchPressure = ExtendedISAModel.STANDARD_PRESSURE;
61         private AtmosphericModel atmosphericModel = null;
62         
63
64         private double timeStep = RK4SimulationStepper.RECOMMENDED_TIME_STEP;
65         private double maximumAngle = RK4SimulationStepper.RECOMMENDED_ANGLE_STEP;
66         
67         private boolean calculateExtras = true;
68         
69
70         private List<ChangeListener> listeners = new ArrayList<ChangeListener>();
71         
72         
73
74         public GUISimulationConditions(Rocket rocket) {
75                 this.rocket = rocket;
76         }
77         
78         
79
80         public Rocket getRocket() {
81                 return rocket;
82         }
83         
84         
85         public String getMotorConfigurationID() {
86                 return motorID;
87         }
88         
89         /**
90          * Set the motor configuration ID.  This must be a valid motor configuration ID of
91          * the rocket, otherwise the configuration is set to <code>null</code>.
92          * 
93          * @param id    the configuration to set.
94          */
95         public void setMotorConfigurationID(String id) {
96                 if (id != null)
97                         id = id.intern();
98                 if (!rocket.isMotorConfigurationID(id))
99                         id = null;
100                 if (id == motorID)
101                         return;
102                 motorID = id;
103                 fireChangeEvent();
104         }
105         
106         
107         public double getLaunchRodLength() {
108                 return launchRodLength;
109         }
110         
111         public void setLaunchRodLength(double launchRodLength) {
112                 if (MathUtil.equals(this.launchRodLength, launchRodLength))
113                         return;
114                 this.launchRodLength = launchRodLength;
115                 fireChangeEvent();
116         }
117         
118         
119         public double getLaunchRodAngle() {
120                 return launchRodAngle;
121         }
122         
123         public void setLaunchRodAngle(double launchRodAngle) {
124                 launchRodAngle = MathUtil.clamp(launchRodAngle, 0, MAX_LAUNCH_ROD_ANGLE);
125                 if (MathUtil.equals(this.launchRodAngle, launchRodAngle))
126                         return;
127                 this.launchRodAngle = launchRodAngle;
128                 fireChangeEvent();
129         }
130         
131         
132         public double getLaunchRodDirection() {
133                 return launchRodDirection;
134         }
135         
136         public void setLaunchRodDirection(double launchRodDirection) {
137                 launchRodDirection = MathUtil.reduce180(launchRodDirection);
138                 if (MathUtil.equals(this.launchRodDirection, launchRodDirection))
139                         return;
140                 this.launchRodDirection = launchRodDirection;
141                 fireChangeEvent();
142         }
143         
144         
145
146         public double getWindSpeedAverage() {
147                 return windAverage;
148         }
149         
150         public void setWindSpeedAverage(double windAverage) {
151                 if (MathUtil.equals(this.windAverage, windAverage))
152                         return;
153                 this.windAverage = MathUtil.max(windAverage, 0);
154                 fireChangeEvent();
155         }
156         
157         
158         public double getWindSpeedDeviation() {
159                 return windAverage * windTurbulence;
160         }
161         
162         public void setWindSpeedDeviation(double windDeviation) {
163                 if (windAverage < 0.1) {
164                         windAverage = 0.1;
165                 }
166                 setWindTurbulenceIntensity(windDeviation / windAverage);
167         }
168         
169         
170         /**
171          * Return the wind turbulence intensity (standard deviation / average).
172          * 
173          * @return  the turbulence intensity
174          */
175         public double getWindTurbulenceIntensity() {
176                 return windTurbulence;
177         }
178         
179         /**
180          * Set the wind standard deviation to match the given turbulence intensity.
181          * 
182          * @param intensity   the turbulence intensity
183          */
184         public void setWindTurbulenceIntensity(double intensity) {
185                 // Does not check equality so that setWindSpeedDeviation can be sure of event firing
186                 this.windTurbulence = intensity;
187                 fireChangeEvent();
188         }
189         
190         
191
192
193
194         public double getLaunchAltitude() {
195                 return launchAltitude;
196         }
197         
198         public void setLaunchAltitude(double altitude) {
199                 if (MathUtil.equals(this.launchAltitude, altitude))
200                         return;
201                 this.launchAltitude = altitude;
202                 fireChangeEvent();
203         }
204         
205         
206         public double getLaunchLatitude() {
207                 return launchLatitude;
208         }
209         
210         public void setLaunchLatitude(double launchLatitude) {
211                 launchLatitude = MathUtil.clamp(launchLatitude, -90, 90);
212                 if (MathUtil.equals(this.launchLatitude, launchLatitude))
213                         return;
214                 this.launchLatitude = launchLatitude;
215                 fireChangeEvent();
216         }
217         
218         
219
220
221
222         public boolean isISAAtmosphere() {
223                 return useISA;
224         }
225         
226         public void setISAAtmosphere(boolean isa) {
227                 if (isa == useISA)
228                         return;
229                 useISA = isa;
230                 fireChangeEvent();
231         }
232         
233         
234         public double getLaunchTemperature() {
235                 return launchTemperature;
236         }
237         
238         
239
240         public void setLaunchTemperature(double launchTemperature) {
241                 if (MathUtil.equals(this.launchTemperature, launchTemperature))
242                         return;
243                 this.launchTemperature = launchTemperature;
244                 this.atmosphericModel = null;
245                 fireChangeEvent();
246         }
247         
248         
249
250         public double getLaunchPressure() {
251                 return launchPressure;
252         }
253         
254         
255
256         public void setLaunchPressure(double launchPressure) {
257                 if (MathUtil.equals(this.launchPressure, launchPressure))
258                         return;
259                 this.launchPressure = launchPressure;
260                 this.atmosphericModel = null;
261                 fireChangeEvent();
262         }
263         
264         
265         /**
266          * Returns an atmospheric model corresponding to the launch conditions.  The
267          * atmospheric models may be shared between different calls.
268          * 
269          * @return      an AtmosphericModel object.
270          */
271         public AtmosphericModel getAtmosphericModel() {
272                 if (useISA) {
273                         return ISA_ATMOSPHERIC_MODEL;
274                 }
275                 if (atmosphericModel == null) {
276                         atmosphericModel = new ExtendedISAModel(launchAltitude,
277                                         launchTemperature, launchPressure);
278                 }
279                 return atmosphericModel;
280         }
281         
282         
283         public double getTimeStep() {
284                 return timeStep;
285         }
286         
287         public void setTimeStep(double timeStep) {
288                 if (MathUtil.equals(this.timeStep, timeStep))
289                         return;
290                 this.timeStep = timeStep;
291                 fireChangeEvent();
292         }
293         
294         public double getMaximumStepAngle() {
295                 return maximumAngle;
296         }
297         
298         public void setMaximumStepAngle(double maximumAngle) {
299                 maximumAngle = MathUtil.clamp(maximumAngle, 1 * Math.PI / 180, 20 * Math.PI / 180);
300                 if (MathUtil.equals(this.maximumAngle, maximumAngle))
301                         return;
302                 this.maximumAngle = maximumAngle;
303                 fireChangeEvent();
304         }
305         
306         
307
308         public boolean getCalculateExtras() {
309                 return calculateExtras;
310         }
311         
312         
313
314         public void setCalculateExtras(boolean calculateExtras) {
315                 if (this.calculateExtras == calculateExtras)
316                         return;
317                 this.calculateExtras = calculateExtras;
318                 fireChangeEvent();
319         }
320         
321         
322
323         @Override
324         public GUISimulationConditions clone() {
325                 try {
326                         GUISimulationConditions copy = (GUISimulationConditions) super.clone();
327                         copy.listeners = new ArrayList<ChangeListener>();
328                         return copy;
329                 } catch (CloneNotSupportedException e) {
330                         throw new BugException(e);
331                 }
332         }
333         
334         
335         public void copyFrom(GUISimulationConditions src) {
336                 
337                 if (this.rocket == src.rocket) {
338                         
339                         this.motorID = src.motorID;
340                         
341                 } else {
342                         
343                         if (src.rocket.hasMotors(src.motorID)) {
344                                 // Try to find a matching motor ID
345                                 String motorDesc = src.rocket.getMotorConfigurationDescription(src.motorID);
346                                 String matchID = null;
347                                 
348                                 for (String id : this.rocket.getMotorConfigurationIDs()) {
349                                         if (motorDesc.equals(this.rocket.getMotorConfigurationDescription(id))) {
350                                                 matchID = id;
351                                                 break;
352                                         }
353                                 }
354                                 
355                                 this.motorID = matchID;
356                         } else {
357                                 this.motorID = null;
358                         }
359                 }
360                 
361                 this.launchAltitude = src.launchAltitude;
362                 this.launchLatitude = src.launchLatitude;
363                 this.launchPressure = src.launchPressure;
364                 this.launchRodAngle = src.launchRodAngle;
365                 this.launchRodDirection = src.launchRodDirection;
366                 this.launchRodLength = src.launchRodLength;
367                 this.launchTemperature = src.launchTemperature;
368                 this.maximumAngle = src.maximumAngle;
369                 this.timeStep = src.timeStep;
370                 this.windAverage = src.windAverage;
371                 this.windTurbulence = src.windTurbulence;
372                 this.calculateExtras = src.calculateExtras;
373                 
374                 fireChangeEvent();
375         }
376         
377         
378
379         /**
380          * Compares whether the two simulation conditions are equal.  The two are considered
381          * equal if the rocket, motor id and all variables are equal.
382          */
383         @Override
384         public boolean equals(Object other) {
385                 if (!(other instanceof GUISimulationConditions))
386                         return false;
387                 GUISimulationConditions o = (GUISimulationConditions) other;
388                 return ((this.rocket == o.rocket) &&
389                                 this.motorID == o.motorID &&
390                                 MathUtil.equals(this.launchAltitude, o.launchAltitude) &&
391                                 MathUtil.equals(this.launchLatitude, o.launchLatitude) &&
392                                 MathUtil.equals(this.launchPressure, o.launchPressure) &&
393                                 MathUtil.equals(this.launchRodAngle, o.launchRodAngle) &&
394                                 MathUtil.equals(this.launchRodDirection, o.launchRodDirection) &&
395                                 MathUtil.equals(this.launchRodLength, o.launchRodLength) &&
396                                 MathUtil.equals(this.launchTemperature, o.launchTemperature) &&
397                                 MathUtil.equals(this.maximumAngle, o.maximumAngle) &&
398                                 MathUtil.equals(this.timeStep, o.timeStep) &&
399                                 MathUtil.equals(this.windAverage, o.windAverage) &&
400                                 MathUtil.equals(this.windTurbulence, o.windTurbulence) && this.calculateExtras == o.calculateExtras);
401         }
402         
403         /**
404          * Hashcode method compatible with {@link #equals(Object)}.
405          */
406         @Override
407         public int hashCode() {
408                 if (motorID == null)
409                         return rocket.hashCode();
410                 return rocket.hashCode() + motorID.hashCode();
411         }
412         
413         @Override
414         public void addChangeListener(ChangeListener listener) {
415                 listeners.add(listener);
416         }
417         
418         @Override
419         public void removeChangeListener(ChangeListener listener) {
420                 listeners.remove(listener);
421         }
422         
423         private final ChangeEvent event = new ChangeEvent(this);
424         
425         private void fireChangeEvent() {
426                 ChangeListener[] array = listeners.toArray(new ChangeListener[0]);
427                 
428                 for (int i = array.length - 1; i >= 0; i--) {
429                         array[i].stateChanged(event);
430                 }
431         }
432         
433         
434         // TODO: HIGH: Clean up
435         @Deprecated
436         public SimulationConditions toSimulationConditions() {
437                 SimulationConditions conditions = new SimulationConditions();
438                 
439                 conditions.setRocket((Rocket) getRocket().copy());
440                 conditions.setMotorConfigurationID(getMotorConfigurationID());
441                 conditions.setLaunchRodLength(getLaunchRodLength());
442                 conditions.setLaunchRodAngle(getLaunchRodAngle());
443                 conditions.setLaunchRodDirection(getLaunchRodDirection());
444                 conditions.setLaunchAltitude(getLaunchAltitude());
445                 conditions.setLaunchLatitude(getLaunchLatitude());
446                 
447                 PinkNoiseWindModel windModel = new PinkNoiseWindModel();
448                 // FIXME:  Random seed
449                 windModel.setSeed(1);
450                 windModel.setAverage(getWindSpeedAverage());
451                 windModel.setStandardDeviation(getWindSpeedDeviation());
452                 conditions.setWindModel(windModel);
453                 
454                 conditions.setAtmosphericModel(getAtmosphericModel());
455                 
456                 BasicGravityModel gravityModel = new BasicGravityModel(getLaunchLatitude());
457                 conditions.setGravityModel(gravityModel);
458                 
459                 conditions.setAerodynamicCalculator(new BarrowmanCalculator());
460                 conditions.setMassCalculator(new BasicMassCalculator());
461                 
462                 conditions.setTimeStep(getTimeStep());
463                 conditions.setMaximumAngleStep(getMaximumStepAngle());
464                 
465                 conditions.setCalculateExtras(getCalculateExtras());
466                 
467                 return conditions;
468         }
469         
470 }