DGP - added DensityType parsing for recovery devices
This commit is contained in:
parent
755f2de608
commit
ae96ee9024
@ -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 <C> the specific RocketComponent subtype for which the concrete handler can create
|
||||
*/
|
||||
public abstract class BaseHandler<C extends RocketComponent> extends ElementHandler {
|
||||
|
||||
@ -30,6 +32,11 @@ public abstract class BaseHandler<C extends RocketComponent> 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<C extends RocketComponent> 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<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.
|
||||
*/
|
||||
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<C extends RocketComponent> 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.
|
||||
|
||||
@ -17,9 +17,8 @@ import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A SAX handler for Rocksim's Parachute XML type.
|
||||
* <p/>
|
||||
*/
|
||||
class ParachuteHandler extends PositionDependentHandler<Parachute> {
|
||||
class ParachuteHandler extends RecoveryDeviceHandler<Parachute> {
|
||||
/**
|
||||
* The OpenRocket Parachute instance
|
||||
*/
|
||||
@ -43,11 +42,17 @@ class ParachuteHandler extends PositionDependentHandler<Parachute> {
|
||||
c.addChild(chute);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
|
||||
return PlainTextHandler.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
|
||||
throws SAXException {
|
||||
@ -73,8 +78,7 @@ class ParachuteHandler extends PositionDependentHandler<Parachute> {
|
||||
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<Parachute> {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -12,6 +12,8 @@ import java.util.HashMap;
|
||||
/**
|
||||
* An abstract base class that handles position dependencies for all lower level components that
|
||||
* are position aware.
|
||||
*
|
||||
* @param <C> the specific position dependent RocketComponent subtype for which the concrete handler can create
|
||||
*/
|
||||
public abstract class PositionDependentHandler<C extends RocketComponent> extends BaseHandler<C> {
|
||||
|
||||
@ -21,6 +23,9 @@ public abstract class PositionDependentHandler<C extends RocketComponent> extend
|
||||
/** Temporary position. */
|
||||
private RocketComponent.Position position;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
|
||||
throws SAXException {
|
||||
|
||||
114
src/net/sf/openrocket/file/rocksim/RecoveryDeviceHandler.java
Normal file
114
src/net/sf/openrocket/file/rocksim/RecoveryDeviceHandler.java
Normal file
@ -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 <C> either a Streamer or Parachute
|
||||
*/
|
||||
public abstract class RecoveryDeviceHandler<C extends RecoveryDevice> extends PositionDependentHandler<C> {
|
||||
|
||||
/**
|
||||
* 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<String, String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
56
src/net/sf/openrocket/file/rocksim/RocksimDensityType.java
Normal file
56
src/net/sf/openrocket/file/rocksim/RocksimDensityType.java
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Streamer> {
|
||||
class StreamerHandler extends RecoveryDeviceHandler<Streamer> {
|
||||
|
||||
/**
|
||||
* The OpenRocket Streamer.
|
||||
@ -37,11 +36,17 @@ class StreamerHandler extends PositionDependentHandler<Streamer> {
|
||||
c.addChild(streamer);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
|
||||
return PlainTextHandler.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
|
||||
throws SAXException {
|
||||
@ -66,30 +71,13 @@ class StreamerHandler extends PositionDependentHandler<Streamer> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
@ -190,6 +189,31 @@ public class StreamerHandlerTest extends RocksimTestBase {
|
||||
handler.endHandler("Streamer", attributes, null, warnings);
|
||||
assertEquals(RocketComponent.Position.BOTTOM, component.getRelativePosition());
|
||||
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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user