LCOV - code coverage report
Current view: top level - AP_Parachute - AP_Parachute.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 70 81 86.4 %
Date: 2026-09-09 11:02:08 Functions: 8 8 100.0 %

          Line data    Source code
       1             : #include "AP_Parachute.h"
       2             : 
       3             : #if HAL_PARACHUTE_ENABLED
       4             : 
       5             : #include <AP_Relay/AP_Relay.h>
       6             : #include <AP_Math/AP_Math.h>
       7             : #include <RC_Channel/RC_Channel.h>
       8             : #include <SRV_Channel/SRV_Channel.h>
       9             : #include <AP_Notify/AP_Notify.h>
      10             : #include <AP_HAL/AP_HAL.h>
      11             : #include <AP_Logger/AP_Logger.h>
      12             : #include <GCS_MAVLink/GCS.h>
      13             : #include <AP_Arming/AP_Arming.h>
      14             : 
      15             : extern const AP_HAL::HAL& hal;
      16             : 
      17             : const AP_Param::GroupInfo AP_Parachute::var_info[] = {
      18             : 
      19             :     // @Param: ENABLED
      20             :     // @DisplayName: Parachute release enabled or disabled
      21             :     // @Description: Parachute release enabled or disabled
      22             :     // @Values: 0:Disabled,1:Enabled
      23             :     // @User: Standard
      24             :     AP_GROUPINFO_FLAGS("ENABLED", 0, AP_Parachute, _enabled, 0, AP_PARAM_FLAG_ENABLE),
      25             : 
      26             :     // @Param: TYPE
      27             :     // @DisplayName: Parachute release mechanism type (relay or servo)
      28             :     // @Description: Parachute release mechanism type (relay number in versions prior to 4.5, or servo). Values 0-3 all are relay. Relay number used for release is set by RELAYx_FUNCTION in 4.5 or later. 
      29             :     // @Values: 0: Relay,10:Servo 
      30             : 
      31             :     // @User: Standard
      32             :     AP_GROUPINFO("TYPE", 1, AP_Parachute, _release_type, AP_PARACHUTE_TRIGGER_TYPE_RELAY_0),
      33             : 
      34             :     // @Param: SERVO_ON
      35             :     // @DisplayName: Parachute Servo ON PWM value
      36             :     // @Description: Parachute Servo PWM value in microseconds when parachute is released
      37             :     // @Range: 1000 2000
      38             :     // @Units: PWM
      39             :     // @Increment: 1
      40             :     // @User: Standard
      41             :     AP_GROUPINFO("SERVO_ON", 2, AP_Parachute, _servo_on_pwm, AP_PARACHUTE_SERVO_ON_PWM_DEFAULT),
      42             : 
      43             :     // @Param: SERVO_OFF
      44             :     // @DisplayName: Servo OFF PWM value
      45             :     // @Description: Parachute Servo PWM value in microseconds when parachute is not released
      46             :     // @Range: 1000 2000
      47             :     // @Units: PWM
      48             :     // @Increment: 1
      49             :     // @User: Standard
      50             :     AP_GROUPINFO("SERVO_OFF", 3, AP_Parachute, _servo_off_pwm, AP_PARACHUTE_SERVO_OFF_PWM_DEFAULT),
      51             : 
      52             :     // @Param: ALT_MIN
      53             :     // @DisplayName: Parachute min altitude in meters above home
      54             :     // @Description: Parachute min altitude above home.  Parachute will not be released below this altitude.  0 to disable alt check.
      55             :     // @Range: 0 32000
      56             :     // @Units: m
      57             :     // @Increment: 1
      58             :     // @User: Standard
      59             :     AP_GROUPINFO("ALT_MIN", 4, AP_Parachute, _alt_min, AP_PARACHUTE_ALT_MIN_DEFAULT),
      60             : 
      61             :     // @Param: DELAY_MS
      62             :     // @DisplayName: Parachute release delay
      63             :     // @Description: Delay in millseconds between motor stop and chute release
      64             :     // @Range: 0 5000
      65             :     // @Units: ms
      66             :     // @Increment: 1
      67             :     // @User: Standard
      68             :     AP_GROUPINFO("DELAY_MS", 5, AP_Parachute, _delay_ms, AP_PARACHUTE_RELEASE_DELAY_MS),
      69             : 
      70             :     // @Param: CRT_SINK
      71             :     // @DisplayName: Critical sink speed rate in m/s to trigger emergency parachute
      72             :     // @Description: Release parachute when critical sink rate is reached
      73             :     // @Range: 0 15
      74             :     // @Units: m/s
      75             :     // @Increment: 1
      76             :     // @User: Standard
      77             :     AP_GROUPINFO("CRT_SINK", 6, AP_Parachute, _critical_sink, AP_PARACHUTE_CRITICAL_SINK_DEFAULT),
      78             : 
      79             :     // @Param: OPTIONS
      80             :     // @DisplayName: Parachute options
      81             :     // @Description: Optional behaviour for parachute
      82             :     // @Bitmask: 0:hold open forever after release,1:skip disarm before parachute release
      83             :     // @User: Standard
      84             :     AP_GROUPINFO("OPTIONS", 7, AP_Parachute, _options, AP_PARACHUTE_OPTIONS_DEFAULT),
      85             : 
      86             :     AP_GROUPEND
      87             : };
      88             : 
      89             : /// enabled - enable or disable parachute release
      90          44 : void AP_Parachute::enabled(bool on_off)
      91             : {
      92          44 :     _enabled.set(on_off);
      93             : 
      94             :     // clear release_time
      95          44 :     _release_time = 0;
      96             : 
      97          44 :     LOGGER_WRITE_EVENT(_enabled ? LogEvent::PARACHUTE_ENABLED : LogEvent::PARACHUTE_DISABLED);
      98          44 : }
      99             : 
     100             : /// release - release parachute
     101          15 : void AP_Parachute::release()
     102             : {
     103             :     // exit immediately if not enabled
     104          15 :     if (_enabled <= 0) {
     105           0 :         return;
     106             :     }
     107             : 
     108          15 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO,"Parachute: Released");
     109          15 :     LOGGER_WRITE_EVENT(LogEvent::PARACHUTE_RELEASED);
     110             : 
     111          15 :     bool need_disarm = (_options.get() & uint32_t(Options::SkipDisarmBeforeParachuteRelease)) == 0;
     112          15 :     if (need_disarm) {
     113             :         // stop motors to avoid parachute tangling
     114          15 :         AP::arming().disarm(AP_Arming::Method::PARACHUTE_RELEASE);
     115             :     }
     116             : 
     117             :     // set release time to current system time
     118          15 :     if (_release_time == 0) {
     119          15 :         _release_time = AP_HAL::millis();
     120             :     }
     121             : 
     122          15 :     _release_initiated = true;
     123             : 
     124             :     // update AP_Notify
     125          15 :     AP_Notify::flags.parachute_release = true;
     126             : }
     127             : 
     128             : /// update - shuts off the trigger should be called at about 10hz
     129      643739 : void AP_Parachute::update()
     130             : {
     131             :     // exit immediately if not enabled or parachute not to be released
     132      643739 :     if (_enabled <= 0) {
     133      530907 :         return;
     134             :     }
     135             : 
     136             :     // calc time since release
     137      112832 :     uint32_t time_diff = AP_HAL::millis() - _release_time;
     138      112832 :     uint32_t delay_ms = _delay_ms<=0 ? 0: (uint32_t)_delay_ms;
     139             : 
     140      112832 :     bool hold_forever = (_options.get() & uint32_t(Options::HoldOpen)) != 0;
     141             : 
     142             :     // check if we should release parachute
     143      112832 :     if ((_release_time != 0) && !_release_in_progress) {
     144        2032 :         if (time_diff >= delay_ms) {
     145          15 :             if (_release_type == AP_PARACHUTE_TRIGGER_TYPE_SERVO) {
     146             :                 // move servo
     147          15 :                 SRV_Channels::set_output_pwm(SRV_Channel::k_parachute_release, _servo_on_pwm);
     148             : #if AP_RELAY_ENABLED
     149           0 :             } else if (_release_type <= AP_PARACHUTE_TRIGGER_TYPE_RELAY_3) {
     150             :                 // set relay
     151           0 :                 AP_Relay*_relay = AP::relay();
     152           0 :                 if (_relay != nullptr) {
     153           0 :                     _relay->set(AP_Relay_Params::FUNCTION::PARACHUTE, true); 
     154             :                 }
     155             : #endif
     156             :             }
     157          15 :             _release_in_progress = true;
     158          15 :             _released = true;
     159             :         }
     160      112832 :     } else if ((_release_time == 0) ||
     161        5335 :                (!hold_forever && time_diff >= delay_ms + AP_PARACHUTE_RELEASE_DURATION_MS)) {
     162      105469 :         if (_release_type == AP_PARACHUTE_TRIGGER_TYPE_SERVO) {
     163             :             // move servo back to off position
     164      105468 :             SRV_Channels::set_output_pwm(SRV_Channel::k_parachute_release, _servo_off_pwm);
     165             : #if AP_RELAY_ENABLED
     166           1 :         } else if (_release_type <= AP_PARACHUTE_TRIGGER_TYPE_RELAY_3) {
     167             :             // set relay back to zero volts
     168           1 :             AP_Relay*_relay = AP::relay();
     169           1 :             if (_relay != nullptr) {
     170           1 :                 _relay->set(AP_Relay_Params::FUNCTION::PARACHUTE, false);
     171             :             }
     172             : #endif
     173             :         }
     174             :         // reset released flag and release_time
     175      105469 :         _release_in_progress = false;
     176      105469 :         _release_time = 0;
     177             :         // update AP_Notify
     178      105469 :         AP_Notify::flags.parachute_release = false;
     179             :     }
     180             : }
     181             : 
     182             : // set_sink_rate - set vehicle sink rate
     183      643747 : void AP_Parachute::set_sink_rate(float sink_rate)
     184             : {
     185             :     // reset sink time if critical sink rate check is disabled or vehicle is not flying
     186      643747 :     if ((_critical_sink <= 0) || !_is_flying) {
     187      643385 :         _sink_time_ms = 0;
     188      643385 :         return;
     189             :     }
     190             : 
     191             :     // reset sink_time if vehicle is not sinking too fast
     192         362 :     if (sink_rate <= _critical_sink) {
     193         349 :         _sink_time_ms = 0;
     194         349 :         return;
     195             :     }
     196             : 
     197             :     // start time when sinking too fast
     198          13 :     if (_sink_time_ms == 0) {
     199           1 :         _sink_time_ms = AP_HAL::millis();
     200             :     }
     201             : }
     202             : 
     203             : // trigger parachute release if sink_rate is below critical_sink_rate for 1sec
     204      558244 : void AP_Parachute::check_sink_rate()
     205             : {
     206             :     // return immediately if parachute is being released or vehicle is not flying
     207      558244 :     if (_release_initiated || !_is_flying) {
     208      154542 :         return;
     209             :     }
     210             : 
     211             :     // if vehicle is sinking too fast for more than a second release parachute
     212      403702 :     if ((_sink_time_ms > 0) && ((AP_HAL::millis() - _sink_time_ms) > 1000)) {
     213           1 :         release();
     214             :     }
     215             : }
     216             : 
     217             : // check settings are valid
     218       49591 : bool AP_Parachute::arming_checks(size_t buflen, char *buffer) const
     219             : {
     220       49591 :     if (_enabled > 0) {
     221         430 :         if (_release_type == AP_PARACHUTE_TRIGGER_TYPE_SERVO) {
     222         430 :             if (!SRV_Channels::function_assigned(SRV_Channel::k_parachute_release)) {
     223           0 :                 hal.util->snprintf(buffer, buflen, "Chute has no channel");
     224           0 :                 return false;
     225             :             }
     226             :         } else {
     227             : #if AP_RELAY_ENABLED
     228           0 :             AP_Relay*_relay = AP::relay();
     229           0 :             if (_relay == nullptr || !_relay->enabled(AP_Relay_Params::FUNCTION::PARACHUTE)) {
     230           0 :                 hal.util->snprintf(buffer, buflen, "Chute has no relay");
     231           0 :                 return false;
     232             :             }
     233             : #else
     234             :             hal.util->snprintf(buffer, buflen, "AP_Relay not available");
     235             :             return false;
     236             : #endif
     237             :         }
     238             : 
     239         430 :         if (_release_initiated) {
     240          38 :             hal.util->snprintf(buffer, buflen, "Chute is released");
     241          38 :             return false;
     242             :         }
     243             :     }
     244       49553 :     return true;
     245             : }
     246             : 
     247             : #if AP_RELAY_ENABLED
     248             : // Return the relay index that would be used for param conversion to relay functions
     249        1693 : bool AP_Parachute::get_legacy_relay_index(int8_t &index) const
     250             : {
     251             :     // PARAMETER_CONVERSION - Added: Dec-2023
     252        1693 :     if (_release_type > AP_PARACHUTE_TRIGGER_TYPE_RELAY_3) {
     253             :         // Not relay type
     254          24 :         return false;
     255             :     }
     256        1669 :     if (!enabled() && !_enabled.configured()) {
     257             :         // Disabled and parameter never changed
     258             :         // Copter manual parachute release enables and deploys in one step
     259             :         // This means it is possible for parachute to still function correctly if disabled at boot
     260             :         // Checking if the enable param is configured means the user must have setup chute at some point
     261             :         // this means relay parm conversion will be done if parachute has ever been enabled
     262             :         // Parachute has priority in relay param conversion, so this might mean we overwrite some other function
     263        1630 :         return false;
     264             :     }
     265          39 :     index = _release_type; 
     266          39 :     return true;
     267             : }
     268             : #endif
     269             : 
     270             : // singleton instance
     271             : AP_Parachute *AP_Parachute::_singleton;
     272             : 
     273             : namespace AP {
     274             : 
     275       80091 : AP_Parachute *parachute()
     276             : {
     277       80091 :     return AP_Parachute::get_singleton();
     278             : }
     279             : 
     280             : }
     281             : #endif // HAL_PARACHUTE_ENABLED

Generated by: LCOV version 1.14