1 package net.sf.openrocket.rocketcomponent;
3 import net.sf.openrocket.l10n.Translator;
4 import net.sf.openrocket.material.Material;
5 import net.sf.openrocket.preset.ComponentPreset;
6 import net.sf.openrocket.simulation.FlightEvent;
7 import net.sf.openrocket.startup.Application;
8 import net.sf.openrocket.util.MathUtil;
9 import net.sf.openrocket.util.Pair;
13 * RecoveryDevice is a class representing devices that slow down descent.
14 * Recovery devices report that they have no aerodynamic effect, since they
15 * are within the rocket during ascent.
17 * A recovery device includes a surface material of which it is made of.
18 * The mass of the component is calculated based on the material and the
19 * area of the device from {@link #getArea()}. {@link #getComponentMass()}
20 * may be overridden if additional mass needs to be included.
22 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
24 public abstract class RecoveryDevice extends MassObject {
25 private static final Translator trans = Application.getTranslator();
27 public static enum DeployEvent {
28 LAUNCH(trans.get("RecoveryDevice.DeployEvent.LAUNCH")) {
30 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
31 return e.getType() == FlightEvent.Type.LAUNCH;
34 EJECTION(trans.get("RecoveryDevice.DeployEvent.EJECTION")) {
36 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
37 if (e.getType() != FlightEvent.Type.EJECTION_CHARGE)
39 RocketComponent charge = e.getSource();
40 return charge.getStageNumber() == source.getStageNumber();
43 APOGEE(trans.get("RecoveryDevice.DeployEvent.APOGEE")) {
45 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
46 return e.getType() == FlightEvent.Type.APOGEE;
49 ALTITUDE(trans.get("RecoveryDevice.DeployEvent.ALTITUDE")) {
50 @SuppressWarnings("unchecked")
52 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
53 if (e.getType() != FlightEvent.Type.ALTITUDE)
56 double alt = ((RecoveryDevice) source).getDeployAltitude();
57 Pair<Double, Double> altitude = (Pair<Double, Double>) e.getData();
59 return (altitude.getU() >= alt) && (altitude.getV() <= alt);
62 LOWER_STAGE_SEPARATION(trans.get("RecoveryDevice.DeployEvent.LOWER_STAGE_SEPARATION")) {
64 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
65 if (e.getType() != FlightEvent.Type.STAGE_SEPARATION)
68 int separation = e.getSource().getStageNumber();
69 int current = source.getStageNumber();
70 return (current + 1 == separation);
73 NEVER(trans.get("RecoveryDevice.DeployEvent.NEVER")) {
75 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
80 private final String description;
82 DeployEvent(String description) {
83 this.description = description;
86 public abstract boolean isActivationEvent(FlightEvent e, RocketComponent source);
89 public String toString() {
96 private DeployEvent deployEvent = DeployEvent.EJECTION;
97 private double deployAltitude = 200;
98 private double deployDelay = 0;
100 private double cd = Parachute.DEFAULT_CD;
101 private boolean cdAutomatic = true;
104 private Material.Surface material;
107 public RecoveryDevice() {
108 this(Application.getPreferences().getDefaultComponentMaterial(RecoveryDevice.class, Material.Type.SURFACE));
111 public RecoveryDevice(Material material) {
113 setMaterial(material);
116 public RecoveryDevice(double length, double radius, Material material) {
117 super(length, radius);
118 setMaterial(material);
124 public abstract double getArea();
126 public abstract double getComponentCD(double mach);
130 public double getCD() {
134 public double getCD(double mach) {
136 cd = getComponentCD(mach);
140 public void setCD(double cd) {
141 if (MathUtil.equals(this.cd, cd) && !isCDAutomatic())
144 this.cdAutomatic = false;
145 fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE);
149 public boolean isCDAutomatic() {
153 public void setCDAutomatic(boolean auto) {
154 if (cdAutomatic == auto)
156 this.cdAutomatic = auto;
157 fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE);
162 public final Material getMaterial() {
166 public final void setMaterial(Material mat) {
167 if (!(mat instanceof Material.Surface)) {
168 throw new IllegalArgumentException("Attempted to set non-surface material " + mat);
170 if (mat.equals(material))
172 this.material = (Material.Surface) mat;
174 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
180 public DeployEvent getDeployEvent() {
184 public void setDeployEvent(DeployEvent deployEvent) {
185 if (this.deployEvent == deployEvent)
187 this.deployEvent = deployEvent;
188 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
192 public double getDeployAltitude() {
193 return deployAltitude;
196 public void setDeployAltitude(double deployAltitude) {
197 if (MathUtil.equals(this.deployAltitude, deployAltitude))
199 this.deployAltitude = deployAltitude;
200 if (getDeployEvent() == DeployEvent.ALTITUDE)
201 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
203 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
207 public double getDeployDelay() {
211 public void setDeployDelay(double delay) {
212 delay = MathUtil.max(delay, 0);
213 if (MathUtil.equals(this.deployDelay, delay))
215 this.deployDelay = delay;
216 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
222 public double getComponentMass() {
223 return getArea() * getMaterial().getDensity();
227 protected void loadFromPreset(ComponentPreset preset) {
228 if ( preset.has(ComponentPreset.MATERIAL)) {
229 Material m = preset.get(ComponentPreset.MATERIAL);
230 this.material = (Material.Surface)m;
232 super.loadFromPreset(preset);
233 fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);