85be91201282613b629491f40c9f82eb53e1ca3d
[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;
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                 Configuration configuration = status.getConfiguration();
172                 
173                 // Iterate over the motors and calculate combined thrust
174                 MotorInstanceConfiguration mic = status.getMotorConfiguration();
175                 if (!stepMotors) {
176                         mic = mic.clone();
177                 }
178                 mic.step(status.getSimulationTime() + timestep, acceleration, atmosphericConditions);
179                 thrust = 0;
180                 for (MotorId id : mic.getMotorIDs()) {
181                         if (configuration.isComponentActive((RocketComponent) mic.getMotorMount(id))) {
182                                 MotorInstance motor = mic.getMotorInstance(id);
183                                 thrust += motor.getThrust();
184                         }
185                 }
186                 
187                 // Post-listeners
188                 thrust = SimulationListenerHelper.firePostThrustCalculation(status, thrust);
189                 
190                 checkNaN(thrust);
191                 
192                 return thrust;
193         }
194         
195         
196         /**
197          * Check that the provided value is not NaN.
198          * 
199          * @param d                                     the double value to check.
200          * @throws BugException         if the value is NaN.
201          */
202         protected void checkNaN(double d) {
203                 if (Double.isNaN(d)) {
204                         throw new BugException("Simulation resulted in not-a-number (NaN) value, please report a bug.");
205                 }
206         }
207         
208         /**
209          * Check that the provided coordinate is not NaN.
210          * 
211          * @param c                                     the coordinate value to check.
212          * @throws BugException         if the value is NaN.
213          */
214         protected void checkNaN(Coordinate c) {
215                 if (c.isNaN()) {
216                         throw new BugException("Simulation resulted in not-a-number (NaN) value, please report a bug, c=" + c);
217                 }
218         }
219         
220         
221         /**
222          * Check that the provided quaternion is not NaN.
223          * 
224          * @param q                                     the quaternion value to check.
225          * @throws BugException         if the value is NaN.
226          */
227         protected void checkNaN(Quaternion q) {
228                 if (q.isNaN()) {
229                         throw new BugException("Simulation resulted in not-a-number (NaN) value, please report a bug, q=" + q);
230                 }
231         }
232 }