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