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