7b33ae04874937e02d3ac70757b66f3c837a31b4
[debian/openrocket] / src / net / sf / openrocket / simulation / AbstractSimulationStepper.java
1 package net.sf.openrocket.simulation;
2
3 import net.sf.openrocket.masscalc.MassCalculator;
4 import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
5 import net.sf.openrocket.motor.MotorId;
6 import net.sf.openrocket.motor.MotorInstance;
7 import net.sf.openrocket.motor.MotorInstanceConfiguration;
8 import net.sf.openrocket.simulation.exception.SimulationException;
9 import net.sf.openrocket.simulation.listeners.SimulationListenerHelper;
10 import net.sf.openrocket.util.BugException;
11 import net.sf.openrocket.util.Coordinate;
12 import net.sf.openrocket.util.Quaternion;
13
14 public abstract class AbstractSimulationStepper implements SimulationStepper {
15         
16         /**
17          * Compute the atmospheric conditions, allowing listeners to override.
18          * 
19          * @param status        the simulation status
20          * @return                      the atmospheric conditions to use
21          * @throws SimulationException  if a listener throws SimulationException
22          */
23         protected AtmosphericConditions modelAtmosphericConditions(SimulationStatus status) throws SimulationException {
24                 AtmosphericConditions conditions;
25                 
26                 // Call pre-listener
27                 conditions = SimulationListenerHelper.firePreAtmosphericModel(status);
28                 if (conditions != null) {
29                         return conditions;
30                 }
31                 
32                 // Compute conditions
33                 double altitude = status.getRocketPosition().z + status.getSimulationConditions().getLaunchAltitude();
34                 conditions = status.getSimulationConditions().getAtmosphericModel().getConditions(altitude);
35                 
36                 // Call post-listener
37                 conditions = SimulationListenerHelper.firePostAtmosphericModel(status, conditions);
38                 
39                 checkNaN(conditions.getPressure());
40                 checkNaN(conditions.getTemperature());
41                 
42                 return conditions;
43         }
44         
45         
46
47         /**
48          * Compute the wind to use, allowing listeners to override.
49          * 
50          * @param status        the simulation status
51          * @return                      the wind conditions to use
52          * @throws SimulationException  if a listener throws SimulationException
53          */
54         protected Coordinate modelWindVelocity(SimulationStatus status) throws SimulationException {
55                 Coordinate wind;
56                 
57                 // Call pre-listener
58                 wind = SimulationListenerHelper.firePreWindModel(status);
59                 if (wind != null) {
60                         return wind;
61                 }
62                 
63                 // Compute conditions
64                 double altitude = status.getRocketPosition().z + status.getSimulationConditions().getLaunchAltitude();
65                 wind = status.getSimulationConditions().getWindModel().getWindVelocity(status.getSimulationTime(), altitude);
66                 
67                 // Call post-listener
68                 wind = SimulationListenerHelper.firePostWindModel(status, wind);
69                 
70                 checkNaN(wind);
71                 
72                 return wind;
73         }
74         
75         
76
77         /**
78          * Compute the gravity to use, allowing listeners to override.
79          * 
80          * @param status        the simulation status
81          * @return                      the gravitational acceleration to use
82          * @throws SimulationException  if a listener throws SimulationException
83          */
84         protected double modelGravity(SimulationStatus status) throws SimulationException {
85                 double gravity;
86                 
87                 // Call pre-listener
88                 gravity = SimulationListenerHelper.firePreGravityModel(status);
89                 if (!Double.isNaN(gravity)) {
90                         return gravity;
91                 }
92                 
93                 // Compute conditions
94                 double altitude = status.getRocketPosition().z + status.getSimulationConditions().getLaunchAltitude();
95                 gravity = status.getSimulationConditions().getGravityModel().getGravity(altitude);
96                 
97                 // Call post-listener
98                 gravity = SimulationListenerHelper.firePostGravityModel(status, gravity);
99                 
100                 checkNaN(gravity);
101                 
102                 return gravity;
103         }
104         
105         
106
107         /**
108          * Compute the mass data to use, allowing listeners to override.
109          * 
110          * @param status        the simulation status
111          * @return                      the mass data to use
112          * @throws SimulationException  if a listener throws SimulationException
113          */
114         protected MassData calculateMassData(SimulationStatus status) throws SimulationException {
115                 MassData mass;
116                 Coordinate cg;
117                 double longitudinalInertia, rotationalInertia;
118                 
119                 // Call pre-listener
120                 mass = SimulationListenerHelper.firePreMassCalculation(status);
121                 if (mass != null) {
122                         return mass;
123                 }
124                 
125                 MassCalculator calc = status.getSimulationConditions().getMassCalculator();
126                 cg = calc.getCG(status.getConfiguration(), status.getMotorConfiguration());
127                 longitudinalInertia = calc.getLongitudinalInertia(status.getConfiguration(), status.getMotorConfiguration());
128                 rotationalInertia = calc.getRotationalInertia(status.getConfiguration(), status.getMotorConfiguration());
129                 mass = new MassData(cg, longitudinalInertia, rotationalInertia);
130                 
131                 // Call post-listener
132                 mass = SimulationListenerHelper.firePostMassCalculation(status, mass);
133                 
134                 checkNaN(mass.getCG());
135                 checkNaN(mass.getLongitudinalInertia());
136                 checkNaN(mass.getRotationalInertia());
137                 
138                 return mass;
139         }
140         
141         
142
143
144
145         /**
146          * Calculate the average thrust produced by the motors in the current configuration, allowing
147          * listeners to override.  The average is taken between <code>status.time</code> and 
148          * <code>status.time + timestep</code>.
149          * <p>
150          * TODO: HIGH:  This method does not take into account any moments generated by off-center motors.
151          *  
152          * @param status                                        the current simulation status.
153          * @param timestep                                      the time step of the current iteration.
154          * @param acceleration                          the current (approximate) acceleration
155          * @param atmosphericConditions         the current atmospheric conditions
156          * @param stepMotors                            whether to step the motors forward or work on a clone object
157          * @return                                                      the average thrust during the time step.
158          */
159         protected double calculateThrust(SimulationStatus status, double timestep,
160                         double acceleration, AtmosphericConditions atmosphericConditions,
161                         boolean stepMotors) throws SimulationException {
162                 double thrust;
163                 
164                 // Pre-listeners
165                 thrust = SimulationListenerHelper.firePreThrustCalculation(status);
166                 if (!Double.isNaN(thrust)) {
167                         return thrust;
168                 }
169                 
170                 // Iterate over the motors and calculate combined thrust
171                 MotorInstanceConfiguration configuration = status.getMotorConfiguration();
172                 if (!stepMotors) {
173                         configuration = configuration.clone();
174                 }
175                 configuration.step(status.getSimulationTime() + timestep, acceleration, atmosphericConditions);
176                 thrust = 0;
177                 for (MotorId id : configuration.getMotorIDs()) {
178                         MotorInstance motor = configuration.getMotorInstance(id);
179                         thrust += motor.getThrust();
180                 }
181                 
182                 // Post-listeners
183                 thrust = SimulationListenerHelper.firePostThrustCalculation(status, thrust);
184                 
185                 checkNaN(thrust);
186                 
187                 return thrust;
188         }
189         
190         
191         /**
192          * Check that the provided value is not NaN.
193          * 
194          * @param d                                     the double value to check.
195          * @throws BugException         if the value is NaN.
196          */
197         protected void checkNaN(double d) {
198                 if (Double.isNaN(d)) {
199                         throw new BugException("Simulation resulted in not-a-number (NaN) value, please report a bug.");
200                 }
201         }
202         
203         /**
204          * Check that the provided coordinate is not NaN.
205          * 
206          * @param c                                     the coordinate value to check.
207          * @throws BugException         if the value is NaN.
208          */
209         protected void checkNaN(Coordinate c) {
210                 if (c.isNaN()) {
211                         throw new BugException("Simulation resulted in not-a-number (NaN) value, please report a bug.");
212                 }
213         }
214         
215         
216         /**
217          * Check that the provided quaternion is not NaN.
218          * 
219          * @param q                                     the quaternion value to check.
220          * @throws BugException         if the value is NaN.
221          */
222         protected void checkNaN(Quaternion q) {
223                 if (q.isNaN()) {
224                         throw new BugException("Simulation resulted in not-a-number (NaN) value, please report a bug.");
225                 }
226         }
227 }