d149b83cacdade4da3d9f6264040c7db830d8b7b
[debian/openrocket] / src / net / sf / openrocket / gui / print / DesignReport.java
1 /*
2  * DesignReport.java
3  */
4 package net.sf.openrocket.gui.print;
5
6 import java.awt.Graphics2D;
7 import java.io.IOException;
8 import java.text.DecimalFormat;
9 import java.util.ArrayList;
10 import java.util.Iterator;
11 import java.util.List;
12
13 import net.sf.openrocket.document.OpenRocketDocument;
14 import net.sf.openrocket.document.Simulation;
15 import net.sf.openrocket.gui.figureelements.FigureElement;
16 import net.sf.openrocket.gui.figureelements.RocketInfo;
17 import net.sf.openrocket.gui.scalefigure.RocketPanel;
18 import net.sf.openrocket.logging.LogHelper;
19 import net.sf.openrocket.masscalc.BasicMassCalculator;
20 import net.sf.openrocket.masscalc.MassCalculator;
21 import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
22 import net.sf.openrocket.motor.Motor;
23 import net.sf.openrocket.rocketcomponent.Configuration;
24 import net.sf.openrocket.rocketcomponent.MotorMount;
25 import net.sf.openrocket.rocketcomponent.Rocket;
26 import net.sf.openrocket.rocketcomponent.RocketComponent;
27 import net.sf.openrocket.rocketcomponent.Stage;
28 import net.sf.openrocket.simulation.FlightData;
29 import net.sf.openrocket.simulation.exception.SimulationException;
30 import net.sf.openrocket.startup.Application;
31 import net.sf.openrocket.unit.Unit;
32 import net.sf.openrocket.unit.UnitGroup;
33 import net.sf.openrocket.util.Chars;
34 import net.sf.openrocket.util.Coordinate;
35 import net.sf.openrocket.util.Prefs;
36
37 import com.itextpdf.text.Document;
38 import com.itextpdf.text.DocumentException;
39 import com.itextpdf.text.Element;
40 import com.itextpdf.text.Paragraph;
41 import com.itextpdf.text.Rectangle;
42 import com.itextpdf.text.pdf.BaseFont;
43 import com.itextpdf.text.pdf.DefaultFontMapper;
44 import com.itextpdf.text.pdf.PdfContentByte;
45 import com.itextpdf.text.pdf.PdfPCell;
46 import com.itextpdf.text.pdf.PdfPTable;
47 import com.itextpdf.text.pdf.PdfWriter;
48
49 /**
50  * <pre>
51  * #  Title # Section describing the rocket in general without motors
52  * # Section describing the rocket in general without motors
53  * <p/>
54  * design name
55  * empty mass & CG
56  * CP position
57  * CP position at 5 degree AOA (or similar)
58  * number of stages
59  * parachute/streamer sizes
60  * max. diameter (caliber)
61  * velocity at exit of rail/rod
62  * minimum safe velocity reached in x inches/cm
63  * <p/>
64  * # Section for each motor configuration
65  * <p/>
66  * a summary of the motors, e.g. 3xC6-0; B4-6
67  * a list of the motors including the manufacturer, designation (maybe also info like burn time, grams of propellant,
68  * total impulse)
69  * total grams of propellant
70  * total impulse
71  * takeoff weight
72  * CG and CP position, stability margin
73  * predicted flight altitude, max. velocity and max. acceleration
74  * predicted velocity at chute deployment
75  * predicted descent rate
76  * Thrust to Weight Ratio of each stage
77  * <p/>
78  * </pre>
79  */
80 public class DesignReport {
81         
82         /**
83          * The logger.
84          */
85         private static final LogHelper log = Application.getLogger();
86         
87         /**
88          * The OR Document.
89          */
90         private OpenRocketDocument rocketDocument;
91         
92         /**
93          * A panel used for rendering of the design diagram.
94          */
95         final RocketPanel panel;
96         
97         /**
98          * The iText document.
99          */
100         protected Document document;
101         
102         /** The displayed strings. */
103         private static final String STAGES = "Stages: ";
104         private static final String MASS_WITH_MOTORS = "Mass (with motors): ";
105         private static final String MASS_WITH_MOTOR = "Mass (with motor): ";
106         private static final String MASS_EMPTY = "Mass (Empty): ";
107         private static final String STABILITY = "Stability: ";
108         private static final String CG = "CG: ";
109         private static final String CP = "CP: ";
110         private static final String MOTOR = "Motor";
111         private static final String AVG_THRUST = "Avg Thrust";
112         private static final String BURN_TIME = "Burn Time";
113         private static final String MAX_THRUST = "Max Thrust";
114         private static final String TOTAL_IMPULSE = "Total Impulse";
115         private static final String THRUST_TO_WT = "Thrust to Wt";
116         private static final String PROPELLANT_WT = "Propellant Wt";
117         private static final String SIZE = "Size";
118         private static final String ALTITUDE = "Altitude";
119         private static final String FLIGHT_TIME = "Flight Time";
120         private static final String TIME_TO_APOGEE = "Time to Apogee";
121         private static final String VELOCITY_OFF_PAD = "Velocity off Pad";
122         private static final String MAX_VELOCITY = "Max Velocity";
123         private static final String LANDING_VELOCITY = "Landing Velocity";
124         private static final String ROCKET_DESIGN = "Rocket Design";
125         private static final double GRAVITY_CONSTANT = 9.80665d;
126         
127         /**
128          * Constructor.
129          *
130          * @param theRocDoc the OR document
131          * @param theIDoc   the iText document
132          */
133         public DesignReport(OpenRocketDocument theRocDoc, Document theIDoc) {
134                 document = theIDoc;
135                 rocketDocument = theRocDoc;
136                 panel = new RocketPanel(rocketDocument);
137         }
138         
139         /**
140          * Main entry point.  Prints the rocket drawing and design data.
141          *
142          * @param writer a direct byte writer
143          */
144         public void writeToDocument(PdfWriter writer) {
145                 if (writer == null) {
146                         return;
147                 }
148                 com.itextpdf.text.Rectangle pageSize = document.getPageSize();
149                 int pageImageableWidth = (int) pageSize.getWidth() - (int) pageSize.getBorderWidth() * 2;
150                 int pageImageableHeight = (int) pageSize.getHeight() / 2 - (int) pageSize.getBorderWidthTop();
151                 
152                 PrintUtilities.addText(document, PrintUtilities.BIG_BOLD, ROCKET_DESIGN);
153                 
154                 Rocket rocket = rocketDocument.getRocket();
155                 final Configuration configuration = rocket.getDefaultConfiguration();
156                 configuration.setAllStages();
157                 PdfContentByte canvas = writer.getDirectContent();
158                 
159                 final PrintFigure figure = new PrintFigure(configuration);
160                 
161                 FigureElement cp = panel.getExtraCP();
162                 FigureElement cg = panel.getExtraCG();
163                 RocketInfo text = panel.getExtraText();
164                 
165                 double scale = paintRocketDiagram(pageImageableWidth, pageImageableHeight, canvas, figure, cp, cg);
166                 
167                 canvas.beginText();
168                 try {
169                         canvas.setFontAndSize(BaseFont.createFont(PrintUtilities.NORMAL.getFamilyname(), BaseFont.CP1252,
170                                                                                                                 BaseFont.EMBEDDED), PrintUtilities.NORMAL_FONT_SIZE);
171                 } catch (DocumentException e) {
172                         log.error("Could not set font.", e);
173                 } catch (IOException e) {
174                         log.error("Could not create font.", e);
175                 }
176                 int figHeightPts = (int) (PrintUnit.METERS.toPoints(figure.getFigureHeight()) * 0.4 * (scale / PrintUnit.METERS
177                                 .toPoints(1)));
178                 final int diagramHeight = pageImageableHeight * 2 - 70 - (figHeightPts);
179                 canvas.moveText(document.leftMargin() + pageSize.getBorderWidthLeft(), diagramHeight);
180                 canvas.moveTextWithLeading(0, -16);
181                 
182                 float initialY = canvas.getYTLM();
183                 
184                 canvas.showText(rocketDocument.getRocket().getName());
185                 
186                 canvas.newlineShowText(STAGES);
187                 canvas.showText("" + rocket.getStageCount());
188                 
189
190                 if (configuration.hasMotors()) {
191                         if (configuration.getStageCount() > 1) {
192                                 canvas.newlineShowText(MASS_WITH_MOTORS);
193                         } else {
194                                 canvas.newlineShowText(MASS_WITH_MOTOR);
195                         }
196                 } else {
197                         canvas.newlineShowText(MASS_EMPTY);
198                 }
199                 canvas.showText(text.getMass(UnitGroup.UNITS_MASS.getDefaultUnit()));
200                 
201                 canvas.newlineShowText(STABILITY);
202                 canvas.showText(text.getStability());
203                 
204                 canvas.newlineShowText(CG);
205                 canvas.showText(text.getCg());
206                 
207                 canvas.newlineShowText(CP);
208                 canvas.showText(text.getCp());
209                 canvas.endText();
210                 
211                 try {
212                         //Move the internal pointer of the document below that of what was just written using the direct byte buffer.
213                         Paragraph paragraph = new Paragraph();
214                         float finalY = canvas.getYTLM();
215                         int heightOfDiagramAndText = (int) (pageSize.getHeight() - (finalY - initialY + diagramHeight));
216                         
217                         paragraph.setSpacingAfter(heightOfDiagramAndText);
218                         document.add(paragraph);
219                         
220                         String[] motorIds = rocket.getMotorConfigurationIDs();
221                         
222                         List<Double> stageMasses = getStageMasses(rocket);
223                         
224                         for (int j = 0; j < motorIds.length; j++) {
225                                 String motorId = motorIds[j];
226                                 if (motorId != null) {
227                                         PdfPTable parent = new PdfPTable(2);
228                                         parent.setWidthPercentage(100);
229                                         parent.setHorizontalAlignment(Element.ALIGN_LEFT);
230                                         parent.setSpacingBefore(0);
231                                         parent.setWidths(new int[] { 1, 3 });
232                                         int leading = 0;
233                                         //The first motor config is always null.  Skip it and the top-most motor, then set the leading.
234                                         if (j > 1) {
235                                                 leading = 25;
236                                         }
237                                         addFlightData(rocket, motorId, parent, leading);
238                                         addMotorData(rocket, motorId, parent);
239                                         document.add(parent);
240                                 }
241                         }
242                 } catch (DocumentException e) {
243                         log.error("Could not modify document.", e);
244                 }
245         }
246         
247         /**
248          * Get the motor list for all motor mounts.
249          *
250          * @param theRocket the rocket object
251          * @param theMid    the motor id
252          *
253          * @return a list of Motor
254          */
255         private List<Motor> getMotorList(final Rocket theRocket, final String theMid) {
256                 Iterator<RocketComponent> components = theRocket.iterator();
257                 final List<Motor> motorList = new ArrayList<Motor>();
258                 while (components.hasNext()) {
259                         RocketComponent rocketComponent = components.next();
260                         if (rocketComponent instanceof MotorMount) {
261                                 MotorMount mm = (MotorMount) rocketComponent;
262                                 final Motor motor = mm.getMotor(theMid);
263                                 if (motor != null) {
264                                         motorList.add(motor);
265                                 }
266                         }
267                 }
268                 return motorList;
269         }
270         
271         /**
272          * Paint a diagram of the rocket into the PDF document.
273          *
274          * @param thePageImageableWidth  the number of points in the width of the page available for drawing
275          * @param thePageImageableHeight the number of points in the height of the page available for drawing
276          * @param theCanvas              the direct byte writer
277          * @param theFigure              the print figure
278          * @param theCp                  the center of pressure figure element
279          * @param theCg                  the center of gravity figure element
280          *
281          * @return the scale of the diagram
282          */
283         private double paintRocketDiagram(final int thePageImageableWidth, final int thePageImageableHeight,
284                                                                                 final PdfContentByte theCanvas, final PrintFigure theFigure,
285                                                                                 final FigureElement theCp, final FigureElement theCg) {
286                 theFigure.clearAbsoluteExtra();
287                 theFigure.clearRelativeExtra();
288                 theFigure.addRelativeExtra(theCp);
289                 theFigure.addRelativeExtra(theCg);
290                 theFigure.updateFigure();
291                 
292                 double scale =
293                                 (thePageImageableWidth * 2.2) / theFigure.getFigureWidth();
294                 
295                 theFigure.setScale(scale);
296                 /*
297                  * page dimensions are in points-per-inch, which, in Java2D, are the same as pixels-per-inch; thus we don't need any conversion
298                  */
299                 theFigure.setSize(thePageImageableWidth, thePageImageableHeight);
300                 theFigure.updateFigure();
301                 
302
303                 final DefaultFontMapper mapper = new DefaultFontMapper();
304                 Graphics2D g2d = theCanvas.createGraphics(thePageImageableWidth, thePageImageableHeight * 2, mapper);
305                 g2d.translate(20, 120);
306                 
307                 g2d.scale(0.4d, 0.4d);
308                 theFigure.paint(g2d);
309                 g2d.dispose();
310                 return scale;
311         }
312         
313         /**
314          * Add the motor data for a motor configuration to the table.
315          *
316          * @param rocket        the rocket
317          * @param motorId       the motor ID to output
318          * @param parent        the parent to which the motor data will be added
319          */
320         private void addMotorData(Rocket rocket, String motorId, final PdfPTable parent) {
321                 
322                 PdfPTable motorTable = new PdfPTable(8);
323                 motorTable.setWidthPercentage(68);
324                 motorTable.setHorizontalAlignment(Element.ALIGN_LEFT);
325                 
326                 final PdfPCell motorCell = ITextHelper.createCell(MOTOR, PdfPCell.BOTTOM);
327                 final int mPad = 10;
328                 motorCell.setPaddingLeft(mPad);
329                 motorTable.addCell(motorCell);
330                 motorTable.addCell(ITextHelper.createCell(AVG_THRUST, PdfPCell.BOTTOM));
331                 motorTable.addCell(ITextHelper.createCell(BURN_TIME, PdfPCell.BOTTOM));
332                 motorTable.addCell(ITextHelper.createCell(MAX_THRUST, PdfPCell.BOTTOM));
333                 motorTable.addCell(ITextHelper.createCell(TOTAL_IMPULSE, PdfPCell.BOTTOM));
334                 motorTable.addCell(ITextHelper.createCell(THRUST_TO_WT, PdfPCell.BOTTOM));
335                 motorTable.addCell(ITextHelper.createCell(PROPELLANT_WT, PdfPCell.BOTTOM));
336                 motorTable.addCell(ITextHelper.createCell(SIZE, PdfPCell.BOTTOM));
337                 
338                 DecimalFormat ttwFormat = new DecimalFormat("0.00");
339                 
340                 MassCalculator massCalc = new BasicMassCalculator();
341                 
342                 Configuration config = new Configuration(rocket);
343                 config.setMotorConfigurationID(motorId);
344                 
345                 int totalMotorCount = 0;
346                 double totalPropMass = 0;
347                 double totalImpulse = 0;
348                 double totalTTW = 0;
349                 
350                 int stage = 0;
351                 double stageMass = 0;
352                 
353                 boolean topBorder = false;
354                 for (RocketComponent c : rocket) {
355                         
356                         if (c instanceof Stage) {
357                                 config.setToStage(stage);
358                                 stage++;
359                                 stageMass = massCalc.getCG(config, MassCalcType.LAUNCH_MASS).weight;
360                                 // Calculate total thrust-to-weight from only lowest stage motors
361                                 totalTTW = 0;
362                                 topBorder = true;
363                         }
364                         
365                         if (c instanceof MotorMount && ((MotorMount) c).isMotorMount()) {
366                                 MotorMount mount = (MotorMount) c;
367                                 
368                                 if (mount.isMotorMount() && mount.getMotor(motorId) != null) {
369                                         Motor motor = mount.getMotor(motorId);
370                                         int motorCount = c.toAbsolute(Coordinate.NUL).length;
371                                         
372
373                                         int border = Rectangle.NO_BORDER;
374                                         if (topBorder) {
375                                                 border = Rectangle.TOP;
376                                                 topBorder = false;
377                                         }
378                                         
379                                         String name = motor.getDesignation();
380                                         if (motorCount > 1) {
381                                                 name += " (" + Chars.TIMES + motorCount + ")";
382                                         }
383                                         
384                                         final PdfPCell motorVCell = ITextHelper.createCell(name, border);
385                                         motorVCell.setPaddingLeft(mPad);
386                                         motorTable.addCell(motorVCell);
387                                         motorTable.addCell(ITextHelper.createCell(
388                                                         UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(motor.getAverageThrustEstimate()), border));
389                                         motorTable.addCell(ITextHelper.createCell(
390                                                         UnitGroup.UNITS_FLIGHT_TIME.getDefaultUnit().toStringUnit(motor.getBurnTimeEstimate()), border));
391                                         motorTable.addCell(ITextHelper.createCell(
392                                                         UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(motor.getMaxThrustEstimate()), border));
393                                         motorTable.addCell(ITextHelper.createCell(
394                                                         UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit(motor.getTotalImpulseEstimate()), border));
395                                         
396                                         double ttw = motor.getAverageThrustEstimate() / (stageMass * GRAVITY_CONSTANT);
397                                         motorTable.addCell(ITextHelper.createCell(
398                                                         ttwFormat.format(ttw) + ":1", border));
399                                         
400                                         double propMass = (motor.getLaunchCG().weight - motor.getEmptyCG().weight);
401                                         motorTable.addCell(ITextHelper.createCell(
402                                                         UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(propMass), border));
403                                         
404                                         final Unit motorUnit = UnitGroup.UNITS_MOTOR_DIMENSIONS.getDefaultUnit();
405                                         motorTable.addCell(ITextHelper.createCell(motorUnit.toString(motor.getDiameter()) +
406                                                                                                                                 "/" +
407                                                                                                                                 motorUnit.toString(motor.getLength()) + " " +
408                                                                                                                                 motorUnit.toString(), border));
409                                         
410                                         // Sum up total count
411                                         totalMotorCount += motorCount;
412                                         totalPropMass += propMass * motorCount;
413                                         totalImpulse += motor.getTotalImpulseEstimate() * motorCount;
414                                         totalTTW += ttw * motorCount;
415                                 }
416                         }
417                 }
418                 
419                 if (totalMotorCount > 1) {
420                         int border = Rectangle.TOP;
421                         final PdfPCell motorVCell = ITextHelper.createCell("Total:", border);
422                         motorVCell.setPaddingLeft(mPad);
423                         motorTable.addCell(motorVCell);
424                         motorTable.addCell(ITextHelper.createCell("", border));
425                         motorTable.addCell(ITextHelper.createCell("", border));
426                         motorTable.addCell(ITextHelper.createCell("", border));
427                         motorTable.addCell(ITextHelper.createCell(
428                                                 UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit(totalImpulse), border));
429                         motorTable.addCell(ITextHelper.createCell(
430                                         ttwFormat.format(totalTTW) + ":1", border));
431                         motorTable.addCell(ITextHelper.createCell(
432                                                 UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(totalPropMass), border));
433                         motorTable.addCell(ITextHelper.createCell("", border));
434                         
435                 }
436                 
437                 PdfPCell c = new PdfPCell(motorTable);
438                 c.setBorder(PdfPCell.LEFT);
439                 c.setBorderWidthTop(0f);
440                 parent.addCell(c);
441         }
442         
443         
444         /**
445          * Add the motor data for a motor configuration to the table.
446          *
447          * @param theRocket the rocket
448          * @param motorId   a motor configuration id
449          * @param parent    the parent to which the motor data will be added
450          * @param leading   the number of points for the leading
451          */
452         private void addFlightData(final Rocket theRocket, final String motorId, final PdfPTable parent, int leading) {
453                 
454                 // Perform flight simulation
455                 Rocket duplicate = theRocket.copyWithOriginalID();
456                 FlightData flight = null;
457                 try {
458                         Simulation simulation = Prefs.getBackgroundSimulation(duplicate);
459                         simulation.getOptions().setMotorConfigurationID(motorId);
460                         simulation.simulate();
461                         flight = simulation.getSimulatedData();
462                 } catch (SimulationException e1) {
463                         // Ignore
464                 }
465                 
466                 // Output the flight data
467                 if (flight != null) {
468                         try {
469                                 final Unit distanceUnit = UnitGroup.UNITS_DISTANCE.getDefaultUnit();
470                                 final Unit velocityUnit = UnitGroup.UNITS_VELOCITY.getDefaultUnit();
471                                 final Unit flightTimeUnit = UnitGroup.UNITS_FLIGHT_TIME.getDefaultUnit();
472                                 
473                                 PdfPTable labelTable = new PdfPTable(2);
474                                 labelTable.setWidths(new int[] { 3, 2 });
475                                 final Paragraph chunk = ITextHelper.createParagraph(stripBrackets(
476                                                         theRocket.getMotorConfigurationNameOrDescription(motorId)), PrintUtilities.BOLD);
477                                 chunk.setLeading(leading);
478                                 chunk.setSpacingAfter(3f);
479                                 
480                                 document.add(chunk);
481                                 
482                                 final PdfPCell cell = ITextHelper.createCell(ALTITUDE, 2, 2);
483                                 cell.setUseBorderPadding(false);
484                                 cell.setBorderWidthTop(0f);
485                                 labelTable.addCell(cell);
486                                 labelTable.addCell(ITextHelper.createCell(distanceUnit.toStringUnit(flight.getMaxAltitude()), 2, 2));
487                                 
488                                 labelTable.addCell(ITextHelper.createCell(FLIGHT_TIME, 2, 2));
489                                 labelTable.addCell(ITextHelper.createCell(flightTimeUnit.toStringUnit(flight.getFlightTime()), 2, 2));
490                                 
491                                 labelTable.addCell(ITextHelper.createCell(TIME_TO_APOGEE, 2, 2));
492                                 labelTable.addCell(ITextHelper.createCell(flightTimeUnit.toStringUnit(flight.getTimeToApogee()), 2, 2));
493                                 
494                                 labelTable.addCell(ITextHelper.createCell(VELOCITY_OFF_PAD, 2, 2));
495                                 labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getLaunchRodVelocity()), 2, 2));
496                                 
497                                 labelTable.addCell(ITextHelper.createCell(MAX_VELOCITY, 2, 2));
498                                 labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getMaxVelocity()), 2, 2));
499                                 
500                                 labelTable.addCell(ITextHelper.createCell(LANDING_VELOCITY, 2, 2));
501                                 labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getGroundHitVelocity()), 2, 2));
502                                 
503                                 //Add the table to the parent; have to wrap it in a cell
504                                 PdfPCell c = new PdfPCell(labelTable);
505                                 c.setBorder(PdfPCell.RIGHT);
506                                 c.setBorderWidthTop(0);
507                                 c.setTop(0);
508                                 parent.addCell(c);
509                         } catch (DocumentException e) {
510                                 log.error("Could not add flight data to document.", e);
511                         }
512                 }
513         }
514         
515         /**
516          * Strip [] brackets from a string.
517          *
518          * @param target the original string
519          *
520          * @return target with [] removed
521          */
522         private String stripBrackets(String target) {
523                 return stripLeftBracket(stripRightBracket(target));
524         }
525         
526         /**
527          * Strip [ from a string.
528          *
529          * @param target the original string
530          *
531          * @return target with [ removed
532          */
533         private String stripLeftBracket(String target) {
534                 return target.replace("[", "");
535         }
536         
537         /**
538          * Strip ] from a string.
539          *
540          * @param target the original string
541          *
542          * @return target with ] removed
543          */
544         private String stripRightBracket(String target) {
545                 return target.replace("]", "");
546         }
547         
548         
549         /**
550          * Return a list of cumulative stage masses.  The latter masses include the mass
551          * of the upper stages as well.
552          *
553          * @param rocket        the rocket
554          * @return                      a list containing the cumulative stage masses
555          */
556         private List<Double> getStageMasses(final Rocket rocket) {
557                 List<Double> masses = new ArrayList<Double>();
558                 int stages = rocket.getStageCount();
559                 
560                 Configuration config = new Configuration(rocket);
561                 MassCalculator calc = new BasicMassCalculator();
562                 
563                 for (int i = 0; i < stages; i++) {
564                         config.setToStage(i);
565                         masses.add(calc.getCG(config, MassCalcType.NO_MOTORS).weight);
566                 }
567                 
568                 return masses;
569         }
570         
571 }