Added gravitational acceleration and propellant mass datatypes.
[debian/openrocket] / core / src / net / sf / openrocket / masscalc / BasicMassCalculator.java
1 package net.sf.openrocket.masscalc;
2
3 import static net.sf.openrocket.util.MathUtil.pow2;
4
5 import java.util.ArrayList;
6 import java.util.HashMap;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.Map;
10
11 import net.sf.openrocket.motor.Motor;
12 import net.sf.openrocket.motor.MotorId;
13 import net.sf.openrocket.motor.MotorInstance;
14 import net.sf.openrocket.motor.MotorInstanceConfiguration;
15 import net.sf.openrocket.rocketcomponent.Configuration;
16 import net.sf.openrocket.rocketcomponent.MotorMount;
17 import net.sf.openrocket.rocketcomponent.RocketComponent;
18 import net.sf.openrocket.util.Coordinate;
19 import net.sf.openrocket.util.MathUtil;
20
21 public class BasicMassCalculator extends AbstractMassCalculator {
22         
23         private static final double MIN_MASS = 0.001 * MathUtil.EPSILON;
24         
25
26         /*
27          * Cached data.  All CG data is in absolute coordinates.  All moments of inertia
28          * are relative to their respective CG.
29          */
30         private Coordinate[] cgCache = null;
31         private double longitudinalInertiaCache[] = null;
32         private double rotationalInertiaCache[] = null;
33         
34         
35
36         //////////////////  Mass property calculations  ///////////////////
37         
38
39         /**
40          * Return the CG of the rocket with the specified motor status (no motors,
41          * ignition, burnout).
42          */
43         @Override
44         public Coordinate getCG(Configuration configuration, MassCalcType type) {
45                 checkCache(configuration);
46                 calculateStageCache(configuration);
47                 
48                 Coordinate totalCG = null;
49                 
50                 // Stage contribution
51                 for (int stage : configuration.getActiveStages()) {
52                         totalCG = cgCache[stage].average(totalCG);
53                 }
54                 
55                 if (totalCG == null)
56                         totalCG = Coordinate.NUL;
57                 
58                 // Add motor CGs
59                 String motorId = configuration.getMotorConfigurationID();
60                 if (type != MassCalcType.NO_MOTORS && motorId != null) {
61                         Iterator<MotorMount> iterator = configuration.motorIterator();
62                         while (iterator.hasNext()) {
63                                 MotorMount mount = iterator.next();
64                                 RocketComponent comp = (RocketComponent) mount;
65                                 Motor motor = mount.getMotor(motorId);
66                                 if (motor == null)
67                                         continue;
68                                 
69                                 Coordinate motorCG = type.getCG(motor).add(mount.getMotorPosition(motorId));
70                                 Coordinate[] cgs = comp.toAbsolute(motorCG);
71                                 for (Coordinate cg : cgs) {
72                                         totalCG = totalCG.average(cg);
73                                 }
74                         }
75                 }
76                 
77                 return totalCG;
78         }
79         
80         
81
82
83         /**
84          * Return the CG of the rocket with the provided motor configuration.
85          */
86         @Override
87         public Coordinate getCG(Configuration configuration, MotorInstanceConfiguration motors) {
88                 checkCache(configuration);
89                 calculateStageCache(configuration);
90                 
91                 Coordinate totalCG = getCG(configuration, MassCalcType.NO_MOTORS);
92                 
93                 // Add motor CGs
94                 if (motors != null) {
95                         for (MotorId id : motors.getMotorIDs()) {
96                                 int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber();
97                                 if (configuration.isStageActive(stage)) {
98                                         
99                                         MotorInstance motor = motors.getMotorInstance(id);
100                                         Coordinate position = motors.getMotorPosition(id);
101                                         Coordinate cg = motor.getCG().add(position);
102                                         totalCG = totalCG.average(cg);
103                                         
104                                 }
105                         }
106                 }
107                 return totalCG;
108         }
109         
110         /**
111          * Return the longitudinal inertia of the rocket with the specified motor instance
112          * configuration.
113          * 
114          * @param configuration         the current motor instance configuration
115          * @return                                      the longitudinal inertia of the rocket
116          */
117         @Override
118         public double getLongitudinalInertia(Configuration configuration, MotorInstanceConfiguration motors) {
119                 checkCache(configuration);
120                 calculateStageCache(configuration);
121                 
122                 final Coordinate totalCG = getCG(configuration, motors);
123                 double totalInertia = 0;
124                 
125                 // Stages
126                 for (int stage : configuration.getActiveStages()) {
127                         Coordinate stageCG = cgCache[stage];
128                         
129                         totalInertia += (longitudinalInertiaCache[stage] +
130                                         stageCG.weight * MathUtil.pow2(stageCG.x - totalCG.x));
131                 }
132                 
133
134                 // Motors
135                 if (motors != null) {
136                         for (MotorId id : motors.getMotorIDs()) {
137                                 int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber();
138                                 if (configuration.isStageActive(stage)) {
139                                         MotorInstance motor = motors.getMotorInstance(id);
140                                         Coordinate position = motors.getMotorPosition(id);
141                                         Coordinate cg = motor.getCG().add(position);
142                                         
143                                         double inertia = motor.getLongitudinalInertia();
144                                         totalInertia += inertia + cg.weight * MathUtil.pow2(cg.x - totalCG.x);
145                                 }
146                         }
147                 }
148                 
149                 return totalInertia;
150         }
151         
152         
153
154         /**
155          * Return the rotational inertia of the rocket with the specified motor instance
156          * configuration.
157          * 
158          * @param configuration         the current motor instance configuration
159          * @return                                      the rotational inertia of the rocket
160          */
161         @Override
162         public double getRotationalInertia(Configuration configuration, MotorInstanceConfiguration motors) {
163                 checkCache(configuration);
164                 calculateStageCache(configuration);
165                 
166                 final Coordinate totalCG = getCG(configuration, motors);
167                 double totalInertia = 0;
168                 
169                 // Stages
170                 for (int stage : configuration.getActiveStages()) {
171                         Coordinate stageCG = cgCache[stage];
172                         
173                         totalInertia += (rotationalInertiaCache[stage] +
174                                         stageCG.weight * (MathUtil.pow2(stageCG.y - totalCG.y) +
175                                                         MathUtil.pow2(stageCG.z - totalCG.z)));
176                 }
177                 
178
179                 // Motors
180                 if (motors != null) {
181                         for (MotorId id : motors.getMotorIDs()) {
182                                 int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber();
183                                 if (configuration.isStageActive(stage)) {
184                                         MotorInstance motor = motors.getMotorInstance(id);
185                                         Coordinate position = motors.getMotorPosition(id);
186                                         Coordinate cg = motor.getCG().add(position);
187                                         
188                                         double inertia = motor.getRotationalInertia();
189                                         totalInertia += inertia + cg.weight * (MathUtil.pow2(cg.y - totalCG.y) +
190                                                         MathUtil.pow2(cg.z - totalCG.z));
191                                 }
192                         }
193                 }
194                 
195                 return totalInertia;
196         }
197         
198         /**
199          * Return the total mass of the motors
200          * 
201          * @param configuration         the current motor instance configuration
202          * @return                                      the total mass of all motors
203          */
204         @Override
205         public double getPropellantMass(Configuration configuration, MotorInstanceConfiguration motors){
206                 double mass = 0;
207                                 
208                 // add up the masses of all motors in the rocket
209                 if (motors != null) {
210                         for (MotorId id : motors.getMotorIDs()) {
211                                 MotorInstance motor = motors.getMotorInstance(id);                                      
212                                 mass = mass + motor.getCG().weight - motor.getParentMotor().getEmptyCG().weight;
213                         }
214                 }
215                 return mass;
216         }
217         
218         @Override
219         public Map<RocketComponent, Coordinate> getCGAnalysis(Configuration configuration, MassCalcType type) {
220                 checkCache(configuration);
221                 calculateStageCache(configuration);
222                 
223                 Map<RocketComponent, Coordinate> map = new HashMap<RocketComponent, Coordinate>();
224                 
225                 for (RocketComponent c : configuration) {
226                         Coordinate[] cgs = c.toAbsolute(c.getCG());
227                         Coordinate totalCG = Coordinate.NUL;
228                         for (Coordinate cg : cgs) {
229                                 totalCG = totalCG.average(cg);
230                         }
231                         map.put(c, totalCG);
232                 }
233                 
234                 map.put(configuration.getRocket(), getCG(configuration, type));
235                 
236                 return map;
237         }
238         
239         ////////  Cache computations  ////////
240         
241         private void calculateStageCache(Configuration config) {
242                 if (cgCache == null) {
243                         
244                         int stages = config.getRocket().getStageCount();
245                         
246                         cgCache = new Coordinate[stages];
247                         longitudinalInertiaCache = new double[stages];
248                         rotationalInertiaCache = new double[stages];
249                         
250                         for (int i = 0; i < stages; i++) {
251                                 RocketComponent stage = config.getRocket().getChild(i);
252                                 MassData data = calculateAssemblyMassData(stage);
253                                 cgCache[i] = stage.toAbsolute(data.cg)[0];
254                                 longitudinalInertiaCache[i] = data.longitudinalInertia;
255                                 rotationalInertiaCache[i] = data.rotationalInetria;
256                         }
257                         
258                 }
259         }
260         
261         
262
263         /**
264          * Returns the mass and inertia data for this component and all subcomponents.
265          * The inertia is returned relative to the CG, and the CG is in the coordinates
266          * of the specified component, not global coordinates.
267          */
268         private MassData calculateAssemblyMassData(RocketComponent parent) {
269                 MassData parentData = new MassData();
270                 
271                 // Calculate data for this component
272                 parentData.cg = parent.getComponentCG();
273                 if (parentData.cg.weight < MIN_MASS)
274                         parentData.cg = parentData.cg.setWeight(MIN_MASS);
275                 
276
277                 // Override only this component's data
278                 if (!parent.getOverrideSubcomponents()) {
279                         if (parent.isMassOverridden())
280                                 parentData.cg = parentData.cg.setWeight(MathUtil.max(parent.getOverrideMass(), MIN_MASS));
281                         if (parent.isCGOverridden())
282                                 parentData.cg = parentData.cg.setXYZ(parent.getOverrideCG());
283                 }
284                 
285                 parentData.longitudinalInertia = parent.getLongitudinalUnitInertia() * parentData.cg.weight;
286                 parentData.rotationalInetria = parent.getRotationalUnitInertia() * parentData.cg.weight;
287                 
288
289                 // Combine data for subcomponents
290                 for (RocketComponent sibling : parent.getChildren()) {
291                         Coordinate combinedCG;
292                         double dx2, dr2;
293                         
294                         // Compute data of sibling
295                         MassData siblingData = calculateAssemblyMassData(sibling);
296                         Coordinate[] siblingCGs = sibling.toRelative(siblingData.cg, parent);
297                         
298                         for (Coordinate siblingCG : siblingCGs) {
299                                 
300                                 // Compute CG of this + sibling
301                                 combinedCG = parentData.cg.average(siblingCG);
302                                 
303                                 // Add effect of this CG change to parent inertia
304                                 dx2 = pow2(parentData.cg.x - combinedCG.x);
305                                 parentData.longitudinalInertia += parentData.cg.weight * dx2;
306                                 
307                                 dr2 = pow2(parentData.cg.y - combinedCG.y) + pow2(parentData.cg.z - combinedCG.z);
308                                 parentData.rotationalInetria += parentData.cg.weight * dr2;
309                                 
310
311                                 // Add inertia of sibling
312                                 parentData.longitudinalInertia += siblingData.longitudinalInertia;
313                                 parentData.rotationalInetria += siblingData.rotationalInetria;
314                                 
315                                 // Add effect of sibling CG change
316                                 dx2 = pow2(siblingData.cg.x - combinedCG.x);
317                                 parentData.longitudinalInertia += siblingData.cg.weight * dx2;
318                                 
319                                 dr2 = pow2(siblingData.cg.y - combinedCG.y) + pow2(siblingData.cg.z - combinedCG.z);
320                                 parentData.rotationalInetria += siblingData.cg.weight * dr2;
321                                 
322                                 // Set combined CG
323                                 parentData.cg = combinedCG;
324                         }
325                 }
326                 
327                 // Override total data
328                 if (parent.getOverrideSubcomponents()) {
329                         if (parent.isMassOverridden()) {
330                                 double oldMass = parentData.cg.weight;
331                                 double newMass = MathUtil.max(parent.getOverrideMass(), MIN_MASS);
332                                 parentData.longitudinalInertia = parentData.longitudinalInertia * newMass / oldMass;
333                                 parentData.rotationalInetria = parentData.rotationalInetria * newMass / oldMass;
334                                 parentData.cg = parentData.cg.setWeight(newMass);
335                         }
336                         if (parent.isCGOverridden()) {
337                                 double oldx = parentData.cg.x;
338                                 double newx = parent.getOverrideCGX();
339                                 parentData.longitudinalInertia += parentData.cg.weight * pow2(oldx - newx);
340                                 parentData.cg = parentData.cg.setX(newx);
341                         }
342                 }
343                 
344                 return parentData;
345         }
346         
347         
348         private static class MassData {
349                 public Coordinate cg = Coordinate.NUL;
350                 public double longitudinalInertia = 0;
351                 public double rotationalInetria = 0;
352         }
353         
354         
355         @Override
356         protected void voidMassCache() {
357                 super.voidMassCache();
358                 this.cgCache = null;
359                 this.longitudinalInertiaCache = null;
360                 this.rotationalInertiaCache = null;
361         }
362         
363         
364
365
366         @Override
367         public int getModID() {
368                 return 0;
369         }
370         
371
372
373 }