LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduSub - motors.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 41 94 43.6 %
Date: 2026-09-09 11:02:08 Functions: 5 8 62.5 %

          Line data    Source code
       1             : #include "Sub.h"
       2             : 
       3             : // enable_motor_output() - enable and output lowest possible value to motors
       4         130 : void Sub::enable_motor_output()
       5             : {
       6         130 :     motors.output_min();
       7         130 : }
       8             : 
       9             : // motors_output - send output to motors library which will adjust and send to ESCs and servos
      10     2180683 : void Sub::motors_output()
      11             : {
      12             :     // Sub has no pre-takeoff RPM gate that asserts the spool-up block,
      13             :     // so clear it every cycle before motors.output() runs output_logic().
      14             :     // Without this the block set during ground-idle ramp would never clear
      15             :     // and the spool would stay in GROUND_IDLE.
      16     2180683 :     motors.set_spoolup_block(false);
      17             :     
      18             :     // Motor detection mode controls the thrusters directly
      19     2180683 :     if (control_mode == Mode::Number::MOTOR_DETECT){
      20           0 :         return;
      21             :     }
      22             :     // check if we are performing the motor test
      23     2180683 :     if (ap.motor_test) {
      24           0 :         verify_motor_test();
      25             :     } else {
      26     2180683 :         motors.set_interlock(true);
      27     2180683 :         auto &srv = AP::srv();
      28     2180683 :         srv.cork();
      29     2180683 :         SRV_Channels::calc_pwm();
      30     2180683 :         SRV_Channels::output_ch_all();
      31     2180683 :         motors.output();
      32     2180683 :         srv.push();
      33             :     }
      34             : }
      35             : 
      36             : // Initialize new style motor test
      37             : // Perform checks to see if it is ok to begin the motor test
      38             : // Returns true if motor test has begun
      39           0 : bool Sub::init_motor_test()
      40             : {
      41           0 :     uint32_t tnow = AP_HAL::millis();
      42             : 
      43             :     // Ten second cooldown period required with no do_set_motor requests required
      44             :     // after failure.
      45           0 :     if (tnow < last_do_motor_test_fail_ms + 10000 && last_do_motor_test_fail_ms > 0) {
      46           0 :         gcs().send_text(MAV_SEVERITY_CRITICAL, "10 second cooldown required after motor test");
      47           0 :         return false;
      48             :     }
      49             : 
      50             :     // check if safety switch has been pushed
      51           0 :     if (hal.util->safety_switch_state() == AP_HAL::Util::SAFETY_DISARMED) {
      52           0 :         gcs().send_text(MAV_SEVERITY_CRITICAL,"Disarm hardware safety switch before testing motors.");
      53           0 :         return false;
      54             :     }
      55             : 
      56             :     // Make sure we are on the ground
      57           0 :     if (!motors.armed()) {
      58           0 :         gcs().send_text(MAV_SEVERITY_WARNING, "Arm motors before testing motors.");
      59           0 :         return false;
      60             :     }
      61             : 
      62           0 :     enable_motor_output(); // set all motor outputs to zero
      63           0 :     ap.motor_test = true;
      64             : 
      65           0 :     return true;
      66             : }
      67             : 
      68             : // Verify new style motor test
      69             : // The motor test will fail if the interval between received
      70             : // MAV_CMD_DO_SET_MOTOR requests exceeds a timeout period
      71             : // Returns true if it is ok to proceed with new style motor test
      72           0 : bool Sub::verify_motor_test()
      73             : {
      74           0 :     bool pass = true;
      75             : 
      76             :     // Require at least 2 Hz incoming do_set_motor requests
      77           0 :     if (AP_HAL::millis() > last_do_motor_test_ms + 500) {
      78           0 :         gcs().send_text(MAV_SEVERITY_INFO, "Motor test timed out!");
      79           0 :         pass = false;
      80             :     }
      81             : 
      82           0 :     if (!pass) {
      83           0 :         ap.motor_test = false;
      84           0 :         AP::arming().disarm(AP_Arming::Method::MOTORTEST);
      85           0 :         last_do_motor_test_fail_ms = AP_HAL::millis();
      86           0 :         return false;
      87             :     }
      88             : 
      89           0 :     return true;
      90             : }
      91             : 
      92           0 : bool Sub::handle_do_motor_test(mavlink_command_int_t command) {
      93           0 :     last_do_motor_test_ms = AP_HAL::millis();
      94             : 
      95             :     // If we are not already testing motors, initialize test
      96             :     static uint32_t tLastInitializationFailed = 0;
      97           0 :     if(!ap.motor_test) {
      98             :         // Do not allow initializations attempt under 2 seconds
      99             :         // If one fails, we need to give the user time to fix the issue
     100             :         // instead of spamming error messages
     101           0 :         if (AP_HAL::millis() > (tLastInitializationFailed + 2000)) {
     102           0 :             if (!init_motor_test()) {
     103           0 :                 gcs().send_text(MAV_SEVERITY_WARNING, "motor test initialization failed!");
     104           0 :                 tLastInitializationFailed = AP_HAL::millis();
     105           0 :                 return false; // init fail
     106             :             }
     107             :         } else {
     108           0 :             return false;
     109             :         }
     110             :     }
     111             : 
     112           0 :     float motor_number = command.param1;
     113           0 :     float throttle_type = command.param2;
     114           0 :     float throttle = command.param3;
     115             :     // float timeout_s = command.param4; // not used
     116             :     // float motor_count = command.param5; // not used
     117           0 :     const uint32_t test_type = command.y;
     118             : 
     119           0 :     if (test_type != MOTOR_TEST_ORDER_BOARD) {
     120           0 :         gcs().send_text(MAV_SEVERITY_WARNING, "bad test type %0.2f", (double)test_type);
     121           0 :         return false; // test type not supported here
     122             :     }
     123             : 
     124           0 :     if (is_equal(throttle_type, (float)MOTOR_TEST_THROTTLE_PILOT)) {
     125           0 :         gcs().send_text(MAV_SEVERITY_WARNING, "bad throttle type %0.2f", (double)throttle_type);
     126             : 
     127           0 :         return false; // throttle type not supported here
     128             :     }
     129             : 
     130           0 :     if (is_equal(throttle_type, (float)MOTOR_TEST_THROTTLE_PWM)) {
     131           0 :         return motors.output_test_num(motor_number, throttle); // true if motor output is set
     132             :     }
     133             : 
     134           0 :     if (is_equal(throttle_type, (float)MOTOR_TEST_THROTTLE_PERCENT)) {
     135           0 :         throttle = constrain_float(throttle, 0.0f, 100.0f);
     136           0 :         throttle = channel_throttle->get_radio_min() + throttle * 0.01f * (channel_throttle->get_radio_max() - channel_throttle->get_radio_min());
     137           0 :         return motors.output_test_num(motor_number, throttle); // true if motor output is set
     138             :     }
     139             : 
     140           0 :     return false;
     141             : }
     142             : 
     143             : 
     144             : // translate wpnav roll/pitch outputs to lateral/forward
     145      496998 : void Sub::translate_wpnav_rp(float &lateral_out, float &forward_out)
     146             : {
     147             :     // get roll and pitch targets in centidegrees
     148      496998 :     int32_t lateral = wp_nav.get_roll();
     149      496998 :     int32_t forward = -wp_nav.get_pitch(); // output is reversed
     150             : 
     151             :     // constrain target forward/lateral values
     152             :     // The outputs of wp_nav.get_roll and get_pitch should already be constrained to these values
     153      496998 :     const float angle_max_cd = attitude_control.lean_angle_max_cd();
     154      496998 :     lateral = constrain_int16(lateral, -angle_max_cd, angle_max_cd);
     155      496998 :     forward = constrain_int16(forward, -angle_max_cd, angle_max_cd);
     156             : 
     157             :     // Normalize
     158      496998 :     lateral_out = (float)lateral/(float)angle_max_cd;
     159      496998 :     forward_out = (float)forward/(float)angle_max_cd;
     160      496998 : }
     161             : 
     162             : // translate wpnav roll/pitch outputs to lateral/forward
     163      127956 : void Sub::translate_circle_nav_rp(float &lateral_out, float &forward_out)
     164             : {
     165             :     // get roll and pitch targets in centidegrees
     166      127956 :     int32_t lateral = circle_nav.get_roll_cd();
     167      127956 :     int32_t forward = -circle_nav.get_pitch_cd(); // output is reversed
     168             : 
     169             :     // constrain target forward/lateral values
     170      127956 :     const float angle_max_cd = attitude_control.lean_angle_max_cd();
     171      127956 :     lateral = constrain_int16(lateral, -angle_max_cd, angle_max_cd);
     172      127956 :     forward = constrain_int16(forward, -angle_max_cd, angle_max_cd);
     173             : 
     174             :     // Normalize
     175      127956 :     lateral_out = (float)lateral/(float)angle_max_cd;
     176      127956 :     forward_out = (float)forward/(float)angle_max_cd;
     177      127956 : }
     178             : 
     179             : // translate pos_control roll/pitch outputs to lateral/forward
     180      145877 : void Sub::translate_pos_control_rp(float &lateral_out, float &forward_out)
     181             : {
     182             :     // get roll and pitch targets in centidegrees
     183      145877 :     int32_t lateral = pos_control.get_roll_cd();
     184      145877 :     int32_t forward = -pos_control.get_pitch_cd(); // output is reversed
     185             : 
     186             :     // constrain target forward/lateral values
     187      145877 :     const float angle_max_cd = attitude_control.lean_angle_max_cd();
     188      145877 :     lateral = constrain_int16(lateral, -angle_max_cd, angle_max_cd);
     189      145877 :     forward = constrain_int16(forward, -angle_max_cd, angle_max_cd);
     190             : 
     191             :     // Normalize
     192      145877 :     lateral_out = (float)lateral/(float)angle_max_cd;
     193      145877 :     forward_out = (float)forward/(float)angle_max_cd;
     194      145877 : }

Generated by: LCOV version 1.14