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.simulation.FlightEvent;
6 import net.sf.openrocket.startup.Application;
7 import net.sf.openrocket.util.MathUtil;
8 import net.sf.openrocket.util.Pair;
12 * RecoveryDevice is a class representing devices that slow down descent.
13 * Recovery devices report that they have no aerodynamic effect, since they
14 * are within the rocket during ascent.
16 * A recovery device includes a surface material of which it is made of.
17 * The mass of the component is calculated based on the material and the
18 * area of the device from {@link #getArea()}. {@link #getComponentMass()}
19 * may be overridden if additional mass needs to be included.
21 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
23 public abstract class RecoveryDevice extends MassObject {
24 private static final Translator trans = Application.getTranslator();
26 public static enum DeployEvent {
27 LAUNCH(trans.get("RecoveryDevice.DeployEvent.LAUNCH")) {
29 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
30 return e.getType() == FlightEvent.Type.LAUNCH;
33 EJECTION(trans.get("RecoveryDevice.DeployEvent.EJECTION")) {
35 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
36 if (e.getType() != FlightEvent.Type.EJECTION_CHARGE)
38 RocketComponent charge = e.getSource();
39 return charge.getStageNumber() == source.getStageNumber();
42 APOGEE(trans.get("RecoveryDevice.DeployEvent.APOGEE")) {
44 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
45 return e.getType() == FlightEvent.Type.APOGEE;
48 ALTITUDE(trans.get("RecoveryDevice.DeployEvent.ALTITUDE")) {
49 @SuppressWarnings("unchecked")
51 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
52 if (e.getType() != FlightEvent.Type.ALTITUDE)
55 double alt = ((RecoveryDevice) source).getDeployAltitude();
56 Pair<Double, Double> altitude = (Pair<Double, Double>) e.getData();
58 return (altitude.getU() >= alt) && (altitude.getV() <= alt);
61 LOWER_STAGE_SEPARATION(trans.get("RecoveryDevice.DeployEvent.LOWER_STAGE_SEPARATION")) {
63 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
64 if (e.getType() != FlightEvent.Type.STAGE_SEPARATION)
67 int separation = e.getSource().getStageNumber();
68 int current = source.getStageNumber();
69 return (current + 1 == separation);
72 NEVER(trans.get("RecoveryDevice.DeployEvent.NEVER")) {
74 public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
79 private final String description;
81 DeployEvent(String description) {
82 this.description = description;
85 public abstract boolean isActivationEvent(FlightEvent e, RocketComponent source);
88 public String toString() {
95 private DeployEvent deployEvent = DeployEvent.EJECTION;
96 private double deployAltitude = 200;
97 private double deployDelay = 0;
99 private double cd = Parachute.DEFAULT_CD;
100 private boolean cdAutomatic = true;
103 private Material.Surface material;
106 public RecoveryDevice() {
107 this(Application.getPreferences().getDefaultComponentMaterial(RecoveryDevice.class, Material.Type.SURFACE));
110 public RecoveryDevice(Material material) {
112 setMaterial(material);
115 public RecoveryDevice(double length, double radius, Material material) {
116 super(length, radius);
117 setMaterial(material);
123 public abstract double getArea();
125 public abstract double getComponentCD(double mach);
129 public double getCD() {
133 public double getCD(double mach) {
135 cd = getComponentCD(mach);
139 public void setCD(double cd) {
140 if (MathUtil.equals(this.cd, cd) && !isCDAutomatic())
143 this.cdAutomatic = false;
144 fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE);
148 public boolean isCDAutomatic() {
152 public void setCDAutomatic(boolean auto) {
153 if (cdAutomatic == auto)
155 this.cdAutomatic = auto;
156 fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE);
161 public final Material getMaterial() {
165 public final void setMaterial(Material mat) {
166 if (!(mat instanceof Material.Surface)) {
167 throw new IllegalArgumentException("Attempted to set non-surface material " + mat);
169 if (mat.equals(material))
171 this.material = (Material.Surface) mat;
172 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
178 public DeployEvent getDeployEvent() {
182 public void setDeployEvent(DeployEvent deployEvent) {
183 if (this.deployEvent == deployEvent)
185 this.deployEvent = deployEvent;
186 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
190 public double getDeployAltitude() {
191 return deployAltitude;
194 public void setDeployAltitude(double deployAltitude) {
195 if (MathUtil.equals(this.deployAltitude, deployAltitude))
197 this.deployAltitude = deployAltitude;
198 if (getDeployEvent() == DeployEvent.ALTITUDE)
199 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
201 fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
205 public double getDeployDelay() {
209 public void setDeployDelay(double delay) {
210 delay = MathUtil.max(delay, 0);
211 if (MathUtil.equals(this.deployDelay, delay))
213 this.deployDelay = delay;
214 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
220 public double getComponentMass() {
221 return getArea() * getMaterial().getDensity();