LCOV - code coverage report
Current view: top level - AP_Compass - Compass_PerMotor.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 4 68 5.9 %
Date: 2026-09-09 11:02:08 Functions: 1 6 16.7 %

          Line data    Source code
       1             : /*
       2             :   per-motor compass compensation
       3             :  */
       4             : 
       5             : #include "AP_Compass.h"
       6             : #include <GCS_MAVLink/GCS.h>
       7             : #include <SRV_Channel/SRV_Channel.h>
       8             : 
       9             : extern const AP_HAL::HAL &hal;
      10             : 
      11             : const AP_Param::GroupInfo Compass_PerMotor::var_info[] = {
      12             :     // @Param: _EN
      13             :     // @DisplayName: per-motor compass correction enable
      14             :     // @Description: This enables per-motor compass corrections
      15             :     // @Values: 0:Disabled,1:Enabled
      16             :     // @User: Advanced
      17             :     AP_GROUPINFO_FLAGS("_EN",  1, Compass_PerMotor, enable, 0, AP_PARAM_FLAG_ENABLE),
      18             : 
      19             :     // @Param: _EXP
      20             :     // @DisplayName: per-motor exponential correction
      21             :     // @Description: This is the exponential correction for the power output of the motor for per-motor compass correction
      22             :     // @Range: 0 2
      23             :     // @Increment: 0.01
      24             :     // @User: Advanced
      25             :     AP_GROUPINFO("_EXP", 2, Compass_PerMotor, expo, 0.65),
      26             : 
      27             :     // @Param: 1_X
      28             :     // @DisplayName: Compass per-motor1 X
      29             :     // @Description: Compensation for X axis of motor1
      30             :     // @User: Advanced
      31             : 
      32             :     // @Param: 1_Y
      33             :     // @DisplayName: Compass per-motor1 Y
      34             :     // @Description: Compensation for Y axis of motor1
      35             :     // @User: Advanced
      36             : 
      37             :     // @Param: 1_Z
      38             :     // @DisplayName: Compass per-motor1 Z
      39             :     // @Description: Compensation for Z axis of motor1
      40             :     // @User: Advanced
      41             :     AP_GROUPINFO("1",  3, Compass_PerMotor, compensation[0], 0),
      42             : 
      43             :     // @Param: 2_X
      44             :     // @DisplayName: Compass per-motor2 X
      45             :     // @Description: Compensation for X axis of motor2
      46             :     // @User: Advanced
      47             : 
      48             :     // @Param: 2_Y
      49             :     // @DisplayName: Compass per-motor2 Y
      50             :     // @Description: Compensation for Y axis of motor2
      51             :     // @User: Advanced
      52             : 
      53             :     // @Param: 2_Z
      54             :     // @DisplayName: Compass per-motor2 Z
      55             :     // @Description: Compensation for Z axis of motor2
      56             :     // @User: Advanced
      57             :     AP_GROUPINFO("2",  4, Compass_PerMotor, compensation[1], 0),
      58             : 
      59             :     // @Param: 3_X
      60             :     // @DisplayName: Compass per-motor3 X
      61             :     // @Description: Compensation for X axis of motor3
      62             :     // @User: Advanced
      63             : 
      64             :     // @Param: 3_Y
      65             :     // @DisplayName: Compass per-motor3 Y
      66             :     // @Description: Compensation for Y axis of motor3
      67             :     // @User: Advanced
      68             : 
      69             :     // @Param: 3_Z
      70             :     // @DisplayName: Compass per-motor3 Z
      71             :     // @Description: Compensation for Z axis of motor3
      72             :     // @User: Advanced
      73             :     AP_GROUPINFO("3",  5, Compass_PerMotor, compensation[2], 0),
      74             : 
      75             :     // @Param: 4_X
      76             :     // @DisplayName: Compass per-motor4 X
      77             :     // @Description: Compensation for X axis of motor4
      78             :     // @User: Advanced
      79             : 
      80             :     // @Param: 4_Y
      81             :     // @DisplayName: Compass per-motor4 Y
      82             :     // @Description: Compensation for Y axis of motor4
      83             :     // @User: Advanced
      84             : 
      85             :     // @Param: 4_Z
      86             :     // @DisplayName: Compass per-motor4 Z
      87             :     // @Description: Compensation for Z axis of motor4
      88             :     // @User: Advanced
      89             :     AP_GROUPINFO("4",  6, Compass_PerMotor, compensation[3], 0),
      90             :     
      91             :     AP_GROUPEND
      92             : };
      93             : 
      94             : // constructor
      95        2098 : Compass_PerMotor::Compass_PerMotor(Compass &_compass) :
      96       10490 :     compass(_compass)
      97             : {
      98        2098 :     AP_Param::setup_object_defaults(this, var_info);
      99        2098 : }
     100             : 
     101             : // return current scaled motor output
     102           0 : float Compass_PerMotor::scaled_output(uint8_t motor)
     103             : {
     104           0 :     if (!have_motor_map) {
     105           0 :         if (SRV_Channels::find_channel(SRV_Channel::k_motor1, motor_map[0]) &&
     106           0 :             SRV_Channels::find_channel(SRV_Channel::k_motor2, motor_map[1]) &&
     107           0 :             SRV_Channels::find_channel(SRV_Channel::k_motor3, motor_map[2]) &&
     108           0 :             SRV_Channels::find_channel(SRV_Channel::k_motor4, motor_map[3])) {
     109           0 :             have_motor_map = true;
     110             :         }
     111             :     }
     112           0 :     if (!have_motor_map) {
     113           0 :         return 0;
     114             :     }
     115             :     
     116             :     // this currently assumes first 4 channels. 
     117           0 :     uint16_t pwm = hal.rcout->read_last_sent(motor_map[motor]);
     118             : 
     119             :     // get 0 to 1 motor demand
     120           0 :     float output = (hal.rcout->scale_esc_to_unity(pwm)+1) * 0.5f;
     121             : 
     122           0 :     if (output <= 0) {
     123           0 :         return 0;
     124             :     }
     125             :     
     126             :     // scale for voltage
     127           0 :     output *= voltage;
     128             : 
     129             :     // apply expo correction
     130           0 :     output = powf(output, expo);
     131           0 :     return output;
     132             : }
     133             : 
     134             : // per-motor calibration update
     135           0 : void Compass_PerMotor::calibration_start(void)
     136             : {
     137           0 :     for (uint8_t i=0; i<4; i++) {
     138           0 :         field_sum[i].zero();
     139           0 :         output_sum[i] = 0;
     140           0 :         count[i] = 0;
     141           0 :         start_ms[i] = 0;
     142             :     }
     143             : 
     144             :     // we need to ensure we get current data by throwing away several
     145             :     // samples. The offsets may have just changed from an offset
     146             :     // calibration
     147           0 :     for (uint8_t i=0; i<4; i++) {
     148           0 :         compass.read();
     149           0 :         hal.scheduler->delay(50);
     150             :     }
     151             :     
     152           0 :     base_field = compass.get_field(0);
     153           0 :     running = true;
     154           0 : }
     155             : 
     156             : // per-motor calibration update
     157           0 : void Compass_PerMotor::calibration_update(void)
     158             : {
     159           0 :     uint32_t now = AP_HAL::millis();
     160             :     
     161             :     // accumulate per-motor sums
     162           0 :     for (uint8_t i=0; i<4; i++) {
     163           0 :         float output = scaled_output(i);
     164             : 
     165           0 :         if (output <= 0) {
     166             :             // motor is off
     167           0 :             start_ms[i] = 0;
     168           0 :             continue;
     169             :         }
     170           0 :         if (start_ms[i] == 0) {
     171           0 :             start_ms[i] = now;
     172             :         }
     173           0 :         if (now - start_ms[i] < 500) {
     174             :             // motor must run for 0.5s to settle
     175           0 :             continue;
     176             :         }
     177             : 
     178             :         // accumulate a sample
     179           0 :         field_sum[i] += compass.get_field(0);
     180           0 :         output_sum[i] += output;
     181           0 :         count[i]++;
     182             :     }
     183           0 : }
     184             : 
     185             : // calculate per-motor calibration values
     186           0 : void Compass_PerMotor::calibration_end(void)
     187             : {
     188           0 :     for (uint8_t i=0; i<4; i++) {
     189           0 :         if (count[i] == 0) {
     190           0 :             continue;
     191             :         }
     192             : 
     193             :         // calculate effective output
     194           0 :         float output = output_sum[i] / count[i];
     195             : 
     196             :         // calculate amount that field changed from base field
     197           0 :         Vector3f field_change = base_field - (field_sum[i] / count[i]);
     198           0 :         if (output <= 0) {
     199           0 :             continue;
     200             :         }
     201             :         
     202           0 :         Vector3f c = field_change / output;
     203           0 :         compensation[i].set_and_save(c);
     204             :     }
     205             : 
     206             :     // enable per-motor compensation
     207           0 :     enable.set_and_save(1);
     208             :     
     209           0 :     running = false;
     210           0 : }
     211             : 
     212             : /*
     213             :   calculate total offset for per-motor compensation
     214             :  */
     215           0 : void Compass_PerMotor::compensate(Vector3f &offset)
     216             : {
     217           0 :     offset.zero();
     218             : 
     219           0 :     if (running) {
     220             :         // don't compensate while calibrating
     221           0 :         return;
     222             :     }
     223             : 
     224           0 :     for (uint8_t i=0; i<4; i++) {
     225           0 :         float output = scaled_output(i);
     226             : 
     227           0 :         const Vector3f &c = compensation[i].get();
     228             : 
     229           0 :         offset += c * output;
     230             :     }
     231             : }

Generated by: LCOV version 1.14