DGP - added DensityType parsing for recovery devices
[debian/openrocket] / src / net / sf / openrocket / file / rocksim / RecoveryDeviceHandler.java
1 /*
2  * RecoveryDeviceHandler.java
3  */
4 package net.sf.openrocket.file.rocksim;
5
6 import net.sf.openrocket.aerodynamics.WarningSet;
7 import net.sf.openrocket.material.Material;
8 import net.sf.openrocket.rocketcomponent.RecoveryDevice;
9 import net.sf.openrocket.rocketcomponent.RocketComponent;
10 import org.xml.sax.SAXException;
11
12 import java.util.HashMap;
13
14 /**
15  * A handler specific to streamers and parachutes.  This is done because Rocksim allows any type of material to be
16  * used as a recovery device, which causes oddities with respect to densities.  Density computation is overridden
17  * here to try to correctly compute a material's density in OpenRocket units.
18  *
19  * @param <C>  either a Streamer or Parachute
20  */
21 public abstract class RecoveryDeviceHandler<C extends RecoveryDevice> extends PositionDependentHandler<C> {
22
23     /**
24      * The thickness.  Not used by every component, and some component handlers may parse it for their own purposes.
25      */
26     private double thickness = 0d;
27     /**
28      * The Rocksim calculated mass.  Used only when not overridden and when Rocksim says density == 0 (Rocksim bug).
29      */
30     private Double calcMass = 0d;
31
32     /**
33      * {@inheritDoc}
34      */
35     @Override
36     public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
37             throws SAXException {
38         super.closeElement(element, attributes, content, warnings);
39
40         try {
41             if ("Thickness".equals(element)) {
42                 thickness = Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_LENGTH;
43             }
44             if ("CalcMass".equals(element)) {
45                 calcMass = Math.max(0d, Double.parseDouble(content) / RocksimHandler.ROCKSIM_TO_OPENROCKET_MASS);
46             }
47         }
48         catch (NumberFormatException nfe) {
49             warnings.add("Could not convert " + element + " value of " + content + ".  It is expected to be a number.");
50         }
51     }
52
53
54     /**
55      * Compute the density.  Rocksim does strange things with densities.  For some streamer material it's in cubic,
56      * rather than square, units.  In those cases it needs to be converted to an appropriate SURFACE material density.
57      *
58      * @param type       the rocksim density
59      * @param rawDensity the density as specified in the Rocksim design file
60      * @return a value in OpenRocket SURFACE density units
61      */
62     protected double computeDensity(RocksimDensityType type, double rawDensity) {
63
64         double result;
65
66         if (rawDensity > 0d) {
67             //ROCKSIM_SURFACE is a square area density; compute normally
68             //ROCKSIM_LINE is a single length dimension (kg/m) but Rocksim ignores thickness for this type and treats
69             //it like a SURFACE.
70             if (RocksimDensityType.ROCKSIM_SURFACE.equals(type) || RocksimDensityType.ROCKSIM_LINE.equals(type)) {
71                 result = rawDensity / RocksimDensityType.ROCKSIM_SURFACE.asOpenRocket();
72             }
73             //ROCKSIM_BULK is a cubic area density; multiple by thickness to make per square area; the result, when
74             //multiplied by the area will then equal Rocksim's computed mass.
75             else {
76                 result = (rawDensity / type.asOpenRocket()) * thickness;
77             }
78         }
79         else {
80             result = calcMass / getComponent().getArea();
81             //A Rocksim bug on streamers/parachutes results in a 0 density at times.  When that is detected, try
82             //to compute an approximate density from Rocksim's computed mass.
83             if (RocksimDensityType.ROCKSIM_BULK.equals(type)) {
84                 //ROCKSIM_BULK is a cubic area density; multiple by thickness to make per square area
85                 result *= thickness;
86             }
87         }
88         return result;
89     }
90
91     /**
92      * Set the relative position onto the component.  This cannot be done directly because setRelativePosition is not
93      * public in all components.
94      *
95      * @param position the OpenRocket position
96      */
97     @Override
98     public void setRelativePosition(RocketComponent.Position position) {
99         getComponent().setRelativePosition(position);
100     }
101
102     /**
103      * Get the required type of material for this component.  This is the OpenRocket type, which does NOT always
104      * correspond to Rocksim.  Some streamer material is defined as BULK in the Rocksim file.  In those cases
105      * it is adjusted in this handler.
106      *
107      * @return SURFACE
108      */
109     @Override
110     public Material.Type getMaterialType() {
111         return Material.Type.SURFACE;
112     }
113
114 }