@@ -42,17 +42,40 @@ Detects apogee when the rocket starts descending.
4242 **Detection criteria: **
4343- Vertical velocity becomes negative (descent starts)
4444
45+ Numeric Altitude Trigger
46+ ------------------------
47+
48+ You can also pass a numeric altitude (int or float) to deploy at a fixed height
49+ while descending.
50+
51+ .. code-block :: python
52+
53+ main = Parachute(
54+ name = " Main" ,
55+ cd_s = 10.0 ,
56+ trigger = 400 , # meters above ground level
57+ sampling_rate = 100 ,
58+ lag = 0.5
59+ )
60+
4561 Custom Triggers
4662---------------
4763
4864You can create custom triggers that use acceleration data:
4965
50- Motor Burnout Trigger (Custom Example)
51- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
66+ .. code-block :: python
67+
68+ from rocketpy import Parachute
69+
70+ Custom Trigger: Motor Burnout
71+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5272
5373Motor burnout is highly mission-dependent, so it is recommended as a custom
5474trigger with user-defined thresholds:
5575
76+ Logic: check for a drop in vertical or total acceleration once the rocket is
77+ above a minimum height and still ascending.
78+
5679.. code-block :: python
5780
5881 def burnout_trigger_factory (
@@ -76,6 +99,10 @@ trigger with user-defined thresholds:
7699
77100 return burnout_trigger
78101
102+ Usage:
103+
104+ .. code-block :: python
105+
79106 drogue = Parachute(
80107 name = " Drogue" ,
81108 cd_s = 1.0 ,
@@ -89,36 +116,65 @@ trigger with user-defined thresholds:
89116 lag = 1.5 ,
90117 )
91118
92- Apogee by Acceleration (Custom Example)
93- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
119+ Custom Trigger: Apogee by Acceleration
120+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
121+
122+ Logic: near-zero vertical velocity with downward acceleration.
94123
95124.. code-block :: python
96125
97126 def apogee_acc_trigger (_pressure , _height , state_vector , u_dot ):
127+ if u_dot is None or len (u_dot) < 6 :
128+ return False
98129 vz = state_vector[5 ]
99130 az = u_dot[5 ]
131+ # Apogee is indicated by near-zero vertical velocity and downward acceleration.
100132 return abs (vz) < 1.0 and az < - 0.1
101133
102- Free-fall (Custom Example)
103- ~~~~~~~~~~~~~~~~~~~~~~~~~~
134+ Usage:
135+
136+ .. code-block :: python
137+
138+ main = Parachute(
139+ name = " Main" ,
140+ cd_s = 10.0 ,
141+ trigger = apogee_acc_trigger,
142+ sampling_rate = 100 ,
143+ lag = 0.5
144+ )
145+
146+ Custom Trigger: Free-fall
147+ ~~~~~~~~~~~~~~~~~~~~~~~~~
148+
149+ Logic: low total acceleration while descending above a small height.
104150
105151.. code-block :: python
106152
107153 def freefall_trigger (_pressure , height , state_vector , u_dot ):
154+ if u_dot is None or len (u_dot) < 6 :
155+ return False
108156 ax, ay, az = u_dot[3 ], u_dot[4 ], u_dot[5 ]
109157 total_acc = (ax** 2 + ay** 2 + az** 2 ) ** 0.5
110158 vz = state_vector[5 ]
159+ # Free-fall is a low-acceleration phase while descending.
111160 return height > 5.0 and vz < - 0.2 and total_acc < 11.5
112161
113- Liftoff by Acceleration (Custom Example)
114- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
162+ Usage:
115163
116164.. code-block :: python
117165
118- def liftoff_trigger (_pressure , _height , _state_vector , u_dot ):
119- ax, ay, az = u_dot[3 ], u_dot[4 ], u_dot[5 ]
120- total_acc = (ax** 2 + ay** 2 + az** 2 ) ** 0.5
121- return total_acc > 15.0
166+ drogue = Parachute(
167+ name = " Drogue" ,
168+ cd_s = 1.0 ,
169+ trigger = freefall_trigger,
170+ sampling_rate = 100 ,
171+ lag = 1.5
172+ )
173+
174+ Custom Trigger: Threshold-based
175+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
176+
177+ Logic: deploy when total acceleration drops below a threshold while descending.
122178
123179.. code-block :: python
124180
@@ -144,6 +200,9 @@ Liftoff by Acceleration (Custom Example)
144200 True to trigger parachute deployment
145201 """
146202 # Extract acceleration components (m/s²)
203+ if u_dot is None or len (u_dot) < 6 :
204+ return False
205+
147206 ax = u_dot[3 ]
148207 ay = u_dot[4 ]
149208 az = u_dot[5 ]
@@ -155,7 +214,10 @@ Liftoff by Acceleration (Custom Example)
155214 vz = state_vector[5 ]
156215 return total_acc < 5.0 and vz < - 10.0
157216
158- # Use custom trigger
217+ Usage:
218+
219+ .. code-block :: python
220+
159221 parachute = Parachute(
160222 name = " Custom" ,
161223 cd_s = 2.0 ,
@@ -164,10 +226,13 @@ Liftoff by Acceleration (Custom Example)
164226 lag = 1.0
165227 )
166228
167- Sensor and Acceleration Triggers
168- ---------------------------------
229+ Custom Trigger: Sensors and Acceleration
230+ ----------------------------------------
231+
232+ Triggers can also accept sensor data alongside acceleration.
169233
170- Triggers can also accept sensor data alongside acceleration:
234+ Logic: compare measured vertical acceleration from an Accelerometer with the
235+ computed ``u_dot `` value.
171236
172237.. code-block :: python
173238
@@ -186,18 +251,29 @@ Triggers can also accept sensor data alongside acceleration:
186251 -------
187252 bool
188253 """
189- # Access sensor measurements
190- if len (sensors) > 0 :
191- imu_reading = sensors[0 ].measurement
254+ # Access accelerometer measurements (vector)
255+ if len (sensors) == 0 :
256+ return False
257+
258+ acc_reading = sensors[0 ].measurement
259+ if acc_reading is None or len (acc_reading) < 3 :
260+ return False
192261
193- # Define threshold for IMU reading (example value)
194- threshold = 100.0 # Adjust based on sensor units and trigger criteria
262+ if u_dot is None or len (u_dot) < 6 :
263+ return False
195264
196- # Use acceleration data
265+ # Example threshold for measured vertical acceleration (m/s^2)
266+ meas_az = acc_reading[2 ]
267+ threshold = - 5.0
268+
269+ # Use acceleration data from both sensor and dynamics
197270 az = u_dot[5 ]
198271
199- # Combine sensor and acceleration logic
200- return az < - 5.0 and imu_reading > threshold
272+ return (az < - 5.0 ) and (meas_az < threshold)
273+
274+ Usage:
275+
276+ .. code-block :: python
201277
202278 parachute = Parachute(
203279 name = " Advanced" ,
@@ -206,46 +282,23 @@ Triggers can also accept sensor data alongside acceleration:
206282 sampling_rate = 100
207283 )
208284
209- Sensor Noise
210- ------------
211-
212- For realistic IMU behavior, use RocketPy sensors with their own noise models.
213- Parachute trigger functions can receive ``sensors `` and use those measurements
214- directly instead of injecting noise in the flight solver.
215-
216- Performance Considerations
217- --------------------------
285+ .. note ::
218286
219- Computing acceleration (``u_dot ``) requires evaluating the equations of motion,
220- which adds computational cost. RocketPy optimizes this by:
287+ For realistic IMU behavior, use RocketPy sensors with their own noise models.
288+ Parachute trigger functions can receive ``sensors `` and use those measurements
289+ directly instead of injecting noise in the flight solver. See the
290+ :doc: `Sensor Classes </reference/classes/sensors/index >` for available sensors.
221291
222- 1. **Lazy evaluation **: ``u_dot `` is only computed if the trigger actually needs it
223- 2. **Metadata detection **: The wrapper inspects trigger signatures to determine requirements
224- 3. **Caching **: Derivative evaluations are reused when possible
225-
226- **Trigger signature detection: **
227-
228- - 3 parameters ``(p, h, y) ``: Legacy trigger, no ``u_dot `` computed
229- - 4 parameters with ``u_dot ``: Only acceleration computed
230- - 4 parameters with ``sensors ``: Only sensors passed
231- - 5 parameters: Both sensors and acceleration provided
232-
233- Best Practices
234- --------------
235-
236- 1. **Choose appropriate sampling rates **: 50-200 Hz is typical for flight computers
237- 2. **Add realistic noise **: Real IMUs have noise; simulate it for validation
238- 3. **Test edge cases **: Verify triggers work at low altitudes, high speeds, etc.
239- 4. **Use robust custom logic **: Add mission-specific guards and thresholds
240- 5. **Document custom triggers **: Include detection criteria in docstrings
241-
242- Example: Complete Dual-Deploy System
292+ Example: Simulating Drogue Deployment
243293-------------------------------------
244294
295+ Logic: in RocketPy, only one parachute is active at a time, so you can simulate
296+ dual-deploy by treating the first trigger as a drogue event and the second as the
297+ main deployment.
298+
245299.. code-block :: python
246300
247301 from rocketpy import Rocket, Parachute, Flight, Environment
248- import numpy as np
249302
250303 # Environment and rocket setup
251304 env = Environment(latitude = 32.99 , longitude = - 106.97 , elevation = 1400 )
@@ -258,7 +311,7 @@ Example: Complete Dual-Deploy System
258311 if u_dot is None or len (u_dot) < 6 :
259312 return False
260313 az = u_dot[5 ]
261- vz = state_vector[5 ] if len (state_vector) > 5 else 0
314+ vz = state_vector[5 ]
262315 return height > 10 and vz > 1 and az < - 8.0
263316
264317 drogue = Parachute(
@@ -271,9 +324,11 @@ Example: Complete Dual-Deploy System
271324 )
272325 my_rocket.add_parachute(drogue)
273326
274- # Main parachute: Deploy at 800m using custom trigger
275- def main_deploy_trigger (pressure , height , state_vector , u_dot ):
276- """ Deploy main at 800m while descending with positive vertical acceleration."""
327+ # Main parachute: Deploy at 800m using a custom trigger
328+ def main_deploy_trigger (_pressure , height , state_vector , u_dot ):
329+ """ Deploy main at 800m while descending with modest acceleration."""
330+ if u_dot is None or len (u_dot) < 6 :
331+ return False
277332 vz = state_vector[5 ]
278333 az = u_dot[5 ]
279334 return height < 800 and vz < - 5 and az > - 15
0 commit comments