geodetic computations
[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().getLaunchSite().getAltitude();
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().getLaunchSite().getAltitude();
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                 gravity = status.getSimulationConditions().getGravityModel().getGravity(status.getRocketWorldPosition());
97                 
98                 // Call post-listener
99                 gravity = SimulationListenerHelper.firePostGravityModel(status, gravity);
100                 
101                 checkNaN(gravity);
102                 
103                 return gravity;
104         }
105         
106         
107
108         /**
109          * Compute the mass data to use, allowing listeners to override.
110          * 
111          * @param status        the simulation status
112          * @return                      the mass data to use
113          * @throws SimulationException  if a listener throws SimulationException
114          */
115         protected MassData calculateMassData(SimulationStatus status) throws SimulationException {
116                 MassData mass;
117                 Coordinate cg;
118                 double longitudinalInertia, rotationalInertia;
119                 
120                 // Call pre-listener
121                 mass = SimulationListenerHelper.firePreMassCalculation(status);
122                 if (mass != null) {
123                         return mass;
124                 }
125                 
126                 MassCalculator calc = status.getSimulationConditions().getMassCalculator();
127                 cg = calc.getCG(status.getConfiguration(), status.getMotorConfiguration());
128                 longitudinalInertia = calc.getLongitudinalInertia(status.getConfiguration(), status.getMotorConfiguration());
129                 rotationalInertia = calc.getRotationalInertia(status.getConfiguration(), status.getMotorConfiguration());
130                 mass = new MassData(cg, longitudinalInertia, rotationalInertia);
131                 
132                 // Call post-listener
133                 mass = SimulationListenerHelper.firePostMassCalculation(status, mass);
134                 
135                 checkNaN(mass.getCG());
136                 checkNaN(mass.getLongitudinalInertia());
137                 checkNaN(mass.getRotationalInertia());
138                 
139                 return mass;
140         }
141         
142         
143
144
145
146         /**
147          * Calculate the average thrust produced by the motors in the current configuration, allowing
148          * listeners to override.  The average is taken between <code>status.time</code> and 
149          * <code>status.time + timestep</code>.
150          * <p>
151          * TODO: HIGH:  This method does not take into account any moments generated by off-center motors.
152          *  
153          * @param status                                        the current simulation status.
154          * @param timestep                                      the time step of the current iteration.
155          * @param acceleration                          the current (approximate) acceleration
156          * @param atmosphericConditions         the current atmospheric conditions
157          * @param stepMotors                            whether to step the motors forward or work on a clone object
158          * @return                                                      the average thrust during the time step.
159          */
160         protected double calculateThrust(SimulationStatus status, double timestep,
161                         double acceleration, AtmosphericConditions atmosphericConditions,
162                         boolean stepMotors) throws SimulationException {
163                 double thrust;
164                 
165                 // Pre-listeners
166                 thrust = SimulationListenerHelper.firePreThrustCalculation(status);
167                 if (!Double.isNaN(thrust)) {
168                         return thrust;
169                 }
170                 
171                 // Iterate over the motors and calculate combined thrust
172                 MotorInstanceConfiguration configuration = status.getMotorConfiguration();
173                 if (!stepMotors) {
174                         configuration = configuration.clone();
175                 }
176                 configuration.step(status.getSimulationTime() + timestep, acceleration, atmosphericConditions);
177                 thrust = 0;
178                 for (MotorId id : configuration.getMotorIDs()) {
179                         MotorInstance motor = configuration.getMotorInstance(id);
180                         thrust += motor.getThrust();
181                 }
182                 
183                 // Post-listeners
184                 thrust = SimulationListenerHelper.firePostThrustCalculation(status, thrust);
185                 
186                 checkNaN(thrust);
187                 
188                 return thrust;
189         }
190         
191         
192         /**
193          * Check that the provided value is not NaN.
194          * 
195          * @param d                                     the double value to check.
196          * @throws BugException         if the value is NaN.
197          */
198         protected void checkNaN(double d) {
199                 if (Double.isNaN(d)) {
200                         throw new BugException("Simulation resulted in not-a-number (NaN) value, please report a bug.");
201                 }
202         }
203         
204         /**
205          * Check that the provided coordinate is not NaN.
206          * 
207          * @param c                                     the coordinate value to check.
208          * @throws BugException         if the value is NaN.
209          */
210         protected void checkNaN(Coordinate c) {
211                 if (c.isNaN()) {
212                         throw new BugException("Simulation resulted in not-a-number (NaN) value, please report a bug, c=" + c);
213                 }
214         }
215         
216         
217         /**
218          * Check that the provided quaternion is not NaN.
219          * 
220          * @param q                                     the quaternion value to check.
221          * @throws BugException         if the value is NaN.
222          */
223         protected void checkNaN(Quaternion q) {
224                 if (q.isNaN()) {
225                         throw new BugException("Simulation resulted in not-a-number (NaN) value, please report a bug, q=" + q);
226                 }
227         }
228 }