From: rodinia814 Date: Sun, 21 Mar 2010 03:24:12 +0000 (+0000) Subject: DGP - added DensityType parsing for recovery devices X-Git-Tag: upstream/1.1.0~2^2~3 X-Git-Url: https://git.gag.com/?p=debian%2Fopenrocket;a=commitdiff_plain;h=60aab148ca35918cafe88a98de8e1efd8960ce00 DGP - added DensityType parsing for recovery devices git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@51 180e2498-e6e9-4542-8430-84ac67f01cd8 --- diff --git a/src/net/sf/openrocket/file/rocksim/BaseHandler.java b/src/net/sf/openrocket/file/rocksim/BaseHandler.java index e9a8469e..700c2f9b 100644 --- a/src/net/sf/openrocket/file/rocksim/BaseHandler.java +++ b/src/net/sf/openrocket/file/rocksim/BaseHandler.java @@ -15,6 +15,8 @@ import java.util.HashMap; /** * An abstract base class that handles common parsing. All Rocksim component handlers are subclassed from here. + * + * @param the specific RocketComponent subtype for which the concrete handler can create */ public abstract class BaseHandler extends ElementHandler { @@ -30,6 +32,11 @@ public abstract class BaseHandler extends ElementHand * The density of the material in the component. */ private Double density = 0d; + /** + * The internal Rocksim density type. + */ + private RocksimDensityType densityType = RocksimDensityType.ROCKSIM_BULK; + /** * The material name. */ @@ -57,31 +64,54 @@ public abstract class BaseHandler extends ElementHand mass = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS); } if ("Density".equals(element)) { - density = Math.max(0d, Double.parseDouble(content) / getDensityConversion()); + density = Math.max(0d, Double.parseDouble(content) ); } if ("KnownCG".equals(element)) { cg = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH); } - if ("UseKnownCG".equals(element)) { + if ("UseKnownCG".equals(element)) { //Rocksim sets UseKnownCG to true to control the override of both cg and mass boolean override = "1".equals(content); setOverride(component, override, mass, cg); } + if ("DensityType".equals(element)) { + densityType = RocksimDensityType.fromCode(Integer.parseInt(content)); + } } catch (NumberFormatException nfe) { warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); } } + /** + * {@inheritDoc} + */ @Override public void endHandler(String element, HashMap 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. */ + density = computeDensity(densityType, density); RocketComponent component = getComponent(); updateComponentMaterial(component, materialName, getMaterialType(), density); } + /** + * Compute the density. Rocksim does strange things with densities. For some streamer material it's in cubic, + * rather than square, units. In those cases it needs to be converted to an appropriate SURFACE material density. + * Some G10 fiberglass materials are in cubic units, other G10 fiberglass is in square units. And due to a + * Rocksim bug, some densities are 0 when they clearly should not be. + * + * This may be overridden for specific component density computations. + * + * @param type the rocksim density + * @param rawDensity the density as specified in the Rocksim design file + * @return a value in OpenRocket SURFACE density units + */ + protected double computeDensity(RocksimDensityType type, double rawDensity) { + return rawDensity / type.asOpenRocket(); + } + /** * 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. @@ -163,23 +193,6 @@ public abstract class BaseHandler extends ElementHand return Material.newMaterial(type, "RS: " + name, density, true); } - /** - * Get the appropriate density conversion for different types of materials. - * - * @return a conversion value that is assumed to be in Rocksim Units / OpenRocket Units - */ - private double getDensityConversion() { - switch (getMaterialType()) { - case LINE: - return RocksimHandler.ROCKSIM_TO_OPENROCKET_LINE_DENSITY; - case SURFACE: - return RocksimHandler.ROCKSIM_TO_OPENROCKET_SURFACE_DENSITY; - case BULK: - default: - return RocksimHandler.ROCKSIM_TO_OPENROCKET_BULK_DENSITY; - } - } - /** * 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. diff --git a/src/net/sf/openrocket/file/rocksim/ParachuteHandler.java b/src/net/sf/openrocket/file/rocksim/ParachuteHandler.java index f104a07e..6cadaff5 100644 --- a/src/net/sf/openrocket/file/rocksim/ParachuteHandler.java +++ b/src/net/sf/openrocket/file/rocksim/ParachuteHandler.java @@ -17,9 +17,8 @@ import java.util.HashMap; /** * A SAX handler for Rocksim's Parachute XML type. - *

*/ -class ParachuteHandler extends PositionDependentHandler { +class ParachuteHandler extends RecoveryDeviceHandler { /** * The OpenRocket Parachute instance */ @@ -43,11 +42,17 @@ class ParachuteHandler extends PositionDependentHandler { c.addChild(chute); } + /** + * {@inheritDoc} + */ @Override public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { return PlainTextHandler.INSTANCE; } + /** + * {@inheritDoc} + */ @Override public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { @@ -73,8 +78,7 @@ class ParachuteHandler extends PositionDependentHandler { 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)); + chute.setLineLength(Math.max(0, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH)); } if ("SpillHoleDia".equals(element)) { //Not supported in OpenRocket @@ -108,25 +112,5 @@ class ParachuteHandler extends PositionDependentHandler { 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; - } - } diff --git a/src/net/sf/openrocket/file/rocksim/PositionDependentHandler.java b/src/net/sf/openrocket/file/rocksim/PositionDependentHandler.java index 9d457227..c65e1aec 100644 --- a/src/net/sf/openrocket/file/rocksim/PositionDependentHandler.java +++ b/src/net/sf/openrocket/file/rocksim/PositionDependentHandler.java @@ -10,17 +10,22 @@ import org.xml.sax.SAXException; import java.util.HashMap; /** - * An abstract base class that handles position dependencies for all lower level components that + * An abstract base class that handles position dependencies for all lower level components that * are position aware. + * + * @param the specific position dependent RocketComponent subtype for which the concrete handler can create */ public abstract class PositionDependentHandler extends BaseHandler { /** Temporary position value. */ private Double positionValue; - + /** Temporary position. */ private RocketComponent.Position position; + /** + * {@inheritDoc} + */ @Override public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { @@ -33,12 +38,12 @@ public abstract class PositionDependentHandler extend 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 @@ -54,16 +59,16 @@ public abstract class PositionDependentHandler extend } /** - * Set the relative position onto the component. This cannot be done directly because setRelativePosition is not + * 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 @@ -76,5 +81,5 @@ public abstract class PositionDependentHandler extend component.setPositionValue(location); } } - + } diff --git a/src/net/sf/openrocket/file/rocksim/RecoveryDeviceHandler.java b/src/net/sf/openrocket/file/rocksim/RecoveryDeviceHandler.java new file mode 100644 index 00000000..387e29ba --- /dev/null +++ b/src/net/sf/openrocket/file/rocksim/RecoveryDeviceHandler.java @@ -0,0 +1,114 @@ +/* + * RecoveryDeviceHandler.java + */ +package net.sf.openrocket.file.rocksim; + +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.material.Material; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import org.xml.sax.SAXException; + +import java.util.HashMap; + +/** + * A handler specific to streamers and parachutes. This is done because Rocksim allows any type of material to be + * used as a recovery device, which causes oddities with respect to densities. Density computation is overridden + * here to try to correctly compute a material's density in OpenRocket units. + * + * @param either a Streamer or Parachute + */ +public abstract class RecoveryDeviceHandler extends PositionDependentHandler { + + /** + * The thickness. Not used by every component, and some component handlers may parse it for their own purposes. + */ + private double thickness = 0d; + /** + * The Rocksim calculated mass. Used only when not overridden and when Rocksim says density == 0 (Rocksim bug). + */ + private Double calcMass = 0d; + + /** + * {@inheritDoc} + */ + @Override + public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) + throws SAXException { + super.closeElement(element, attributes, content, warnings); + + try { + if ("Thickness".equals(element)) { + thickness = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH; + } + if ("CalcMass".equals(element)) { + calcMass = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS); + } + } + catch (NumberFormatException nfe) { + warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); + } + } + + + /** + * Compute the density. Rocksim does strange things with densities. For some streamer material it's in cubic, + * rather than square, units. In those cases it needs to be converted to an appropriate SURFACE material density. + * + * @param type the rocksim density + * @param rawDensity the density as specified in the Rocksim design file + * @return a value in OpenRocket SURFACE density units + */ + protected double computeDensity(RocksimDensityType type, double rawDensity) { + + double result; + + if (rawDensity > 0d) { + //ROCKSIM_SURFACE is a square area density; compute normally + //ROCKSIM_LINE is a single length dimension (kg/m) but Rocksim ignores thickness for this type and treats + //it like a SURFACE. + if (RocksimDensityType.ROCKSIM_SURFACE.equals(type) || RocksimDensityType.ROCKSIM_LINE.equals(type)) { + result = rawDensity / RocksimDensityType.ROCKSIM_SURFACE.asOpenRocket(); + } + //ROCKSIM_BULK is a cubic area density; multiple by thickness to make per square area; the result, when + //multiplied by the area will then equal Rocksim's computed mass. + else { + result = (rawDensity / type.asOpenRocket()) * thickness; + } + } + else { + result = calcMass / getComponent().getArea(); + //A Rocksim bug on streamers/parachutes results in a 0 density at times. When that is detected, try + //to compute an approximate density from Rocksim's computed mass. + if (RocksimDensityType.ROCKSIM_BULK.equals(type)) { + //ROCKSIM_BULK is a cubic area density; multiple by thickness to make per square area + result *= thickness; + } + } + return result; + } + + /** + * 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) { + getComponent().setRelativePosition(position); + } + + /** + * Get the required type of material for this component. This is the OpenRocket type, which does NOT always + * correspond to Rocksim. Some streamer material is defined as BULK in the Rocksim file. In those cases + * it is adjusted in this handler. + * + * @return SURFACE + */ + @Override + public Material.Type getMaterialType() { + return Material.Type.SURFACE; + } + +} diff --git a/src/net/sf/openrocket/file/rocksim/RocksimDensityType.java b/src/net/sf/openrocket/file/rocksim/RocksimDensityType.java new file mode 100644 index 00000000..c672b62b --- /dev/null +++ b/src/net/sf/openrocket/file/rocksim/RocksimDensityType.java @@ -0,0 +1,56 @@ +/* + * RocksimDensityType.java + */ +package net.sf.openrocket.file.rocksim; + +/** + * Models the nose cone shape of a rocket. Maps from Rocksim's notion to OpenRocket's. + */ +enum RocksimDensityType { + ROCKSIM_BULK (0, RocksimHandler.ROCKSIM_TO_OPENROCKET_BULK_DENSITY), + ROCKSIM_SURFACE(1, RocksimHandler.ROCKSIM_TO_OPENROCKET_SURFACE_DENSITY), + ROCKSIM_LINE (2, RocksimHandler.ROCKSIM_TO_OPENROCKET_LINE_DENSITY); + + /** The Rocksim enumeration value. Sent in XML. */ + private final int ordinal; + + /** The corresponding OpenRocket shape. */ + private final double conversion; + + /** + * Constructor. + * + * @param idx the Rocksim shape code + * @param theConversion the numerical conversion ratio to OpenRocket + */ + private RocksimDensityType(int idx, double theConversion) { + ordinal = idx; + conversion = theConversion; + } + + /** + * Get the OpenRocket shape that corresponds to the Rocksim value. + * + * @return a conversion + */ + public double asOpenRocket() { + return conversion; + } + + /** + * Lookup an instance of this enum based upon the Rocksim code. + * + * @param rocksimDensityType the Rocksim code (from XML) + * @return an instance of this enum + */ + public static RocksimDensityType fromCode(int rocksimDensityType) { + RocksimDensityType[] values = values(); + for (RocksimDensityType value : values) { + if (value.ordinal == rocksimDensityType) { + return value; + } + } + return ROCKSIM_BULK; //Default + } +} + diff --git a/src/net/sf/openrocket/file/rocksim/StreamerHandler.java b/src/net/sf/openrocket/file/rocksim/StreamerHandler.java index 9db287d2..d638f62b 100644 --- a/src/net/sf/openrocket/file/rocksim/StreamerHandler.java +++ b/src/net/sf/openrocket/file/rocksim/StreamerHandler.java @@ -6,7 +6,6 @@ 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; @@ -16,7 +15,7 @@ import java.util.HashMap; /** * A SAX handler for Streamer components. */ -class StreamerHandler extends PositionDependentHandler { +class StreamerHandler extends RecoveryDeviceHandler { /** * The OpenRocket Streamer. @@ -37,11 +36,17 @@ class StreamerHandler extends PositionDependentHandler { c.addChild(streamer); } + /** + * {@inheritDoc} + */ @Override public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) { return PlainTextHandler.INSTANCE; } + /** + * {@inheritDoc} + */ @Override public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { @@ -66,30 +71,13 @@ class StreamerHandler extends PositionDependentHandler { } } - @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 + * {@inheritDoc} */ @Override - public void setRelativePosition(RocketComponent.Position position) { - streamer.setRelativePosition(position); + public Streamer getComponent() { + return streamer; } - /** - * Get the required type of material for this component. - * - * @return BULK - */ - @Override - public Material.Type getMaterialType() { - return Material.Type.SURFACE; - } } diff --git a/test/net/sf/openrocket/file/rocksim/StreamerHandlerTest.java b/test/net/sf/openrocket/file/rocksim/StreamerHandlerTest.java index 964ae52a..3051398d 100644 --- a/test/net/sf/openrocket/file/rocksim/StreamerHandlerTest.java +++ b/test/net/sf/openrocket/file/rocksim/StreamerHandlerTest.java @@ -6,7 +6,6 @@ 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; @@ -77,7 +76,7 @@ public class StreamerHandlerTest extends RocksimTestBase { * @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"); @@ -91,7 +90,7 @@ public class StreamerHandlerTest extends RocksimTestBase { 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); @@ -104,7 +103,7 @@ public class StreamerHandlerTest extends RocksimTestBase { 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); @@ -183,13 +182,38 @@ public class StreamerHandlerTest extends RocksimTestBase { 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); - + 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); + assertEquals(component.getPositionValue(), 10d/RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH); + + handler.closeElement("Thickness", attributes, "0.02", warnings); + assertEquals(0.01848, handler.computeDensity(RocksimDensityType.ROCKSIM_BULK, 924d)); + + //Test Density Type 0 (Bulk) + handler.closeElement("Density", attributes, "924.0", warnings); + handler.closeElement("DensityType", attributes, "0", warnings); + handler.endHandler("Streamer", attributes, null, warnings); + assertEquals(0.01848d, component.getMaterial().getDensity()); + + //Test Density Type 1 (Surface) + handler.closeElement("Density", attributes, "0.006685", warnings); + handler.closeElement("DensityType", attributes, "1", warnings); + handler.endHandler("Streamer", attributes, null, warnings); + assertTrue(Math.abs(0.06685d - component.getMaterial().getDensity()) < 0.00001); + + //Test Density Type 2 (Line) + handler.closeElement("Density", attributes, "0.223225", warnings); + handler.closeElement("DensityType", attributes, "2", warnings); + handler.closeElement("Len", attributes, "3810.", warnings); + handler.closeElement("Width", attributes, "203.2", warnings); + handler.endHandler("Streamer", attributes, null, warnings); + + assertEquals(1.728190092, component.getMass()); + } - + }