+2010-03-13 Doug Pedrick / Sampo Niskanen
+
+ * Initial RockSim design loading support
+
2010-03-10 Sampo Niskanen
* Released version 1.0.0
-Feature roadmap for OpenRocket 1.0
+Feature roadmap for OpenRocket 2.0
----------------------------------
-Must-have:
+- Logging system into use
+- Reduce memory footprint
+- Reduce memory leakage
+- Load thrust curves from external directory
+- Support duplicate motor definitions
-Maybe:
Done:
-----
-- Search field in motor selection dialog
-- Motor selection/editing from Edit configurations dialog
-- Change FreeformFinSet to throw checked exceptions
-- Fix engine block icons
-- Exporting flight data
-- Split cluster into separate components
-- Create application icon and take into use
-- Error dialog for uncaught exceptions
-- Check where plot data ends (all rest NaN)
-- Example rocket designs (hybrid w/ dual deployment, staged rocket)
-- Better error/warning dialogs when reading/writing files
-- Store custom materials
-- Read more thrust curve formats
-- Showing events in plots
-- Table boolean selecting by clicking label
-- Test automatic exception reporting (for 0.9.3)
-- Draw remaining event icons (for 0.9.3)
-- Update "About" dialog with icon and source info
-In 0.9.4:
-- Through-the-wall fins
-- Make ThicknessRingComponent implement RadialParent and allow
- attaching components to a TubeCoupler (for 0.9.4)
-- Save file as oldest OpenRocket format possible (for 0.9.4)
-- Non-exception bug handling
-- JTree text is cropped unnecessarily
-- Allow editing user-defined materials
-- [BUG] All configuration dialogs too high
-- Simulation plot dialog forces dialog one button row too high (All/None)
-- Add styrofoam and depron materials
-- Inform user about software updates
-In 0.9.5:
-- Add label to motor panel to tell current number of stages
-In 0.9.6:
-- Take into account all fins in interference effects
-- Two-fin rocket stable at large number of roll angles?!
-- Add slight randomness to yaw moment
-- Update simulation time step filtering from exponential MA to
- exponential growth until time step is reached (t1 = 1.5*t0)
-- Limit time step during while on launch rod
-- Re-investigate 15% reduction of three-fin CNa
-In 1.0.0:
-- BUG: Simulation table max. acceleration takes into account parachute deceleration
-- Go through thrust curves and select best ones
-- Updated splash screen
-- BUG: Invalid fin points possible when removing points
+
+- Reading .RKT format
\ No newline at end of file
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.zip.GZIPInputStream;
+import java.util.Arrays;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.file.openrocket.OpenRocketLoader;
+import net.sf.openrocket.file.rocksim.RocksimLoader;
/**
private static final byte[] GZIP_SIGNATURE = { 31, -117 }; // 0x1f, 0x8b
private static final byte[] OPENROCKET_SIGNATURE =
"<openrocket".getBytes(Charset.forName("US-ASCII"));
+ private static final byte[] ROCKSIM_SIGNATURE =
+ "<RockSimDoc".getBytes(Charset.forName("US-ASCII"));
private final OpenRocketLoader openRocketLoader = new OpenRocketLoader();
+
+ private final RocksimLoader rocksimLoader = new RocksimLoader();
@Override
protected OpenRocketDocument loadFromStream(InputStream source) throws IOException,
match = 0;
}
}
-
+
+ byte[] typeIdentifier = Arrays.copyOf(buffer, ROCKSIM_SIGNATURE.length);
+ if (Arrays.equals(ROCKSIM_SIGNATURE, typeIdentifier)) {
+ return loadUsing(source, rocksimLoader);
+ }
throw new RocketLoadException("Unsupported or corrupt file.");
}
--- /dev/null
+/*
+ * AttachedPartsHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+
+import java.util.HashMap;
+
+/**
+ * A SAX handler for the Rocksim AttachedParts XML type.
+ */
+class AttachedPartsHandler extends ElementHandler {
+ /** The parent component. */
+ private final RocketComponent component;
+
+ /**
+ * Constructor.
+ *
+ * @param c the parent
+ *
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public AttachedPartsHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The parent component of any attached part may not be null.");
+ }
+ component = c;
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ if ("FinSet".equals(element)) {
+ return new FinSetHandler(component);
+ }
+ if ("CustomFinSet".equals(element)) {
+ return new FinSetHandler(component);
+ }
+ if ("LaunchLug".equals(element)) {
+ return new LaunchLugHandler(component);
+ }
+ if ("Parachute".equals(element)) {
+ return new ParachuteHandler(component);
+ }
+ if ("Streamer".equals(element)) {
+ return new StreamerHandler(component);
+ }
+ if ("MassObject".equals(element)) {
+ return new MassObjectHandler(component);
+ }
+ if ("Ring".equals(element)) {
+ return new RingHandler(component);
+ }
+ if ("BodyTube".equals(element)) {
+ return new InnerBodyTubeHandler(component);
+ }
+ if ("Transition".equals(element)) {
+ return new TransitionHandler(component);
+ }
+ if ("TubeFinSet".equals(element)) {
+ warnings.add("Tube fins are not currently supported. Ignoring.");
+ }
+ if ("RingTail".equals(element)) {
+ warnings.add("Ring tails are not currently supported. Ignoring.");
+ }
+ if ("ExternalPod".equals(element)) {
+ warnings.add("Pods are not currently supported. Ignoring.");
+ }
+ return null;
+ }
+}
+
--- /dev/null
+/*
+ * BaseHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import org.xml.sax.SAXException;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * An abstract base class that handles common parsing. All Rocksim component handlers are subclassed from here.
+ */
+public abstract class BaseHandler<C extends RocketComponent> extends ElementHandler {
+
+ /**
+ * The overridden mass.
+ */
+ private Double mass = 0d;
+ /**
+ * The overridden Cg.
+ */
+ private Double cg = 0d;
+ /**
+ * The density of the material in the component.
+ */
+ private Double density = 0d;
+ /**
+ * The material name.
+ */
+ private String materialName = "";
+
+ /**
+ * The SAX method called when the closing element tag is reached.
+ *
+ * @param element the element name.
+ * @param attributes attributes of the element.
+ * @param content the textual content of the element.
+ * @param warnings the warning set to store warnings in.
+ * @throws SAXException
+ */
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ final C component = getComponent();
+ try {
+ if ("Name".equals(element)) {
+ component.setName(content);
+ }
+ if ("KnownMass".equals(element)) {
+ mass = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS);
+ }
+ if ("Density".equals(element)) {
+ density = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_DENSITY);
+ }
+ if ("KnownCG".equals(element)) {
+ cg = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ }
+ if ("UseKnownCG".equals(element)) {
+ boolean override = "1".equals(content);
+ setOverride(component, override, mass, cg);
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+ @Override
+ public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ /* Because of the order of XML elements in Rocksim, not all information is known at the time it really needs
+ to be acted upon. So we keep temporary instance variables to be used here at the end of the parsing.
+ */
+ RocketComponent component = getComponent();
+ updateComponentMaterial(component, materialName, getMaterialType(), density);
+ }
+
+ /**
+ * If the Rocksim component does not override the mass, then create a Material based upon the density defined
+ * for that component. This *should* result in a consistent representation of Cg between Rocksim and OpenRocket.
+ *
+ * @param component the component
+ * @param type the type of the material
+ * @param density the density in g/cm^3
+ * @param definedMaterial the material that is currently defined on the component; used only to get the name
+ * as it appears in Rocksim
+ */
+ public static void updateComponentMaterial(RocketComponent component, String definedMaterial, Material.Type type,
+ double density) {
+ if (definedMaterial != null) {
+ Material custom = createCustomMaterial(type, definedMaterial, density);
+ setMaterial(component, custom);
+ }
+ }
+
+ /**
+ * Override the mass and Cg of the component.
+ *
+ * @param component the component
+ * @param override true if any override should happen
+ * @param mass the override mass
+ * @param cg the override cg
+ */
+ public static void setOverride(RocketComponent component, boolean override, double mass, double cg) {
+ if (override) {
+ component.setCGOverridden(override);
+ component.setMassOverridden(override);
+ component.setOverrideSubcomponents(false); //Rocksim does not support this type of override
+ component.setOverrideMass(mass);
+ component.setOverrideCGX(cg);
+ }
+ }
+
+ /**
+ * Get the component this handler is working upon.
+ *
+ * @return a component
+ */
+ protected abstract C getComponent();
+
+ /**
+ * Get the required type of material for this component.
+ *
+ * @return the required material type
+ */
+ protected abstract Material.Type getMaterialType();
+
+ /**
+ * Some CG positions in Rocksim do not correspond to the CG position reference in OpenRocket.
+ *
+ * @param theCG the CG value to really use when overriding CG on the OpenRocket component
+ */
+ protected void setCG(double theCG) {
+ cg = theCG;
+ }
+
+ /**
+ * Set the material name as specified in the Rocksim design file.
+ *
+ * @param content the material name
+ */
+ protected void setMaterialName(String content) {
+ materialName = content;
+ }
+
+ /**
+ * Create a custom material based on the density.
+ *
+ * @param type the type of the material
+ * @param name the name of the component
+ * @param density the density in g/cm^3
+ *
+ * @return a Material instance
+ */
+ public static Material createCustomMaterial(Material.Type type, String name, double density) {
+ return Material.newMaterial(type, "RS: " + name, density, true);
+ }
+
+ /**
+ * Set the material onto an instance of RocketComponent. This is done because only some subtypes of RocketComponent
+ * have the setMaterial method. Unfortunately the supertype cannot be used.
+ *
+ * @param component the component who's material is to be set
+ * @param material the material to be set on the component (defined by getComponent())
+ */
+ private static void setMaterial(RocketComponent component, Material material) {
+ try {
+ final Method method = getMethod(component, "setMaterial", new Class[]{Material.class});
+ if (method != null) {
+ method.invoke(component, material);
+ }
+ }
+ catch (IllegalAccessException ignored) {
+ }
+ catch (InvocationTargetException ignored) {
+ }
+ }
+
+ /**
+ * Find a method by name and argument list.
+ *
+ * @param component the component who's material is to be seta
+ * @param name the method name
+ * @param args the class types of the parameters
+ *
+ * @return the Method instance, or null
+ */
+ private static Method getMethod(RocketComponent component, String name, Class[] args) {
+ Method method = null;
+ try {
+ method = component.getClass().getMethod(name, args);
+ }
+ catch (NoSuchMethodException ignored) {
+ }
+ return method;
+ }
+
+}
--- /dev/null
+/*
+ * BodyTubeHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+
+/**
+ * A SAX handler for Rocksim Body Tubes.
+ */
+class BodyTubeHandler extends BaseHandler<BodyTube> {
+ /**
+ * The OpenRocket BodyTube.
+ */
+ private final BodyTube bodyTube;
+
+ /**
+ * Constructor.
+ *
+ * @param c parent component
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public BodyTubeHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The parent component of a body tube may not be null.");
+ }
+ bodyTube = new BodyTube();
+ c.addChild(bodyTube);
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ if ("AttachedParts".equals(element)) {
+ return new AttachedPartsHandler(bodyTube);
+ }
+ return PlainTextHandler.INSTANCE;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ super.closeElement(element, attributes, content, warnings);
+
+ try {
+ if ("OD".equals(element)) {
+ bodyTube.setRadius(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS);
+ }
+ if ("ID".equals(element)) {
+ final double r = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS;
+ bodyTube.setInnerRadius(r);
+ }
+ if ("Len".equals(element)) {
+ bodyTube.setLength(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ }
+ if ("FinishCode".equals(element)) {
+ bodyTube.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket());
+ }
+ if ("IsMotorMount".equals(element)) {
+ bodyTube.setMotorMount("1".equals(content));
+ }
+ if ("EngineOverhang".equals(element)) {
+ bodyTube.setMotorOverhang(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ }
+ if ("Material".equals(element)) {
+ setMaterialName(content);
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+ /**
+ * Get the component this handler is working upon.
+ *
+ * @return a component
+ */
+ @Override
+ public BodyTube getComponent() {
+ return bodyTube;
+ }
+
+ /**
+ * Get the required type of material for this component.
+ *
+ * @return BULK
+ */
+ public Material.Type getMaterialType() {
+ return Material.Type.BULK;
+ }
+}
+
--- /dev/null
+/*
+ * FinSetHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.FinSet;
+import net.sf.openrocket.rocketcomponent.ExternalComponent;
+import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
+import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
+import net.sf.openrocket.rocketcomponent.FreeformFinSet;
+import net.sf.openrocket.rocketcomponent.IllegalFinPointException;
+import net.sf.openrocket.util.Coordinate;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * A SAX handler for Rocksim fin sets. Because the type of
+ * fin may not be known first (in Rocksim file format, the fin shape type is in the middle of the XML structure),
+ * and because we're using SAX not DOM, all of the fin characteristics are kept here until the closing FinSet tag.
+ * At that point, <code>asOpenRocket</code> method is called to construct the corresponding OpenRocket FinSet.
+ */
+class FinSetHandler extends ElementHandler {
+ /**
+ * The parent component.
+ */
+ private final RocketComponent component;
+
+ /**
+ * The name of the fin.
+ */
+ private String name;
+ /**
+ * The Rocksim fin shape code.
+ */
+ private int shapeCode;
+ /**
+ * The location of the fin on its parent.
+ */
+ private double location = 0.0d;
+ /**
+ * The OpenRocket Position which gives the absolute/relative positiong for location.
+ */
+ private RocketComponent.Position position;
+ /**
+ * The number of fins in this fin set.
+ */
+ private int finCount;
+ /**
+ * The length of the root chord.
+ */
+ private double rootChord = 0.0d;
+ /**
+ * The length of the tip chord.
+ */
+ private double tipChord = 0.0d;
+ /**
+ * The length of the mid-chord (aka height).
+ */
+ private double midChordLen = 0.0d;
+ /**
+ * The distance of the leading edge from root to top.
+ */
+ private double sweepDistance = 0.0d;
+ /**
+ * The angle the fins have been rotated from the y-axis, if looking down the tube, in radians.
+ */
+ private double radialAngle = 0.0d;
+ /**
+ * The thickness of the fins.
+ */
+ private double thickness;
+ /**
+ * The finish of the fins.
+ */
+ private ExternalComponent.Finish finish;
+ /**
+ * The shape of the tip.
+ */
+ private int tipShapeCode;
+ /**
+ * The length of the TTW tab.
+ */
+ private double tabLength = 0.0d;
+ /**
+ * The depth of the TTW tab.
+ */
+ private double tabDepth = 0.0d;
+ /**
+ * The offset of the tab, from the front of the fin.
+ */
+ private double taboffset = 0.0d;
+ /**
+ * The elliptical semi-span (height).
+ */
+ private double semiSpan;
+ /**
+ * The list of custom points.
+ */
+ private String pointList;
+ /**
+ * Override the Cg and mass.
+ */
+ private boolean override = false;
+ /**
+ * The overridden mass.
+ */
+ private Double mass = 0d;
+ /**
+ * The overridden Cg.
+ */
+ private Double cg = 0d;
+ /**
+ * The density of the material in the component.
+ */
+ private Double density = 0d;
+ /**
+ * The material name.
+ */
+ private String materialName = "";
+ /**
+ * The Rocksim calculated mass.
+ */
+ private Double calcMass = 0d;
+ /**
+ * The Rocksim calculated cg.
+ */
+ private Double calcCg = 0d;
+
+
+ /**
+ * Constructor.
+ *
+ * @param c the parent
+ *
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public FinSetHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The parent component of a fin set may not be null.");
+ }
+ component = c;
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ return PlainTextHandler.INSTANCE;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ try {
+ if ("Name".equals(element)) {
+ name = content;
+ }
+ if ("Material".equals(element)) {
+ materialName = content;
+ }
+ if ("FinishCode".equals(element)) {
+ finish = RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket();
+ }
+ if ("Xb".equals(element)) {
+ location = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("LocationMode".equals(element)) {
+ position = RocksimLocationMode.fromCode(Integer.parseInt(content)).asOpenRocket();
+ }
+ if ("FinCount".equals(element)) {
+ finCount = Integer.parseInt(content);
+ }
+ if ("RootChord".equals(element)) {
+ rootChord = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("TipChord".equals(element)) {
+ tipChord = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("SemiSpan".equals(element)) {
+ semiSpan = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("MidChordLen".equals(element)) {
+ midChordLen = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("SweepDistance".equals(element)) {
+ sweepDistance = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("Thickness".equals(element)) {
+ thickness = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("TipShapeCode".equals(element)) {
+ tipShapeCode = Integer.parseInt(content);
+ }
+ if ("TabLength".equals(element)) {
+ tabLength = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("TabDepth".equals(element)) {
+ tabDepth = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("TabOffset".equals(element)) {
+ taboffset = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("RadialAngle".equals(element)) {
+ radialAngle = Double.parseDouble(content);
+ }
+ if ("ShapeCode".equals(element)) {
+ shapeCode = Integer.parseInt(content);
+ }
+ if ("PointList".equals(element)) {
+ pointList = content;
+ }
+ if ("KnownMass".equals(element)) {
+ mass = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS);
+ }
+ if ("Density".equals(element)) {
+ density = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_DENSITY);
+ }
+ if ("KnownCG".equals(element)) {
+ cg = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS);
+ }
+ if ("UseKnownCG".equals(element)) {
+ override = "1".equals(content);
+ }
+ if ("CalcMass".equals(element)) {
+ calcMass = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS;
+ }
+ if ("CalcCg".equals(element)) {
+ calcCg = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+ @Override
+ public void endHandler(String element, HashMap<String, String> attributes,
+ String content, WarningSet warnings) throws SAXException {
+ //Create the fin set and correct for overrides and actual material densities
+ final FinSet finSet = asOpenRocket(warnings);
+ BaseHandler.setOverride(finSet, override, mass, cg);
+ if (!override && finSet.getCrossSection().equals(FinSet.CrossSection.AIRFOIL)) {
+ //Override mass anyway. This is done only for AIRFOIL because Rocksim does not compute different
+ //mass/cg for different cross sections, but OpenRocket does. This can lead to drastic differences
+ //in mass. To counteract that, the cross section value is retained but the mass/cg is overridden
+ //with the calculated values from Rocksim. This will best approximate the Rocksim design in OpenRocket.
+ BaseHandler.setOverride(finSet, true, calcMass, calcCg);
+ }
+ BaseHandler.updateComponentMaterial(finSet, materialName, Material.Type.BULK, density);
+ component.addChild(finSet);
+ }
+
+
+ /**
+ * Convert the parsed Rocksim data values in this object to an instance of OpenRocket's FinSet.
+ *
+ * @param warnings the warning set to convey incompatibilities to the user
+ * @return a FinSet instance
+ */
+ public FinSet asOpenRocket(WarningSet warnings) {
+ FinSet result;
+
+ if (shapeCode == 0) {
+ //Trapezoidal
+ result = new TrapezoidFinSet();
+ ((TrapezoidFinSet) result).setFinShape(rootChord, tipChord, sweepDistance, midChordLen, thickness);
+ }
+ else if (shapeCode == 1) {
+ //Elliptical
+ result = new EllipticalFinSet();
+ ((EllipticalFinSet) result).setHeight(semiSpan);
+ ((EllipticalFinSet) result).setLength(rootChord);
+ }
+ else if (shapeCode == 2) {
+
+ result = new FreeformFinSet();
+ try {
+ ((FreeformFinSet) result).setPoints(toCoordinates(pointList, warnings));
+ }
+ catch (IllegalFinPointException e) {
+ warnings.add("Illegal fin point set. " + e.getMessage() + " Ignoring.");
+ }
+ }
+ else {
+ return null;
+ }
+ result.setThickness(thickness);
+ result.setName(name);
+ result.setFinCount(finCount);
+ result.setFinish(finish);
+ //All TTW tabs in Rocksim are relative to the front of the fin.
+ result.setTabRelativePosition(FinSet.TabRelativePosition.FRONT);
+ result.setTabHeight(tabDepth);
+ result.setTabLength(tabLength);
+ result.setTabShift(taboffset);
+ result.setBaseRotation(radialAngle);
+ result.setCrossSection(convertTipShapeCode(tipShapeCode));
+ result.setRelativePosition(position);
+ PositionDependentHandler.setLocation(result, position, location);
+ return result;
+
+ }
+
+ /**
+ * Convert a Rocksim string that represents fin plan points into an array of OpenRocket coordinates.
+ *
+ * @param pointList a comma and pipe delimited string of X,Y coordinates from Rocksim. This is of the format:
+ * <pre>x0,y0|x1,y1|x2,y2|... </pre>
+ * @param warnings the warning set to convey incompatibilities to the user
+ *
+ * @return an array of OpenRocket Coordinates
+ */
+ private Coordinate[] toCoordinates(String pointList, WarningSet warnings) {
+ List<Coordinate> result = new ArrayList<Coordinate>();
+ if (pointList != null && !pointList.isEmpty()) {
+ String[] points = pointList.split("\\Q|\\E");
+ for (String point : points) {
+ String[] aPoint = point.split(",");
+ try {
+ if (aPoint.length > 1) {
+ Coordinate c = new Coordinate(
+ Double.parseDouble(aPoint[0]) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH,
+ Double.parseDouble(aPoint[1]) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ result.add(c);
+ }
+ else {
+ warnings.add("Invalid fin point pair.");
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Fin point not in numeric format.");
+ }
+ }
+ if (!result.isEmpty()) {
+ //OpenRocket requires fin plan points be ordered from leading root chord to trailing root chord in the
+ //Coordinate array.
+ Coordinate last = result.get(result.size() - 1);
+ if (last.x == 0 && last.y == 0) {
+ Collections.reverse(result);
+ }
+ }
+ }
+ final Coordinate[] coords = new Coordinate[result.size()];
+ return result.toArray(coords);
+ }
+
+
+ /**
+ * Convert a Rocksim tip shape to an OpenRocket CrossSection.
+ *
+ * @param tipShape the tip shape code from Rocksim
+ * @return a CrossSection instance
+ */
+ private FinSet.CrossSection convertTipShapeCode(int tipShape) {
+ switch (tipShape) {
+ case 0:
+ return FinSet.CrossSection.SQUARE;
+ case 1:
+ return FinSet.CrossSection.ROUNDED;
+ case 2:
+ return FinSet.CrossSection.AIRFOIL;
+ default:
+ return FinSet.CrossSection.SQUARE;
+ }
+ }
+
+}
+
--- /dev/null
+/*
+ * InnerBodyTubeHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.InnerTube;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+
+/**
+ * A SAX handler for Rocksim inside tubes.
+ */
+class InnerBodyTubeHandler extends PositionDependentHandler<InnerTube> {
+
+ /**
+ * The OpenRocket InnerTube instance.
+ */
+ private final InnerTube bodyTube;
+
+ /**
+ * Constructor.
+ *
+ * @param c the parent component
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public InnerBodyTubeHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The parent component of an inner tube may not be null.");
+ }
+ bodyTube = new InnerTube();
+ c.addChild(bodyTube);
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ if ("AttachedParts".equals(element)) {
+ return new AttachedPartsHandler(bodyTube);
+ }
+ return PlainTextHandler.INSTANCE;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ super.closeElement(element, attributes, content, warnings);
+
+ try {
+ if ("OD".equals(element)) {
+ bodyTube.setOuterRadius(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS);
+ }
+ if ("ID".equals(element)) {
+ final double r = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS;
+ bodyTube.setInnerRadius(r);
+ }
+ if ("Len".equals(element)) {
+ bodyTube.setLength(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ }
+ if ("IsMotorMount".equals(element)) {
+ bodyTube.setMotorMount("1".equals(content));
+ }
+ if ("EngineOverhang".equals(element)) {
+ bodyTube.setMotorOverhang(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ }
+ if ("Material".equals(element)) {
+ setMaterialName(content);
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+ /**
+ * Get the InnerTube component this handler is working upon.
+ *
+ * @return an InnerTube component
+ */
+ @Override
+ public InnerTube getComponent() {
+ return bodyTube;
+ }
+
+ /**
+ * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not
+ * public in all components.
+ *
+ * @param position the OpenRocket position
+ */
+ @Override
+ public void setRelativePosition(RocketComponent.Position position) {
+ bodyTube.setRelativePosition(position);
+ }
+
+ /**
+ * Get the required type of material for this component.
+ *
+ * @return BULK
+ */
+ @Override
+ public Material.Type getMaterialType() {
+ return Material.Type.BULK;
+ }
+
+}
--- /dev/null
+/*
+ * LaunchLugHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.LaunchLug;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+
+/**
+ * The SAX handler for Rocksim Launch Lugs.
+ */
+class LaunchLugHandler extends PositionDependentHandler<LaunchLug> {
+
+ /**
+ * The OpenRocket LaunchLug instance.
+ */
+ private final LaunchLug lug;
+
+ /**
+ * Constructor.
+ *
+ * @param c the parent
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public LaunchLugHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The parent component of a launch lug may not be null.");
+ }
+ lug = new LaunchLug();
+ c.addChild(lug);
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ return PlainTextHandler.INSTANCE;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ super.closeElement(element, attributes, content, warnings);
+
+ try {
+ if ("OD".equals(element)) {
+ lug.setRadius(Math.max(0, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS));
+ }
+ if ("ID".equals(element)) {
+ lug.setInnerRadius(Math.max(0, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS));
+ }
+ if ("Len".equals(element)) {
+ lug.setLength(Math.max(0, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH));
+ }
+ if ("Material".equals(element)) {
+ setMaterialName(content);
+ }
+ if ("FinishCode".equals(element)) {
+ lug.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket());
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+ /**
+ * Get the LaunchLug component this handler is working upon.
+ *
+ * @return a LaunchLug component
+ */
+ @Override
+ public LaunchLug getComponent() {
+ return lug;
+ }
+
+ /**
+ * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not
+ * public in all components.
+ *
+ * @param position the OpenRocket position
+ */
+ @Override
+ public void setRelativePosition(RocketComponent.Position position) {
+ lug.setRelativePosition(position);
+ }
+
+ /**
+ * Get the required type of material for this component.
+ *
+ * @return BULK
+ */
+ @Override
+ public Material.Type getMaterialType() {
+ return Material.Type.BULK;
+ }
+}
+
--- /dev/null
+/*
+ * MassObjectHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.MassComponent;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+
+/**
+ * A SAX handler for Rocksim's MassObject XML type.
+ */
+class MassObjectHandler extends PositionDependentHandler<MassComponent> {
+
+ /**
+ * The Rocksim Mass length fudge factor. Rocksim completely exaggerates the length of a mass object to the point
+ * that it looks ridiculous in OpenRocket. This fudge factor is here merely to get the typical mass object to
+ * render in the OpenRocket UI with it's bounds mostly inside it's parent. The odd thing about it is that
+ * Rocksim does not expose the length of a mass object in the UI and actually treats mass objects as point objects -
+ * not 3 or even 2 dimensional.
+ */
+ public static final int MASS_LEN_FUDGE_FACTOR = 100;
+
+ /**
+ * The OpenRocket MassComponent - counterpart to the RS MassObject.
+ */
+ private final MassComponent mass;
+
+ /**
+ * Constructor.
+ *l
+ * @param c the parent component
+ *
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public MassObjectHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The parent component of a mass component may not be null.");
+ }
+ mass = new MassComponent();
+ c.addChild(mass);
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ return PlainTextHandler.INSTANCE;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ super.closeElement(element, attributes, content, warnings);
+ try {
+ if ("Len".equals(element)) {
+ mass.setLength(Double.parseDouble(content) / (RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH * MASS_LEN_FUDGE_FACTOR));
+ }
+ if ("KnownMass".equals(element)) {
+ mass.setComponentMass(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS);
+ }
+ if ("KnownCG".equals(element)) {
+ //Setting the CG of the Mass Object to 0 is important because of the different ways that Rocksim and
+ //OpenRocket treat mass objects. Rocksim treats them as points (even though the data file contains a
+ //length) and because Rocksim sets the CG of the mass object to really be relative to the front of
+ //the parent. But that value is already assumed in the position and position value for the component.
+ //Thus it needs to be set to 0 to say that the mass object's CG is at the point of the mass object.
+ super.setCG(0);
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+ /**
+ * Get the component this handler is working upon.
+ *
+ * @return a component
+ */
+ @Override
+ public MassComponent getComponent() {
+ return mass;
+ }
+
+ /**
+ * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not
+ * public in all components.
+ *
+ * @param position the OpenRocket position
+ */
+ public void setRelativePosition(RocketComponent.Position position) {
+ mass.setRelativePosition(position);
+ }
+
+ /**
+ * Get the required type of material for this component. Does not apply to MassComponents.
+ *
+ * @return BULK
+ */
+ @Override
+ public Material.Type getMaterialType() {
+ return Material.Type.BULK;
+ }
+
+}
--- /dev/null
+/*
+ * NoseConeHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.NoseCone;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.Transition;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+
+/**
+ * The SAX nose cone handler for Rocksim NoseCones.
+ */
+class NoseConeHandler extends BaseHandler<NoseCone> {
+
+ /**
+ * The OpenRocket NoseCone.
+ */
+ private final NoseCone noseCone = new NoseCone();
+
+ /**
+ * The wall thickness. Used for hollow nose cones.
+ */
+ private double thickness = 0d;
+
+ /**
+ * Constructor.
+ *
+ * @param c the parent component to the nosecone
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public NoseConeHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The parent component of a nose cone may not be null.");
+ }
+ c.addChild(noseCone);
+ noseCone.setAftRadiusAutomatic(false);
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ //Nose cones in Rocksim may have attached parts - namely Mass Objects - as children.
+ if ("AttachedParts".equals(element)) {
+ return new AttachedPartsHandler(noseCone);
+ }
+ return PlainTextHandler.INSTANCE;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes,
+ String content, WarningSet warnings) throws SAXException {
+ super.closeElement(element, attributes, content, warnings);
+
+ try {
+ if ("ShapeCode".equals(element)) {
+ noseCone.setType(RocksimNoseConeCode.fromCode(Integer.parseInt(content)).asOpenRocket());
+ }
+ if ("Len".equals(element)) {
+ noseCone.setLength(Math.max(0, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH));
+ }
+ if ("BaseDia".equals(element)) {
+ noseCone.setAftRadius(Math.max(0, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS));
+ }
+ if ("WallThickness".equals(element)) {
+ thickness = Math.max(0, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ }
+ if ("ShoulderOD".equals(element)) {
+ noseCone.setAftShoulderRadius(Math.max(0, Double.parseDouble(
+ content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS));
+ }
+ if ("ShoulderLen".equals(element)) {
+ noseCone.setAftShoulderLength(Math.max(0, Double.parseDouble(
+ content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH));
+ }
+ if ("ShapeParameter".equals(element)) {
+ //The Rocksim ShapeParameter only applies to certain shapes, although it is included
+ //in the design file for all nose cones. Applying it when it should not be causes oddities so
+ //a check is made for the allowable shapes.
+ if (Transition.Shape.POWER.equals(noseCone.getType()) ||
+ Transition.Shape.HAACK.equals(noseCone.getType()) ||
+ Transition.Shape.PARABOLIC.equals(noseCone.getType())) {
+ noseCone.setShapeParameter(Double.parseDouble(content));
+ }
+ }
+ if ("ConstructionType".equals(element)) {
+ int typeCode = Integer.parseInt(content);
+ if (typeCode == 0) {
+ //SOLID
+ noseCone.setFilled(true);
+ }
+ else if (typeCode == 1) {
+ //HOLLOW
+ noseCone.setFilled(false);
+ }
+ }
+ if ("FinishCode".equals(element)) {
+ noseCone.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket());
+ }
+ if ("Material".equals(element)) {
+ setMaterialName(content);
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+ @Override
+ public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ super.endHandler(element, attributes, content, warnings);
+
+ if (noseCone.isFilled()) {
+ noseCone.setAftShoulderThickness(noseCone.getAftShoulderRadius());
+ }
+ else {
+ noseCone.setThickness(thickness);
+ noseCone.setAftShoulderThickness(thickness);
+ }
+ }
+
+ /**
+ * Get the nose cone component this handler is working upon.
+ *
+ * @return a nose cone component
+ */
+ @Override
+ public NoseCone getComponent() {
+ return noseCone;
+ }
+
+ /**
+ * Get the required type of material for this component.
+ *
+ * @return BULK
+ */
+ public Material.Type getMaterialType() {
+ return Material.Type.BULK;
+ }
+
+}
--- /dev/null
+/*
+ * ParachuteHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.InnerTube;
+import net.sf.openrocket.rocketcomponent.Parachute;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+
+/**
+ * A SAX handler for Rocksim's Parachute XML type.
+ * <p/>
+ */
+class ParachuteHandler extends PositionDependentHandler<Parachute> {
+ /**
+ * The OpenRocket Parachute instance
+ */
+ private final Parachute chute;
+ /**
+ * The shroud line density.
+ */
+ private double shroudLineDensity = 0.0d;
+
+ /**
+ * Constructor.
+ *
+ * @param c the parent component
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public ParachuteHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The parent of a parachute may not be null.");
+ }
+ chute = new Parachute();
+ c.addChild(chute);
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ return PlainTextHandler.INSTANCE;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ super.closeElement(element, attributes, content, warnings);
+ try {
+ if ("Dia".equals(element)) {
+ chute.setDiameter(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ /* Rocksim doesn't have a packed parachute radius, so we approximate it. */
+ double packed;
+ RocketComponent parent = chute.getParent();
+ if (parent instanceof BodyTube) {
+ packed = ((BodyTube) parent).getRadius() * 0.9;
+ }
+ else if (parent instanceof InnerTube) {
+ packed = ((InnerTube) parent).getInnerRadius() * 0.9;
+ }
+ else {
+ packed = chute.getDiameter() * 0.025;
+ }
+ chute.setRadius(packed);
+ }
+ if ("ShroudLineCount".equals(element)) {
+ chute.setLineCount(Math.max(0, Integer.parseInt(content)));
+ }
+ if ("ShroudLineLen".equals(element)) {
+ chute.setLineLength(Math.max(0, Double.parseDouble(
+ content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH));
+ }
+ if ("SpillHoleDia".equals(element)) {
+ //Not supported in OpenRocket
+ double spillHoleRadius = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS;
+ warnings.add("Parachute spill holes are not supported. Ignoring.");
+ }
+ if ("ShroudLineMassPerMM".equals(element)) {
+ shroudLineDensity = Double.parseDouble(content) * 10d/ RocksimHandler.ROCKSIM_TO_OPENROCKET_DENSITY;
+ }
+ if ("ShroudLineMaterial".equals(element)) {
+ chute.setLineMaterial(BaseHandler.createCustomMaterial(Material.Type.LINE, content, shroudLineDensity));
+ }
+ if ("DragCoefficient".equals(element)) {
+ chute.setCD(Double.parseDouble(content));
+ }
+ if ("Material".equals(element)) {
+ setMaterialName(content);
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+ /**
+ * Get the component this handler is working upon.
+ *
+ * @return a component
+ */
+ public Parachute getComponent() {
+ return chute;
+ }
+
+ /**
+ * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not
+ * public in all components.
+ *
+ * @param position the OpenRocket position
+ */
+ public void setRelativePosition(RocketComponent.Position position) {
+ chute.setRelativePosition(position);
+ }
+
+ /**
+ * Get the required type of material for this component.
+ *
+ * @return BULK
+ */
+ @Override
+ public Material.Type getMaterialType() {
+ return Material.Type.SURFACE;
+ }
+
+}
+
--- /dev/null
+/*
+ * PositionDependentHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+
+/**
+ * An abstract base class that handles position dependencies for all lower level components that
+ * are position aware.
+ */
+public abstract class PositionDependentHandler<C extends RocketComponent> extends BaseHandler<C> {
+
+ /** Temporary position value. */
+ private Double positionValue;
+
+ /** Temporary position. */
+ private RocketComponent.Position position;
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ super.closeElement(element, attributes, content, warnings);
+ if ("Xb".equals(element)) {
+ positionValue = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("LocationMode".equals(element)) {
+ position = RocksimLocationMode.fromCode(Integer.parseInt(
+ content)).asOpenRocket();
+ }
+ }
+
+ /**
+ * This method sets the position information onto the component. Rocksim splits the location/position
+ * information into two disparate data elements. Both pieces of data are necessary to map into OpenRocket's
+ * position model.
+ *
+ * @param element the element name
+ * @param attributes the attributes
+ * @param content the content of the element
+ * @param warnings the warning set to store warnings in.
+ * @throws org.xml.sax.SAXException not thrown
+ */
+ @Override
+ public void endHandler(String element, HashMap<String, String> attributes,
+ String content, WarningSet warnings) throws SAXException {
+ super.endHandler(element, attributes, content, warnings);
+ setRelativePosition(position);
+ setLocation(getComponent(), position, positionValue);
+ }
+
+ /**
+ * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not
+ * public in all components.
+ *
+ * @param position the OpenRocket position
+ */
+ protected abstract void setRelativePosition(RocketComponent.Position position);
+
+ /**
+ * Set the position of a component.
+ *
+ * @param component the component
+ * @param position the relative position
+ * @param location the actual position value
+ */
+ public static void setLocation(RocketComponent component, RocketComponent.Position position, double location) {
+ if (position.equals(RocketComponent.Position.BOTTOM)) {
+ component.setPositionValue(-1d * location);
+ }
+ else {
+ component.setPositionValue(location);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * RingHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.CenteringRing;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+
+/**
+ * A SAX handler for centering rings and bulkheads.
+ */
+class RingHandler extends PositionDependentHandler<CenteringRing> {
+
+ /**
+ * The OpenRocket Ring.
+ */
+ private final CenteringRing ring;
+
+ /**
+ * Constructor.
+ *
+ * @param c the parent component
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public RingHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The parent of a ring may not be null.");
+ }
+ ring = new CenteringRing();
+ c.addChild(ring);
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ return PlainTextHandler.INSTANCE;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ super.closeElement(element, attributes, content, warnings);
+
+ try {
+ if ("OD".equals(element)) {
+ ring.setOuterRadius(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS);
+ }
+ if ("ID".equals(element)) {
+ ring.setInnerRadius(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS);
+ }
+ if ("Len".equals(element)) {
+ ring.setLength(Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ }
+ if ("Material".equals(element)) {
+ setMaterialName(content);
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+ /**
+ * Get the ring component this handler is working upon.
+ *
+ * @return a component
+ */
+ @Override
+ public CenteringRing getComponent() {
+ return ring;
+ }
+
+ /**
+ * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not
+ * public in all components.
+ *
+ * @param position the OpenRocket position
+ */
+ @Override
+ public void setRelativePosition(RocketComponent.Position position) {
+ ring.setRelativePosition(position);
+ }
+
+ /**
+ * Get the required type of material for this component.
+ *
+ * @return BULK
+ */
+ @Override
+ public Material.Type getMaterialType() {
+ return Material.Type.BULK;
+ }
+}
+
--- /dev/null
+/*
+ * RocksimFinishCode.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.rocketcomponent.ExternalComponent;
+
+/**
+ * Models the finish of a component.
+ */
+enum RocksimFinishCode {
+ POLISHED(0, ExternalComponent.Finish.POLISHED),
+ GLOSS(1, ExternalComponent.Finish.SMOOTH),
+ MATT(2, ExternalComponent.Finish.NORMAL),
+ UNFINISHED(3, ExternalComponent.Finish.UNFINISHED);
+
+ /** The Rocksim code (from XML). */
+ private final int ordinal;
+
+ /** The corresponding OpenRocket finish. */
+ private final ExternalComponent.Finish finish;
+
+ /**
+ * Constructor.
+ *
+ * @param idx the Rocksim enum value
+ * @param theFinish the OpenRocket finish
+ */
+ private RocksimFinishCode(int idx, ExternalComponent.Finish theFinish) {
+ ordinal = idx;
+ finish = theFinish;
+ }
+
+ /**
+ * Get the OpenRocket finish.
+ *
+ * @return a Finish instance
+ */
+ public ExternalComponent.Finish asOpenRocket() {
+ return finish;
+ }
+
+ /**
+ * Lookup an instance of this enum from a Rocksim value.
+ *
+ * @param rocksimFinishCode the Rocksim value
+ *
+ * @return an instance of this enum; Defaults to MATT
+ */
+ public static RocksimFinishCode fromCode(int rocksimFinishCode) {
+ RocksimFinishCode[] values = values();
+ for (RocksimFinishCode value : values) {
+ if (value.ordinal == rocksimFinishCode) {
+ return value;
+ }
+ }
+ return MATT; //Default
+ }
+
+}
+
--- /dev/null
+/*
+ * RocksimHandler.java
+ *
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.Warning;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.Stage;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+
+/**
+ * This class is a Sax element handler for Rocksim version 9 design files. It parses the Rocksim file (typically
+ * a .rkt extension) and creates corresponding OpenRocket components. This is a best effort approach and may not
+ * be an exact replica.
+ * <p/>
+ * Limitations: Rocksim flight simulations are not imported; tube fins are not supported; Rocksim 'pods' are not supported.
+ */
+public class RocksimHandler extends ElementHandler {
+
+ /**
+ * Length conversion. Rocksim is in millimeters, OpenRocket in meters.
+ */
+ public static final int ROCKSIM_TO_OPENROCKET_LENGTH = 1000;
+
+ /**
+ * Mass conversion. Rocksim is in grams, OpenRocket in kilograms.
+ */
+ public static final int ROCKSIM_TO_OPENROCKET_MASS = 1000;
+
+ /**
+ * Density conversion. Rocksim is in milligrams/cubic centimeter, OpenRocket in grams/cubic centimeter.
+ */
+ public static final int ROCKSIM_TO_OPENROCKET_DENSITY = 1;
+
+ /**
+ * Radius conversion. Rocksim is always in diameters, OpenRocket mostly in radius.
+ */
+ public static final int ROCKSIM_TO_OPENROCKET_RADIUS = 2 * ROCKSIM_TO_OPENROCKET_LENGTH;
+
+ /**
+ * The main content handler.
+ */
+ private RocksimContentHandler handler = null;
+
+ /**
+ * Return the OpenRocketDocument read from the file, or <code>null</code> if a document
+ * has not been read yet.
+ *
+ * @return the document read, or null.
+ */
+ public OpenRocketDocument getDocument() {
+ return handler.getDocument();
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes,
+ WarningSet warnings) {
+
+ // Check for unknown elements
+ if (!element.equals("RockSimDocument")) {
+ warnings.add(Warning.fromString("Unknown element " + element + ", ignoring."));
+ return null;
+ }
+
+ // Check for first call
+ if (handler != null) {
+ warnings.add(Warning.fromString("Multiple document elements found, ignoring later "
+ + "ones."));
+ return null;
+ }
+
+ handler = new RocksimContentHandler();
+ return handler;
+ }
+
+}
+
+/**
+ * Handles the content of the <DesignInformation> tag.
+ */
+class RocksimContentHandler extends ElementHandler {
+ /**
+ * The OpenRocketDocument that is the container for the rocket.
+ */
+ private final OpenRocketDocument doc;
+
+ /**
+ * The top-level component, from which all child components are added.
+ */
+ private final Rocket rocket;
+
+ /**
+ * The rocksim file version.
+ */
+ private String version;
+
+ /**
+ * Constructor.
+ */
+ public RocksimContentHandler() {
+ this.rocket = new Rocket();
+ this.doc = new OpenRocketDocument(rocket);
+ }
+
+ /**
+ * Get the OpenRocket document that has been created from parsing the Rocksim design file.
+ *
+ * @return the instantiated OpenRocketDocument
+ */
+ public OpenRocketDocument getDocument() {
+ return doc;
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes,
+ WarningSet warnings) {
+ if ("DesignInformation".equals(element)) {
+ //The next sub-element is "RocketDesign", which is really the only thing that matters. Rather than
+ //create another handler just for that element, handle it here.
+ return this;
+ }
+ if ("FileVersion".equals(element)) {
+ return PlainTextHandler.INSTANCE;
+ }
+ if ("RocketDesign".equals(element)) {
+ return new RocketDesignHandler(rocket);
+ }
+ return null;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes,
+ String content, WarningSet warnings) throws SAXException {
+ /**
+ * SAX handler for Rocksim file version number. The value is not used currently, but could be used in the future
+ * for backward/forward compatibility reasons (different lower level handlers could be called via a strategy pattern).
+ */
+ if ("FileVersion".equals(element)) {
+ version = content;
+ }
+ }
+
+ /**
+ * Answer the file version.
+ *
+ * @return the version of the Rocksim design file
+ */
+ public String getVersion() {
+ return version;
+ }
+}
+
+
+/**
+ * A SAX handler for the high level Rocksim design. This structure includes sub-structures for each of the stages.
+ * Correct functioning of this handler is predicated on the stage count element appearing before the actual stage parts
+ * structures. If that invariant is not true, then behavior will be unpredictable.
+ */
+class RocketDesignHandler extends ElementHandler {
+ /**
+ * The parent component.
+ */
+ private final RocketComponent component;
+ /**
+ * The parsed stage count. Defaults to 1.
+ */
+ private int stageCount = 1;
+ /**
+ * The overridden stage 1 mass.
+ */
+ private double stage1Mass = 0d;
+ /**
+ * The overridden stage 2 mass.
+ */
+ private double stage2Mass = 0d;
+ /**
+ * The overridden stage 3 mass.
+ */
+ private double stage3Mass = 0d;
+ /**
+ * The overridden stage 1 Cg.
+ */
+ private double stage1CG = 0d;
+ /**
+ * The overridden stage 2 Cg.
+ */
+ private double stage2CG = 0d;
+ /**
+ * The overridden stage 3 Cg.
+ */
+ private double stage3CG = 0d;
+
+ /**
+ * Constructor.
+ *
+ * @param c the parent component
+ */
+ public RocketDesignHandler(RocketComponent c) {
+ component = c;
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ /**
+ * In Rocksim stages are from the top down, so a single stage rocket is actually stage '3'. A 2-stage
+ * rocket defines stage '2' as the initial booster with stage '3' sitting atop it. And so on.
+ */
+ if ("Stage3Parts".equals(element)) {
+ final Stage stage = new Stage();
+ if (stage3Mass > 0.0d) {
+ stage.setMassOverridden(true);
+ stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
+ stage.setOverrideMass(stage3Mass);
+ }
+ if (stage3CG > 0.0d) {
+ stage.setCGOverridden(true);
+ stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
+ stage.setOverrideCGX(stage3CG);
+ }
+ component.addChild(stage);
+ return new StageHandler(stage);
+ }
+ if ("Stage2Parts".equals(element)) {
+ if (stageCount >= 2) {
+ final Stage stage = new Stage();
+ if (stage2Mass > 0.0d) {
+ stage.setMassOverridden(true);
+ stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
+ stage.setOverrideMass(stage2Mass);
+ }
+ if (stage2CG > 0.0d) {
+ stage.setCGOverridden(true);
+ stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
+ stage.setOverrideCGX(stage2CG);
+ }
+ component.addChild(stage);
+ return new StageHandler(stage);
+ }
+ }
+ if ("Stage1Parts".equals(element)) {
+ if (stageCount == 3) {
+ final Stage stage = new Stage();
+ if (stage1Mass > 0.0d) {
+ stage.setMassOverridden(true);
+ stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
+ stage.setOverrideMass(stage1Mass);
+ }
+ if (stage1CG > 0.0d) {
+ stage.setCGOverridden(true);
+ stage.setOverrideSubcomponents(true); //Rocksim does not support this type of override
+ stage.setOverrideCGX(stage1CG);
+ }
+ component.addChild(stage);
+ return new StageHandler(stage);
+ }
+ }
+ if ("Name".equals(element)) {
+ return PlainTextHandler.INSTANCE;
+ }
+ if ("StageCount".equals(element)) {
+ return PlainTextHandler.INSTANCE;
+ }
+ if ("Stage3Mass".equals(element)) {
+ return PlainTextHandler.INSTANCE;
+ }
+ if ("Stage2Mass".equals(element)) {
+ return PlainTextHandler.INSTANCE;
+ }
+ if ("Stage1Mass".equals(element)) {
+ return PlainTextHandler.INSTANCE;
+ }
+ if ("Stage3CG".equals(element)) {
+ return PlainTextHandler.INSTANCE;
+ }
+ if ("Stage2CGAlone".equals(element)) {
+ return PlainTextHandler.INSTANCE;
+ }
+ if ("Stage1CGAlone".equals(element)) {
+ return PlainTextHandler.INSTANCE;
+ }
+ return null;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes,
+ String content, WarningSet warnings) throws SAXException {
+ try {
+ if ("Name".equals(element)) {
+ component.setName(content);
+ }
+ if ("StageCount".equals(element)) {
+ stageCount = Integer.parseInt(content);
+ }
+ if ("Stage3Mass".equals(element)) {
+ stage3Mass = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS;
+ }
+ if ("Stage2Mass".equals(element)) {
+ stage2Mass = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS;
+ }
+ if ("Stage1Mass".equals(element)) {
+ stage1Mass = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS;
+ }
+ if ("Stage3CG".equals(element)) {
+ stage3CG = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("Stage2CGAlone".equals(element)) {
+ stage2CG = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ if ("Stage1CGAlone".equals(element)) {
+ stage1CG = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+}
+
+/**
+ * A SAX handler for a Rocksim stage.
+ */
+class StageHandler extends ElementHandler {
+ /**
+ * The parent OpenRocket component.
+ */
+ private final RocketComponent component;
+
+ /**
+ * Constructor.
+ *
+ * @param c the parent component
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public StageHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The stage component may not be null.");
+ }
+ component = c;
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ if ("NoseCone".equals(element)) {
+ return new NoseConeHandler(component);
+ }
+ if ("BodyTube".equals(element)) {
+ return new BodyTubeHandler(component);
+ }
+ if ("Transition".equals(element)) {
+ return new TransitionHandler(component);
+ }
+ return null;
+ }
+}
--- /dev/null
+/*
+ * RocksimLoader.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.file.RocketLoader;
+import net.sf.openrocket.file.RocketLoadException;
+import net.sf.openrocket.file.simplesax.SimpleSAX;
+import net.sf.openrocket.document.OpenRocketDocument;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * This class is the main entry point for Rocksim design file imported to OpenRocket. Currently only Rocksim v9
+ * file formats are supported, although it is possible that v8 formats will work for most components.
+ *
+ * In the cases of v9 components that exist in Rocksim but have no corollary in OpenRocket a message is added to
+ * a warning set and presented to the user. In effect, this loading is a 'best-effort' mapping and is not meant to
+ * be an exact representation of any possible Rocksim design in an OpenRocket format.
+ *
+ * Rocksim simulations are not imported.
+ *
+ * Wish List:
+ * Material interface (or at least make them abstract in RocketComponent)
+ * setMaterial
+ * getMaterial
+ */
+public class RocksimLoader extends RocketLoader {
+ /**
+ * This method is called by the default implementations of {@link #load(java.io.File)}
+ * and {@link #load(java.io.InputStream)} to load the rocket.
+ *
+ * @throws net.sf.openrocket.file.RocketLoadException
+ * if an error occurs during loading.
+ */
+ @Override
+ protected OpenRocketDocument loadFromStream(InputStream source) throws IOException, RocketLoadException {
+
+ InputSource xmlSource = new InputSource(source);
+
+ RocksimHandler handler = new RocksimHandler();
+
+ try {
+ SimpleSAX.readXML(xmlSource, handler, warnings);
+ } catch (SAXException e) {
+ throw new RocketLoadException("Malformed XML in input.", e);
+ }
+
+ final OpenRocketDocument document = handler.getDocument();
+ document.setFile(null);
+ return document;
+ }
+}
--- /dev/null
+/*
+ * RocksimLocationMode.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+
+/**
+ * Models the relative position of parts on a rocket. Maps from Rocksim's notion to OpenRocket's.
+ */
+enum RocksimLocationMode {
+ FRONT_OF_OWNING_PART (0, RocketComponent.Position.TOP),
+ FROM_TIP_OF_NOSE (1, RocketComponent.Position.ABSOLUTE),
+ BACK_OF_OWNING_PART (2, RocketComponent.Position.BOTTOM);
+
+ /** The value Rocksim uses internally (and in the XML file). */
+ private final int ordinal;
+
+ /** The OpenRocket position equivalent. */
+ private final RocketComponent.Position position;
+
+ /**
+ * Constructor.
+ *
+ * @param idx the rocksim enum value
+ * @param theOpenRocketPosition the corresponding OpenRocket position
+ */
+ RocksimLocationMode(int idx, RocketComponent.Position theOpenRocketPosition) {
+ ordinal = idx;
+ position = theOpenRocketPosition;
+ }
+
+ /**
+ * Get the OpenRocket position.
+ *
+ * @return the position instance
+ */
+ public RocketComponent.Position asOpenRocket() {
+ return position;
+ }
+
+ /**
+ * Lookup an instance of this class from a rocksim enum value.
+ *
+ * @param rocksimCode the rocksim enum value
+ *
+ * @return an instance of this enum
+ */
+ public static RocksimLocationMode fromCode(int rocksimCode) {
+ RocksimLocationMode[] values = values();
+ for (RocksimLocationMode value : values) {
+ if (value.ordinal == rocksimCode) {
+ return value;
+ }
+ }
+ return FRONT_OF_OWNING_PART;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * RocksimNoseConeCode.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.rocketcomponent.Transition;
+
+/**
+ * Models the nose cone shape of a rocket. Maps from Rocksim's notion to OpenRocket's.
+ */
+enum RocksimNoseConeCode {
+ CONICAL (0, Transition.Shape.CONICAL),
+ OGIVE (1, Transition.Shape.OGIVE),
+ PARABOLIC (2, Transition.Shape.ELLIPSOID), //Rocksim' PARABOLIC most closely resembles an ELLIPSOID in OpenRocket
+ ELLIPTICAL (3, Transition.Shape.ELLIPSOID),
+ POWER_SERIES (4, Transition.Shape.POWER),
+ PARABOLIC_SERIES(5, Transition.Shape.PARABOLIC),
+ HAACK (6, Transition.Shape.HAACK);
+
+ /** The Rocksim enumeration value. Sent in XML. */
+ private final int ordinal;
+
+ /** The corresponding OpenRocket shape. */
+ private final Transition.Shape shape;
+
+ /**
+ * Constructor.
+ *
+ * @param idx the Rocksim shape code
+ * @param aShape the corresponding OpenRocket shape
+ */
+ private RocksimNoseConeCode(int idx, Transition.Shape aShape) {
+ ordinal = idx;
+ shape = aShape;
+ }
+
+ /**
+ * Get the OpenRocket shape that corresponds to the Rocksim shape.
+ *
+ * @return a shape
+ */
+ public Transition.Shape asOpenRocket() {
+ return shape;
+ }
+
+ /**
+ * Lookup an instance of this enum based upon the Rocksim code.
+ *
+ * @param rocksimShapeCode the Rocksim code (from XML)
+ * @return an instance of this enum
+ */
+ public static RocksimNoseConeCode fromCode(int rocksimShapeCode) {
+ RocksimNoseConeCode[] values = values();
+ for (RocksimNoseConeCode value : values) {
+ if (value.ordinal == rocksimShapeCode) {
+ return value;
+ }
+ }
+ return PARABOLIC; //Default
+ }
+}
--- /dev/null
+/*
+ * StreamerHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.Streamer;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+
+/**
+ * A SAX handler for Streamer components.
+ */
+class StreamerHandler extends PositionDependentHandler<Streamer> {
+
+ /**
+ * The OpenRocket Streamer.
+ */
+ private final Streamer streamer;
+
+ /**
+ * Constructor.
+ *
+ * @param c the parent component
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public StreamerHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The parent of a streamer may not be null.");
+ }
+ streamer = new Streamer();
+ c.addChild(streamer);
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ return PlainTextHandler.INSTANCE;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ super.closeElement(element, attributes, content, warnings);
+
+ try {
+ if ("Width".equals(element)) {
+ streamer.setStripWidth(Math.max(0, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH));
+ }
+ if ("Len".equals(element)) {
+ streamer.setStripLength(Math.max(0, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH));
+ }
+ if ("DragCoefficient".equals(element)) {
+ streamer.setCD(Double.parseDouble(content));
+ }
+ if ("Material".equals(element)) {
+ setMaterialName(content);
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+ @Override
+ public Streamer getComponent() {
+ return streamer;
+ }
+
+ /**
+ * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not
+ * public in all components.
+ *
+ * @param position the OpenRocket position
+ */
+ @Override
+ public void setRelativePosition(RocketComponent.Position position) {
+ streamer.setRelativePosition(position);
+ }
+
+ /**
+ * Get the required type of material for this component.
+ *
+ * @return BULK
+ */
+ @Override
+ public Material.Type getMaterialType() {
+ return Material.Type.SURFACE;
+ }
+}
+
--- /dev/null
+/*
+ * TransitionHandler.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.file.simplesax.ElementHandler;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.Transition;
+import org.xml.sax.SAXException;
+
+import java.util.HashMap;
+
+/**
+ * The SAX handler for Transition components.
+ */
+class TransitionHandler extends BaseHandler<Transition> {
+ /**
+ * The OpenRocket Transition.
+ */
+ private final Transition transition = new Transition();
+
+ /**
+ * The wall thickness. Used for hollow nose cones.
+ */
+ private double thickness = 0d;
+
+ /**
+ * Constructor.
+ *
+ * @param c the parent component
+ * @throws IllegalArgumentException thrown if <code>c</code> is null
+ */
+ public TransitionHandler(RocketComponent c) throws IllegalArgumentException {
+ if (c == null) {
+ throw new IllegalArgumentException("The parent of a transition may not be null.");
+ }
+ c.addChild(transition);
+ }
+
+ @Override
+ public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
+ return PlainTextHandler.INSTANCE;
+ }
+
+ @Override
+ public void closeElement(String element, HashMap<String, String> attributes,
+ String content, WarningSet warnings) throws SAXException {
+ super.closeElement(element, attributes, content, warnings);
+
+ try {
+ if ("ShapeCode".equals(element)) {
+ transition.setType(RocksimNoseConeCode.fromCode(Integer.parseInt(content)).asOpenRocket());
+ }
+ if ("Len".equals(element)) {
+ transition.setLength(Math.max(0, Double.parseDouble(
+ content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH));
+ }
+ if ("FrontDia".equals(element)) {
+ transition.setForeRadius(Math.max(0, Double.parseDouble(
+ content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS));
+ }
+ if ("RearDia".equals(element)) {
+ transition.setAftRadius(Math.max(0, Double.parseDouble(
+ content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS));
+ }
+ if ("WallThickness".equals(element)) {
+ thickness = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ }
+ if ("FrontShoulderDia".equals(element)) {
+ transition.setForeShoulderRadius(Math.max(0d, Double.parseDouble(
+ content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS));
+ }
+ if ("RearShoulderDia".equals(element)) {
+ transition.setAftShoulderRadius(Math.max(0d, Double.parseDouble(
+ content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS));
+ }
+ if ("FrontShoulderLen".equals(element)) {
+ transition.setForeShoulderLength(Math.max(0d, Double.parseDouble(
+ content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH));
+ }
+ if ("RearShoulderLen".equals(element)) {
+ transition.setAftShoulderLength(Math.max(0d, Double.parseDouble(
+ content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH));
+ }
+ if ("ShapeParameter".equals(element)) {
+ if (Transition.Shape.POWER.equals(transition.getType()) ||
+ Transition.Shape.HAACK.equals(transition.getType()) ||
+ Transition.Shape.PARABOLIC.equals(transition.getType())) {
+ transition.setShapeParameter(Double.parseDouble(content));
+ }
+ }
+ if ("ConstructionType".equals(element)) {
+ int typeCode = Integer.parseInt(content);
+ if (typeCode == 0) {
+ //SOLID
+ transition.setFilled(true);
+ }
+ else if (typeCode == 1) {
+ //HOLLOW
+ transition.setFilled(false);
+ }
+ }
+ if ("FinishCode".equals(element)) {
+ transition.setFinish(RocksimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket());
+ }
+ if ("Material".equals(element)) {
+ setMaterialName(content);
+ }
+ }
+ catch (NumberFormatException nfe) {
+ warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
+ }
+ }
+
+ @Override
+ public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ throws SAXException {
+ super.endHandler(element, attributes, content, warnings);
+
+ if (transition.isFilled()) {
+ transition.setAftShoulderThickness(transition.getAftShoulderRadius());
+ transition.setForeShoulderThickness(transition.getForeShoulderRadius());
+ }
+ else {
+ transition.setThickness(thickness);
+ transition.setAftShoulderThickness(thickness);
+ transition.setForeShoulderThickness(thickness);
+ }
+ }
+
+
+ @Override
+ public Transition getComponent() {
+ return transition;
+ }
+
+ /**
+ * Get the required type of material for this component.
+ *
+ * @return BULK
+ */
+ public Material.Type getMaterialType() {
+ return Material.Type.BULK;
+ }
+
+
+}
+
private static final RocketSaver ROCKET_SAVER = new OpenRocketSaver();
- /**
- * File filter for filtering only rocket designs.
- */
- private static final FileFilter ROCKET_DESIGN_FILTER = new FileFilter() {
- @Override
- public String getDescription() {
- return "OpenRocket designs (*.ork)";
- }
- @Override
- public boolean accept(File f) {
- if (f.isDirectory())
- return true;
- String name = f.getName().toLowerCase();
- return name.endsWith(".ork") || name.endsWith(".ork.gz");
- }
- };
-
-
+ // FileFilters for different types of rocket design files
+ private static final FileFilter ALL_DESIGNS_FILTER =
+ new SimpleFileFilter("All rocket designs (*.ork; *.rkt)",
+ ".ork", ".ork.gz", ".rkt", ".rkt.gz");
+
+ private static final FileFilter OPENROCKET_DESIGN_FILTER =
+ new SimpleFileFilter("OpenRocket designs (*.ork)", ".ork", ".ork.gz");
+
+ private static final FileFilter ROCKSIM_DESIGN_FILTER =
+ new SimpleFileFilter("RockSim designs (*.rkt)", ".rkt", ".rkt.gz");
+
+
+
public static final int COMPONENT_TAB = 0;
public static final int SIMULATION_TAB = 1;
private void openAction() {
JFileChooser chooser = new JFileChooser();
- chooser.setFileFilter(ROCKET_DESIGN_FILTER);
+
+ chooser.addChoosableFileFilter(ALL_DESIGNS_FILTER);
+ chooser.addChoosableFileFilter(OPENROCKET_DESIGN_FILTER);
+ chooser.addChoosableFileFilter(ROCKSIM_DESIGN_FILTER);
+ chooser.setFileFilter(ALL_DESIGNS_FILTER);
+
+ chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
chooser.setMultiSelectionEnabled(true);
chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
if (chooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
-
private static boolean open(URL url, BasicFrame parent) {
String filename = null;
File file = document.getFile();
if (file==null) {
return saveAsAction();
- } else {
- return saveAs(file);
}
+
+ // Saving RockSim designs is not supported
+ if (ROCKSIM_DESIGN_FILTER.accept(file)) {
+ file = new File(file.getAbsolutePath().replaceAll(".[rR][kK][tT](.[gG][zZ])?$",
+ ".ork"));
+
+ int option = JOptionPane.showConfirmDialog(this, new Object[] {
+ "Saving designs in RockSim format is not supported.",
+ "Save in OpenRocket format instead ("+file.getName()+")?"
+ }, "Save "+file.getName(), JOptionPane.YES_NO_OPTION,
+ JOptionPane.QUESTION_MESSAGE, null);
+ if (option != JOptionPane.YES_OPTION)
+ return false;
+
+ document.setFile(file);
+ }
+ return saveAs(file);
}
private boolean saveAsAction() {
File file = null;
while (file == null) {
+ // TODO: HIGH: what if *.rkt chosen?
StorageOptionChooser storageChooser =
new StorageOptionChooser(document, document.getDefaultStorageOptions());
JFileChooser chooser = new JFileChooser();
- chooser.setFileFilter(ROCKET_DESIGN_FILTER);
+ chooser.setFileFilter(OPENROCKET_DESIGN_FILTER);
chooser.setCurrentDirectory(Prefs.getDefaultDirectory());
chooser.setAccessory(storageChooser);
if (document.getFile() != null)
--- /dev/null
+package net.sf.openrocket.gui.main;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+/**
+ * A FileFilter similar to FileNameExtensionFilter except that
+ * it allows multipart extensions (.ork.gz).
+ *
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class SimpleFileFilter extends FileFilter {
+
+ private final String description;
+ private final String[] extensions;
+
+
+ /**
+ * Sole constructor.
+ *
+ * @param description the description of this file filter.
+ * @param extensions an array of extensions that match this filter.
+ */
+ public SimpleFileFilter(String description, String ... extensions) {
+ this.description = description;
+ this.extensions = new String[extensions.length];
+ for (int i=0; i<extensions.length; i++) {
+ String ext = extensions[i].toLowerCase();
+ if (ext.charAt(0) == '.') {
+ this.extensions[i] = ext;
+ } else {
+ this.extensions[i] = '.' + ext;
+ }
+ }
+ }
+
+
+
+ @Override
+ public boolean accept(File file) {
+ if (file == null)
+ return false;
+ if (file.isDirectory())
+ return true;
+
+ String filename = file.getName();
+ filename = filename.toLowerCase();
+ for (String ext: extensions) {
+ if (filename.endsWith(ext))
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+}
--- /dev/null
+/*
+ * BaseRocksimTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Field;
+
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+
+/**
+ * A base class for the Rocksim tests. Includes code from the junitx.addons project.
+ */
+public abstract class BaseRocksimTest extends TestCase {
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public BaseRocksimTest(String name) {
+ super(name);
+ }
+
+ public void assertContains (RocketComponent child, RocketComponent[] components) {
+ for (int i = 0; i < components.length; i++) {
+ if (components[i].equals(child)) {
+ return;
+ }
+ }
+ fail("Component array did not contain child.");
+ }
+
+ /**
+ * Returns the value of the field on the specified object. The name
+ * parameter is a <code>String</code> specifying the simple name of the
+ * desired field.<p>
+ *
+ * The object is first searched for any matching field. If no matching
+ * field is found, the superclasses are recursively searched.
+ *
+ * @exception NoSuchFieldException if a field with the specified name is
+ * not found.
+ */
+ public static Object getField(Object object,
+ String name)
+ throws NoSuchFieldException {
+ if (object == null) {
+ throw new IllegalArgumentException("Invalid null object argument");
+ }
+ for (Class cls = object.getClass();
+ cls != null;
+ cls = cls.getSuperclass()) {
+ try {
+ Field field = cls.getDeclaredField(name);
+ field.setAccessible(true);
+ return field.get(object);
+ } catch (Exception ex) {
+ /* in case of an exception, we will throw a new
+ * NoSuchFieldException object */
+ ;
+ }
+ }
+ throw new NoSuchFieldException("Could get value for field " +
+ object.getClass().getName() + "." + name);
+ }
+
+ /**
+ * Returns the value of the field on the specified class. The name
+ * parameter is a <code>String</code> specifying the simple name of the
+ * desired field.<p>
+ *
+ * The class is first searched for any matching field. If no matching
+ * field is found, the superclasses are recursively searched.
+ *
+ * @exception NoSuchFieldException if a field with the specified name is
+ * not found.
+ */
+ public static Object getField(Class cls,
+ String name)
+ throws NoSuchFieldException {
+ if (cls == null) {
+ throw new IllegalArgumentException("Invalid null cls argument");
+ }
+ Class base = cls;
+ while (base != null) {
+ try {
+ Field field = base.getDeclaredField(name);
+ field.setAccessible(true);
+ return field.get(base);
+ } catch (Exception ex) {
+ /* in case of an exception, we will throw a new
+ * NoSuchFieldException object */
+ ;
+ }
+ base = base.getSuperclass();
+ }
+ throw new NoSuchFieldException("Could get value for static field " +
+ cls.getName() + "." + name);
+ }
+
+
+}
--- /dev/null
+/*
+ * BodyTubeHandlerTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.database.Databases;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.Stage;
+import net.sf.openrocket.rocketcomponent.ExternalComponent;
+
+import java.util.HashMap;
+
+/**
+ * BodyTubeHandler Tester.
+ *
+ */
+public class BodyTubeHandlerTest extends BaseRocksimTest {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = BodyTubeHandler.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = BodyTubeHandlerTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(BodyTubeHandlerTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public BodyTubeHandlerTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+
+ /**
+ * Method: constructor
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testConstructor() throws Exception {
+
+ try {
+ new BodyTubeHandler(null);
+ fail("Should have thrown IllegalArgumentException");
+ }
+ catch (IllegalArgumentException iae) {
+ //success
+ }
+
+ Stage stage = new Stage();
+ BodyTubeHandler handler = new BodyTubeHandler(stage);
+ BodyTube component = (BodyTube) getField(handler, "bodyTube");
+ assertContains(component, stage.getChildren());
+ }
+
+ /**
+ * Method: openElement(String element, HashMap<String, String> attributes, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testOpenElement() throws Exception {
+ assertEquals(PlainTextHandler.INSTANCE, new BodyTubeHandler(new Stage()).openElement(null, null, null));
+ assertNotNull(new BodyTubeHandler(new Stage()).openElement("AttachedParts", null, null));
+ }
+
+ /**
+ *
+ * Method: closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testCloseElement() throws Exception {
+ Stage stage = new Stage();
+ BodyTubeHandler handler = new BodyTubeHandler(stage);
+ BodyTube component = (BodyTube) getField(handler, "bodyTube");
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ handler.closeElement("OD", attributes, "-1", warnings);
+ assertEquals(0d, component.getInnerRadius());
+ handler.closeElement("OD", attributes, "0", warnings);
+ assertEquals(0d, component.getInnerRadius());
+ handler.closeElement("OD", attributes, "75", warnings);
+ assertEquals(75d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getInnerRadius());
+ handler.closeElement("OD", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("ID", attributes, "-1", warnings);
+ assertEquals(0d, component.getInnerRadius());
+ handler.closeElement("ID", attributes, "0", warnings);
+ assertEquals(0d, component.getInnerRadius());
+ handler.closeElement("ID", attributes, "75", warnings);
+ assertEquals(75d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getInnerRadius());
+ handler.closeElement("ID", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Len", attributes, "-1", warnings);
+ assertEquals(0d, component.getLength());
+ handler.closeElement("Len", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("IsMotorMount", attributes, "1", warnings);
+ assertTrue(component.isMotorMount());
+ handler.closeElement("IsMotorMount", attributes, "0", warnings);
+ assertFalse(component.isMotorMount());
+ handler.closeElement("IsMotorMount", attributes, "foo", warnings);
+ assertFalse(component.isMotorMount());
+
+ handler.closeElement("EngineOverhang", attributes, "-1", warnings);
+ assertEquals(-1d/RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getMotorOverhang());
+ handler.closeElement("EngineOverhang", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getMotorOverhang());
+ handler.closeElement("EngineOverhang", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getMotorOverhang());
+ handler.closeElement("EngineOverhang", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("FinishCode", attributes, "-1", warnings);
+ assertEquals(ExternalComponent.Finish.NORMAL, component.getFinish());
+ handler.closeElement("FinishCode", attributes, "100", warnings);
+ assertEquals(ExternalComponent.Finish.NORMAL, component.getFinish());
+ handler.closeElement("FinishCode", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Name", attributes, "Test Name", warnings);
+ assertEquals("Test Name", component.getName());
+ }
+
+ /**
+ * Method: getComponent()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetComponent() throws Exception {
+ assertTrue(new BodyTubeHandler(new Stage()).getComponent() instanceof BodyTube);
+ }
+
+ /**
+ * Method: getMaterialType()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetMaterialType() throws Exception {
+ assertEquals(Material.Type.BULK, new BodyTubeHandler(new Stage()).getMaterialType());
+ }
+
+}
--- /dev/null
+/*
+ * FinSetHandlerTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
+import net.sf.openrocket.rocketcomponent.FinSet;
+import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
+import net.sf.openrocket.util.Coordinate;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * FinSetHandler Tester.
+ *
+ */
+public class FinSetHandlerTest extends TestCase {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = FinSetHandler.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = FinSetHandlerTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(FinSetHandlerTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public FinSetHandlerTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+
+ /**
+ * Method: asOpenRocket(WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testAsOpenRocket() throws Exception {
+
+ FinSetHandler dto = new FinSetHandler(new BodyTube());
+
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ dto.closeElement("Name", attributes, "The name", warnings);
+ dto.closeElement("ShapeCode", attributes, "0", warnings);
+ dto.closeElement("Xb", attributes, "2", warnings);
+ dto.closeElement("FinCount", attributes, "4", warnings);
+ dto.closeElement("RootChord", attributes, "10", warnings);
+ dto.closeElement("TipChord", attributes, "11", warnings);
+ dto.closeElement("SemiSpan", attributes, "12", warnings);
+ dto.closeElement("MidChordLen", attributes, "13", warnings);
+ dto.closeElement("SweepDistance", attributes, "14", warnings);
+ dto.closeElement("Thickness", attributes, "200", warnings);
+ dto.closeElement("TipShapeCode", attributes, "1", warnings);
+ dto.closeElement("TabLength", attributes, "400", warnings);
+ dto.closeElement("TabDepth", attributes, "500", warnings);
+ dto.closeElement("TabOffset", attributes, "30", warnings);
+ dto.closeElement("RadialAngle", attributes, ".123", warnings);
+ dto.closeElement("PointList", attributes, "20,0|2,2|0,0", warnings);
+ dto.closeElement("LocationMode", attributes, "0", warnings);
+
+ WarningSet set = new WarningSet();
+ FinSet fins = dto.asOpenRocket(set);
+ assertNotNull(fins);
+ assertEquals(0, set.size());
+
+ assertEquals("The name", fins.getName());
+ assertTrue(fins instanceof TrapezoidFinSet);
+ assertEquals(4, fins.getFinCount());
+
+ assertEquals(0.2d, fins.getThickness());
+ assertEquals(0.4d, fins.getTabLength());
+ assertEquals(0.5d, fins.getTabHeight());
+ assertEquals(0.03d, fins.getTabShift());
+ assertEquals(.123d, fins.getBaseRotation());
+
+ dto.closeElement("ShapeCode", attributes, "1", warnings);
+ fins = dto.asOpenRocket(set);
+ assertNotNull(fins);
+ assertEquals(0, set.size());
+
+ assertEquals("The name", fins.getName());
+ assertTrue(fins instanceof EllipticalFinSet);
+ assertEquals(4, fins.getFinCount());
+
+ assertEquals(0.2d, fins.getThickness());
+ assertEquals(0.4d, fins.getTabLength());
+ assertEquals(0.5d, fins.getTabHeight());
+ assertEquals(0.03d, fins.getTabShift());
+ assertEquals(.123d, fins.getBaseRotation());
+ }
+
+
+ /**
+ * Method: toCoordinates(String pointList)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testToCoordinates() throws Exception {
+ FinSetHandler holder = new FinSetHandler(new BodyTube());
+ Method method = FinSetHandler.class.getDeclaredMethod("toCoordinates", String.class, WarningSet.class);
+ method.setAccessible(true);
+
+ WarningSet warnings = new WarningSet();
+ //Null finlist
+ String finlist = null;
+ Coordinate[] result = (Coordinate[])method.invoke(holder, finlist, warnings);
+ assertNotNull(result);
+ assertTrue(0 == result.length);
+
+ //Empty string finlist
+ finlist = "";
+ result = (Coordinate[])method.invoke(holder, finlist, warnings);
+ assertNotNull(result);
+ assertTrue(0 == result.length);
+
+ //Invalid finlist (only x coordinate)
+ finlist = "10.0";
+ result = (Coordinate[])method.invoke(holder, finlist, warnings);
+ assertNotNull(result);
+ assertTrue(0 == result.length);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ //Invalid finlist (non-numeric character)
+ finlist = "10.0,asdf";
+ result = (Coordinate[])method.invoke(holder, finlist, warnings);
+ assertNotNull(result);
+ assertTrue(0 == result.length);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ //Invalid finlist (all delimiters)
+ finlist = "||||||";
+ result = (Coordinate[])method.invoke(holder, finlist, warnings);
+ assertNotNull(result);
+ assertTrue(0 == result.length);
+ assertEquals(0, warnings.size());
+ warnings.clear();
+
+ //One point finlist - from a parsing view it's valid; from a practical view it may not be, but that's outside
+ //the scope of this test case
+ finlist = "10.0,5.0";
+ result = (Coordinate[])method.invoke(holder, finlist, warnings);
+ assertNotNull(result);
+ assertTrue(1 == result.length);
+ assertEquals(0, warnings.size());
+ warnings.clear();
+
+ //Two point finlist - from a parsing view it's valid; from a practical view it may not be, but that's outside
+ //the scope of this test case
+ finlist = "10.0,5.0|3.3,4.4";
+ result = (Coordinate[])method.invoke(holder, finlist, warnings);
+ assertNotNull(result);
+ assertTrue(2 == result.length);
+ assertEquals(0, warnings.size());
+ warnings.clear();
+
+ //Normal four point finlist.
+ finlist = "518.16,0|517.494,37.2145|1.31261,6.77283|0,0|";
+ result = (Coordinate[])method.invoke(holder, finlist, warnings);
+ assertNotNull(result);
+ assertTrue(4 == result.length);
+ assertEquals(new Coordinate(0,0), result[0]);
+ assertEquals(0, warnings.size());
+ warnings.clear();
+
+ //Normal four point finlist with spaces.
+ finlist = "518.16 , 0 | 517.494 , 37.2145 | 1.31261,6.77283|0,0|";
+ result = (Coordinate[])method.invoke(holder, finlist, warnings);
+ assertNotNull(result);
+ assertTrue(4 == result.length);
+ assertEquals(new Coordinate(0,0), result[0]);
+ assertEquals(new Coordinate(.51816,0), result[3]);
+ assertEquals(0, warnings.size());
+ warnings.clear();
+
+ //Reversed Normal four point finlist.
+ finlist = "0,0|1.31261,6.77283|517.494,37.2145|518.16,0|";
+ result = (Coordinate[])method.invoke(holder, finlist, warnings);
+ assertNotNull(result);
+ assertTrue(4 == result.length);
+ assertEquals(new Coordinate(0,0), result[0]);
+ assertEquals(new Coordinate(.51816,0), result[3]);
+ assertEquals(0, warnings.size());
+ warnings.clear();
+
+ }
+}
--- /dev/null
+/*
+ * InnerBodyTubeHandlerTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.database.Databases;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.InnerTube;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.Stage;
+
+import java.util.HashMap;
+
+/**
+ * InnerBodyTubeHandler Tester.
+ *
+ */
+public class InnerBodyTubeHandlerTest extends BaseRocksimTest {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = InnerBodyTubeHandler.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = InnerBodyTubeHandlerTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(InnerBodyTubeHandlerTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public InnerBodyTubeHandlerTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+
+ /**
+ * Method: constructor
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testConstructor() throws Exception {
+
+ try {
+ new InnerBodyTubeHandler(null);
+ fail("Should have thrown IllegalArgumentException");
+ }
+ catch (IllegalArgumentException iae) {
+ //success
+ }
+
+ BodyTube tube = new BodyTube();
+ InnerBodyTubeHandler handler = new InnerBodyTubeHandler(tube);
+ InnerTube component = (InnerTube) getField(handler, "bodyTube");
+ assertContains(component, tube.getChildren());
+ }
+
+ /**
+ * Method: openElement(String element, HashMap<String, String> attributes, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testOpenElement() throws Exception {
+ assertEquals(PlainTextHandler.INSTANCE, new InnerBodyTubeHandler(new BodyTube()).openElement(null, null, null));
+ assertNotNull(new InnerBodyTubeHandler(new BodyTube()).openElement("AttachedParts", null, null));
+ }
+
+ /**
+ *
+ * Method: closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testCloseElement() throws Exception {
+ BodyTube tube = new BodyTube();
+ InnerBodyTubeHandler handler = new InnerBodyTubeHandler(tube);
+ InnerTube component = (InnerTube) getField(handler, "bodyTube");
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ handler.closeElement("OD", attributes, "-1", warnings);
+ assertEquals(0d, component.getInnerRadius());
+ handler.closeElement("OD", attributes, "0", warnings);
+ assertEquals(0d, component.getInnerRadius());
+ handler.closeElement("OD", attributes, "75", warnings);
+ assertEquals(75d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getInnerRadius());
+ handler.closeElement("OD", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("ID", attributes, "-1", warnings);
+ assertEquals(0d, component.getInnerRadius());
+ handler.closeElement("ID", attributes, "0", warnings);
+ assertEquals(0d, component.getInnerRadius());
+ handler.closeElement("ID", attributes, "75", warnings);
+ assertEquals(75d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getInnerRadius());
+ handler.closeElement("ID", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Len", attributes, "-1", warnings);
+ assertEquals(0d, component.getLength());
+ handler.closeElement("Len", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("IsMotorMount", attributes, "1", warnings);
+ assertTrue(component.isMotorMount());
+ handler.closeElement("IsMotorMount", attributes, "0", warnings);
+ assertFalse(component.isMotorMount());
+ handler.closeElement("IsMotorMount", attributes, "foo", warnings);
+ assertFalse(component.isMotorMount());
+
+ handler.closeElement("EngineOverhang", attributes, "-1", warnings);
+ assertEquals(-1d/RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getMotorOverhang());
+ handler.closeElement("EngineOverhang", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getMotorOverhang());
+ handler.closeElement("EngineOverhang", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getMotorOverhang());
+ handler.closeElement("EngineOverhang", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Name", attributes, "Test Name", warnings);
+ assertEquals("Test Name", component.getName());
+ }
+
+ /**
+ * Method: setRelativePosition(RocketComponent.Position position)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testSetRelativePosition() throws Exception {
+ BodyTube tube = new BodyTube();
+ InnerBodyTubeHandler handler = new InnerBodyTubeHandler(tube);
+ InnerTube component = (InnerTube) getField(handler, "bodyTube");
+ handler.setRelativePosition(RocketComponent.Position.ABSOLUTE);
+ assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition());
+ }
+
+ /**
+ * Method: getComponent()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetComponent() throws Exception {
+ assertTrue(new InnerBodyTubeHandler(new BodyTube()).getComponent() instanceof InnerTube);
+ }
+
+ /**
+ * Method: getMaterialType()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetMaterialType() throws Exception {
+ assertEquals(Material.Type.BULK, new InnerBodyTubeHandler(new BodyTube()).getMaterialType());
+ }
+
+
+
+
+}
--- /dev/null
+/*
+ * LaunchLugHandlerTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.database.Databases;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.ExternalComponent;
+import net.sf.openrocket.rocketcomponent.LaunchLug;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+
+import java.util.HashMap;
+
+/**
+ * LaunchLugHandler Tester.
+ *
+ */
+public class LaunchLugHandlerTest extends BaseRocksimTest {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = LaunchLugHandler.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = LaunchLugHandlerTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(LaunchLugHandlerTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public LaunchLugHandlerTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Method: constructor
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testConstructor() throws Exception {
+
+ try {
+ new LaunchLugHandler(null);
+ fail("Should have thrown IllegalArgumentException");
+ }
+ catch (IllegalArgumentException iae) {
+ //success
+ }
+
+ BodyTube tube = new BodyTube();
+ LaunchLugHandler handler = new LaunchLugHandler(tube);
+ LaunchLug component = (LaunchLug) getField(handler, "lug");
+ assertContains(component, tube.getChildren());
+ }
+
+ /**
+ * Method: openElement(String element, HashMap<String, String> attributes, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testOpenElement() throws Exception {
+ assertEquals(PlainTextHandler.INSTANCE, new LaunchLugHandler(new BodyTube()).openElement(null, null, null));
+ }
+
+ /**
+ *
+ * Method: closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testCloseElement() throws Exception {
+ BodyTube tube = new BodyTube();
+ LaunchLugHandler handler = new LaunchLugHandler(tube);
+ LaunchLug component = (LaunchLug) getField(handler, "lug");
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ handler.closeElement("OD", attributes, "-1", warnings);
+ assertEquals(0d, component.getRadius());
+ handler.closeElement("OD", attributes, "0", warnings);
+ assertEquals(0d, component.getRadius());
+ handler.closeElement("OD", attributes, "75", warnings);
+ assertEquals(75d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getRadius());
+ handler.closeElement("OD", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("ID", attributes, "-1", warnings);
+ assertEquals(0d, component.getInnerRadius());
+ handler.closeElement("ID", attributes, "0", warnings);
+ assertEquals(0d, component.getInnerRadius());
+ handler.closeElement("ID", attributes, "75", warnings);
+ assertEquals(75d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getInnerRadius());
+ handler.closeElement("ID", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Len", attributes, "-1", warnings);
+ assertEquals(0d, component.getLength());
+ handler.closeElement("Len", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("FinishCode", attributes, "-1", warnings);
+ assertEquals(ExternalComponent.Finish.NORMAL, component.getFinish());
+ handler.closeElement("FinishCode", attributes, "100", warnings);
+ assertEquals(ExternalComponent.Finish.NORMAL, component.getFinish());
+ handler.closeElement("FinishCode", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Name", attributes, "Test Name", warnings);
+ assertEquals("Test Name", component.getName());
+ }
+
+ /**
+ * Method: setRelativePosition(RocketComponent.Position position)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testSetRelativePosition() throws Exception {
+ BodyTube tube = new BodyTube();
+ LaunchLugHandler handler = new LaunchLugHandler(tube);
+ LaunchLug component = (LaunchLug) getField(handler, "lug");
+ handler.setRelativePosition(RocketComponent.Position.ABSOLUTE);
+ assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition());
+ }
+
+ /**
+ * Method: getComponent()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetComponent() throws Exception {
+ assertTrue(new LaunchLugHandler(new BodyTube()).getComponent() instanceof LaunchLug);
+ }
+
+ /**
+ * Method: getMaterialType()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetMaterialType() throws Exception {
+ assertEquals(Material.Type.BULK, new LaunchLugHandler(new BodyTube()).getMaterialType());
+ }
+
+
+}
--- /dev/null
+/*
+ * MassObjectHandlerTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.MassComponent;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.Stage;
+import net.sf.openrocket.rocketcomponent.NoseCone;
+import net.sf.openrocket.rocketcomponent.Transition;
+import net.sf.openrocket.aerodynamics.WarningSet;
+
+import java.util.HashMap;
+
+/**
+ * MassObjectHandler Tester.
+ *
+ */
+public class MassObjectHandlerTest extends BaseRocksimTest {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = MassObjectHandler.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = MassObjectHandlerTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(MassObjectHandlerTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public MassObjectHandlerTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Method: constructor
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testConstructor() throws Exception {
+
+ try {
+ new MassObjectHandler(null);
+ fail("Should have thrown IllegalArgumentException");
+ }
+ catch (IllegalArgumentException iae) {
+ //success
+ }
+
+ BodyTube tube = new BodyTube();
+ MassObjectHandler handler = new MassObjectHandler(tube);
+ MassComponent component = (MassComponent) getField(handler, "mass");
+ assertContains(component, tube.getChildren());
+ }
+
+ /**
+ * Method: openElement(String element, HashMap<String, String> attributes, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testOpenElement() throws Exception {
+ assertEquals(PlainTextHandler.INSTANCE, new MassObjectHandler(new BodyTube()).openElement(null, null, null));
+ }
+
+ /**
+ *
+ * Method: closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testCloseElement() throws Exception {
+ BodyTube tube = new BodyTube();
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ MassObjectHandler handler = new MassObjectHandler(tube);
+ MassComponent component = (MassComponent) getField(handler, "mass");
+
+ handler.closeElement("Len", attributes, "-1", warnings);
+ assertEquals(0d, component.getLength());
+ handler.closeElement("Len", attributes, "10", warnings);
+ assertEquals(10d / (MassObjectHandler.MASS_LEN_FUDGE_FACTOR * RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH)
+ , component.getLength());
+ handler.closeElement("Len", attributes, "10.0", warnings);
+ assertEquals(10d / (MassObjectHandler.MASS_LEN_FUDGE_FACTOR * RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH)
+ , component.getLength());
+ handler.closeElement("Len", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("KnownMass", attributes, "-1", warnings);
+ assertEquals(0d, component.getComponentMass());
+ handler.closeElement("KnownMass", attributes, "100", warnings);
+ assertEquals(100d / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS, component.getComponentMass());
+ handler.closeElement("KnownMass", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ }
+
+ /**
+ * Method: setRelativePosition(RocketComponent.Position position)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testSetRelativePosition() throws Exception {
+ BodyTube tube = new BodyTube();
+ MassObjectHandler handler = new MassObjectHandler(tube);
+ MassComponent component = (MassComponent) getField(handler, "mass");
+ handler.setRelativePosition(RocketComponent.Position.ABSOLUTE);
+ assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition());
+ }
+
+ /**
+ * Method: getComponent()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetComponent() throws Exception {
+ assertTrue(new MassObjectHandler(new BodyTube()).getComponent() instanceof MassComponent);
+ }
+
+ /**
+ * Method: getMaterialType()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetMaterialType() throws Exception {
+ assertEquals(Material.Type.BULK, new MassObjectHandler(new BodyTube()).getMaterialType());
+ }
+
+
+}
--- /dev/null
+/*
+ * NoseConeHandlerTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.NoseCone;
+import net.sf.openrocket.rocketcomponent.Stage;
+import net.sf.openrocket.rocketcomponent.Transition;
+import net.sf.openrocket.rocketcomponent.ExternalComponent;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.database.Databases;
+
+import java.util.HashMap;
+
+/**
+ * NoseConeHandler Tester.
+ *
+ */
+public class NoseConeHandlerTest extends BaseRocksimTest {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = NoseConeHandler.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = NoseConeHandlerTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(NoseConeHandlerTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public NoseConeHandlerTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Method: constructor
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testConstructor() throws Exception {
+
+ try {
+ new NoseConeHandler(null);
+ fail("Should have thrown IllegalArgumentException");
+ }
+ catch (IllegalArgumentException iae) {
+ //success
+ }
+
+ Stage stage = new Stage();
+ NoseConeHandler handler = new NoseConeHandler(stage);
+ NoseCone component = (NoseCone) getField(handler, "noseCone");
+ assertContains(component, stage.getChildren());
+ }
+
+ /**
+ * Method: openElement(String element, HashMap<String, String> attributes, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testOpenElement() throws Exception {
+ assertEquals(PlainTextHandler.INSTANCE, new NoseConeHandler(new Stage()).openElement(null, null, null));
+ assertNotNull(new NoseConeHandler(new Stage()).openElement("AttachedParts", null, null));
+ }
+
+ /**
+ *
+ * Method: closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testCloseElement() throws Exception {
+
+ Stage stage = new Stage();
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ NoseConeHandler handler = new NoseConeHandler(stage);
+ NoseCone component = (NoseCone) getField(handler, "noseCone");
+
+ handler.closeElement("ShapeCode", attributes, "0", warnings);
+ assertEquals(Transition.Shape.CONICAL, component.getType());
+ handler.closeElement("ShapeCode", attributes, "1", warnings);
+ assertEquals(Transition.Shape.OGIVE, component.getType());
+ handler.closeElement("ShapeCode", attributes, "17", warnings);
+ assertEquals(RocksimNoseConeCode.PARABOLIC.asOpenRocket(), component.getType()); //test of default
+ handler.closeElement("ShapeCode", attributes, "foo", warnings);
+ assertNotNull(component.getType());
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Len", attributes, "-1", warnings);
+ assertEquals(0d, component.getLength());
+ handler.closeElement("Len", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("BaseDia", attributes, "-1", warnings);
+ assertEquals(0d, component.getAftRadius());
+ handler.closeElement("BaseDia", attributes, "100", warnings);
+ assertEquals(100d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getAftRadius());
+ handler.closeElement("BaseDia", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+
+ final double aft = 100d;
+ component.setAftRadius(aft);
+
+ handler.closeElement("ConstructionType", attributes, "0", warnings);
+ component.setAftShoulderRadius(1.1d);
+ handler.closeElement("WallThickness", attributes, "-1", warnings);
+ handler.endHandler("Transition", attributes, null, warnings);
+ assertEquals(component.getAftRadius(), component.getThickness());
+ assertEquals(component.getAftShoulderThickness(), component.getAftShoulderThickness());
+ handler.closeElement("WallThickness", attributes, "100", warnings);
+ handler.endHandler("Transition", attributes, null, warnings);
+ assertEquals(aft, component.getThickness());
+ handler.closeElement("WallThickness", attributes, "foo", warnings);
+ handler.endHandler("Transition", attributes, null, warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("ConstructionType", attributes, "1", warnings);
+ component.setAftShoulderRadius(1.1d);
+ handler.closeElement("WallThickness", attributes, "-1", warnings);
+ handler.endHandler("Transition", attributes, null, warnings);
+ assertEquals(0d, component.getThickness());
+ assertEquals(0d, component.getAftShoulderThickness());
+ handler.closeElement("WallThickness", attributes, "1.1", warnings);
+ handler.endHandler("Transition", attributes, null, warnings);
+ assertEquals(1.1d/RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getThickness());
+ assertEquals(1.1d/RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getAftShoulderThickness());
+
+ handler.closeElement("ShoulderLen", attributes, "-1", warnings);
+ assertEquals(0d, component.getAftShoulderLength());
+ handler.closeElement("ShoulderLen", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getAftShoulderLength());
+ handler.closeElement("ShoulderLen", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getAftShoulderLength());
+ handler.closeElement("ShoulderLen", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("ShoulderOD", attributes, "-1", warnings);
+ assertEquals(0d, component.getAftShoulderRadius());
+ handler.closeElement("ShoulderOD", attributes, "100", warnings);
+ assertEquals(100d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getAftShoulderRadius());
+ handler.closeElement("ShoulderOD", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ component.setType(Transition.Shape.HAACK);
+ handler.closeElement("ShapeParameter", attributes, "-1", warnings);
+ assertEquals(0d, component.getShapeParameter());
+ handler.closeElement("ShapeParameter", attributes, "100", warnings);
+ assertEquals(Transition.Shape.HAACK.maxParameter(), component.getShapeParameter());
+ handler.closeElement("ShapeParameter", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ assertEquals("Could not convert ShapeParameter value of foo. It is expected to be a number.",
+ warnings.iterator().next().toString());
+
+ warnings.clear();
+
+ component.setType(Transition.Shape.CONICAL);
+ component.setShapeParameter(0d);
+ handler.closeElement("ShapeParameter", attributes, "100", warnings);
+ assertEquals(0d, component.getShapeParameter());
+
+ handler.closeElement("FinishCode", attributes, "-1", warnings);
+ assertEquals(ExternalComponent.Finish.NORMAL, component.getFinish());
+ handler.closeElement("FinishCode", attributes, "100", warnings);
+ assertEquals(ExternalComponent.Finish.NORMAL, component.getFinish());
+ handler.closeElement("FinishCode", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Name", attributes, "Test Name", warnings);
+ assertEquals("Test Name", component.getName());
+
+ handler.closeElement("Material", attributes, "Some Material", warnings);
+ handler.endHandler("NoseCone", attributes, null, warnings);
+ assertTrue(component.getMaterial().getName().contains("Some Material"));
+ }
+
+ /**
+ * Method: getComponent()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetComponent() throws Exception {
+ assertTrue(new NoseConeHandler(new Stage()).getComponent() instanceof NoseCone);
+ }
+
+ /**
+ * Method: getMaterialType()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetMaterialType() throws Exception {
+ assertEquals(Material.Type.BULK, new NoseConeHandler(new Stage()).getMaterialType());
+ }
+}
--- /dev/null
+/*
+ * ParachuteHandlerTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.database.Databases;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.Parachute;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+
+import java.util.HashMap;
+
+/**
+ * ParachuteHandler Tester.
+ */
+public class ParachuteHandlerTest extends BaseRocksimTest {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = ParachuteHandler.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = ParachuteHandlerTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(ParachuteHandlerTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public ParachuteHandlerTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Method: openElement(String element, HashMap<String, String> attributes, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testOpenElement() throws Exception {
+ assertEquals(PlainTextHandler.INSTANCE, new ParachuteHandler(new BodyTube()).openElement(null, null, null));
+ }
+
+ /**
+ * Method: closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testCloseElement() throws Exception {
+
+ BodyTube tube = new BodyTube();
+ ParachuteHandler handler = new ParachuteHandler(tube);
+ Parachute component = (Parachute) getField(handler, "chute");
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ handler.closeElement("Name", attributes, "Test Name", warnings);
+ assertEquals("Test Name", component.getName());
+
+ handler.closeElement("DragCoefficient", attributes, "0.94", warnings);
+ assertEquals(0.94d, component.getCD());
+ handler.closeElement("DragCoefficient", attributes, "-0.94", warnings);
+ assertEquals(-0.94d, component.getCD());
+ handler.closeElement("DragCoefficient", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Dia", attributes, "-1", warnings);
+ assertEquals(-1d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getDiameter());
+ handler.closeElement("Dia", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getDiameter());
+ handler.closeElement("Dia", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("ShroudLineCount", attributes, "-1", warnings);
+ assertEquals(0, component.getLineCount());
+ handler.closeElement("ShroudLineCount", attributes, "10", warnings);
+ assertEquals(10, component.getLineCount());
+ handler.closeElement("ShroudLineCount", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("ShroudLineLen", attributes, "-1", warnings);
+ assertEquals(0d, component.getLineLength());
+ handler.closeElement("ShroudLineLen", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLineLength());
+ handler.closeElement("ShroudLineLen", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ }
+
+ /**
+ * Method: constructor
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testConstructor() throws Exception {
+
+ try {
+ new ParachuteHandler(null);
+ fail("Should have thrown IllegalArgumentException");
+ }
+ catch (IllegalArgumentException iae) {
+ //success
+ }
+
+ BodyTube tube = new BodyTube();
+ ParachuteHandler handler = new ParachuteHandler(tube);
+ Parachute component = (Parachute) getField(handler, "chute");
+ assertContains(component, tube.getChildren());
+ }
+
+ /**
+ * Method: setRelativePosition(RocketComponent.Position position)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testSetRelativePosition() throws Exception {
+ BodyTube tube = new BodyTube();
+ ParachuteHandler handler = new ParachuteHandler(tube);
+ Parachute component = (Parachute) getField(handler, "chute");
+ handler.setRelativePosition(RocketComponent.Position.ABSOLUTE);
+ assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition());
+ }
+
+ /**
+ * Method: getComponent()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetComponent() throws Exception {
+ assertTrue(new ParachuteHandler(new BodyTube()).getComponent() instanceof Parachute);
+ }
+
+ /**
+ * Method: getMaterialType()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetMaterialType() throws Exception {
+ assertEquals(Material.Type.SURFACE, new ParachuteHandler(new BodyTube()).getMaterialType());
+ }
+
+ /**
+ * Method: endHandler()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testEndHandler() throws Exception {
+ BodyTube tube = new BodyTube();
+ ParachuteHandler handler = new ParachuteHandler(tube);
+ Parachute component = (Parachute) getField(handler, "chute");
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ handler.closeElement("Xb", attributes, "-10", warnings);
+ handler.closeElement("LocationMode", attributes, "1", warnings);
+ handler.endHandler("Parachute", attributes, null, warnings);
+ assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition());
+ assertEquals(component.getPositionValue(), -10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+
+ handler.closeElement("Xb", attributes, "-10", warnings);
+ handler.closeElement("LocationMode", attributes, "2", warnings);
+ handler.endHandler("Parachute", attributes, null, warnings);
+ assertEquals(RocketComponent.Position.BOTTOM, component.getRelativePosition());
+ assertEquals(component.getPositionValue(), 10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ }
+
+
+}
--- /dev/null
+/*
+ * RingHandlerTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.database.Databases;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.CenteringRing;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+
+import java.util.HashMap;
+
+/**
+ * RingHandler Tester.
+ */
+public class RingHandlerTest extends BaseRocksimTest {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = RingHandler.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = RingHandlerTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(RingHandlerTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public RingHandlerTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Method: openElement(String element, HashMap<String, String> attributes, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testOpenElement() throws Exception {
+ assertEquals(PlainTextHandler.INSTANCE, new RingHandler(new BodyTube()).openElement(null, null, null));
+ }
+
+ /**
+ * Method: closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testCloseElement() throws Exception {
+
+ BodyTube tube = new BodyTube();
+ RingHandler handler = new RingHandler(tube);
+ CenteringRing component = (CenteringRing) getField(handler, "ring");
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ handler.closeElement("OD", attributes, "0", warnings);
+ assertEquals(0d, component.getOuterRadius());
+ handler.closeElement("OD", attributes, "75", warnings);
+ assertEquals(75d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getOuterRadius());
+ handler.closeElement("OD", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("ID", attributes, "0", warnings);
+ assertEquals(0d, component.getInnerRadius());
+ handler.closeElement("ID", attributes, "75", warnings);
+ assertEquals(75d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getInnerRadius());
+ handler.closeElement("ID", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Len", attributes, "-1", warnings);
+ assertEquals(0d, component.getLength());
+ handler.closeElement("Len", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Name", attributes, "Test Name", warnings);
+ assertEquals("Test Name", component.getName());
+ }
+
+
+ /**
+ * Method: constructor
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testConstructor() throws Exception {
+
+ try {
+ new RingHandler(null);
+ fail("Should have thrown IllegalArgumentException");
+ }
+ catch (IllegalArgumentException iae) {
+ //success
+ }
+
+ BodyTube tube = new BodyTube();
+ RingHandler handler = new RingHandler(tube);
+ CenteringRing component = (CenteringRing) getField(handler, "ring");
+ assertContains(component, tube.getChildren());
+ }
+
+ /**
+ * Method: setRelativePosition(RocketComponent.Position position)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testSetRelativePosition() throws Exception {
+ BodyTube tube = new BodyTube();
+ RingHandler handler = new RingHandler(tube);
+ CenteringRing component = (CenteringRing) getField(handler, "ring");
+ handler.setRelativePosition(RocketComponent.Position.ABSOLUTE);
+ assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition());
+ }
+
+ /**
+ * Method: getComponent()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetComponent() throws Exception {
+ assertTrue(new RingHandler(new BodyTube()).getComponent() instanceof CenteringRing);
+ }
+
+ /**
+ * Method: getMaterialType()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetMaterialType() throws Exception {
+ assertEquals(Material.Type.BULK, new RingHandler(new BodyTube()).getMaterialType());
+ }
+
+
+}
--- /dev/null
+/*
+ * RocksimContentHandlerTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.framework.TestCase;
+
+/**
+ * RocksimContentHandler Tester.
+ *
+ */
+public class RocksimContentHandlerTest extends TestCase {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = RocksimContentHandler.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = RocksimContentHandlerTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(RocksimContentHandlerTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public RocksimContentHandlerTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ *
+ * Method: getDocument()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetDocument() throws Exception {
+ RocksimContentHandler handler = new RocksimContentHandler();
+ assertNotNull(handler.getDocument());
+ }
+
+}
--- /dev/null
+/*
+ * RocksimLoaderTest.java
+ *
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.rocketcomponent.Stage;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+
+/**
+ * RocksimLoader Tester.
+ *
+ */
+public class RocksimLoaderTest extends TestCase {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = RocksimLoader.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = RocksimLoaderTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(RocksimLoaderTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public RocksimLoaderTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ *
+ * Method: loadFromStream(InputStream source)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testLoadFromStream() throws Exception {
+ RocksimLoader loader = new RocksimLoader();
+ //Stupid single stage rocket
+ InputStream stream = this.getClass().getResourceAsStream("rocksimTestRocket1.rkt");
+ OpenRocketDocument doc = loader.loadFromStream(new BufferedInputStream(stream));
+
+ assertNotNull(doc);
+ Rocket rocket = doc.getRocket();
+ assertNotNull(rocket);
+ assertEquals("FooBar Test", doc.getRocket().getName());
+ assertTrue(loader.getWarnings().isEmpty());
+
+ stream = this.getClass().getResourceAsStream("rocksimTestRocket2.rkt");
+ doc = loader.loadFromStream(new BufferedInputStream(stream));
+
+ assertNotNull(doc);
+ rocket = doc.getRocket();
+ assertNotNull(rocket);
+
+ //Do some simple asserts; the important thing here is just validating that the mass and cg were
+ //not overridden for each stage.
+ assertEquals("Three Stage Everything Included Rocket", doc.getRocket().getName());
+ assertEquals(1, loader.getWarnings().size());
+ assertEquals(3, rocket.getStageCount());
+ Stage stage1 = (Stage)rocket.getChild(0);
+ assertFalse(stage1.isMassOverridden());
+ assertFalse(stage1.isCGOverridden());
+ Stage stage2 = (Stage)rocket.getChild(1);
+ assertFalse(stage2.isMassOverridden());
+ assertFalse(stage2.isCGOverridden());
+ Stage stage3 = (Stage)rocket.getChild(2);
+ assertFalse(stage3.isMassOverridden());
+ assertFalse(stage3.isCGOverridden());
+
+ stream = this.getClass().getResourceAsStream("rocksimTestRocket3.rkt");
+ doc = loader.loadFromStream(new BufferedInputStream(stream));
+
+ assertNotNull(doc);
+ rocket = doc.getRocket();
+ assertNotNull(rocket);
+ assertEquals("Three Stage Everything Included Rocket - Override Total Mass/CG", doc.getRocket().getName());
+ assertEquals(3, rocket.getStageCount());
+ stage1 = (Stage)rocket.getChild(0);
+ stage2 = (Stage)rocket.getChild(1);
+ stage3 = (Stage)rocket.getChild(2);
+
+ //Do some 1st level and simple asserts; the idea here is to not do a deep validation as that
+ //should have been covered elsewhere. Assert that the stage overrides are correct.
+ assertEquals(2, stage1.getChildCount());
+ assertEquals("Nose cone", stage1.getChild(0).getName());
+ assertEquals("Body tube", stage1.getChild(1).getName());
+ assertTrue(stage1.isMassOverridden());
+ assertEquals(0.185d, stage1.getOverrideMass());
+ assertTrue(stage1.isCGOverridden());
+ assertEquals(0.3d, stage1.getOverrideCG().x);
+ assertEquals(4, loader.getWarnings().size());
+
+ assertEquals(1, stage2.getChildCount());
+ assertEquals("2nd Stage Tube", stage2.getChild(0).getName());
+ assertTrue(stage2.isMassOverridden());
+ assertEquals(0.21d, stage2.getOverrideMass());
+ assertTrue(stage2.isCGOverridden());
+ assertEquals(0.4d, stage2.getOverrideCG().x);
+
+ assertEquals(2, stage3.getChildCount());
+ assertEquals("Transition", stage3.getChild(0).getName());
+ assertEquals("Body tube", stage3.getChild(1).getName());
+ assertTrue(stage2.isMassOverridden());
+ assertEquals(0.33d, stage3.getOverrideMass());
+ assertTrue(stage2.isCGOverridden());
+ assertEquals(0.5d, stage3.getOverrideCG().x);
+ }
+
+}
--- /dev/null
+/*
+ * StreamerHandlerTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.database.Databases;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.BodyTube;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.rocketcomponent.Streamer;
+
+import java.util.HashMap;
+
+/**
+ * StreamerHandler Tester.
+ */
+public class StreamerHandlerTest extends BaseRocksimTest {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = StreamerHandler.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = StreamerHandlerTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(StreamerHandlerTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public StreamerHandlerTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Method: openElement(String element, HashMap<String, String> attributes, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testOpenElement() throws Exception {
+ assertEquals(PlainTextHandler.INSTANCE, new StreamerHandler(new BodyTube()).openElement(null, null, null));
+ }
+
+ /**
+ * Method: closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testCloseElement() throws Exception {
+
+ BodyTube tube = new BodyTube();
+ StreamerHandler handler = new StreamerHandler(tube);
+ Streamer component = (Streamer) getField(handler, "streamer");
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ handler.closeElement("Width", attributes, "0", warnings);
+ assertEquals(0d/ RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getStripWidth());
+ handler.closeElement("Width", attributes, "10", warnings);
+ assertEquals(10d/ RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getStripWidth());
+ handler.closeElement("Width", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Len", attributes, "-1", warnings);
+ assertEquals(0d, component.getStripLength());
+ handler.closeElement("Len", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getStripLength());
+ handler.closeElement("Len", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getStripLength());
+ handler.closeElement("Len", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Name", attributes, "Test Name", warnings);
+ assertEquals("Test Name", component.getName());
+
+ handler.closeElement("DragCoefficient", attributes, "0.94", warnings);
+ assertEquals(0.94d, component.getCD());
+ handler.closeElement("DragCoefficient", attributes, "-0.94", warnings);
+ assertEquals(-0.94d, component.getCD());
+ handler.closeElement("DragCoefficient", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ }
+
+ /**
+ * Method: constructor
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testConstructor() throws Exception {
+
+ try {
+ new StreamerHandler(null);
+ fail("Should have thrown IllegalArgumentException");
+ }
+ catch (IllegalArgumentException iae) {
+ //success
+ }
+
+ BodyTube tube = new BodyTube();
+ StreamerHandler handler = new StreamerHandler(tube);
+ Streamer component = (Streamer) getField(handler, "streamer");
+ assertContains(component, tube.getChildren());
+ }
+
+ /**
+ * Method: setRelativePosition(RocketComponent.Position position)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testSetRelativePosition() throws Exception {
+ BodyTube tube = new BodyTube();
+ StreamerHandler handler = new StreamerHandler(tube);
+ Streamer component = (Streamer) getField(handler, "streamer");
+ handler.setRelativePosition(RocketComponent.Position.ABSOLUTE);
+ assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition());
+ }
+
+ /**
+ * Method: getComponent()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetComponent() throws Exception {
+ assertTrue(new StreamerHandler(new BodyTube()).getComponent() instanceof Streamer);
+ }
+
+ /**
+ * Method: getMaterialType()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetMaterialType() throws Exception {
+ assertEquals(Material.Type.SURFACE, new StreamerHandler(new BodyTube()).getMaterialType());
+ }
+
+ /**
+ * Method: endHandler()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testEndHandler() throws Exception {
+ BodyTube tube = new BodyTube();
+ StreamerHandler handler = new StreamerHandler(tube);
+ Streamer component = (Streamer) getField(handler, "streamer");
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ handler.closeElement("Xb", attributes, "-10", warnings);
+ handler.closeElement("LocationMode", attributes, "1", warnings);
+ handler.endHandler("Streamer", attributes, null, warnings);
+ assertEquals(RocketComponent.Position.ABSOLUTE, component.getRelativePosition());
+ assertEquals(component.getPositionValue(), -10d/RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+
+ handler.closeElement("Xb", attributes, "-10", warnings);
+ handler.closeElement("LocationMode", attributes, "2", warnings);
+ handler.endHandler("Streamer", attributes, null, warnings);
+ assertEquals(RocketComponent.Position.BOTTOM, component.getRelativePosition());
+ assertEquals(component.getPositionValue(), 10d/RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH);
+ }
+
+}
--- /dev/null
+/*
+ * TransitionHandlerTest.java
+ */
+package net.sf.openrocket.file.rocksim;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.database.Databases;
+import net.sf.openrocket.file.simplesax.PlainTextHandler;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.rocketcomponent.ExternalComponent;
+import net.sf.openrocket.rocketcomponent.Stage;
+import net.sf.openrocket.rocketcomponent.Transition;
+
+import java.util.HashMap;
+
+/**
+ * TransitionHandler Tester.
+ */
+public class TransitionHandlerTest extends BaseRocksimTest {
+
+ /**
+ * The class under test.
+ */
+ public static final Class classUT = TransitionHandler.class;
+
+ /**
+ * The test class (this class).
+ */
+ public static final Class testClass = TransitionHandlerTest.class;
+
+ /**
+ * Create a test suite of all tests within this test class.
+ *
+ * @return a suite of tests
+ */
+ public static Test suite() {
+ return new TestSuite(TransitionHandlerTest.class);
+ }
+
+ /**
+ * Test constructor.
+ *
+ * @param name the name of the test to run.
+ */
+ public TransitionHandlerTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Setup the fixture.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Teardown the fixture.
+ */
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Method: constructor
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testConstructor() throws Exception {
+
+ try {
+ new TransitionHandler(null);
+ fail("Should have thrown IllegalArgumentException");
+ }
+ catch (IllegalArgumentException iae) {
+ //success
+ }
+
+ Stage stage = new Stage();
+ TransitionHandler handler = new TransitionHandler(stage);
+ Transition component = (Transition) getField(handler, "transition");
+ assertContains(component, stage.getChildren());
+ }
+
+ /**
+ * Method: openElement(String element, HashMap<String, String> attributes, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testOpenElement() throws Exception {
+ assertEquals(PlainTextHandler.INSTANCE, new TransitionHandler(new Stage()).openElement(null, null, null));
+ }
+
+ /**
+ * Method: closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testCloseElement() throws Exception {
+
+ Stage stage = new Stage();
+ HashMap<String, String> attributes = new HashMap<String, String>();
+ WarningSet warnings = new WarningSet();
+
+ TransitionHandler handler = new TransitionHandler(stage);
+ Transition component = (Transition) getField(handler, "transition");
+
+ handler.closeElement("ShapeCode", attributes, "0", warnings);
+ assertEquals(Transition.Shape.CONICAL, component.getType());
+ handler.closeElement("ShapeCode", attributes, "1", warnings);
+ assertEquals(Transition.Shape.OGIVE, component.getType());
+ handler.closeElement("ShapeCode", attributes, "17", warnings);
+ assertEquals(RocksimNoseConeCode.PARABOLIC.asOpenRocket(), component.getType()); //test of default
+ handler.closeElement("ShapeCode", attributes, "foo", warnings);
+ assertNotNull(component.getType());
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Len", attributes, "-1", warnings);
+ assertEquals(0d, component.getLength());
+ handler.closeElement("Len", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getLength());
+ handler.closeElement("Len", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("FrontDia", attributes, "-1", warnings);
+ assertEquals(0d, component.getForeRadius());
+ handler.closeElement("FrontDia", attributes, "100", warnings);
+ assertEquals(100d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getForeRadius());
+ handler.closeElement("FrontDia", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("RearDia", attributes, "-1", warnings);
+ assertEquals(0d, component.getAftRadius());
+ handler.closeElement("RearDia", attributes, "100", warnings);
+ assertEquals(100d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getAftRadius());
+ handler.closeElement("RearDia", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ final double aft = 100d;
+ component.setAftRadius(aft);
+
+ handler.closeElement("ConstructionType", attributes, "0", warnings);
+ component.setAftShoulderRadius(1.1d);
+ component.setForeShoulderRadius(1.1d);
+ handler.closeElement("WallThickness", attributes, "-1", warnings);
+ handler.endHandler("Transition", attributes, null, warnings);
+ assertEquals(component.getAftRadius(), component.getThickness());
+ assertEquals(component.getAftShoulderThickness(), component.getAftShoulderThickness());
+ assertEquals(component.getForeShoulderThickness(), component.getForeShoulderThickness());
+ handler.closeElement("WallThickness", attributes, "100", warnings);
+ handler.endHandler("Transition", attributes, null, warnings);
+ assertEquals(aft, component.getThickness());
+ handler.closeElement("WallThickness", attributes, "foo", warnings);
+ handler.endHandler("Transition", attributes, null, warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("ConstructionType", attributes, "1", warnings);
+ component.setAftShoulderRadius(1.1d);
+ component.setForeShoulderRadius(1.1d);
+ handler.closeElement("WallThickness", attributes, "-1", warnings);
+ handler.endHandler("Transition", attributes, null, warnings);
+ assertEquals(0d, component.getThickness());
+ assertEquals(0d, component.getAftShoulderThickness());
+ assertEquals(0d, component.getForeShoulderThickness());
+ handler.closeElement("WallThickness", attributes, "1.1", warnings);
+ handler.endHandler("Transition", attributes, null, warnings);
+ assertEquals(1.1d/RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getThickness());
+ assertEquals(1.1d/RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getAftShoulderThickness());
+ assertEquals(1.1d/RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getForeShoulderThickness());
+
+
+ handler.closeElement("FrontShoulderLen", attributes, "-1", warnings);
+ assertEquals(0d, component.getForeShoulderLength());
+ handler.closeElement("FrontShoulderLen", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getForeShoulderLength());
+ handler.closeElement("FrontShoulderLen", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getForeShoulderLength());
+ handler.closeElement("FrontShoulderLen", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("RearShoulderLen", attributes, "-1", warnings);
+ assertEquals(0d, component.getAftShoulderLength());
+ handler.closeElement("RearShoulderLen", attributes, "10", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getAftShoulderLength());
+ handler.closeElement("RearShoulderLen", attributes, "10.0", warnings);
+ assertEquals(10d / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH, component.getAftShoulderLength());
+ handler.closeElement("RearShoulderLen", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("FrontShoulderDia", attributes, "-1", warnings);
+ assertEquals(0d, component.getForeShoulderRadius());
+ handler.closeElement("FrontShoulderDia", attributes, "100", warnings);
+ assertEquals(100d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getForeShoulderRadius());
+ handler.closeElement("FrontShoulderDia", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("RearShoulderDia", attributes, "-1", warnings);
+ assertEquals(0d, component.getAftShoulderRadius());
+ handler.closeElement("RearShoulderDia", attributes, "100", warnings);
+ assertEquals(100d / RocksimHandler.ROCKSIM_TO_OPENROCKET_RADIUS, component.getAftShoulderRadius());
+ handler.closeElement("RearShoulderDia", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ component.setType(Transition.Shape.HAACK);
+ handler.closeElement("ShapeParameter", attributes, "-1", warnings);
+ assertEquals(0d, component.getShapeParameter());
+ handler.closeElement("ShapeParameter", attributes, "100", warnings);
+ assertEquals(Transition.Shape.HAACK.maxParameter(), component.getShapeParameter());
+ handler.closeElement("ShapeParameter", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ assertEquals("Could not convert ShapeParameter value of foo. It is expected to be a number.",
+ warnings.iterator().next().toString());
+
+ warnings.clear();
+
+ component.setType(Transition.Shape.CONICAL);
+ component.setShapeParameter(0d);
+ handler.closeElement("ShapeParameter", attributes, "100", warnings);
+ assertEquals(0d, component.getShapeParameter());
+
+ handler.closeElement("FinishCode", attributes, "-1", warnings);
+ assertEquals(ExternalComponent.Finish.NORMAL, component.getFinish());
+ handler.closeElement("FinishCode", attributes, "100", warnings);
+ assertEquals(ExternalComponent.Finish.NORMAL, component.getFinish());
+ handler.closeElement("FinishCode", attributes, "foo", warnings);
+ assertEquals(1, warnings.size());
+ warnings.clear();
+
+ handler.closeElement("Name", attributes, "Test Name", warnings);
+ assertEquals("Test Name", component.getName());
+
+ handler.closeElement("Material", attributes, "Some Material", warnings);
+ handler.endHandler("Transition", attributes, null, warnings);
+ assertTrue(component.getMaterial().getName().contains("Some Material"));
+ }
+
+ /**
+ * Method: getComponent()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetComponent() throws Exception {
+ assertTrue(new TransitionHandler(new Stage()).getComponent() instanceof Transition);
+ }
+
+ /**
+ * Method: getMaterialType()
+ *
+ * @throws Exception thrown if something goes awry
+ */
+ public void testGetMaterialType() throws Exception {
+ assertEquals(Material.Type.BULK, new TransitionHandler(new Stage()).getMaterialType());
+ }
+
+
+}
--- /dev/null
+<RockSimDocument>
+<FileVersion>3</FileVersion>
+<DesignInformation>
+<RocketDesign>
+ <Name>FooBar Test</Name>
+<CalculateCD>1</CalculateCD>
+<ProCalculateCD>1</ProCalculateCD>
+<ProCalculateCN>1</ProCalculateCN>
+<FixedCd>0.75</FixedCd>
+<FixedCd2>0.8</FixedCd2>
+<FixedCd3>0.81</FixedCd3>
+<FixedCd2Alone>0.95</FixedCd2Alone>
+<FixedCd3Alone>0.95</FixedCd3Alone>
+<StageCount>1</StageCount>
+<Stage3Mass>0.</Stage3Mass>
+<Stage2Mass>0.</Stage2Mass>
+<Stage1Mass>0.</Stage1Mass>
+<Stage321CG>0.</Stage321CG>
+<Stage32CG>0.</Stage32CG>
+<Stage3CG>0.</Stage3CG>
+<Stage2CGAlone>0.</Stage2CGAlone>
+<Stage1CGAlone>0.</Stage1CGAlone>
+<CPCalcFlags>1</CPCalcFlags>
+<LaunchGuideLength>914.4</LaunchGuideLength>
+<UseKnownMass>0</UseKnownMass>
+<DefaultFinish>0</DefaultFinish>
+<FinishMedium>0</FinishMedium>
+<FinishCoatCount>1</FinishCoatCount>
+<GlueType>0</GlueType>
+<CPSimFlags>1</CPSimFlags>
+<LastSerialNumber>11</LastSerialNumber>
+<DisplayFlags>1</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<BarromanXN>0,1215.49,0,0</BarromanXN>
+<BarrowmanCNa>0,35.9506,0,0</BarrowmanCNa>
+<RockSimXN>0,1226.38,0,0</RockSimXN>
+<RockSimCNa>0,47.5027,0,0</RockSimCNa>
+<RockSimCNa90>0,0,0,0</RockSimCNa90>
+<RockSimXN90>0,0,0,0</RockSimXN90>
+<ViewType>0</ViewType>
+<ViewStageCount>1</ViewStageCount>
+<ViewTypeEdit>0</ViewTypeEdit>
+<ViewStageCountEdit>1</ViewStageCountEdit>
+<ZoomFactor>0.</ZoomFactor>
+<ZoomFactorEdit>0.</ZoomFactorEdit>
+<ScrollPosX>0</ScrollPosX>
+<ScrollPosY>0</ScrollPosY>
+<ScrollPosXEdit>0</ScrollPosXEdit>
+<ScrollPosYEdit>0</ScrollPosYEdit>
+<ThreeDFlags>0</ThreeDFlags>
+<ThreeDFlagsEdit>0</ThreeDFlagsEdit>
+<UseModelSprite>0</UseModelSprite>
+<StaticMarginRef>0</StaticMarginRef>
+<UserRefDiameter>0.</UserRefDiameter>
+<SideMarkerHeight>10.</SideMarkerHeight>
+<SideDimensionHeight>10.</SideDimensionHeight>
+<BaseMarkerHeight>10.</BaseMarkerHeight>
+<BaseDimensionHeight>10.</BaseDimensionHeight>
+<ShowGlideCP>0</ShowGlideCP>
+<ShowGridTypeSide>0</ShowGridTypeSide>
+<ShowGridTypeBase>0</ShowGridTypeBase>
+<GridSpacing>10.</GridSpacing>
+<GridOpacity>0.15</GridOpacity>
+<GridColor>black</GridColor>
+<MaxDiaWithFins>424.688</MaxDiaWithFins>
+<MaxDiaWithoutFins>66.04</MaxDiaWithoutFins>
+<MaxLenWithFins>1417.95</MaxLenWithFins>
+<MaxLenWithoutFins>1332.23</MaxLenWithoutFins>
+<MinXExtent>0.</MinXExtent>
+<MaxXExtent>1417.95</MaxXExtent>
+<CalculatedMaxStageDia>0,66.04,0,0</CalculatedMaxStageDia>
+<CalculatedStageLen>0,1332.23,0,0</CalculatedStageLen>
+<Cd3>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cd3>
+<Cd32>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cd32>
+<Cd321>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cd321>
+<Cb3>
+<PolyData useXYOnly="1" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cb3>
+<Cb32>
+<PolyData useXYOnly="1" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cb32>
+<Cb321>
+<PolyData useXYOnly="1" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cb321>
+<CNa3>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CNa3>
+<CNa32>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CNa32>
+<CNa321>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CNa321>
+<CP3>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CP3>
+<CP32>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CP32>
+<CP321>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CP321>
+<SimulationEventList>
+</SimulationEventList>
+<Stage3Parts>
+<NoseCone>
+<PartMfg>Apogee</PartMfg>
+<KnownMass>24.1</KnownMass>
+<Density>1049.21</Density>
+<Material>Polystyrene PS</Material>
+<Name>Nose cone</Name>
+<KnownCG>65.3999</KnownCG>
+<UseKnownCG>1</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>126.438</CalcMass>
+<CalcCG>348.443</CalcCG>
+<WettedSurface>0.0356487</WettedSurface>
+<PaintedSurface>0.0356487</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>19470</PartNo>
+<PartDesc>PNC-70A</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>2</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>2.</BarrowmanCNa>
+<BarrowmanXN>0.264319</BarrowmanXN>
+<RockSimCNa>2.</RockSimCNa>
+<RockSimXN>0.264319</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>0.</Station>
+<Len>396.875</Len>
+<BaseDia>57.15</BaseDia>
+<FinishCode>0</FinishCode>
+<ShapeCode>0</ShapeCode>
+<ConstructionType>1</ConstructionType>
+<ShoulderLen>58.3997</ShoulderLen>
+<WallThickness>2.159</WallThickness>
+<ShapeParameter>0.</ShapeParameter>
+<ShoulderOD>53.1012</ShoulderOD>
+<BaseExtensionLen>66.675</BaseExtensionLen>
+<CoreDia>0.</CoreDia>
+<CoreLen>0.</CoreLen>
+<AttachedParts>
+</AttachedParts>
+</NoseCone>
+<BodyTube>
+<PartMfg>Estes</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>10.636</CalcMass>
+<CalcCG>180.34</CalcCG>
+<WettedSurface>0.0748306</WettedSurface>
+<PaintedSurface>0.0748306</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>EST 3090</PartNo>
+<PartDesc>BT-80</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>3</SerialNo>
+<DisplayFlags>1</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>463.55</Station>
+<OD>66.04</OD>
+<ID>65.786</ID>
+<Len>360.68</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>0</IsMotorMount>
+<MotorDia>0.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>0</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+</AttachedParts>
+</BodyTube>
+<Transition>
+<PartMfg>BalsaMachining.com</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>128.148</Density>
+<Material>Balsa</Material>
+<Name>Transition</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>39.355</CalcMass>
+<CalcCG>37.0332</CalcCG>
+<WettedSurface>0.0096001</WettedSurface>
+<PaintedSurface>0.0096001</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>TA7080</PartNo>
+<PartDesc>Transition T70 to T80 2 in long</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>7</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>-0.92852</BarrowmanCNa>
+<BarrowmanXN>0.848729</BarrowmanXN>
+<RockSimCNa>-0.92852</RockSimCNa>
+<RockSimXN>0.848729</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>824.23</Station>
+<FrontDia>66.04</FrontDia>
+<RearDia>53.34</RearDia>
+<Len>50.8</Len>
+<FinishCode>0</FinishCode>
+<FrontShoulderLen>35.56</FrontShoulderLen>
+<RearShoulderLen>35.56</RearShoulderLen>
+<ConstructionType>0</ConstructionType>
+<WallThickness>0.</WallThickness>
+<FrontShoulderDia>55.118</FrontShoulderDia>
+<RearShoulderDia>53.34</RearShoulderDia>
+<CoreDia>0.</CoreDia>
+<ShapeCode>0</ShapeCode>
+<ShapeParameter>0.</ShapeParameter>
+<EquivNoseLen>264.12</EquivNoseLen>
+<EquivNoseOffset>213.32</EquivNoseOffset>
+<AttachedParts>
+</AttachedParts>
+</Transition>
+<BodyTube>
+<PartMfg>Estes</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>44.449</CalcMass>
+<CalcCG>222.25</CalcCG>
+<WettedSurface>0.0787423</WettedSurface>
+<PaintedSurface>0.0787423</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>Estes</PartNo>
+<PartDesc>BT-70</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>8</SerialNo>
+<DisplayFlags>1</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>875.03</Station>
+<OD>56.388</OD>
+<ID>55.372</ID>
+<Len>444.5</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>0</IsMotorMount>
+<MotorDia>0.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>0</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+<FinSet>
+<PartMfg>Public Missiles</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1905.24</Density>
+<Material>G10 fiberglass</Material>
+<Name>Fin set</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>260.35</Xb>
+<CalcMass>153.85</CalcMass>
+<CalcCG>155.575</CalcCG>
+<WettedSurface>0.0339112</WettedSurface>
+<PaintedSurface>0.101734</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>FIN-A-01</PartNo>
+<PartDesc>Fins</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>9</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>34.8792</BarrowmanCNa>
+<BarrowmanXN>1.26026</BarrowmanXN>
+<RockSimCNa>46.4312</RockSimCNa>
+<RockSimXN>1.26026</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>1135.38</Station>
+<FinCount>3</FinCount>
+<RootChord>184.15</RootChord>
+<TipChord>0.</TipChord>
+<SemiSpan>184.15</SemiSpan>
+<MidChordLen>264.956</MidChordLen>
+<SweepDistance>282.575</SweepDistance>
+<Thickness>1.5875</Thickness>
+<ShapeCode>0</ShapeCode>
+<FinishCode>0</FinishCode>
+<TipShapeCode>0</TipShapeCode>
+<TabLength>0.</TabLength>
+<TabDepth>0.</TabDepth>
+<TabOffset>0.</TabOffset>
+<SweepMode>1</SweepMode>
+<SweepAngle>0.990003</SweepAngle>
+<RockSimXNPerFin>0.</RockSimXNPerFin>
+<RockSimRadialXNPerFin>89.5773</RockSimRadialXNPerFin>
+<RockSimCNaPerFin>26.8071</RockSimCNaPerFin>
+<TaperRatio>0.</TaperRatio>
+<CantAngle>0.</CantAngle>
+<CantPivotPoint>0.</CantPivotPoint>
+<AttachedParts>
+</AttachedParts>
+</FinSet>
+<BodyTube>
+<PartMfg>Estes</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>13.008</CalcMass>
+<CalcCG>228.6</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>EST 3086</PartNo>
+<PartDesc>BT-50</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>11</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>875.03</Station>
+<OD>24.7904</OD>
+<ID>24.13</ID>
+<Len>457.2</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>1</IsMotorMount>
+<MotorDia>24.</MotorDia>
+<EngineOverhang>12.7</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>1</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+</AttachedParts>
+</BodyTube>
+</AttachedParts>
+</BodyTube>
+</Stage3Parts>
+<Stage2Parts>
+</Stage2Parts>
+<Stage1Parts>
+</Stage1Parts>
+<SideViewDims>
+</SideViewDims>
+<BaseViewDims>
+</BaseViewDims>
+<VertViewDims>
+</VertViewDims>
+</RocketDesign>
+</DesignInformation>
+<SimulationResultsList>
+<SimulationResults>
+<MaxAltitude>445.129</MaxAltitude>
+<MaxVelocity>68.7587</MaxVelocity>
+<MaxAcceleration>76.6476</MaxAcceleration>
+<TimeToApogee>10.8263</TimeToApogee>
+<OptimalDelay>4.765</OptimalDelay>
+<MultiDelayCount>1</MultiDelayCount>
+<VelocityAtDeplyment>33.4284</VelocityAtDeplyment>
+<AltitudeAtDeployment>405.535</AltitudeAtDeployment>
+<DelayTime>2</DelayTime>
+<EjectTime>8.06125</EjectTime>
+<FinalState>4</FinalState>
+<TimeToBurnout>6.06125</TimeToBurnout>
+<TimeToLanding>22.4787</TimeToLanding>
+<TimeToEject>8.06125</TimeToEject>
+<MinLaunchGuideLen>1.35915</MinLaunchGuideLen>
+<MaxVertVelocity>65.9349</MaxVertVelocity>
+<MaxHorzVelocity>1.0686</MaxHorzVelocity>
+<MaxVertAcceleration>76.5426</MaxVertAcceleration>
+<MaxHorzAcceleration>4.01173</MaxHorzAcceleration>
+<MaxRange>172.076</MaxRange>
+<LaunchStartVelocity>0.</LaunchStartVelocity>
+<LaunchStartRollRate>0.</LaunchStartRollRate>
+<LaunchGuideType>0</LaunchGuideType>
+<LaunchGuideLen>1828.8</LaunchGuideLen>
+<LaunchWindDirection>0</LaunchWindDirection>
+<LaunchWindSpeed>0.</LaunchWindSpeed>
+<LaunchDirection>0</LaunchDirection>
+<LaunchAngle>0.0523599</LaunchAngle>
+<LaunchGuideAzimuth>0.</LaunchGuideAzimuth>
+<LaunchGuideElevation>0.</LaunchGuideElevation>
+<LaunchBarometer>770.314</LaunchBarometer>
+<LaunchLatitude>43.</LaunchLatitude>
+<LaunchLongitude>0.</LaunchLongitude>
+<LaunchHumidity>70.</LaunchHumidity>
+<LaunchTemperature>7.22222</LaunchTemperature>
+<LaunchAltitude>304.8</LaunchAltitude>
+<LaunchLandingAltitude>0.</LaunchLandingAltitude>
+<CompStateMask>0</CompStateMask>
+<LaunchWindPreset>3</LaunchWindPreset>
+<LaunchWindLowSpeed>1.34112</LaunchWindLowSpeed>
+<LaunchWindHighSpeed>3.53162</LaunchWindHighSpeed>
+<LaunchWindTurbulencePreset>2</LaunchWindTurbulencePreset>
+<LaunchWindFrequency>0.01</LaunchWindFrequency>
+<LaunchWindDeltaFrequency>0.02</LaunchWindDeltaFrequency>
+<LaunchUseRandomConditions>0</LaunchUseRandomConditions>
+<LaunchCloudCoverPreset>5</LaunchCloudCoverPreset>
+<LaunchCCLow>0.</LaunchCCLow>
+<LaunchCCHigh>0.1</LaunchCCHigh>
+<LaunchThermalPosPreset>1</LaunchThermalPosPreset>
+<LaunchThermalPos>0.</LaunchThermalPos>
+<LaunchMultipleThermals>0</LaunchMultipleThermals>
+<LaunchMaxThermals>3</LaunchMaxThermals>
+<LaunchThermalDia>300.</LaunchThermalDia>
+<LaunchThermalHeight>2000.</LaunchThermalHeight>
+<LaunchThermalSpeedPreset>1</LaunchThermalSpeedPreset>
+<LaunchThermalSpeed>5.99999</LaunchThermalSpeed>
+<LaunchWindTableSize>0</LaunchWindTableSize>
+<LaunchWindAltTable>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</LaunchWindAltTable>
+<LaunchWindSpeedTable>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</LaunchWindSpeedTable>
+<LaunchWindDirectionTable>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</LaunchWindDirectionTable>
+<LaunchLossPreset>2</LaunchLossPreset>
+<LaunchLossPercent>0.99</LaunchLossPercent>
+<LaunchSiteDia>2000.</LaunchSiteDia>
+<LaunchDriftRangeToLoss>1500.</LaunchDriftRangeToLoss>
+<LaunchPayloadDamagePreset>2</LaunchPayloadDamagePreset>
+<LaunchPayloadDamagePercent>0.98</LaunchPayloadDamagePercent>
+<LaunchEngineMisfirePreset>1</LaunchEngineMisfirePreset>
+<LaunchEngineMisfirePercent>0.</LaunchEngineMisfirePercent>
+<LaunchVariableIgnitionDelay>0</LaunchVariableIgnitionDelay>
+<LaunchVariableIgnitionDelaySec>0.08</LaunchVariableIgnitionDelaySec>
+<LaunchRecDeviceFailurePreset>1</LaunchRecDeviceFailurePreset>
+<LaunchRecDeviceFailurePercent>0.</LaunchRecDeviceFailurePercent>
+<LaunchAriframeFailurePreset>1</LaunchAriframeFailurePreset>
+<LaunchAirframeFailurePercent>0.</LaunchAirframeFailurePercent>
+<LaunchTrackLossPreset>1</LaunchTrackLossPreset>
+<LaunchTeckLossPercent>0.</LaunchTeckLossPercent>
+<CNaMultiplier>1.</CNaMultiplier>
+<CdMultiplier>1.</CdMultiplier>
+<CPOffset>0.</CPOffset>
+<SaveSimAsText>0</SaveSimAsText>
+<OutputAtMaxRes>0</OutputAtMaxRes>
+<MaxSimTim>3600.</MaxSimTim>
+<MassUncertainty>0.</MassUncertainty>
+<MOIUncertainty>0.</MOIUncertainty>
+<CGUncertainty>0.</CGUncertainty>
+<CdUncertainty>0.</CdUncertainty>
+<CnaUncertainty>0.</CnaUncertainty>
+<CPUncertainty>0.</CPUncertainty>
+<FinCantUncertainty>0.</FinCantUncertainty>
+<TotImpulseUncertainty>0.</TotImpulseUncertainty>
+<PropellantUncertainty>0.</PropellantUncertainty>
+<ThrustAxisUncertainty>0.</ThrustAxisUncertainty>
+<WindDirectionUncertainty>0.</WindDirectionUncertainty>
+<WindVelocityUncertainty>0.</WindVelocityUncertainty>
+<LaunchGuideAzimuthUncertainty>0.</LaunchGuideAzimuthUncertainty>
+<LaunchGuideElevationUncertainty>0.</LaunchGuideElevationUncertainty>
+<IgnitionUncertainty>0.</IgnitionUncertainty>
+<CATOUncertainty>0.</CATOUncertainty>
+<DeploymentUncertainty>0.</DeploymentUncertainty>
+<RecoveryDeviceUncertainty>0.</RecoveryDeviceUncertainty>
+<SimulationName>[E6-2] </SimulationName>
+<VelocityAtLaunchGuideEnd>15.4098</VelocityAtLaunchGuideEnd>
+<WindStartAltitude>0.</WindStartAltitude>
+<TimeToWindShear>0.</TimeToWindShear>
+<TimeToFreeFlight>0.25875</TimeToFreeFlight>
+<TimeToMaxVelocity>5.69125</TimeToMaxVelocity>
+<TimeToMaxAcceleration>0.11125</TimeToMaxAcceleration>
+<Nsims>10</Nsims>
+<CalcResolution>1</CalcResolution>
+<SamplesPerSecond>800.</SamplesPerSecond>
+<SimulationType>0</SimulationType>
+<LocationDataServerName>Earth</LocationDataServerName>
+<LocationDataServerDisplayName>Standard earth condistions.</LocationDataServerDisplayName>
+<ExecutionTime>4.078</ExecutionTime>
+<RangeAtLanding>-172.076</RangeAtLanding>
+<DirectionAtLanding>0.</DirectionAtLanding>
+<VelocityAtLanding>56.537</VelocityAtLanding>
+<XVelcoityAtLanding>-3.38254</XVelcoityAtLanding>
+<YVelocityAtLanding>-56.4357</YVelocityAtLanding>
+<ZVelocityAtLanding>0.</ZVelocityAtLanding>
+<StageSeparationTime>0,0,8.06125,0,0</StageSeparationTime>
+<StageEjectTime>-1,-1,-1,-1,-1</StageEjectTime>
+<TimeToDeployment>0,0,0,0,0</TimeToDeployment>
+<DeploymentType>32,32,32,32,32</DeploymentType>
+<SamplesPerSecondDescent>1.</SamplesPerSecondDescent>
+<CalculationFlags>1</CalculationFlags>
+<Mass0>0.233111</Mass0>
+<CG0>0.739954</CG0>
+<LaterialMOI0>0.0208295</LaterialMOI0>
+<RadialMOI0>0.000194272</RadialMOI0>
+<GUID>{b33e529e-1ada-4524-9885-7382d7cf4d64}</GUID>
+<Booster1Staging>
+<SimulationEvent>
+<PartSerialNo>0</PartSerialNo>
+<Type>0</Type>
+<DeployAltitude>0.</DeployAltitude>
+<DeplyTime>0.</DeplyTime>
+<HasDeployed>0</HasDeployed>
+<DeployedAt_Altitude>0.</DeployedAt_Altitude>
+<DeployedAt_Velocity>0.</DeployedAt_Velocity>
+<DeployedAt_Range>0.</DeployedAt_Range>
+<DeployedAt_Time>0.</DeployedAt_Time>
+<DeviceID>-1</DeviceID>
+<TestType>0,0,0</TestType>
+<TestCondition>0,0,0</TestCondition>
+<TestValueAltitude>0,0,0</TestValueAltitude>
+<TestValueDegrees>0,0,0</TestValueDegrees>
+<TestValuePressure>0,0,0</TestValuePressure>
+<TestValueMach>0,0,0</TestValueMach>
+<TestValueTime>0,0,0</TestValueTime>
+<TestValueQ>0,0,0</TestValueQ>
+</SimulationEvent>
+</Booster1Staging>
+<Booster2Staging>
+<SimulationEvent>
+<PartSerialNo>0</PartSerialNo>
+<Type>0</Type>
+<DeployAltitude>0.</DeployAltitude>
+<DeplyTime>0.</DeplyTime>
+<HasDeployed>0</HasDeployed>
+<DeployedAt_Altitude>0.</DeployedAt_Altitude>
+<DeployedAt_Velocity>0.</DeployedAt_Velocity>
+<DeployedAt_Range>0.</DeployedAt_Range>
+<DeployedAt_Time>0.</DeployedAt_Time>
+<DeviceID>-1</DeviceID>
+<TestType>0,0,0</TestType>
+<TestCondition>0,0,0</TestCondition>
+<TestValueAltitude>0,0,0</TestValueAltitude>
+<TestValueDegrees>0,0,0</TestValueDegrees>
+<TestValuePressure>0,0,0</TestValuePressure>
+<TestValueMach>0,0,0</TestValueMach>
+<TestValueTime>0,0,0</TestValueTime>
+<TestValueQ>0,0,0</TestValueQ>
+</SimulationEvent>
+</Booster2Staging>
+<SimulationEvents>
+</SimulationEvents>
+<Stage1Engines>
+</Stage1Engines>
+<Stage2Engines>
+</Stage2Engines>
+<Stage3Engines>
+<EngineSet>
+<EngineCount>1</EngineCount>
+<EngineCode>E6</EngineCode>
+<IgnitionDelay>0.</IgnitionDelay>
+<EngineMfg>Apogee</EngineMfg>
+<EngineOverhang>0.50038</EngineOverhang>
+<CasingCG>0.</CasingCG>
+<MountSerialNo>5</MountSerialNo>
+<EjectionDelay>2.</EjectionDelay>
+<RotateXaboutY>0.</RotateXaboutY>
+<RotateEngineAxisAboutX>0.</RotateEngineAxisAboutX>
+</EngineSet>
+</Stage3Engines>
+<DropItems>
+</DropItems>
+</SimulationResults>
+</SimulationResultsList>
+</RockSimDocument>
--- /dev/null
+<RockSimDocument>
+<FileVersion>3</FileVersion>
+<DesignInformation>
+<RocketDesign>
+<Name>Three Stage Everything Included Rocket</Name>
+<CalculateCD>1</CalculateCD>
+<ProCalculateCD>1</ProCalculateCD>
+<ProCalculateCN>1</ProCalculateCN>
+<FixedCd>0.75</FixedCd>
+<FixedCd2>0.8</FixedCd2>
+<FixedCd3>0.81</FixedCd3>
+<FixedCd2Alone>0.95</FixedCd2Alone>
+<FixedCd3Alone>0.95</FixedCd3Alone>
+<StageCount>3</StageCount>
+<Stage3Mass>0.</Stage3Mass>
+<Stage2Mass>0.</Stage2Mass>
+<Stage1Mass>0.</Stage1Mass>
+<Stage321CG>0.</Stage321CG>
+<Stage32CG>0.</Stage32CG>
+<Stage3CG>0.</Stage3CG>
+<Stage2CGAlone>0.</Stage2CGAlone>
+<Stage1CGAlone>0.</Stage1CGAlone>
+<CPCalcFlags>1</CPCalcFlags>
+<LaunchGuideLength>914.4</LaunchGuideLength>
+<UseKnownMass>0</UseKnownMass>
+<DefaultFinish>0</DefaultFinish>
+<FinishMedium>0</FinishMedium>
+<FinishCoatCount>1</FinishCoatCount>
+<GlueType>0</GlueType>
+<CPSimFlags>1</CPSimFlags>
+<LastSerialNumber>25</LastSerialNumber>
+<DisplayFlags>7</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<BarromanXN>0,29.6333,326.159,887.03</BarromanXN>
+<BarrowmanCNa>0,2,4.93008,33.0782</BarrowmanCNa>
+<RockSimXN>0,29.6333,384.174,883.929</RockSimXN>
+<RockSimCNa>0,2,6.91103,47.1184</RockSimCNa>
+<RockSimCNa90>0,0,0,0</RockSimCNa90>
+<RockSimXN90>0,0,0,0</RockSimXN90>
+<ViewType>0</ViewType>
+<ViewStageCount>3</ViewStageCount>
+<ViewTypeEdit>0</ViewTypeEdit>
+<ViewStageCountEdit>3</ViewStageCountEdit>
+<ZoomFactor>0.</ZoomFactor>
+<ZoomFactorEdit>0.</ZoomFactorEdit>
+<ScrollPosX>0</ScrollPosX>
+<ScrollPosY>0</ScrollPosY>
+<ScrollPosXEdit>0</ScrollPosXEdit>
+<ScrollPosYEdit>0</ScrollPosYEdit>
+<ThreeDFlags>0</ThreeDFlags>
+<ThreeDFlagsEdit>0</ThreeDFlagsEdit>
+<UseModelSprite>0</UseModelSprite>
+<StaticMarginRef>0</StaticMarginRef>
+<UserRefDiameter>0.</UserRefDiameter>
+<SideMarkerHeight>10.</SideMarkerHeight>
+<SideDimensionHeight>10.</SideDimensionHeight>
+<BaseMarkerHeight>10.</BaseMarkerHeight>
+<BaseDimensionHeight>10.</BaseDimensionHeight>
+<ShowGlideCP>0</ShowGlideCP>
+<ShowGridTypeSide>0</ShowGridTypeSide>
+<ShowGridTypeBase>0</ShowGridTypeBase>
+<GridSpacing>10.</GridSpacing>
+<GridOpacity>0.15</GridOpacity>
+<GridColor>black</GridColor>
+<MaxDiaWithFins>339.43</MaxDiaWithFins>
+<MaxDiaWithoutFins>66.3</MaxDiaWithoutFins>
+<MaxLenWithFins>2104.53</MaxLenWithFins>
+<MaxLenWithoutFins>2104.53</MaxLenWithoutFins>
+<MinXExtent>0.</MinXExtent>
+<MaxXExtent>1150.33</MaxXExtent>
+<CalculatedMaxStageDia>0,66.3,66.3,66.3</CalculatedMaxStageDia>
+<CalculatedStageLen>0,296.8,954.2,2104.53</CalculatedStageLen>
+<Cd3>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cd3>
+<Cd32>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cd32>
+<Cd321>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cd321>
+<Cb3>
+<PolyData useXYOnly="1" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cb3>
+<Cb32>
+<PolyData useXYOnly="1" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cb32>
+<Cb321>
+<PolyData useXYOnly="1" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cb321>
+<CNa3>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CNa3>
+<CNa32>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CNa32>
+<CNa321>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CNa321>
+<CP3>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CP3>
+<CP32>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CP32>
+<CP321>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CP321>
+<SimulationEventList>
+</SimulationEventList>
+<Stage3Parts>
+<NoseCone>
+<PartMfg>Aerospace Speciality Products</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>128.148</Density>
+<Material>Balsa</Material>
+<Name>Nose cone</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>42.0747</CalcMass>
+<CalcCG>75.3029</CalcCG>
+<WettedSurface>0.0152945</WettedSurface>
+<PaintedSurface>0.0152945</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>BNC80S</PartNo>
+<PartDesc><![CDATA[T-80H Balsa Nose Cone "S"]]></PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>1</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>2.</BarrowmanCNa>
+<BarrowmanXN>0.0296333</BarrowmanXN>
+<RockSimCNa>2.</RockSimCNa>
+<RockSimXN>0.0296333</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>0.</Station>
+<Len>88.9</Len>
+<BaseDia>66.3</BaseDia>
+<FinishCode>0</FinishCode>
+<ShapeCode>3</ShapeCode>
+<ConstructionType>0</ConstructionType>
+<ShoulderLen>38.1</ShoulderLen>
+<WallThickness>0.</WallThickness>
+<ShapeParameter>0.</ShapeParameter>
+<ShoulderOD>64.3</ShoulderOD>
+<BaseExtensionLen>0.</BaseExtensionLen>
+<CoreDia>0.</CoreDia>
+<CoreLen>0.</CoreLen>
+<AttachedParts>
+<MassObject>
+<PartMfg>Custom</PartMfg>
+<KnownMass>20.</KnownMass>
+<Density>0.</Density>
+<Material>Custom</Material>
+<Name>Clay</Name>
+<KnownCG>17.8</KnownCG>
+<UseKnownCG>1</UseKnownCG>
+<Xb>17.8</Xb>
+<CalcMass>0.</CalcMass>
+<CalcCG>0.</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>2</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>17.8</Station>
+<TypeCode>0</TypeCode>
+<Len>0.</Len>
+<AttachedParts>
+</AttachedParts>
+</MassObject>
+<BodyTube>
+<PartMfg>Apogee</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Attachment Rod</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>6.1</Xb>
+<CalcMass>2.47301</CalcMass>
+<CalcCG>78.85</CalcCG>
+<WettedSurface>0.00684683</WettedSurface>
+<PaintedSurface>0.00684683</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>10062</PartNo>
+<PartDesc>13 mm</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>3</SerialNo>
+<DisplayFlags>1</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>6.1</Station>
+<OD>13.82</OD>
+<ID>13.16</ID>
+<Len>157.7</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>0</IsMotorMount>
+<MotorDia>0.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>1</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Plate</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>78.9</Xb>
+<CalcMass>7.86272</CalcMass>
+<CalcCG>1.585</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC CR-2.56-(2)0.95</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>4</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>85.</Station>
+<OD>66.</OD>
+<ID>13.82</ID>
+<Len>3.17</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<Ring>
+<PartMfg>Custom</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1400.</Density>
+<Material>Carbon Fiber</Material>
+<Name>Sleeve </Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>17.5527</CalcMass>
+<CalcCG>13.85</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>5</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>2</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>136.1</Station>
+<OD>27.7</OD>
+<ID>13.82</ID>
+<Len>27.7</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>3</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<Parachute>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>170.</KnownMass>
+<Density>0.006685</Density>
+<Material>Rip stop nylon</Material>
+<Name>Nose Cone Parachute</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>1</UseKnownCG>
+<Xb>142.6</Xb>
+<CalcMass>15.8812</CalcMass>
+<CalcCG>34.5417</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>1</DensityType>
+<PartNo>LP-50</PartNo>
+<PartDesc>50 In. 16 lines</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>6</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>148.7</Station>
+<Dia>414.5</Dia>
+<SpillHoleDia>0.</SpillHoleDia>
+<SideCount>15</SideCount>
+<ShroudLineCount>16</ShroudLineCount>
+<Thickness>0.05</Thickness>
+<ShroudLineLen>1350.</ShroudLineLen>
+<ChuteCount>1</ChuteCount>
+<ShroudLineMassPerMM>0.00032972</ShroudLineMassPerMM>
+<ShroudLineMaterial>Carpet String (Apogee 29500)</ShroudLineMaterial>
+<DragCoefficient>0.95</DragCoefficient>
+<AttachedParts>
+</AttachedParts>
+</Parachute>
+</AttachedParts>
+</BodyTube>
+</AttachedParts>
+</NoseCone>
+<BodyTube>
+<PartMfg>Estes</PartMfg>
+<KnownMass>15.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Body tube</Name>
+<KnownCG>100.</KnownCG>
+<UseKnownCG>1</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>5.80486</CalcMass>
+<CalcCG>100.</CalcCG>
+<WettedSurface>0.0414942</WettedSurface>
+<PaintedSurface>0.0414942</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>EST 3090</PartNo>
+<PartDesc>BT-80</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>7</SerialNo>
+<DisplayFlags>1</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(54,250,21)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>88.9</Station>
+<OD>66.04</OD>
+<ID>65.79</ID>
+<Len>200.</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>0</IsMotorMount>
+<MotorDia>0.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>0</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>190.2</Xb>
+<CalcMass>7.65502</CalcMass>
+<CalcCG>1.59</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC CR-2.56-0.95</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>8</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>279.1</Station>
+<OD>65.02</OD>
+<ID>25.4</ID>
+<Len>3.18</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>165.</Xb>
+<CalcMass>7.62626</CalcMass>
+<CalcCG>1.585</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC CR-2.56-(2)0.95</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>9</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>253.9</Station>
+<OD>65.</OD>
+<ID>25.4</ID>
+<Len>3.17</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<BodyTube>
+<PartMfg>Public Missiles Ltd.</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>958.705</Density>
+<Material>Kraft phenolic</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>137.9</Xb>
+<CalcMass>9.8818</CalcMass>
+<CalcCG>35.</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>KS-1.1</PartNo>
+<PartDesc>KwikSwitch MMT 29mm</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>-1.58371</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>10</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>226.8</Station>
+<OD>32.26</OD>
+<ID>29.21</ID>
+<Len>70.</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>1</IsMotorMount>
+<MotorDia>29.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>1</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+</AttachedParts>
+</BodyTube>
+</AttachedParts>
+</BodyTube>
+</Stage3Parts>
+<Stage2Parts>
+<BodyTube>
+<PartMfg>Estes</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>2nd Stage Tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>10.4685</CalcMass>
+<CalcCG>180.34</CalcCG>
+<WettedSurface>0.0748306</WettedSurface>
+<PaintedSurface>0.0748306</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>EST 3090</PartNo>
+<PartDesc>BT-80</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>11</SerialNo>
+<DisplayFlags>1</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(54,250,21)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>288.9</Station>
+<OD>66.04</OD>
+<ID>65.79</ID>
+<Len>360.68</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>0</IsMotorMount>
+<MotorDia>0.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>0</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>15.025</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Tube coupler</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>1</UseKnownCG>
+<Xb>-10.</Xb>
+<CalcMass>20.465</CalcMass>
+<CalcCG>35.45</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>TC-2.56</PartNo>
+<PartDesc>Tube Coupler</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>12</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>278.9</Station>
+<OD>65.79</OD>
+<ID>63.25</ID>
+<Len>70.9</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>4</UsageCode>
+<AutoSize>1</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<MassObject>
+<PartMfg>Custom</PartMfg>
+<KnownMass>40.</KnownMass>
+<Density>0.</Density>
+<Material>Custom</Material>
+<Name>Electronics</Name>
+<KnownCG>138.3</KnownCG>
+<UseKnownCG>1</UseKnownCG>
+<Xb>138.3</Xb>
+<CalcMass>0.</CalcMass>
+<CalcCG>0.</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>13</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>427.2</Station>
+<TypeCode>0</TypeCode>
+<Len>0.</Len>
+<AttachedParts>
+</AttachedParts>
+</MassObject>
+<FinSet>
+<PartMfg>Quest</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>128.148</Density>
+<Material>Balsa</Material>
+<Name>Fin set</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>228.6</Xb>
+<CalcMass>1.32269</CalcMass>
+<CalcCG>19.05</CalcCG>
+<WettedSurface>0.0028791</WettedSurface>
+<PaintedSurface>0.0086373</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>Payloader One</PartNo>
+<PartDesc>Fin</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.0830777</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>14</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>2.93008</BarrowmanCNa>
+<BarrowmanXN>0.52856</BarrowmanXN>
+<RockSimCNa>5.49629</RockSimCNa>
+<RockSimXN>0.52856</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>517.5</Station>
+<FinCount>3</FinCount>
+<RootChord>38.1</RootChord>
+<TipChord>24.9</TipChord>
+<SemiSpan>45.7</SemiSpan>
+<MidChordLen>45.7</MidChordLen>
+<SweepDistance>6.6</SweepDistance>
+<Thickness>2.39</Thickness>
+<ShapeCode>0</ShapeCode>
+<FinishCode>0</FinishCode>
+<TipShapeCode>2</TipShapeCode>
+<TabLength>0.</TabLength>
+<TabDepth>0.</TabDepth>
+<TabOffset>0.</TabOffset>
+<SweepMode>1</SweepMode>
+<SweepAngle>0.139993</SweepAngle>
+<RockSimXNPerFin>0.</RockSimXNPerFin>
+<RockSimRadialXNPerFin>54.2741</RockSimRadialXNPerFin>
+<RockSimCNaPerFin>2.83538</RockSimCNaPerFin>
+<TaperRatio>0.653543</TaperRatio>
+<CantAngle>0.</CantAngle>
+<CantPivotPoint>0.</CantPivotPoint>
+<AttachedParts>
+</AttachedParts>
+</FinSet>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>302.</Xb>
+<CalcMass>7.65502</CalcMass>
+<CalcCG>1.59</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC CR-2.56-0.95</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>15</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>590.9</Station>
+<OD>65.02</OD>
+<ID>25.4</ID>
+<Len>3.18</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>335.5</Xb>
+<CalcMass>7.62626</CalcMass>
+<CalcCG>1.585</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC CR-2.56-(2)0.95</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>16</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>624.4</Station>
+<OD>65.</OD>
+<ID>25.4</ID>
+<Len>3.17</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<BodyTube>
+<PartMfg>Public Missiles Ltd.</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>958.705</Density>
+<Material>Kraft phenolic</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>587.4</Xb>
+<CalcMass>9.8818</CalcMass>
+<CalcCG>35.</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>KS-1.1</PartNo>
+<PartDesc>KwikSwitch MMT 29mm</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>-1.58371</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>17</SerialNo>
+<DisplayFlags>1</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>1</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>587.4</Station>
+<OD>32.26</OD>
+<ID>29.21</ID>
+<Len>70.</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>1</IsMotorMount>
+<MotorDia>29.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>1</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+<Ring>
+<PartMfg>Semroc</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Engine block</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>2.69315</CalcMass>
+<CalcCG>2.39</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>TB-7</PartNo>
+<PartDesc>Thrust Block</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>18</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>587.4</Station>
+<OD>29.21</OD>
+<ID>14.61</ID>
+<Len>4.78</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>2</UsageCode>
+<AutoSize>1</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+</AttachedParts>
+</BodyTube>
+</AttachedParts>
+</BodyTube>
+</Stage2Parts>
+<Stage1Parts>
+<Transition>
+<PartMfg>BalsaMachining.com</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>128.148</Density>
+<Material>Balsa</Material>
+<Name>Transition</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>8.62096</CalcMass>
+<CalcCG>25.1578</CalcCG>
+<WettedSurface>0.00986655</WettedSurface>
+<PaintedSurface>0.00986655</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>TA6080</PartNo>
+<PartDesc>Transition T60 to T80 2.25 in long</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>19</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>-1.20451</BarrowmanCNa>
+<BarrowmanXN>0.675971</BarrowmanXN>
+<RockSimCNa>-1.20451</RockSimCNa>
+<RockSimXN>0.675971</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>649.58</Station>
+<FrontDia>66.04</FrontDia>
+<RearDia>41.4</RearDia>
+<Len>57.15</Len>
+<FinishCode>0</FinishCode>
+<FrontShoulderLen>20.</FrontShoulderLen>
+<RearShoulderLen>38.1</RearShoulderLen>
+<ConstructionType>1</ConstructionType>
+<WallThickness>3.</WallThickness>
+<FrontShoulderDia>64.77</FrontShoulderDia>
+<RearShoulderDia>41.38</RearShoulderDia>
+<CoreDia>0.</CoreDia>
+<ShapeCode>0</ShapeCode>
+<ShapeParameter>0.</ShapeParameter>
+<EquivNoseLen>153.172</EquivNoseLen>
+<EquivNoseOffset>96.022</EquivNoseOffset>
+<AttachedParts>
+</AttachedParts>
+</Transition>
+<BodyTube>
+<PartMfg>LOC/Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>33.5306</CalcMass>
+<CalcCG>209.2</CalcCG>
+<WettedSurface>0.0604643</WettedSurface>
+<PaintedSurface>0.0604643</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC BT-2.14</PartNo>
+<PartDesc>Airframe tube</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>20</SerialNo>
+<DisplayFlags>1</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(54,250,21)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>706.73</Station>
+<OD>46.</OD>
+<ID>45.</ID>
+<Len>418.4</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>0</IsMotorMount>
+<MotorDia>0.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>0</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+<FinSet>
+<PartMfg>Quest</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Elliptical Fins</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>69.7</Xb>
+<CalcMass>36.2048</CalcMass>
+<CalcCG>46.</CalcCG>
+<WettedSurface>0.00676907</WettedSurface>
+<PaintedSurface>0.0203072</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>-0.519061</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>21</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>3.64017</BarrowmanCNa>
+<BarrowmanXN>0.802926</BarrowmanXN>
+<RockSimCNa>6.19583</RockSimCNa>
+<RockSimXN>0.802926</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>776.43</Station>
+<FinCount>3</FinCount>
+<RootChord>92.</RootChord>
+<TipChord>46.</TipChord>
+<SemiSpan>46.9</SemiSpan>
+<MidChordLen>46.9</MidChordLen>
+<SweepDistance>33.49</SweepDistance>
+<Thickness>3.18</Thickness>
+<ShapeCode>1</ShapeCode>
+<FinishCode>0</FinishCode>
+<TipShapeCode>0</TipShapeCode>
+<TabLength>0.</TabLength>
+<TabDepth>0.</TabDepth>
+<TabOffset>0.</TabOffset>
+<SweepMode>1</SweepMode>
+<SweepAngle>0.349066</SweepAngle>
+<RockSimXNPerFin>0.</RockSimXNPerFin>
+<RockSimRadialXNPerFin>43.8444</RockSimRadialXNPerFin>
+<RockSimCNaPerFin>3.30201</RockSimCNaPerFin>
+<TaperRatio>0.5</TaperRatio>
+<CantAngle>0.</CantAngle>
+<CantPivotPoint>0.</CantPivotPoint>
+<AttachedParts>
+</AttachedParts>
+</FinSet>
+<CustomFinSet>
+<PartMfg>Public Missiles</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1905.24</Density>
+<Material>G10 fiberglass</Material>
+<Name>Custom Fins</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>241.7</Xb>
+<CalcMass>256.292</CalcMass>
+<CalcCG>78.9922</CalcCG>
+<WettedSurface>0.0355266</WettedSurface>
+<PaintedSurface>0.10658</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>FIN-C-02</PartNo>
+<PartDesc>Fins</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.357967</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>22</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>25.7124</BarrowmanCNa>
+<BarrowmanXN>0.996591</BarrowmanXN>
+<RockSimCNa>34.6308</RockSimCNa>
+<RockSimXN>0.996926</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>948.43</Station>
+<FinCount>3</FinCount>
+<RootChord>152.4</RootChord>
+<TipChord>83.83</TipChord>
+<SemiSpan>146.72</SemiSpan>
+<MidChordLen>146.81</MidChordLen>
+<SweepDistance>39.42</SweepDistance>
+<Thickness>2.38</Thickness>
+<ShapeCode>2</ShapeCode>
+<FinishCode>0</FinishCode>
+<TipShapeCode>2</TipShapeCode>
+<TabLength>101.6</TabLength>
+<TabDepth>10.6</TabDepth>
+<TabOffset>35.6</TabOffset>
+<SweepMode>1</SweepMode>
+<SweepAngle>0.</SweepAngle>
+<RockSimXNPerFin>0.</RockSimXNPerFin>
+<RockSimRadialXNPerFin>89.2598</RockSimRadialXNPerFin>
+<RockSimCNaPerFin>19.9941</RockSimCNaPerFin>
+<TaperRatio>0.550066</TaperRatio>
+<CantAngle>0.</CantAngle>
+<CantPivotPoint>0.</CantPivotPoint>
+<AverageChord>121.07</AverageChord>
+<EffectiveTipChord>83.8257</EffectiveTipChord>
+<PointList>152.4,0|126.202,146.715|36.455,146.715|0,0|</PointList>
+<AutoCalcGridStepX>0,0,0,0,0,0,0</AutoCalcGridStepX>
+<GridStepCountX>10,10,10,10,10,10,10</GridStepCountX>
+<UseAbsoluteGridStepsX>0,0,0,0,0,0,0</UseAbsoluteGridStepsX>
+<GridStepSizeX>0.5,10,0.5,0.5,0.00018939,0.001,1</GridStepSizeX>
+<SnapToSizeX>0.25,5,0.1,0.1,0.00018939,0.001,1</SnapToSizeX>
+<AutoCalcGridStepY>0,0,0,0,0,0,0</AutoCalcGridStepY>
+<GridStepCountY>10,10,10,10,10,10,10</GridStepCountY>
+<UseAbsoluteGridStepsY>0,0,0,0,0,0,0</UseAbsoluteGridStepsY>
+<GridStepSizeY>0.5,10,0.5,0.5,0.00018939,0.001,1</GridStepSizeY>
+<SnapToSizeY>0.25,5,0.1,0.1,0.00018939,0.001,1</SnapToSizeY>
+<AttachedParts>
+</AttachedParts>
+</CustomFinSet>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>379.5</Xb>
+<CalcMass>2.55395</CalcMass>
+<CalcCG>1.59</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC FCR-1.52-1.14</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>23</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>1086.23</Station>
+<OD>45.</OD>
+<ID>24.79</ID>
+<Len>3.18</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>1</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>272.</Xb>
+<CalcMass>2.55395</CalcMass>
+<CalcCG>1.59</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC FCR-1.52-1.14</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>24</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>978.73</Station>
+<OD>45.</OD>
+<ID>24.79</ID>
+<Len>3.18</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>1</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<BodyTube>
+<PartMfg>Estes</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>259.7</Xb>
+<CalcMass>5.22902</CalcMass>
+<CalcCG>91.95</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>EST 3086</PartNo>
+<PartDesc>BT-50</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>25</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>966.43</Station>
+<OD>24.79</OD>
+<ID>24.13</ID>
+<Len>183.9</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>1</IsMotorMount>
+<MotorDia>24.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>1</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+</AttachedParts>
+</BodyTube>
+</AttachedParts>
+</BodyTube>
+</Stage1Parts>
+<SideViewDims>
+</SideViewDims>
+<BaseViewDims>
+</BaseViewDims>
+<VertViewDims>
+</VertViewDims>
+</RocketDesign>
+</DesignInformation>
+<SimulationResultsList>
+</SimulationResultsList>
+</RockSimDocument>
--- /dev/null
+<RockSimDocument>
+<FileVersion>3</FileVersion>
+<DesignInformation>
+<RocketDesign>
+<Name>Three Stage Everything Included Rocket - Override Total Mass/CG</Name>
+<CalculateCD>1</CalculateCD>
+<ProCalculateCD>1</ProCalculateCD>
+<ProCalculateCN>1</ProCalculateCN>
+<FixedCd>0.75</FixedCd>
+<FixedCd2>0.8</FixedCd2>
+<FixedCd3>0.81</FixedCd3>
+<FixedCd2Alone>0.95</FixedCd2Alone>
+<FixedCd3Alone>0.95</FixedCd3Alone>
+<StageCount>3</StageCount>
+<Stage3Mass>185.</Stage3Mass>
+<Stage2Mass>210.</Stage2Mass>
+<Stage1Mass>330.</Stage1Mass>
+<Stage321CG>0.</Stage321CG>
+<Stage32CG>0.</Stage32CG>
+<Stage3CG>300.</Stage3CG>
+<Stage2CGAlone>400.</Stage2CGAlone>
+<Stage1CGAlone>500.</Stage1CGAlone>
+<CPCalcFlags>1</CPCalcFlags>
+<LaunchGuideLength>914.4</LaunchGuideLength>
+<UseKnownMass>0</UseKnownMass>
+<DefaultFinish>0</DefaultFinish>
+<FinishMedium>0</FinishMedium>
+<FinishCoatCount>1</FinishCoatCount>
+<GlueType>0</GlueType>
+<CPSimFlags>1</CPSimFlags>
+<LastSerialNumber>32</LastSerialNumber>
+<DisplayFlags>7</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<BarromanXN>0,29.6333,327.236,966.855</BarromanXN>
+<BarrowmanCNa>0,2,4.95647,83.562</BarrowmanCNa>
+<RockSimXN>0,29.6333,385.082,953.76</RockSimXN>
+<RockSimCNa>0,2,6.95476,97.0629</RockSimCNa>
+<RockSimCNa90>0,0,0,0</RockSimCNa90>
+<RockSimXN90>0,0,0,0</RockSimXN90>
+<ViewType>0</ViewType>
+<ViewStageCount>3</ViewStageCount>
+<ViewTypeEdit>0</ViewTypeEdit>
+<ViewStageCountEdit>3</ViewStageCountEdit>
+<ZoomFactor>0.</ZoomFactor>
+<ZoomFactorEdit>0.</ZoomFactorEdit>
+<ScrollPosX>0</ScrollPosX>
+<ScrollPosY>0</ScrollPosY>
+<ScrollPosXEdit>0</ScrollPosXEdit>
+<ScrollPosYEdit>0</ScrollPosYEdit>
+<ThreeDFlags>0</ThreeDFlags>
+<ThreeDFlagsEdit>0</ThreeDFlagsEdit>
+<UseModelSprite>0</UseModelSprite>
+<StaticMarginRef>0</StaticMarginRef>
+<UserRefDiameter>0.</UserRefDiameter>
+<SideMarkerHeight>10.</SideMarkerHeight>
+<SideDimensionHeight>10.</SideDimensionHeight>
+<BaseMarkerHeight>10.</BaseMarkerHeight>
+<BaseDimensionHeight>10.</BaseDimensionHeight>
+<ShowGlideCP>0</ShowGlideCP>
+<ShowGridTypeSide>0</ShowGridTypeSide>
+<ShowGridTypeBase>0</ShowGridTypeBase>
+<GridSpacing>10.</GridSpacing>
+<GridOpacity>0.15</GridOpacity>
+<GridColor>black</GridColor>
+<MaxDiaWithFins>334.83</MaxDiaWithFins>
+<MaxDiaWithoutFins>66.</MaxDiaWithoutFins>
+<MaxLenWithFins>2155.23</MaxLenWithFins>
+<MaxLenWithoutFins>2155.23</MaxLenWithoutFins>
+<MinXExtent>0.</MinXExtent>
+<MaxXExtent>1150.33</MaxXExtent>
+<CalculatedMaxStageDia>0,66,66,66</CalculatedMaxStageDia>
+<CalculatedStageLen>0,347.5,1004.9,2155.23</CalculatedStageLen>
+<Cd3>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cd3>
+<Cd32>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cd32>
+<Cd321>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cd321>
+<Cb3>
+<PolyData useXYOnly="1" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cb3>
+<Cb32>
+<PolyData useXYOnly="1" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cb32>
+<Cb321>
+<PolyData useXYOnly="1" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</Cb321>
+<CNa3>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CNa3>
+<CNa32>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CNa32>
+<CNa321>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CNa321>
+<CP3>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CP3>
+<CP32>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CP32>
+<CP321>
+<PolyData useXYOnly="0" useSmoothCurveEvaluation="0" count="0">
+<X-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</X-data>
+<A-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</A-data>
+<B-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</B-data>
+<C-data>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</C-data>
+</PolyData>
+</CP321>
+<SimulationEventList>
+</SimulationEventList>
+<Stage3Parts>
+<NoseCone>
+<PartMfg>Aerospace Speciality Products</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>128.148</Density>
+<Material>Balsa</Material>
+<Name>Nose cone</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>42.6368</CalcMass>
+<CalcCG>76.0242</CalcCG>
+<WettedSurface>0.0152192</WettedSurface>
+<PaintedSurface>0.0152192</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>BNC80S</PartNo>
+<PartDesc><![CDATA[T-80H Balsa Nose Cone "S"]]></PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>1</SerialNo>
+<DisplayFlags>1</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>2.</BarrowmanCNa>
+<BarrowmanXN>0.0296333</BarrowmanXN>
+<RockSimCNa>2.</RockSimCNa>
+<RockSimXN>0.0296333</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>0.</Station>
+<Len>88.9</Len>
+<BaseDia>66.</BaseDia>
+<FinishCode>0</FinishCode>
+<ShapeCode>3</ShapeCode>
+<ConstructionType>0</ConstructionType>
+<ShoulderLen>38.1</ShoulderLen>
+<WallThickness>0.</WallThickness>
+<ShapeParameter>0.</ShapeParameter>
+<ShoulderOD>65.9</ShoulderOD>
+<BaseExtensionLen>0.</BaseExtensionLen>
+<CoreDia>0.</CoreDia>
+<CoreLen>0.</CoreLen>
+<AttachedParts>
+<MassObject>
+<PartMfg>Custom</PartMfg>
+<KnownMass>20.</KnownMass>
+<Density>0.</Density>
+<Material>Custom</Material>
+<Name>Clay</Name>
+<KnownCG>17.8</KnownCG>
+<UseKnownCG>1</UseKnownCG>
+<Xb>17.8</Xb>
+<CalcMass>0.</CalcMass>
+<CalcCG>0.</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>2</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>17.8</Station>
+<TypeCode>0</TypeCode>
+<Len>0.</Len>
+<AttachedParts>
+</AttachedParts>
+</MassObject>
+<BodyTube>
+<PartMfg>Apogee</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Attachment Rod</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>6.1</Xb>
+<CalcMass>2.47301</CalcMass>
+<CalcCG>78.85</CalcCG>
+<WettedSurface>0.00684683</WettedSurface>
+<PaintedSurface>0.00684683</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>10062</PartNo>
+<PartDesc>13 mm</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>3</SerialNo>
+<DisplayFlags>1</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>6.1</Station>
+<OD>13.82</OD>
+<ID>13.16</ID>
+<Len>157.7</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>0</IsMotorMount>
+<MotorDia>0.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>1</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Plate</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>78.9</Xb>
+<CalcMass>7.86272</CalcMass>
+<CalcCG>1.585</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC CR-2.56-(2)0.95</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>4</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>85.</Station>
+<OD>66.</OD>
+<ID>13.82</ID>
+<Len>3.17</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<Ring>
+<PartMfg>Custom</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1400.</Density>
+<Material>Carbon Fiber</Material>
+<Name>Sleeve </Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>17.5527</CalcMass>
+<CalcCG>13.85</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>5</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>2</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>136.1</Station>
+<OD>27.7</OD>
+<ID>13.82</ID>
+<Len>27.7</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>3</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<Parachute>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>170.</KnownMass>
+<Density>0.006685</Density>
+<Material>Rip stop nylon</Material>
+<Name>Nose Cone Parachute</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>1</UseKnownCG>
+<Xb>142.6</Xb>
+<CalcMass>15.8812</CalcMass>
+<CalcCG>34.5417</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>1</DensityType>
+<PartNo>LP-50</PartNo>
+<PartDesc>50 In. 16 lines</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>6</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>148.7</Station>
+<Dia>414.5</Dia>
+<SpillHoleDia>0.</SpillHoleDia>
+<SideCount>15</SideCount>
+<ShroudLineCount>16</ShroudLineCount>
+<Thickness>0.05</Thickness>
+<ShroudLineLen>1350.</ShroudLineLen>
+<ChuteCount>1</ChuteCount>
+<ShroudLineMassPerMM>0.00032972</ShroudLineMassPerMM>
+<ShroudLineMaterial>Carpet String (Apogee 29500)</ShroudLineMaterial>
+<DragCoefficient>0.95</DragCoefficient>
+<AttachedParts>
+</AttachedParts>
+</Parachute>
+</AttachedParts>
+</BodyTube>
+</AttachedParts>
+</NoseCone>
+<BodyTube>
+<PartMfg>Estes</PartMfg>
+<KnownMass>15.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Body tube</Name>
+<KnownCG>100.</KnownCG>
+<UseKnownCG>1</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>5.805</CalcMass>
+<CalcCG>100.</CalcCG>
+<WettedSurface>0.0414942</WettedSurface>
+<PaintedSurface>0.0414942</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>EST 3090</PartNo>
+<PartDesc>BT-80</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>7</SerialNo>
+<DisplayFlags>1</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(54,250,21)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>88.9</Station>
+<OD>66.</OD>
+<ID>65.79</ID>
+<Len>200.</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>0</IsMotorMount>
+<MotorDia>0.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>0</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>190.2</Xb>
+<CalcMass>7.65502</CalcMass>
+<CalcCG>1.59</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC CR-2.56-0.95</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>8</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>279.1</Station>
+<OD>65.02</OD>
+<ID>25.4</ID>
+<Len>3.18</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>165.</Xb>
+<CalcMass>7.62626</CalcMass>
+<CalcCG>1.585</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC CR-2.56-(2)0.95</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>9</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>253.9</Station>
+<OD>65.</OD>
+<ID>25.4</ID>
+<Len>3.17</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<BodyTube>
+<PartMfg>Public Missiles Ltd.</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>958.705</Density>
+<Material>Kraft phenolic</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>137.9</Xb>
+<CalcMass>9.8818</CalcMass>
+<CalcCG>35.</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>KS-1.1</PartNo>
+<PartDesc>KwikSwitch MMT 29mm</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>-1.58371</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>10</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>226.8</Station>
+<OD>32.26</OD>
+<ID>29.21</ID>
+<Len>70.</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>1</IsMotorMount>
+<MotorDia>29.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>1</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+</AttachedParts>
+</BodyTube>
+<LaunchLug>
+<PartMfg>Apogee</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Launch lug</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>77.2</Xb>
+<CalcMass>1.11942</CalcMass>
+<CalcCG>38.1</CalcCG>
+<WettedSurface>0.00374518</WettedSurface>
+<PaintedSurface>0.00199411</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>13056</PartNo>
+<PartDesc><![CDATA[1/4" X 3]]></PartDesc>
+<RadialLoc>37.185</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>26</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>166.1</Station>
+<OD>8.33</OD>
+<ID>7.26</ID>
+<Len>76.2</Len>
+<FinishCode>0</FinishCode>
+<AttachedParts>
+</AttachedParts>
+</LaunchLug>
+<ExternalPod>
+<PartMfg>Custom</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>0.</Density>
+<Name>Pod</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>200.</Xb>
+<CalcMass>0.</CalcMass>
+<CalcCG>0.</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<RadialLoc>36.175</RadialLoc>
+<RadialAngle>-1.50735</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>29</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>288.9</Station>
+<Detachable>1</Detachable>
+<AutoCalcRadialDistance>1</AutoCalcRadialDistance>
+<AutoCalcRadialAngle>1</AutoCalcRadialAngle>
+<AttachedParts>
+<BodyTube>
+<PartMfg>Apogee</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>958.705</Density>
+<Material>Kraft phenolic</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>0.400397</CalcMass>
+<CalcCG>29.3</CalcCG>
+<WettedSurface>0.00116902</WettedSurface>
+<PaintedSurface>0.00116902</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>9601</PartNo>
+<PartDesc>6 mm</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>30</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(54,250,21)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>288.9</Station>
+<OD>6.35</OD>
+<ID>5.59</ID>
+<Len>58.6</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>0</IsMotorMount>
+<MotorDia>0.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>0</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+</AttachedParts>
+</BodyTube>
+</AttachedParts>
+</ExternalPod>
+</AttachedParts>
+</BodyTube>
+</Stage3Parts>
+<Stage2Parts>
+<BodyTube>
+<PartMfg>Estes</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>2nd Stage Tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>10.468</CalcMass>
+<CalcCG>180.34</CalcCG>
+<WettedSurface>0.0748306</WettedSurface>
+<PaintedSurface>0.0748306</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>EST 3090</PartNo>
+<PartDesc>BT-80</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>11</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(54,250,21)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>288.9</Station>
+<OD>66.</OD>
+<ID>65.79</ID>
+<Len>360.68</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>0</IsMotorMount>
+<MotorDia>0.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>0</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>15.025</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Tube coupler</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>1</UseKnownCG>
+<Xb>-10.</Xb>
+<CalcMass>20.465</CalcMass>
+<CalcCG>35.45</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>TC-2.56</PartNo>
+<PartDesc>Tube Coupler</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>12</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>278.9</Station>
+<OD>65.79</OD>
+<ID>63.25</ID>
+<Len>70.9</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>4</UsageCode>
+<AutoSize>1</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<MassObject>
+<PartMfg>Custom</PartMfg>
+<KnownMass>40.</KnownMass>
+<Density>0.</Density>
+<Material>Custom</Material>
+<Name>Electronics</Name>
+<KnownCG>138.3</KnownCG>
+<UseKnownCG>1</UseKnownCG>
+<Xb>138.3</Xb>
+<CalcMass>0.</CalcMass>
+<CalcCG>0.</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>13</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>427.2</Station>
+<TypeCode>0</TypeCode>
+<Len>0.</Len>
+<AttachedParts>
+</AttachedParts>
+</MassObject>
+<FinSet>
+<PartMfg>Quest</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>128.148</Density>
+<Material>Balsa</Material>
+<Name>Fin set</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>228.6</Xb>
+<CalcMass>1.32269</CalcMass>
+<CalcCG>19.05</CalcCG>
+<WettedSurface>0.0028791</WettedSurface>
+<PaintedSurface>0.0086373</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>Payloader One</PartNo>
+<PartDesc>Fin</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.0830777</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>14</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>2.95647</BarrowmanCNa>
+<BarrowmanXN>0.52856</BarrowmanXN>
+<RockSimCNa>5.54523</RockSimCNa>
+<RockSimXN>0.52856</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>517.5</Station>
+<FinCount>3</FinCount>
+<RootChord>38.1</RootChord>
+<TipChord>24.9</TipChord>
+<SemiSpan>45.7</SemiSpan>
+<MidChordLen>45.7</MidChordLen>
+<SweepDistance>6.6</SweepDistance>
+<Thickness>2.39</Thickness>
+<ShapeCode>0</ShapeCode>
+<FinishCode>0</FinishCode>
+<TipShapeCode>2</TipShapeCode>
+<TabLength>0.</TabLength>
+<TabDepth>0.</TabDepth>
+<TabOffset>0.</TabOffset>
+<SweepMode>1</SweepMode>
+<SweepAngle>0.139993</SweepAngle>
+<RockSimXNPerFin>0.</RockSimXNPerFin>
+<RockSimRadialXNPerFin>54.2541</RockSimRadialXNPerFin>
+<RockSimCNaPerFin>2.86063</RockSimCNaPerFin>
+<TaperRatio>0.653543</TaperRatio>
+<CantAngle>0.</CantAngle>
+<CantPivotPoint>0.</CantPivotPoint>
+<AttachedParts>
+</AttachedParts>
+</FinSet>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>302.</Xb>
+<CalcMass>7.65502</CalcMass>
+<CalcCG>1.59</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC CR-2.56-0.95</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>15</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>590.9</Station>
+<OD>65.02</OD>
+<ID>25.4</ID>
+<Len>3.18</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>335.5</Xb>
+<CalcMass>7.62626</CalcMass>
+<CalcCG>1.585</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC CR-2.56-(2)0.95</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>16</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>624.4</Station>
+<OD>65.</OD>
+<ID>25.4</ID>
+<Len>3.17</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>0</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<BodyTube>
+<PartMfg>Public Missiles Ltd.</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>958.705</Density>
+<Material>Kraft phenolic</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>587.4</Xb>
+<CalcMass>9.8818</CalcMass>
+<CalcCG>35.</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>KS-1.1</PartNo>
+<PartDesc>KwikSwitch MMT 29mm</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>-1.58371</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>17</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>1</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>587.4</Station>
+<OD>32.26</OD>
+<ID>29.21</ID>
+<Len>70.</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>1</IsMotorMount>
+<MotorDia>29.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>1</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+<Ring>
+<PartMfg>Semroc</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Engine block</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>2.69315</CalcMass>
+<CalcCG>2.39</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>TB-7</PartNo>
+<PartDesc>Thrust Block</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>18</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>587.4</Station>
+<OD>29.21</OD>
+<ID>14.61</ID>
+<Len>4.78</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>2</UsageCode>
+<AutoSize>1</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+</AttachedParts>
+</BodyTube>
+<LaunchLug>
+<PartMfg>Apogee</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Launch lug</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>265.8</Xb>
+<CalcMass>1.11942</CalcMass>
+<CalcCG>38.1</CalcCG>
+<WettedSurface>0.00374518</WettedSurface>
+<PaintedSurface>0.00199411</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>13056</PartNo>
+<PartDesc><![CDATA[1/4" X 3]]></PartDesc>
+<RadialLoc>37.185</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>27</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>554.7</Station>
+<OD>8.33</OD>
+<ID>7.26</ID>
+<Len>76.2</Len>
+<FinishCode>0</FinishCode>
+<AttachedParts>
+</AttachedParts>
+</LaunchLug>
+</AttachedParts>
+</BodyTube>
+</Stage2Parts>
+<Stage1Parts>
+<Transition>
+<PartMfg>BalsaMachining.com</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>128.148</Density>
+<Material>Balsa</Material>
+<Name>Transition</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>8.61936</CalcMass>
+<CalcCG>25.1551</CalcCG>
+<WettedSurface>0.0098623</WettedSurface>
+<PaintedSurface>0.0098623</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>TA6080</PartNo>
+<PartDesc>Transition T60 to T80 2.25 in long</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>19</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>-1.213</BarrowmanCNa>
+<BarrowmanXN>0.675973</BarrowmanXN>
+<RockSimCNa>-1.213</RockSimCNa>
+<RockSimXN>0.675973</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>649.58</Station>
+<FrontDia>66.</FrontDia>
+<RearDia>41.4</RearDia>
+<Len>57.15</Len>
+<FinishCode>0</FinishCode>
+<FrontShoulderLen>20.</FrontShoulderLen>
+<RearShoulderLen>38.1</RearShoulderLen>
+<ConstructionType>1</ConstructionType>
+<WallThickness>3.</WallThickness>
+<FrontShoulderDia>64.77</FrontShoulderDia>
+<RearShoulderDia>41.38</RearShoulderDia>
+<CoreDia>0.</CoreDia>
+<ShapeCode>0</ShapeCode>
+<ShapeParameter>0.</ShapeParameter>
+<EquivNoseLen>153.339</EquivNoseLen>
+<EquivNoseOffset>96.1895</EquivNoseOffset>
+<AttachedParts>
+</AttachedParts>
+</Transition>
+<BodyTube>
+<PartMfg>LOC/Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>0.</CalcMass>
+<CalcCG>nan.</CalcCG>
+<WettedSurface>0.0544179</WettedSurface>
+<PaintedSurface>0.0544179</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC BT-2.14</PartNo>
+<PartDesc>Airframe tube</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>20</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(54,250,21)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>706.73</Station>
+<OD>41.4</OD>
+<ID>41.</ID>
+<Len>418.4</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>0</IsMotorMount>
+<MotorDia>0.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>0</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+<FinSet>
+<PartMfg>Quest</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Elliptical Fins</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>69.7</Xb>
+<CalcMass>36.2048</CalcMass>
+<CalcCG>46.</CalcCG>
+<WettedSurface>0.00676907</WettedSurface>
+<PaintedSurface>0.0203072</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>-0.519061</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>21</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>3.61024</BarrowmanCNa>
+<BarrowmanXN>0.802926</BarrowmanXN>
+<RockSimCNa>6.03948</RockSimCNa>
+<RockSimXN>0.802926</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>776.43</Station>
+<FinCount>3</FinCount>
+<RootChord>92.</RootChord>
+<TipChord>46.</TipChord>
+<SemiSpan>46.9</SemiSpan>
+<MidChordLen>46.9</MidChordLen>
+<SweepDistance>33.49</SweepDistance>
+<Thickness>3.18</Thickness>
+<ShapeCode>1</ShapeCode>
+<FinishCode>0</FinishCode>
+<TipShapeCode>0</TipShapeCode>
+<TabLength>0.</TabLength>
+<TabDepth>0.</TabDepth>
+<TabOffset>0.</TabOffset>
+<SweepMode>1</SweepMode>
+<SweepAngle>0.349066</SweepAngle>
+<RockSimXNPerFin>0.</RockSimXNPerFin>
+<RockSimRadialXNPerFin>41.5444</RockSimRadialXNPerFin>
+<RockSimCNaPerFin>3.21868</RockSimCNaPerFin>
+<TaperRatio>0.5</TaperRatio>
+<CantAngle>0.</CantAngle>
+<CantPivotPoint>0.</CantPivotPoint>
+<AttachedParts>
+</AttachedParts>
+</FinSet>
+<CustomFinSet>
+<PartMfg>Public Missiles</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1905.24</Density>
+<Material>G10 fiberglass</Material>
+<Name>Custom Fins</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>241.7</Xb>
+<CalcMass>256.292</CalcMass>
+<CalcCG>78.9922</CalcCG>
+<WettedSurface>0.0355266</WettedSurface>
+<PaintedSurface>0.10658</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>FIN-C-02</PartNo>
+<PartDesc>Fins</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.357967</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>22</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>25.7037</BarrowmanCNa>
+<BarrowmanXN>0.996526</BarrowmanXN>
+<RockSimCNa>34.1865</RockSimCNa>
+<RockSimXN>0.996822</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>948.43</Station>
+<FinCount>3</FinCount>
+<RootChord>152.4</RootChord>
+<TipChord>84.53</TipChord>
+<SemiSpan>146.72</SemiSpan>
+<MidChordLen>146.81</MidChordLen>
+<SweepDistance>39.07</SweepDistance>
+<Thickness>2.38</Thickness>
+<ShapeCode>2</ShapeCode>
+<FinishCode>0</FinishCode>
+<TipShapeCode>2</TipShapeCode>
+<TabLength>101.6</TabLength>
+<TabDepth>10.6</TabDepth>
+<TabOffset>35.6</TabOffset>
+<SweepMode>1</SweepMode>
+<SweepAngle>0.</SweepAngle>
+<RockSimXNPerFin>0.</RockSimXNPerFin>
+<RockSimRadialXNPerFin>87.0529</RockSimRadialXNPerFin>
+<RockSimCNaPerFin>19.7376</RockSimCNaPerFin>
+<TaperRatio>0.554659</TaperRatio>
+<CantAngle>0.</CantAngle>
+<CantPivotPoint>0.</CantPivotPoint>
+<AverageChord>121.39</AverageChord>
+<EffectiveTipChord>84.5261</EffectiveTipChord>
+<PointList>152.4,0|126.202,146.715|36.455,146.715|0,0|</PointList>
+<AutoCalcGridStepX>0,0,0,0,0,0,0</AutoCalcGridStepX>
+<GridStepCountX>10,10,10,10,10,10,10</GridStepCountX>
+<UseAbsoluteGridStepsX>0,0,0,0,0,0,0</UseAbsoluteGridStepsX>
+<GridStepSizeX>0.5,10,0.5,0.5,0.00018939,0.001,1</GridStepSizeX>
+<SnapToSizeX>0.25,5,0.1,0.1,0.00018939,0.001,1</SnapToSizeX>
+<AutoCalcGridStepY>0,0,0,0,0,0,0</AutoCalcGridStepY>
+<GridStepCountY>10,10,10,10,10,10,10</GridStepCountY>
+<UseAbsoluteGridStepsY>0,0,0,0,0,0,0</UseAbsoluteGridStepsY>
+<GridStepSizeY>0.5,10,0.5,0.5,0.00018939,0.001,1</GridStepSizeY>
+<SnapToSizeY>0.25,5,0.1,0.1,0.00018939,0.001,1</SnapToSizeY>
+<AttachedParts>
+</AttachedParts>
+</CustomFinSet>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>379.5</Xb>
+<CalcMass>1.99074</CalcMass>
+<CalcCG>1.59</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC FCR-1.52-1.14</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>23</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>1086.23</Station>
+<OD>41.4</OD>
+<ID>24.79</ID>
+<Len>3.18</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>1</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<Ring>
+<PartMfg>LOC Precision</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>724.996</Density>
+<Material>Aircraft plywood (LOC)</Material>
+<Name>Centering ring</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>272.</Xb>
+<CalcMass>1.99074</CalcMass>
+<CalcCG>1.59</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>LOC FCR-1.52-1.14</PartNo>
+<PartDesc>Centering Ring</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>24</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>978.73</Station>
+<OD>41.4</OD>
+<ID>24.79</ID>
+<Len>3.18</Len>
+<FinishCode>0</FinishCode>
+<UsageCode>0</UsageCode>
+<AutoSize>1</AutoSize>
+<AttachedParts>
+</AttachedParts>
+</Ring>
+<BodyTube>
+<PartMfg>Estes</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Body tube</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>259.7</Xb>
+<CalcMass>5.22902</CalcMass>
+<CalcCG>91.95</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>EST 3086</PartNo>
+<PartDesc>BT-50</PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>rgb(0,0,250)</AbientColor>
+<DiffuseColor>rgb(0,0,250)</DiffuseColor>
+<SpecularColor>rgb(255,255,255)</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>25</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>rgb(0,0,250)</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>966.43</Station>
+<OD>24.79</OD>
+<ID>24.13</ID>
+<Len>183.9</Len>
+<FinishCode>0</FinishCode>
+<IsMotorMount>1</IsMotorMount>
+<MotorDia>24.</MotorDia>
+<EngineOverhang>0.5</EngineOverhang>
+<FrontExtension>0.</FrontExtension>
+<RearExtension>0.</RearExtension>
+<IsInsideTube>1</IsInsideTube>
+<isStrapOnTube>0</isStrapOnTube>
+<AttachedParts>
+</AttachedParts>
+</BodyTube>
+<TubeFinSet>
+<PartMfg>Custom</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1121.29</Density>
+<Material>Paper</Material>
+<Name>Tube fins</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>12.6878</CalcMass>
+<CalcCG>24.79</CalcCG>
+<WettedSurface>0.0688063</WettedSurface>
+<PaintedSurface>0.0688063</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<RadialLoc>35.395</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>28</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>10.866</BarrowmanCNa>
+<BarrowmanXN>0.707815</BarrowmanXN>
+<RockSimCNa>10.866</RockSimCNa>
+<RockSimXN>0.707815</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>706.73</Station>
+<OD>24.79</OD>
+<ID>24.13</ID>
+<Len>49.58</Len>
+<FinishCode>0</FinishCode>
+<TubeCount>9</TubeCount>
+<MinTubeAngle>0.715549</MinTubeAngle>
+<MaxTubesAllowed>9</MaxTubesAllowed>
+<AttachedParts>
+</AttachedParts>
+</TubeFinSet>
+<RingTail>
+<PartMfg>Custom</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>688.794</Density>
+<Material>Cardboard</Material>
+<Name>Ringtail</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>0.</Xb>
+<CalcMass>2.93707</CalcMass>
+<CalcCG>10.</CalcCG>
+<WettedSurface>0.00474229</WettedSurface>
+<PaintedSurface>0.00444249</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>31</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>2</LocationMode>
+<Color>rgb(255,9,18)</Color>
+<BarrowmanCNa>39.6386</BarrowmanCNa>
+<BarrowmanXN>1.10463</BarrowmanXN>
+<RockSimCNa>39.6386</RockSimCNa>
+<RockSimXN>1.10463</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>1105.13</Station>
+<FinCount>3</FinCount>
+<RootChord>20.</RootChord>
+<TipChord>20.</TipChord>
+<SemiSpan>7.495</SemiSpan>
+<MidChordLen>7.495</MidChordLen>
+<SweepDistance>0.</SweepDistance>
+<Thickness>3.</Thickness>
+<ShapeCode>0</ShapeCode>
+<FinishCode>0</FinishCode>
+<TipShapeCode>0</TipShapeCode>
+<TabLength>0.</TabLength>
+<TabDepth>0.</TabDepth>
+<TabOffset>0.</TabOffset>
+<SweepMode>1</SweepMode>
+<SweepAngle>0.</SweepAngle>
+<RockSimXNPerFin>0.</RockSimXNPerFin>
+<RockSimRadialXNPerFin>0.</RockSimRadialXNPerFin>
+<RockSimCNaPerFin>0.</RockSimCNaPerFin>
+<TaperRatio>0.</TaperRatio>
+<CantAngle>0.</CantAngle>
+<CantPivotPoint>0.</CantPivotPoint>
+<OD>56.39</OD>
+<ID>55.37</ID>
+<Len>20.</Len>
+<TubeDensity>1121.29</TubeDensity>
+<TubeDensityType>0</TubeDensityType>
+<TubeMaterial>Paper</TubeMaterial>
+<AttachedParts>
+</AttachedParts>
+</RingTail>
+<Streamer>
+<PartMfg>Apogee</PartMfg>
+<KnownMass>0.</KnownMass>
+<Density>1309.</Density>
+<Material>Mylar</Material>
+<Name>Streamer</Name>
+<KnownCG>0.</KnownCG>
+<UseKnownCG>0</UseKnownCG>
+<Xb>149.4</Xb>
+<CalcMass>5.67514</CalcMass>
+<CalcCG>50.8</CalcCG>
+<WettedSurface>0.</WettedSurface>
+<PaintedSurface>0.</PaintedSurface>
+<GlueJointLength>0.</GlueJointLength>
+<DensityType>0</DensityType>
+<PartNo>29006</PartNo>
+<PartDesc><![CDATA[4" Wide Mylar]]></PartDesc>
+<RadialLoc>0.</RadialLoc>
+<RadialAngle>0.</RadialAngle>
+<Texture>file=()|position=(0,0,0)|origin=(0.5,0.5,0.5)|scale=(1,1,1)|repeat=(1)|interpolate=(0)|flipr(0)|flips(0)|flipt=(0)|preventseam=(1)</Texture>
+<Opacity>1.</Opacity>
+<Specular>0.</Specular>
+<SpecularPower>1.</SpecularPower>
+<Ambient>0.</Ambient>
+<Diffuse>1.</Diffuse>
+<AbientColor>blue</AbientColor>
+<DiffuseColor>blue</DiffuseColor>
+<SpecularColor>white</SpecularColor>
+<UseSingleColor>1</UseSingleColor>
+<SerialNo>32</SerialNo>
+<DisplayFlags>0</DisplayFlags>
+<MetricsFlags>0</MetricsFlags>
+<LocationMode>0</LocationMode>
+<Color>blue</Color>
+<BarrowmanCNa>0.</BarrowmanCNa>
+<BarrowmanXN>0.</BarrowmanXN>
+<RockSimCNa>0.</RockSimCNa>
+<RockSimXN>0.</RockSimXN>
+<SimpleColorModel>1</SimpleColorModel>
+<ProduceTemplate>0</ProduceTemplate>
+<TemplateUnits>8</TemplateUnits>
+<Removed>0</Removed>
+<Station>856.13</Station>
+<Len>1422.4</Len>
+<Width>101.6</Width>
+<Thickness>0.03</Thickness>
+<StreamerCount>1</StreamerCount>
+<DragCoefficient>0.127</DragCoefficient>
+<IsFolded>0</IsFolded>
+<AutoCalcCd>1</AutoCalcCd>
+<AttachedParts>
+</AttachedParts>
+</Streamer>
+</AttachedParts>
+</BodyTube>
+</Stage1Parts>
+<SideViewDims>
+</SideViewDims>
+<BaseViewDims>
+</BaseViewDims>
+<VertViewDims>
+</VertViewDims>
+</RocketDesign>
+</DesignInformation>
+<SimulationResultsList>
+</SimulationResultsList>
+</RockSimDocument>