1 package net.sf.openrocket.masscalc;
3 import static net.sf.openrocket.util.MathUtil.pow2;
5 import java.util.ArrayList;
6 import java.util.HashMap;
7 import java.util.Iterator;
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;
21 public class BasicMassCalculator extends AbstractMassCalculator {
23 private static final double MIN_MASS = 0.001 * MathUtil.EPSILON;
27 * Cached data. All CG data is in absolute coordinates. All moments of inertia
28 * are relative to their respective CG.
30 private Coordinate[] cgCache = null;
31 private double longitudinalInertiaCache[] = null;
32 private double rotationalInertiaCache[] = null;
36 ////////////////// Mass property calculations ///////////////////
40 * Return the CG of the rocket with the specified motor status (no motors,
44 public Coordinate getCG(Configuration configuration, MassCalcType type) {
45 checkCache(configuration);
46 calculateStageCache(configuration);
48 Coordinate totalCG = null;
51 for (int stage : configuration.getActiveStages()) {
52 totalCG = cgCache[stage].average(totalCG);
56 totalCG = Coordinate.NUL;
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);
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);
84 * Return the CG of the rocket with the provided motor configuration.
87 public Coordinate getCG(Configuration configuration, MotorInstanceConfiguration motors) {
88 checkCache(configuration);
89 calculateStageCache(configuration);
91 Coordinate totalCG = getCG(configuration, MassCalcType.NO_MOTORS);
95 for (MotorId id : motors.getMotorIDs()) {
96 int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber();
97 if (configuration.isStageActive(stage)) {
99 MotorInstance motor = motors.getMotorInstance(id);
100 Coordinate position = motors.getMotorPosition(id);
101 Coordinate cg = motor.getCG().add(position);
102 totalCG = totalCG.average(cg);
111 * Return the longitudinal inertia of the rocket with the specified motor instance
114 * @param configuration the current motor instance configuration
115 * @return the longitudinal inertia of the rocket
118 public double getLongitudinalInertia(Configuration configuration, MotorInstanceConfiguration motors) {
119 checkCache(configuration);
120 calculateStageCache(configuration);
122 final Coordinate totalCG = getCG(configuration, motors);
123 double totalInertia = 0;
126 for (int stage : configuration.getActiveStages()) {
127 Coordinate stageCG = cgCache[stage];
129 totalInertia += (longitudinalInertiaCache[stage] +
130 stageCG.weight * MathUtil.pow2(stageCG.x - totalCG.x));
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);
143 double inertia = motor.getLongitudinalInertia();
144 totalInertia += inertia + cg.weight * MathUtil.pow2(cg.x - totalCG.x);
155 * Return the rotational inertia of the rocket with the specified motor instance
158 * @param configuration the current motor instance configuration
159 * @return the rotational inertia of the rocket
162 public double getRotationalInertia(Configuration configuration, MotorInstanceConfiguration motors) {
163 checkCache(configuration);
164 calculateStageCache(configuration);
166 final Coordinate totalCG = getCG(configuration, motors);
167 double totalInertia = 0;
170 for (int stage : configuration.getActiveStages()) {
171 Coordinate stageCG = cgCache[stage];
173 totalInertia += (rotationalInertiaCache[stage] +
174 stageCG.weight * (MathUtil.pow2(stageCG.y - totalCG.y) +
175 MathUtil.pow2(stageCG.z - totalCG.z)));
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);
188 double inertia = motor.getRotationalInertia();
189 totalInertia += inertia + cg.weight * (MathUtil.pow2(cg.y - totalCG.y) +
190 MathUtil.pow2(cg.z - totalCG.z));
199 * Return the total mass of the motors
201 * @param configuration the current motor instance configuration
202 * @return the total mass of all motors
205 public double getPropellantMass(Configuration configuration, MotorInstanceConfiguration motors){
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;
219 public Map<RocketComponent, Coordinate> getCGAnalysis(Configuration configuration, MassCalcType type) {
220 checkCache(configuration);
221 calculateStageCache(configuration);
223 Map<RocketComponent, Coordinate> map = new HashMap<RocketComponent, Coordinate>();
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);
234 map.put(configuration.getRocket(), getCG(configuration, type));
239 //////// Cache computations ////////
241 private void calculateStageCache(Configuration config) {
242 if (cgCache == null) {
244 int stages = config.getRocket().getStageCount();
246 cgCache = new Coordinate[stages];
247 longitudinalInertiaCache = new double[stages];
248 rotationalInertiaCache = new double[stages];
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;
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.
268 private MassData calculateAssemblyMassData(RocketComponent parent) {
269 MassData parentData = new MassData();
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);
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());
285 parentData.longitudinalInertia = parent.getLongitudinalUnitInertia() * parentData.cg.weight;
286 parentData.rotationalInetria = parent.getRotationalUnitInertia() * parentData.cg.weight;
289 // Combine data for subcomponents
290 for (RocketComponent sibling : parent.getChildren()) {
291 Coordinate combinedCG;
294 // Compute data of sibling
295 MassData siblingData = calculateAssemblyMassData(sibling);
296 Coordinate[] siblingCGs = sibling.toRelative(siblingData.cg, parent);
298 for (Coordinate siblingCG : siblingCGs) {
300 // Compute CG of this + sibling
301 combinedCG = parentData.cg.average(siblingCG);
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;
307 dr2 = pow2(parentData.cg.y - combinedCG.y) + pow2(parentData.cg.z - combinedCG.z);
308 parentData.rotationalInetria += parentData.cg.weight * dr2;
311 // Add inertia of sibling
312 parentData.longitudinalInertia += siblingData.longitudinalInertia;
313 parentData.rotationalInetria += siblingData.rotationalInetria;
315 // Add effect of sibling CG change
316 dx2 = pow2(siblingData.cg.x - combinedCG.x);
317 parentData.longitudinalInertia += siblingData.cg.weight * dx2;
319 dr2 = pow2(siblingData.cg.y - combinedCG.y) + pow2(siblingData.cg.z - combinedCG.z);
320 parentData.rotationalInetria += siblingData.cg.weight * dr2;
323 parentData.cg = combinedCG;
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);
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);
348 private static class MassData {
349 public Coordinate cg = Coordinate.NUL;
350 public double longitudinalInertia = 0;
351 public double rotationalInetria = 0;
356 protected void voidMassCache() {
357 super.voidMassCache();
359 this.longitudinalInertiaCache = null;
360 this.rotationalInertiaCache = null;
367 public int getModID() {