Support for opening recovery device on stage separation
[debian/openrocket] / core / src / net / sf / openrocket / rocketcomponent / RecoveryDevice.java
1 package net.sf.openrocket.rocketcomponent;
2
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;
9
10
11 /**
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.
15  * <p>
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.
20  * 
21  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
22  */
23 public abstract class RecoveryDevice extends MassObject {
24         private static final Translator trans = Application.getTranslator();
25         
26         public static enum DeployEvent {
27                 LAUNCH(trans.get("RecoveryDevice.DeployEvent.LAUNCH")) {
28                         @Override
29                         public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
30                                 return e.getType() == FlightEvent.Type.LAUNCH;
31                         }
32                 },
33                 EJECTION(trans.get("RecoveryDevice.DeployEvent.EJECTION")) {
34                         @Override
35                         public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
36                                 if (e.getType() != FlightEvent.Type.EJECTION_CHARGE)
37                                         return false;
38                                 RocketComponent charge = e.getSource();
39                                 return charge.getStageNumber() == source.getStageNumber();
40                         }
41                 },
42                 APOGEE(trans.get("RecoveryDevice.DeployEvent.APOGEE")) {
43                         @Override
44                         public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
45                                 return e.getType() == FlightEvent.Type.APOGEE;
46                         }
47                 },
48                 ALTITUDE(trans.get("RecoveryDevice.DeployEvent.ALTITUDE")) {
49                         @SuppressWarnings("unchecked")
50                         @Override
51                         public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
52                                 if (e.getType() != FlightEvent.Type.ALTITUDE)
53                                         return false;
54                                 
55                                 double alt = ((RecoveryDevice) source).getDeployAltitude();
56                                 Pair<Double, Double> altitude = (Pair<Double, Double>) e.getData();
57                                 
58                                 return (altitude.getU() >= alt) && (altitude.getV() <= alt);
59                         }
60                 },
61                 LOWER_STAGE_SEPARATION(trans.get("RecoveryDevice.DeployEvent.LOWER_STAGE_SEPARATION")) {
62                         @Override
63                         public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
64                                 if (e.getType() != FlightEvent.Type.STAGE_SEPARATION)
65                                         return false;
66                                 
67                                 int separation = e.getSource().getStageNumber();
68                                 int current = source.getStageNumber();
69                                 return (current + 1 == separation);
70                         }
71                 },
72                 NEVER(trans.get("RecoveryDevice.DeployEvent.NEVER")) {
73                         @Override
74                         public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
75                                 return false;
76                         }
77                 };
78                 
79                 private final String description;
80                 
81                 DeployEvent(String description) {
82                         this.description = description;
83                 }
84                 
85                 public abstract boolean isActivationEvent(FlightEvent e, RocketComponent source);
86                 
87                 @Override
88                 public String toString() {
89                         return description;
90                 }
91                 
92         }
93         
94         
95         private DeployEvent deployEvent = DeployEvent.EJECTION;
96         private double deployAltitude = 200;
97         private double deployDelay = 0;
98         
99         private double cd = Parachute.DEFAULT_CD;
100         private boolean cdAutomatic = true;
101         
102         
103         private Material.Surface material;
104         
105         
106         public RecoveryDevice() {
107                 this(Application.getPreferences().getDefaultComponentMaterial(RecoveryDevice.class, Material.Type.SURFACE));
108         }
109         
110         public RecoveryDevice(Material material) {
111                 super();
112                 setMaterial(material);
113         }
114         
115         public RecoveryDevice(double length, double radius, Material material) {
116                 super(length, radius);
117                 setMaterial(material);
118         }
119         
120         
121         
122         
123         public abstract double getArea();
124         
125         public abstract double getComponentCD(double mach);
126         
127         
128         
129         public double getCD() {
130                 return getCD(0);
131         }
132         
133         public double getCD(double mach) {
134                 if (cdAutomatic)
135                         cd = getComponentCD(mach);
136                 return cd;
137         }
138         
139         public void setCD(double cd) {
140                 if (MathUtil.equals(this.cd, cd) && !isCDAutomatic())
141                         return;
142                 this.cd = cd;
143                 this.cdAutomatic = false;
144                 fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE);
145         }
146         
147         
148         public boolean isCDAutomatic() {
149                 return cdAutomatic;
150         }
151         
152         public void setCDAutomatic(boolean auto) {
153                 if (cdAutomatic == auto)
154                         return;
155                 this.cdAutomatic = auto;
156                 fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE);
157         }
158         
159         
160         
161         public final Material getMaterial() {
162                 return material;
163         }
164         
165         public final void setMaterial(Material mat) {
166                 if (!(mat instanceof Material.Surface)) {
167                         throw new IllegalArgumentException("Attempted to set non-surface material " + mat);
168                 }
169                 if (mat.equals(material))
170                         return;
171                 this.material = (Material.Surface) mat;
172                 fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
173         }
174         
175         
176         
177         
178         public DeployEvent getDeployEvent() {
179                 return deployEvent;
180         }
181         
182         public void setDeployEvent(DeployEvent deployEvent) {
183                 if (this.deployEvent == deployEvent)
184                         return;
185                 this.deployEvent = deployEvent;
186                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
187         }
188         
189         
190         public double getDeployAltitude() {
191                 return deployAltitude;
192         }
193         
194         public void setDeployAltitude(double deployAltitude) {
195                 if (MathUtil.equals(this.deployAltitude, deployAltitude))
196                         return;
197                 this.deployAltitude = deployAltitude;
198                 if (getDeployEvent() == DeployEvent.ALTITUDE)
199                         fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
200                 else
201                         fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
202         }
203         
204         
205         public double getDeployDelay() {
206                 return deployDelay;
207         }
208         
209         public void setDeployDelay(double delay) {
210                 delay = MathUtil.max(delay, 0);
211                 if (MathUtil.equals(this.deployDelay, delay))
212                         return;
213                 this.deployDelay = delay;
214                 fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
215         }
216         
217         
218         
219         @Override
220         public double getComponentMass() {
221                 return getArea() * getMaterial().getDensity();
222         }
223         
224 }