LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduSub - mode_surftrak.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 62 74 83.8 %
Date: 2026-09-09 11:02:08 Functions: 7 7 100.0 %

          Line data    Source code
       1             : #include "Sub.h"
       2             : 
       3             : /*
       4             :  * SURFTRAK (surface tracking) -- a variation on ALT_HOLD (depth hold)
       5             :  *
       6             :  * SURFTRAK starts in the "reset" state (rangefinder_target_cm < 0). SURFTRAK exits the reset state when these
       7             :  * conditions are met:
       8             :  * -- There is a good rangefinder reading (the rangefinder is healthy, the reading is between min and max, etc.)
       9             :  * -- The sub is below SURFTRAK_DEPTH
      10             :  *
      11             :  * During normal operation, SURFTRAK sets the offset target to the current terrain altitude estimate and calls
      12             :  * AC_PosControl to do the rest.
      13             :  *
      14             :  * We generally do not want to reset SURFTRAK if the rangefinder glitches, since that will result in a new rangefinder
      15             :  * target. E.g., if a pilot is running 1m above the seafloor, there is a glitch, and the next rangefinder reading shows
      16             :  * 1.1m, the desired behavior is to move 10cm closer to the seafloor, vs setting a new target of 1.1m above the
      17             :  * seafloor.
      18             :  *
      19             :  * If the pilot takes control, SURFTRAK uses the change in depth readings to adjust the rangefinder target. This
      20             :  * minimizes the "bounce back" that can happen as the slower rangefinder catches up to the quicker barometer.
      21             :  */
      22             : 
      23             : #define INVALID_TARGET (-1)
      24             : #define HAS_VALID_TARGET (rangefinder_target_cm > 0)
      25             : 
      26          83 : ModeSurftrak::ModeSurftrak() :
      27          83 :         rangefinder_target_cm(INVALID_TARGET),
      28          83 :         pilot_in_control(false),
      29          83 :         pilot_control_start_z_cm(0)
      30          83 : { }
      31             : 
      32           4 : bool ModeSurftrak::init(bool ignore_checks)
      33             : {
      34           4 :     if (!ModeAlthold::init(ignore_checks)) {
      35           0 :         return false;
      36             :     }
      37             : 
      38           4 :     reset();
      39             : 
      40           4 :     if (!sub.rangefinder_alt_ok()) {
      41           3 :         sub.gcs().send_text(MAV_SEVERITY_INFO, "waiting for a rangefinder reading");
      42             : #if AP_RANGEFINDER_ENABLED
      43           1 :     } else if (position_control->get_pos_estimate_U_m() * 100.0f >= sub.g.surftrak_depth) {
      44           0 :         sub.gcs().send_text(MAV_SEVERITY_WARNING, "descend below %f meters to hold range", sub.g.surftrak_depth * 0.01f);
      45             : #endif
      46             :     }
      47             : 
      48           4 :     return true;
      49             : }
      50             : 
      51       99757 : void ModeSurftrak::run()
      52             : {
      53       99757 :     run_pre();
      54             : 
      55       99757 :     if (!motors.armed()) {
      56             :         // Forget rangefinder target
      57        4767 :         reset();
      58             :     } else {
      59       94990 :         control_range();
      60             :     }
      61             : 
      62       99757 :     run_post();
      63       99757 : }
      64             : 
      65             : /*
      66             :  * Set the rangefinder target, return true if successful. This may be called from scripting so run a few extra checks.
      67             :  */
      68          38 : bool ModeSurftrak::set_rangefinder_target_cm(float target_cm)
      69             : {
      70          38 :     bool success = false;
      71             : 
      72             : #if AP_RANGEFINDER_ENABLED
      73          38 :     if (sub.control_mode != Number::SURFTRAK) {
      74           0 :         sub.gcs().send_text(MAV_SEVERITY_WARNING, "wrong mode, rangefinder target not set");
      75          38 :     } else if (position_control->get_pos_estimate_U_m() * 100.0f >= sub.g.surftrak_depth) {
      76           0 :         sub.gcs().send_text(MAV_SEVERITY_WARNING, "descend below %f meters to set rangefinder target", sub.g.surftrak_depth * 0.01f);
      77          38 :     } else if (target_cm < sub.rangefinder_state.min*100) {
      78           0 :         sub.gcs().send_text(MAV_SEVERITY_WARNING, "rangefinder target below minimum, ignored");
      79          38 :     } else if (target_cm > sub.rangefinder_state.max*100) {
      80           0 :         sub.gcs().send_text(MAV_SEVERITY_WARNING, "rangefinder target above maximum, ignored");
      81             :     } else {
      82          38 :         success = true;
      83             :     }
      84             : 
      85          38 :     if (success) {
      86          38 :         rangefinder_target_cm = target_cm;
      87          38 :         sub.gcs().send_text(MAV_SEVERITY_INFO, "rangefinder target is %.2f meters", rangefinder_target_cm * 0.01f);
      88             : 
      89             :         // Initialize the terrain offset
      90          38 :         auto terrain_offset_cm = position_control->get_pos_estimate_U_m() * 100.0f - rangefinder_target_cm;
      91          38 :         sub.pos_control.init_pos_terrain_U_cm(terrain_offset_cm);
      92             : 
      93             :     } else {
      94           0 :         reset();
      95             :     }
      96             : #endif
      97             : 
      98          38 :     return success;
      99             : }
     100             : 
     101        4771 : void ModeSurftrak::reset()
     102             : {
     103        4771 :     rangefinder_target_cm = INVALID_TARGET;
     104             : 
     105             :     // Reset the terrain offset
     106        4771 :     sub.pos_control.init_pos_terrain_U_cm(0);
     107        4771 : }
     108             : 
     109             : /*
     110             :  * Main controller, call at 100hz+
     111             :  */
     112       94990 : void ModeSurftrak::control_range() {
     113       94990 :     float target_climb_rate_cms = sub.get_pilot_desired_climb_rate(channel_throttle->get_control_in());
     114       94990 :     target_climb_rate_cms = constrain_float(target_climb_rate_cms, -sub.get_pilot_speed_dn(), g.pilot_speed_up);
     115             : 
     116             :     // Desired_climb_rate returns 0 when within the deadzone
     117       94990 :     if (fabsf(target_climb_rate_cms) < 0.05f)  {
     118       84436 :         if (pilot_in_control) {
     119             :             // Pilot has released control; apply the delta to the rangefinder target
     120           1 :             set_rangefinder_target_cm(rangefinder_target_cm + position_control->get_pos_estimate_U_m() * 100.0f - pilot_control_start_z_cm);
     121           1 :             pilot_in_control = false;
     122             :         }
     123       84436 :         if (sub.ap.at_surface) {
     124             :             // Set target depth to 5 cm below SURFACE_DEPTH and reset
     125           0 :             position_control->set_pos_desired_U_cm(MIN(position_control->get_pos_desired_U_cm(), g.surface_depth - 5.0f));
     126           0 :             reset();
     127       84436 :         } else if (sub.ap.at_bottom) {
     128             :             // Set target depth to 10 cm above bottom and reset
     129           0 :             position_control->set_pos_desired_U_cm(MAX(position_control->get_pos_estimate_U_m() * 100.0f + 10.0f, position_control->get_pos_desired_U_cm()));
     130           0 :             reset();
     131             :         } else {
     132             :             // Typical operation
     133       84436 :             update_surface_offset();
     134             :         }
     135       10554 :     } else if (HAS_VALID_TARGET && !pilot_in_control) {
     136             :         // Pilot has taken control; note the current depth
     137           1 :         pilot_control_start_z_cm = position_control->get_pos_estimate_U_m() * 100.0f;
     138           1 :         pilot_in_control = true;
     139             :     }
     140             : 
     141             :     // Set the target altitude from the climb rate and the terrain offset
     142       94990 :     position_control->D_set_pos_target_from_climb_rate_cms(target_climb_rate_cms);
     143             : 
     144             :     // Run the PID controllers
     145       94990 :     position_control->D_update_controller();
     146       94990 : }
     147             : 
     148             : /*
     149             :  * Update the AC_PosControl terrain offset if we have a good rangefinder reading
     150             :  */
     151       84436 : void ModeSurftrak::update_surface_offset()
     152             : {
     153             : #if AP_RANGEFINDER_ENABLED
     154       84436 :     if (sub.rangefinder_alt_ok()) {
     155             :         // Get the latest terrain offset
     156       79725 :         float rangefinder_terrain_offset_cm = sub.rangefinder_state.rangefinder_terrain_offset_cm;
     157             : 
     158             :         // Handle the first reading or a reset
     159       79725 :         if (!HAS_VALID_TARGET && sub.rangefinder_state.inertial_alt_cm < sub.g.surftrak_depth) {
     160           3 :             set_rangefinder_target_cm(sub.rangefinder_state.inertial_alt_cm - rangefinder_terrain_offset_cm);
     161             :         }
     162             : 
     163       79725 :         if (HAS_VALID_TARGET) {
     164             :             // Will the new offset target cause the sub to ascend above SURFTRAK_DEPTH?
     165       79725 :             float desired_z_cm = rangefinder_terrain_offset_cm + rangefinder_target_cm;
     166       79725 :             if (desired_z_cm >= sub.g.surftrak_depth) {
     167             :                 // Adjust the terrain offset to stay below SURFTRAK_DEPTH, this should avoid "at_surface" events
     168           0 :                 rangefinder_terrain_offset_cm += sub.g.surftrak_depth - desired_z_cm;
     169             :             }
     170             : 
     171             :             // Set the offset target, AC_PosControl will do the rest
     172       79725 :             sub.pos_control.set_pos_terrain_target_U_cm(rangefinder_terrain_offset_cm);
     173             :         }
     174             :     }
     175             : #endif  // AP_RANGEFINDER_ENABLED
     176       84436 : }

Generated by: LCOV version 1.14