LCOV - code coverage report
Current view: top level - AP_AccelCal - AP_AccelCal.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 158 217 72.8 %
Date: 2026-09-09 11:02:08 Functions: 11 14 78.6 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             :    This program is distributed in the hope that it will be useful,
       7             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       8             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       9             :    GNU General Public License for more details.
      10             :    You should have received a copy of the GNU General Public License
      11             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      12             : */
      13             : 
      14             : #include "AP_AccelCal.h"
      15             : 
      16             : #if HAL_INS_ACCELCAL_ENABLED
      17             : 
      18             : #include <stdarg.h>
      19             : #include <AP_HAL/AP_HAL.h>
      20             : #include <GCS_MAVLink/GCS.h>
      21             : 
      22             : #define AP_ACCELCAL_POSITION_REQUEST_INTERVAL_MS 1000
      23             : 
      24             : #define _printf(fmt, args ...) do {                                     \
      25             :         if (_gcs != nullptr) {                                          \
      26             :             _gcs->send_text(MAV_SEVERITY_CRITICAL, fmt, ## args);       \
      27             :         }                                                               \
      28             :     } while (0)
      29             : 
      30             : 
      31             : const extern AP_HAL::HAL& hal;
      32             : static bool _start_collect_sample;
      33             : 
      34             : uint8_t AP_AccelCal::_num_clients = 0;
      35             : AP_AccelCal_Client* AP_AccelCal::_clients[AP_ACCELCAL_MAX_NUM_CLIENTS] {};
      36             : 
      37         691 : void AP_AccelCal::update()
      38             : {
      39         691 :     if (!get_calibrator(0)) {
      40             :         // no calibrators
      41           0 :         return;
      42             :     }
      43             : 
      44         691 :     if (_started) {
      45         468 :         update_status();
      46             : 
      47         468 :         uint8_t num_active_calibrators = 0;
      48        1404 :         for(uint8_t i=0; get_calibrator(i) != nullptr; i++) {
      49         936 :             num_active_calibrators++;
      50             :         }
      51         468 :         if (num_active_calibrators != _num_active_calibrators) {
      52           0 :             fail();
      53           0 :             return;
      54             :         }
      55         468 :         if(_start_collect_sample) {
      56           6 :             collect_sample();
      57             :         }
      58             :         AccelCalibrator *cal;
      59         468 :         switch(_status) {
      60           0 :             case ACCEL_CAL_NOT_STARTED:
      61           0 :                 fail();
      62           0 :                 return;
      63         430 :             case ACCEL_CAL_WAITING_FOR_ORIENTATION: {
      64             :                 // if we're waiting for orientation, first ensure that all calibrators are on the same step
      65             :                 uint8_t step;
      66         430 :                 if ((cal = get_calibrator(0)) == nullptr) {
      67           0 :                     fail();
      68           0 :                     return;
      69             :                 }
      70         430 :                 step = cal->get_num_samples_collected()+1;
      71             : 
      72         860 :                 for(uint8_t i=1 ; (cal = get_calibrator(i))  ; i++) {
      73         430 :                     if (step != cal->get_num_samples_collected()+1) {
      74           0 :                         fail();
      75           0 :                         return;
      76             :                     }
      77             :                 }
      78             :                 // if we're on a new step, print a message describing the step
      79         430 :                 if (step != _step) {
      80           7 :                     _step = step;
      81             : 
      82           7 :                     if(_use_gcs_snoop) {
      83             :                         const char *msg;
      84           7 :                         switch (step) {
      85           2 :                             case ACCELCAL_VEHICLE_POS_LEVEL:
      86           2 :                                 msg = "level";
      87           2 :                                 break;
      88           1 :                             case ACCELCAL_VEHICLE_POS_LEFT:
      89           1 :                                 msg = "on its LEFT side";
      90           1 :                                 break;
      91           1 :                             case ACCELCAL_VEHICLE_POS_RIGHT:
      92           1 :                                 msg = "on its RIGHT side";
      93           1 :                                 break;
      94           1 :                             case ACCELCAL_VEHICLE_POS_NOSEDOWN:
      95           1 :                                 msg = "nose DOWN";
      96           1 :                                 break;
      97           1 :                             case ACCELCAL_VEHICLE_POS_NOSEUP:
      98           1 :                                 msg = "nose UP";
      99           1 :                                 break;
     100           1 :                             case ACCELCAL_VEHICLE_POS_BACK:
     101           1 :                                 msg = "on its BACK";
     102           1 :                                 break;
     103           0 :                             default:
     104           0 :                                 fail();
     105           0 :                                 return;
     106             :                         }
     107           7 :                         _printf("Place vehicle %s and press any key.", msg);
     108           7 :                         _waiting_for_mavlink_ack = true;
     109             :                     }
     110             :                 }
     111             : 
     112         430 :                 uint32_t now = AP_HAL::millis();
     113         430 :                 if (now - _last_position_request_ms > AP_ACCELCAL_POSITION_REQUEST_INTERVAL_MS) {
     114          43 :                     _last_position_request_ms = now;
     115          43 :                     _gcs->send_accelcal_vehicle_position(step);
     116             :                 }
     117         430 :                 break;
     118             :             }
     119          36 :             case ACCEL_CAL_COLLECTING_SAMPLE:
     120             :                 // check for timeout
     121             : 
     122         108 :                 for(uint8_t i=0; (cal = get_calibrator(i)); i++) {
     123          72 :                     cal->check_for_timeout();
     124             :                 }
     125             : 
     126          36 :                 update_status();
     127             : 
     128          36 :                 if (_status == ACCEL_CAL_FAILED) {
     129           0 :                     fail();
     130             :                 }
     131          36 :                 return;
     132           2 :             case ACCEL_CAL_SUCCESS:
     133             :                 // save
     134           2 :                 if (_saving) {
     135           1 :                     bool done = true;
     136           2 :                     for(uint8_t i=0; i<_num_clients; i++) {
     137           1 :                         if (client_active(i) && _clients[i]->_acal_get_saving()) {
     138           0 :                             done = false;
     139           0 :                             break;
     140             :                         }
     141             :                     }
     142           1 :                     if (done) {
     143           1 :                         success();
     144             :                     }
     145           1 :                     return;
     146             :                 } else {
     147           2 :                     for(uint8_t i=0; i<_num_clients; i++) {
     148           1 :                         if(client_active(i) && _clients[i]->_acal_get_fail()) {
     149           0 :                             fail();
     150           0 :                             return;
     151             :                         }
     152             :                     }
     153           2 :                     for(uint8_t i=0; i<_num_clients; i++) {
     154           1 :                         if(client_active(i)) {
     155           1 :                             _clients[i]->_acal_save_calibrations();
     156             :                         }
     157             :                     }
     158           1 :                     _saving = true;
     159             :                 }
     160           1 :                 return;
     161           0 :             default:
     162             :             case ACCEL_CAL_FAILED:
     163           0 :                 fail();
     164           0 :                 return;
     165             :         }
     166         223 :     } else if (_last_result != ACCEL_CAL_NOT_STARTED) {
     167             :         // only continuously report if we have ever completed a calibration
     168         223 :         uint32_t now = AP_HAL::millis();
     169         223 :         if (now - _last_position_request_ms > AP_ACCELCAL_POSITION_REQUEST_INTERVAL_MS) {
     170          22 :             _last_position_request_ms = now;
     171          22 :             switch (_last_result) {
     172          22 :                 case ACCEL_CAL_SUCCESS:
     173          22 :                     _gcs->send_accelcal_vehicle_position(ACCELCAL_VEHICLE_POS_SUCCESS);
     174          22 :                     break;
     175           0 :                 case ACCEL_CAL_FAILED:
     176           0 :                     _gcs->send_accelcal_vehicle_position(ACCELCAL_VEHICLE_POS_FAILED);
     177           0 :                     break;
     178           0 :                 default:
     179             :                     // should never hit this state
     180           0 :                     break;
     181             :             }
     182             :         }
     183             :     }
     184             : }
     185             : 
     186           2 : void AP_AccelCal::start(GCS_MAVLINK *gcs, uint8_t sysid, uint8_t compid)
     187             : {
     188           2 :     if (gcs == nullptr || _started) {
     189           0 :         return;
     190             :     }
     191           2 :     _start_collect_sample = false;
     192           2 :     _num_active_calibrators = 0;
     193             : 
     194             :     AccelCalibrator *cal;
     195           6 :     for(uint8_t i=0; (cal = get_calibrator(i)); i++) {
     196           4 :         cal->clear();
     197           4 :         cal->start(ACCEL_CAL_AXIS_ALIGNED_ELLIPSOID, 6, 0.5f);
     198           4 :         _num_active_calibrators++;
     199             :     }
     200             : 
     201           2 :     _started = true;
     202           2 :     _saving = false;
     203           2 :     _gcs = gcs;
     204           2 :     _sysid = sysid;
     205           2 :     _compid = compid;
     206           2 :     _use_gcs_snoop = true;
     207           2 :     _last_position_request_ms = 0;
     208           2 :     _step = 0;
     209             : 
     210           2 :     _last_result = ACCEL_CAL_NOT_STARTED;
     211             : 
     212           2 :     update_status();
     213             : }
     214             : 
     215           1 : void AP_AccelCal::success()
     216             : {
     217           1 :     _printf("Calibration successful");
     218             : 
     219           2 :     for(uint8_t i=0 ; i < _num_clients ; i++) {
     220           1 :         _clients[i]->_acal_event_success();
     221             :     }
     222             : 
     223           1 :     _last_result = ACCEL_CAL_SUCCESS;
     224             : 
     225           1 :     clear();
     226           1 : }
     227             : 
     228           0 : void AP_AccelCal::cancel()
     229             : {
     230           0 :     _printf("Calibration cancelled");
     231             : 
     232           0 :     for(uint8_t i=0 ; i < _num_clients ; i++) {
     233           0 :         _clients[i]->_acal_event_cancellation();
     234             :     }
     235             : 
     236           0 :     _last_result = ACCEL_CAL_NOT_STARTED;
     237             : 
     238           0 :     clear();
     239           0 : }
     240             : 
     241           0 : void AP_AccelCal::fail()
     242             : {
     243           0 :     _printf("Calibration FAILED");
     244             : 
     245           0 :     for(uint8_t i=0 ; i < _num_clients ; i++) {
     246           0 :         _clients[i]->_acal_event_failure();
     247             :     }
     248             : 
     249           0 :     _last_result = ACCEL_CAL_FAILED;
     250             : 
     251           0 :     clear();
     252           0 : }
     253             : 
     254           1 : void AP_AccelCal::clear()
     255             : {
     256           1 :     if (!_started) {
     257           0 :         return;
     258             :     }
     259             : 
     260             :     AccelCalibrator *cal;
     261           3 :     for(uint8_t i=0 ; (cal = get_calibrator(i))  ; i++) {
     262           2 :         cal->clear();
     263             :     }
     264             : 
     265           1 :     _step = 0;
     266           1 :     _started = false;
     267           1 :     _saving = false;
     268             : 
     269           1 :     update_status();
     270             : }
     271             : 
     272           6 : void AP_AccelCal::collect_sample()
     273             : {
     274           6 :     if (_status != ACCEL_CAL_WAITING_FOR_ORIENTATION) {
     275           0 :         return;
     276             :     }
     277             : 
     278          12 :     for(uint8_t i=0; i<_num_clients; i++) {
     279           6 :         if (client_active(i) && !_clients[i]->_acal_get_ready_to_sample()) {
     280           0 :             _printf("Not ready to sample");
     281           0 :             return;
     282             :         }
     283             :     }
     284             : 
     285             :     AccelCalibrator *cal;
     286          18 :     for(uint8_t i=0 ; (cal = get_calibrator(i))  ; i++) {
     287          12 :         cal->collect_sample();
     288             :     }
     289           6 :     _start_collect_sample = false;
     290           6 :     update_status();
     291             : }
     292             : 
     293        2099 : void AP_AccelCal::register_client(AP_AccelCal_Client* client) {
     294        2099 :     if (client == nullptr || _num_clients >= AP_ACCELCAL_MAX_NUM_CLIENTS) {
     295           0 :         return;
     296             :     }
     297             : 
     298             : 
     299        2100 :     for(uint8_t i=0; i<_num_clients; i++) {
     300           1 :         if(_clients[i] == client) {
     301           0 :             return;
     302             :         }
     303             :     }
     304        2099 :     _clients[_num_clients] = client;
     305        2099 :     _num_clients++;
     306             : }
     307             : 
     308        7425 : AccelCalibrator* AP_AccelCal::get_calibrator(uint8_t index) {
     309             :     AccelCalibrator* ret;
     310        9329 :     for(uint8_t i=0; i<_num_clients; i++) {
     311       13133 :         for(uint8_t j=0 ; (ret = _clients[i]->_acal_get_calibrator(j)) ; j++) {
     312       11229 :             if (index == 0) {
     313        5521 :                 return ret;
     314             :             }
     315        5708 :             index--;
     316             :         }
     317             :     }
     318        1904 :     return nullptr;
     319             : }
     320             : 
     321         515 : void AP_AccelCal::update_status() {
     322             :     AccelCalibrator *cal;
     323             : 
     324         515 :     if (!get_calibrator(0)) {
     325             :         // no calibrators
     326           2 :         _status = ACCEL_CAL_NOT_STARTED;
     327           2 :         return;
     328             :     }
     329             : 
     330        1539 :     for(uint8_t i=0 ; (cal = get_calibrator(i))  ; i++) {
     331        1026 :         if (cal->get_status() == ACCEL_CAL_FAILED) {
     332           0 :             _status = ACCEL_CAL_FAILED;         //fail if even one of the calibration has
     333           0 :             return;
     334             :         }
     335             :     }
     336             : 
     337        1397 :     for(uint8_t i=0 ; (cal = get_calibrator(i))  ; i++) {
     338         956 :         if (cal->get_status() == ACCEL_CAL_COLLECTING_SAMPLE) {
     339          72 :             _status = ACCEL_CAL_COLLECTING_SAMPLE;          // move to Collecting sample state if all the callibrators have
     340          72 :             return;
     341             :         }
     342             :     }
     343             : 
     344         447 :     for(uint8_t i=0 ; (cal = get_calibrator(i))  ; i++) {
     345         444 :         if (cal->get_status() == ACCEL_CAL_WAITING_FOR_ORIENTATION) {
     346         438 :             _status = ACCEL_CAL_WAITING_FOR_ORIENTATION;    // move to waiting for user ack for orientation confirmation
     347         438 :             return;
     348             :         }
     349             :     }
     350             : 
     351           7 :     for(uint8_t i=0 ; (cal = get_calibrator(i))  ; i++) {
     352           5 :         if (cal->get_status() == ACCEL_CAL_NOT_STARTED) {
     353           1 :             _status = ACCEL_CAL_NOT_STARTED;    // we haven't started if all the calibrators haven't
     354           1 :             return;
     355             :         }
     356             :     }
     357             : 
     358           2 :     _status = ACCEL_CAL_SUCCESS;    // we have succeeded calibration if all the calibrators have
     359             : }
     360             : 
     361           9 : bool AP_AccelCal::client_active(uint8_t client_num)
     362             : {
     363           9 :     return (bool)_clients[client_num]->_acal_get_calibrator(0);
     364             : }
     365             : 
     366             : #if HAL_GCS_ENABLED
     367           6 : void AP_AccelCal::handle_command_ack(const mavlink_command_ack_t &packet, uint8_t src_sysid, uint8_t src_compid)
     368             : {
     369           6 :     if(_sysid != src_sysid || _compid != src_compid) {
     370           0 :         return;
     371             :     }
     372             : 
     373           6 :     if (!_waiting_for_mavlink_ack) {
     374           0 :         return;
     375             :     }
     376             :     // this is support for the old, non-accelcal-specific calibration.
     377             :     // The GCS is expected to send back a COMMAND_ACK when the vehicle
     378             :     // is posed, but we placed no constraints on the result code or
     379             :     // the command field in the ack packet.  That meant that any ACK
     380             :     // would move the cal process forward - and since we don't even
     381             :     // check the source system/component here the process could easily
     382             :     // fail due to other ACKs floating around the mavlink network.
     383             :     // GCSs should be moved to using the non-gcs-snoop method.  As a
     384             :     // round-up:
     385             :     // MAVProxy: command=1-6 depending on pose, result=1
     386             :     // QGC: command=0, result=1
     387             :     // MissionPlanner: uses new ACCELCAL_VEHICLE_POS
     388           6 :     if (packet.command > 6) {
     389             :         // not an acknowledgement for a vehicle position
     390           0 :         return;
     391             :     }
     392           6 :     if (packet.result != MAV_RESULT_TEMPORARILY_REJECTED) {
     393             :         // not an acknowledgement for a vehicle position
     394           0 :         return;
     395             :     }
     396           6 :     _waiting_for_mavlink_ack = false;
     397           6 :     _start_collect_sample = true;
     398             : }
     399             : 
     400           0 : bool AP_AccelCal::gcs_vehicle_position(float position)
     401             : {
     402           0 :     _use_gcs_snoop = false;
     403             : 
     404           0 :     if (_status == ACCEL_CAL_WAITING_FOR_ORIENTATION && is_equal((float) _step, position)) {
     405           0 :         _start_collect_sample = true;
     406           0 :         return true;
     407             :     }
     408             : 
     409           0 :     return false;
     410             : }
     411             : #endif
     412             : 
     413             : // true if we are in a calibration process
     414      125549 : bool AP_AccelCal::running(void) const
     415             : {
     416      125549 :     return _status == ACCEL_CAL_WAITING_FOR_ORIENTATION || _status == ACCEL_CAL_COLLECTING_SAMPLE;
     417             : }
     418             : #endif //HAL_INS_ACCELCAL_ENABLED

Generated by: LCOV version 1.14