LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduCopter - motor_test.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 73 101 72.3 %
Date: 2026-09-09 11:02:08 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #include "Copter.h"
       2             : 
       3             : /*
       4             :   mavlink motor test - implements the MAV_CMD_DO_MOTOR_TEST mavlink command so that the GCS/pilot can test an individual motor or flaps
       5             :                        to ensure proper wiring, rotation.
       6             :  */
       7             : 
       8             : // motor test definitions
       9             : #define MOTOR_TEST_TIMEOUT_SEC          600     // max timeout is 10 minutes (600 seconds)
      10             : 
      11             : static uint32_t motor_test_start_ms;        // system time the motor test began
      12             : static uint32_t motor_test_timeout_ms;      // test will timeout this many milliseconds after the motor_test_start_ms
      13             : static uint8_t motor_test_seq;              // motor sequence number of motor being tested
      14             : static uint8_t motor_test_count;            // number of motors to test
      15             : static uint8_t motor_test_throttle_type;    // motor throttle type (0=throttle percentage, 1=PWM, 2=pilot throttle channel pass-through)
      16             : static float motor_test_throttle_value;  // throttle to be sent to motor, value depends upon it's type
      17             : 
      18             : // motor_test_output - checks for timeout and sends updates to motors objects
      19        8001 : void Copter::motor_test_output()
      20             : {
      21             :     // exit immediately if the motor test is not running
      22        8001 :     if (!ap.motor_test) {
      23        1604 :         return;
      24             :     }
      25             : 
      26        8001 :     EXPECT_DELAY_MS(2000);
      27             : 
      28             :     // check for test timeout
      29        8001 :     uint32_t now = AP_HAL::millis();
      30        8001 :     if ((now - motor_test_start_ms) >= motor_test_timeout_ms) {
      31        1608 :         if (motor_test_count > 1) {
      32        1604 :             if (now - motor_test_start_ms < motor_test_timeout_ms*1.5) {
      33             :                 // output zero for 50% of the test time
      34        1600 :                 motors->output_min();
      35             :             } else {
      36             :                 // move onto next motor
      37           4 :                 motor_test_seq++;
      38           4 :                 motor_test_count--;
      39           4 :                 motor_test_start_ms = now;
      40           4 :                 if (!motors->armed()) {
      41           0 :                     motors->armed(true);
      42           0 :                     hal.util->set_soft_armed(true);
      43             :                 }
      44             :             }
      45        1604 :             return;
      46             :         }
      47             :         // stop motor test
      48           4 :         motor_test_stop();
      49             :     } else {
      50        6393 :         int16_t pwm = 0;   // pwm that will be output to the motors
      51             : 
      52             :         // calculate pwm based on throttle type
      53        6393 :         switch (motor_test_throttle_type) {
      54             : 
      55           0 :             case MOTOR_TEST_COMPASS_CAL:
      56           0 :                 compass.set_voltage(battery.voltage());
      57           0 :                 compass.per_motor_calibration_update();
      58             :                 FALLTHROUGH;
      59             : 
      60        3197 :             case MOTOR_TEST_THROTTLE_PERCENT:
      61             :                 // sanity check motor_test_throttle value
      62             : #if FRAME_CONFIG != HELI_FRAME
      63        3197 :                 if (motor_test_throttle_value <= 100) {
      64        3197 :                     int16_t pwm_min = motors->get_pwm_output_min();
      65        3197 :                     int16_t pwm_max = motors->get_pwm_output_max();
      66        3197 :                     pwm = (int16_t) (pwm_min + (pwm_max - pwm_min) * motor_test_throttle_value * 1e-2f);
      67             :                 }
      68             : #endif
      69        3197 :                 break;
      70             : 
      71        3196 :             case MOTOR_TEST_THROTTLE_PWM:
      72        3196 :                 pwm = (int16_t)motor_test_throttle_value;
      73        3196 :                 break;
      74             : 
      75           0 :             case MOTOR_TEST_THROTTLE_PILOT:
      76           0 :                 pwm = channel_throttle->get_radio_in();
      77           0 :                 break;
      78             : 
      79           0 :             default:
      80           0 :                 motor_test_stop();
      81           0 :                 return;
      82             :         }
      83             : 
      84             :         // sanity check throttle values
      85        6393 :         if (pwm < RC_Channel::RC_MIN_LIMIT_PWM || pwm > RC_Channel::RC_MAX_LIMIT_PWM) {
      86           0 :             motor_test_stop();
      87           0 :             return;
      88             :         }
      89             : 
      90             :         // turn on motor to specified pwm value
      91        6393 :         if (!motors->output_test_seq(motor_test_seq, pwm)) {
      92           0 :             gcs().send_text(MAV_SEVERITY_INFO, "Motor Test: cancelled");
      93           0 :             motor_test_stop();
      94             :         }
      95             :     }
      96        8001 : }
      97             : 
      98             : // mavlink_motor_test_check - perform checks before motor tests can begin
      99             : //  return true if tests can continue, false if not
     100           4 : bool Copter::mavlink_motor_control_check(const GCS_MAVLINK &gcs_chan, bool check_rc, const char* mode)
     101             : {
     102             :     // check board has initialised
     103           4 :     if (!ap.initialised) {
     104           0 :         gcs_chan.send_text(MAV_SEVERITY_CRITICAL,"%s: Board initialising", mode);
     105           0 :         return false;
     106             :     }
     107             : 
     108             :     // Check Motor test is allowed
     109           4 :     char failure_msg[100] {};
     110           4 :     if (!motors->motor_test_checks(ARRAY_SIZE(failure_msg), failure_msg)) {
     111           0 :         gcs_chan.send_text(MAV_SEVERITY_CRITICAL,"%s: %s", mode, failure_msg);
     112           0 :         return false;
     113             :     }
     114             : 
     115             :     // check rc has been calibrated
     116           4 :     if (check_rc && !arming.rc_calibration_checks(true)) {
     117           0 :         gcs_chan.send_text(MAV_SEVERITY_CRITICAL,"%s: RC not calibrated", mode);
     118           0 :         return false;
     119             :     }
     120             : 
     121             :     // ensure we are landed
     122           4 :     if (!ap.land_complete) {
     123           0 :         gcs_chan.send_text(MAV_SEVERITY_CRITICAL,"%s: vehicle not landed", mode);
     124           0 :         return false;
     125             :     }
     126             : 
     127             :     // if we got this far the check was successful and the motor test can continue
     128           4 :     return true;
     129             : }
     130             : 
     131             : // mavlink_motor_test_start - start motor test - spin a single motor at a specified pwm
     132             : //  returns MAV_RESULT_ACCEPTED on success, MAV_RESULT_FAILED on failure
     133           4 : MAV_RESULT Copter::mavlink_motor_test_start(const GCS_MAVLINK &gcs_chan, uint8_t motor_seq, uint8_t throttle_type, float throttle_value,
     134             :                                          float timeout_sec, uint8_t motor_count)
     135             : {
     136           4 :     if (motor_count == 0) {
     137           0 :         motor_count = 1;
     138             :     }
     139             :     // if test has not started try to start it
     140           4 :     if (!ap.motor_test) {
     141             :         /* perform checks that it is ok to start test
     142             :            The RC calibrated check can be skipped if direct pwm is
     143             :            supplied
     144             :         */
     145           4 :         if (!mavlink_motor_control_check(gcs_chan, throttle_type != 1, "Motor Test")) {
     146           0 :             return MAV_RESULT_FAILED;
     147             :         } else {
     148             :             // start test
     149           4 :             gcs().send_text(MAV_SEVERITY_INFO, "starting motor test");
     150             : 
     151           4 :             EXPECT_DELAY_MS(3000);
     152             : 
     153             :             // enable and arm motors
     154           4 :             if (!motors->armed()) {
     155           4 :                 motors->output_min();  // output lowest possible value to motors
     156           4 :                 motors->armed(true);
     157           4 :                 hal.util->set_soft_armed(true);
     158             :             }
     159             : 
     160             :             // disable throttle and gps failsafe
     161           4 :             g.failsafe_throttle.set(FS_THR_Action::DISABLED);
     162           4 :             g.failsafe_gcs.set(FS_GCS_Action::DISABLED);
     163           4 :             g.fs_ekf_action.set(FS_EKF_Action::REPORT_ONLY);
     164             : 
     165             :             // turn on notify leds
     166           4 :             AP_Notify::flags.esc_calibration = true;
     167           4 :             ap.motor_test = true;
     168           4 :         }
     169             :     }
     170             : 
     171             :     // set timeout
     172           4 :     motor_test_start_ms = AP_HAL::millis();
     173           4 :     motor_test_timeout_ms = MIN(timeout_sec, MOTOR_TEST_TIMEOUT_SEC) * 1000;
     174             : 
     175             :     // store required output
     176           4 :     motor_test_seq = motor_seq;
     177           4 :     motor_test_count = motor_count;
     178           4 :     motor_test_throttle_type = throttle_type;
     179           4 :     motor_test_throttle_value = throttle_value;
     180             : 
     181           4 :     if (motor_test_throttle_type == MOTOR_TEST_COMPASS_CAL) {
     182           0 :         compass.per_motor_calibration_start();
     183             :     }            
     184             : 
     185             :     // return success
     186           4 :     return MAV_RESULT_ACCEPTED;
     187             : }
     188             : 
     189             : // motor_test_stop - stops the motor test
     190           4 : void Copter::motor_test_stop()
     191             : {
     192             :     // exit immediately if the test is not running
     193           4 :     if (!ap.motor_test) {
     194           0 :         return;
     195             :     }
     196             : 
     197           4 :     gcs().send_text(MAV_SEVERITY_INFO, "finished motor test");    
     198             : 
     199             :     // disarm motors
     200           4 :     motors->armed(false);
     201           4 :     hal.util->set_soft_armed(false);
     202             : 
     203             :     // reset timeout
     204           4 :     motor_test_start_ms = 0;
     205           4 :     motor_test_timeout_ms = 0;
     206             : 
     207             :     // re-enable failsafes
     208           4 :     g.failsafe_throttle.load();
     209           4 :     g.failsafe_gcs.load();
     210           4 :     g.fs_ekf_action.load();
     211             : 
     212           4 :     if (motor_test_throttle_type == MOTOR_TEST_COMPASS_CAL) {
     213           0 :         compass.per_motor_calibration_end();
     214             :     }
     215             : 
     216             :     // turn off notify leds
     217           4 :     AP_Notify::flags.esc_calibration = false;
     218             : 
     219             :     // flag test is complete
     220           4 :     ap.motor_test = false;
     221             : }

Generated by: LCOV version 1.14