#include "Copter.h"
MAV_RESULT Copter::mavlink_compassmot(const GCS_MAVLINK &gcs_chan)
{
#if FRAME_CONFIG == HELI_FRAME
return MAV_RESULT_UNSUPPORTED;
#else
int8_t comp_type;
Vector3f compass_base[COMPASS_MAX_INSTANCES];
Vector3f motor_impact[COMPASS_MAX_INSTANCES];
Vector3f motor_impact_scaled[COMPASS_MAX_INSTANCES];
Vector3f motor_compensation[COMPASS_MAX_INSTANCES];
float throttle_pct;
float throttle_pct_max = 0.0f;
float current_amps_max = 0.0f;
float interference_pct[COMPASS_MAX_INSTANCES]{};
uint32_t last_run_time_ms;
uint32_t last_send_time_ms;
bool updated = false;
uint8_t command_ack_start = command_ack_counter;
if (ap.compass_mot) {
return MAV_RESULT_TEMPORARILY_REJECTED;
} else {
ap.compass_mot = true;
}
if (!AP::compass().available()) {
gcs_chan.send_text(MAV_SEVERITY_CRITICAL, "Compass disabled");
ap.compass_mot = false;
return MAV_RESULT_TEMPORARILY_REJECTED;
}
compass.read();
for (uint8_t i=0; i<compass.get_count(); i++) {
if (!compass.healthy(i)) {
gcs_chan.send_text(MAV_SEVERITY_CRITICAL, "Check compass");
ap.compass_mot = false;
return MAV_RESULT_TEMPORARILY_REJECTED;
}
}
if (!arming.rc_calibration_checks(true)) {
gcs_chan.send_text(MAV_SEVERITY_CRITICAL, "RC not calibrated");
ap.compass_mot = false;
return MAV_RESULT_TEMPORARILY_REJECTED;
}
read_radio();
if (channel_throttle->get_control_in() != 0) {
gcs_chan.send_text(MAV_SEVERITY_CRITICAL, "Throttle not zero");
ap.compass_mot = false;
return MAV_RESULT_TEMPORARILY_REJECTED;
}
if (!ap.land_complete) {
gcs_chan.send_text(MAV_SEVERITY_CRITICAL, "Not landed");
ap.compass_mot = false;
return MAV_RESULT_TEMPORARILY_REJECTED;
}
failsafe_disable();
float current;
if (battery.current_amps(current)) {
comp_type = AP_COMPASS_MOT_COMP_CURRENT;
} else {
comp_type = AP_COMPASS_MOT_COMP_THROTTLE;
}
mavlink_msg_command_ack_send(gcs_chan.get_chan(), MAV_CMD_PREFLIGHT_CALIBRATION,0,
0, 0, 0, 0);
AP_Notify::flags.esc_calibration = true;
gcs_chan.send_text(MAV_SEVERITY_INFO, "Starting calibration");
if (comp_type == AP_COMPASS_MOT_COMP_CURRENT) {
gcs_chan.send_text(MAV_SEVERITY_INFO, "Current");
} else {
gcs_chan.send_text(MAV_SEVERITY_INFO, "Throttle");
}
g.failsafe_throttle.set(FS_THR_DISABLED);
compass.motor_compensation_type(AP_COMPASS_MOT_COMP_DISABLED);
for (uint8_t i=0; i<compass.get_count(); i++) {
compass.set_motor_compensation(i, Vector3f{0,0,0});
}
compass.read();
for (uint8_t i=0; i<compass.get_count(); i++) {
compass_base[i] = compass.get_field(i);
interference_pct[i] = 0.0f;
}
EXPECT_DELAY_MS(5000);
motors->output_min();
motors->armed(true);
hal.util->set_soft_armed(true);
last_run_time_ms = millis();
last_send_time_ms = millis();
while (command_ack_start == command_ack_counter && compass.healthy() && motors->armed()) {
EXPECT_DELAY_MS(5000);
if (millis() - last_run_time_ms < 20) {
hal.scheduler->delay(5);
continue;
}
last_run_time_ms = millis();
read_radio();
auto &srv = AP::srv();
srv.cork();
motors->set_throttle_passthrough_for_esc_calibration(channel_throttle->get_control_in() * 0.001f);
srv.push();
compass.read();
battery.read();
throttle_pct = (float)channel_throttle->get_control_in() * 0.001f;
throttle_pct = constrain_float(throttle_pct,0.0f,1.0f);
throttle_pct_max = MAX(throttle_pct_max, throttle_pct);
if (!battery.current_amps(current)) {
current = 0;
}
current_amps_max = MAX(current_amps_max, current);
if (!is_positive(throttle_pct)) {
for (uint8_t i=0; i<compass.get_count(); i++) {
compass_base[i] = compass_base[i] * 0.99f + compass.get_field(i) * 0.01f;
}
} else {
for (uint8_t i=0; i<compass.get_count(); i++) {
motor_impact[i] = compass.get_field(i) - compass_base[i];
}
if (comp_type == AP_COMPASS_MOT_COMP_THROTTLE) {
for (uint8_t i=0; i<compass.get_count(); i++) {
motor_impact_scaled[i] = motor_impact[i] / throttle_pct;
motor_compensation[i] = motor_compensation[i] * 0.99f - motor_impact_scaled[i] * 0.01f;
}
updated = true;
} else {
for (uint8_t i=0; i<compass.get_count(); i++) {
if (current >= 3.0f) {
motor_impact_scaled[i] = motor_impact[i] / current;
motor_compensation[i] = motor_compensation[i] * 0.99f - motor_impact_scaled[i] * 0.01f;
updated = true;
}
}
}
if (comp_type == AP_COMPASS_MOT_COMP_THROTTLE) {
for (uint8_t i=0; i<compass.get_count(); i++) {
interference_pct[i] = motor_compensation[i].length() / (float)arming.compass_magfield_expected() * 100.0f;
}
} else {
for (uint8_t i=0; i<compass.get_count(); i++) {
interference_pct[i] = motor_compensation[i].length() * (current_amps_max/throttle_pct_max) / (float)arming.compass_magfield_expected() * 100.0f;
}
}
}
if (AP_HAL::millis() - last_send_time_ms > 500) {
last_send_time_ms = AP_HAL::millis();
mavlink_msg_compassmot_status_send(gcs_chan.get_chan(),
channel_throttle->get_control_in(),
current,
interference_pct[0],
motor_compensation[0].x,
motor_compensation[0].y,
motor_compensation[0].z);
#if HAL_WITH_ESC_TELEM
AP::esc_telem().send_esc_telemetry_mavlink(gcs_chan.get_chan());
#endif
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
gcs_chan.send_system_time();
gcs_chan.send_rc_channels();
#endif
}
}
motors->output_min();
motors->armed(false);
hal.util->set_soft_armed(false);
if (updated) {
compass.motor_compensation_type(comp_type);
for (uint8_t i=0; i<compass.get_count(); i++) {
compass.set_motor_compensation(i, motor_compensation[i]);
}
compass.save_motor_compensation();
gcs_chan.send_text(MAV_SEVERITY_INFO, "Calibration successful");
} else {
gcs_chan.send_text(MAV_SEVERITY_NOTICE, "Failed");
compass.motor_compensation_type(AP_COMPASS_MOT_COMP_DISABLED);
}
AP_Notify::flags.esc_calibration = false;
failsafe_enable();
g.failsafe_throttle.load();
ap.compass_mot = false;
return MAV_RESULT_ACCEPTED;
#endif
}