LCOV - code coverage report
Current view: top level - AP_Compass - AP_Compass_DroneCAN.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 8 82 9.8 %
Date: 2026-09-09 11:02:08 Functions: 2 8 25.0 %

          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             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : 
      16             : #include "AP_Compass_DroneCAN.h"
      17             : 
      18             : #if AP_COMPASS_DRONECAN_ENABLED
      19             : 
      20             : #include <AP_HAL/AP_HAL.h>
      21             : 
      22             : #include <AP_CANManager/AP_CANManager.h>
      23             : #include <AP_DroneCAN/AP_DroneCAN.h>
      24             : #include <AP_BoardConfig/AP_BoardConfig.h>
      25             : #include <AP_Logger/AP_Logger.h>
      26             : #include <SITL/SITL.h>
      27             : 
      28             : AP_Compass_DroneCAN::DetectedModules AP_Compass_DroneCAN::_detected_modules[];
      29             : HAL_Semaphore AP_Compass_DroneCAN::_sem_registry;
      30             : 
      31           0 : AP_Compass_DroneCAN::AP_Compass_DroneCAN(AP_DroneCAN* ap_dronecan, uint32_t devid) :
      32           0 :     _devid(devid)
      33             : {
      34           0 : }
      35             : 
      36           5 : bool AP_Compass_DroneCAN::subscribe_msgs(AP_DroneCAN* ap_dronecan)
      37             : {
      38           5 :     const auto driver_index = ap_dronecan->get_driver_index();
      39             : 
      40           5 :     return (Canard::allocate_sub_arg_callback(ap_dronecan, &handle_magnetic_field, driver_index) != nullptr)
      41           5 :         && (Canard::allocate_sub_arg_callback(ap_dronecan, &handle_magnetic_field_2, driver_index) != nullptr)
      42             : #if AP_COMPASS_DRONECAN_HIRES_ENABLED
      43             :         && (Canard::allocate_sub_arg_callback(ap_dronecan, &handle_magnetic_field_hires, driver_index) != nullptr)
      44             : #endif
      45             :     ;
      46             : }
      47             : 
      48      167383 : AP_Compass_Backend* AP_Compass_DroneCAN::probe(uint8_t index)
      49             : {
      50      167383 :     AP_Compass_DroneCAN* driver = nullptr;
      51      167383 :     if (!_detected_modules[index].driver && _detected_modules[index].ap_dronecan) {
      52           0 :         WITH_SEMAPHORE(_sem_registry);
      53             :         // Register new Compass mode to a backend
      54           0 :         driver = NEW_NOTHROW AP_Compass_DroneCAN(_detected_modules[index].ap_dronecan, _detected_modules[index].devid);
      55           0 :         if (driver) {
      56           0 :             if (!driver->init()) {
      57           0 :                 delete driver;
      58           0 :                 return nullptr;
      59             :             }
      60           0 :             _detected_modules[index].driver = driver;
      61             : #if AP_TEST_DRONECAN_DRIVERS
      62             :             // Scroll through the registered compasses, and set the offsets
      63             :             if (driver->_compass.get_offsets(index).is_zero()) {
      64             :                 driver->_compass.set_offsets(index, AP::sitl()->mag_ofs[index]);
      65             :             }
      66             : 
      67             :             // we want to simulate a calibrated compass by default, so set
      68             :             // scale to 1
      69             :             AP_Param::set_default_by_name("COMPASS_SCALE", 1);
      70             :             AP_Param::set_default_by_name("COMPASS_SCALE2", 1);
      71             :             AP_Param::set_default_by_name("COMPASS_SCALE3", 1);
      72             :             driver->save_dev_id(index);
      73             :             driver->set_rotation(index, ROTATION_NONE);
      74             : 
      75             :             // make first compass external
      76             :             driver->set_external(index, true);
      77             : #endif
      78             :         }
      79           0 :     }
      80      167383 :     return driver;
      81             : }
      82             : 
      83           0 : bool AP_Compass_DroneCAN::init()
      84             : {
      85             :     // Adding 1 is necessary to allow backward compatibility, where this field was set as 1 by default
      86           0 :     if (!register_compass(_devid)) {
      87           0 :         return false;
      88             :     }
      89           0 :     set_external(true);
      90             : 
      91           0 :     return true;
      92             : }
      93             : 
      94           0 : AP_Compass_DroneCAN* AP_Compass_DroneCAN::get_dronecan_backend(AP_DroneCAN* ap_dronecan, uint8_t node_id, uint8_t sensor_id)
      95             : {
      96           0 :     if (ap_dronecan == nullptr) {
      97           0 :         return nullptr;
      98             :     }
      99           0 :     for (uint8_t i=0; i<ARRAY_SIZE(_detected_modules); i++) {
     100           0 :         if (_detected_modules[i].driver &&
     101           0 :             _detected_modules[i].ap_dronecan == ap_dronecan &&
     102           0 :             _detected_modules[i].node_id == node_id &&
     103           0 :             _detected_modules[i].sensor_id == sensor_id) {
     104           0 :             return _detected_modules[i].driver;
     105             :         }
     106             :     }
     107             : 
     108           0 :     bool already_detected = false;
     109             :     // Check if there's an empty spot for possible registration
     110           0 :     for (uint8_t i = 0; i < ARRAY_SIZE(_detected_modules); i++) {
     111           0 :         if (_detected_modules[i].ap_dronecan == ap_dronecan && 
     112           0 :             _detected_modules[i].node_id == node_id &&
     113           0 :             _detected_modules[i].sensor_id == sensor_id) {
     114             :             // Already Detected
     115           0 :             already_detected = true;
     116           0 :             break;
     117             :         }
     118             :     }
     119           0 :     if (!already_detected) {
     120           0 :         for (uint8_t i = 0; i < ARRAY_SIZE(_detected_modules); i++) {
     121           0 :             if (nullptr == _detected_modules[i].ap_dronecan) {
     122           0 :                 _detected_modules[i].ap_dronecan = ap_dronecan;
     123           0 :                 _detected_modules[i].node_id = node_id;
     124           0 :                 _detected_modules[i].sensor_id = sensor_id;
     125           0 :                 _detected_modules[i].devid = AP_HAL::Device::make_bus_id(AP_HAL::Device::BUS_TYPE_UAVCAN,
     126           0 :                                                  ap_dronecan->get_driver_index(),
     127             :                                                  node_id,
     128           0 :                                                  sensor_id + 1); // we use sensor_id as devtype
     129           0 :                 break;
     130             :             }
     131             :         }
     132             :     }
     133             : 
     134             :     struct DetectedModules tempslot;
     135             :     // Sort based on the node_id, larger values first
     136             :     // we do this, so that we have repeatable compass
     137             :     // registration, especially in cases of extraneous
     138             :     // CAN compass is connected.
     139           0 :     for (uint8_t i = 1; i < ARRAY_SIZE(_detected_modules); i++) {
     140           0 :         for (uint8_t j = i; j > 0; j--) {
     141           0 :             if (_detected_modules[j].node_id > _detected_modules[j-1].node_id) {
     142           0 :                 tempslot = _detected_modules[j];
     143           0 :                 _detected_modules[j] = _detected_modules[j-1];
     144           0 :                 _detected_modules[j-1] = tempslot;
     145             :             }
     146             :         }
     147             :     }
     148             : 
     149           0 :     return nullptr;
     150             : }
     151             : 
     152           0 : void AP_Compass_DroneCAN::handle_mag_msg(const Vector3f &mag)
     153             : {
     154           0 :     Vector3f raw_field = mag * 1000.0;
     155             : 
     156           0 :     accumulate_sample(raw_field, _instance);
     157           0 : }
     158             : 
     159           0 : void AP_Compass_DroneCAN::handle_magnetic_field(AP_DroneCAN *ap_dronecan, const CanardRxTransfer& transfer, const uavcan_equipment_ahrs_MagneticFieldStrength& msg)
     160             : {
     161           0 :     WITH_SEMAPHORE(_sem_registry);
     162             : 
     163           0 :     Vector3f mag_vector;
     164           0 :     AP_Compass_DroneCAN* driver = get_dronecan_backend(ap_dronecan, transfer.source_node_id, 0);
     165           0 :     if (driver != nullptr) {
     166           0 :         mag_vector[0] = msg.magnetic_field_ga[0];
     167           0 :         mag_vector[1] = msg.magnetic_field_ga[1];
     168           0 :         mag_vector[2] = msg.magnetic_field_ga[2];
     169           0 :         driver->handle_mag_msg(mag_vector);
     170             :     }
     171           0 : }
     172             : 
     173           0 : void AP_Compass_DroneCAN::handle_magnetic_field_2(AP_DroneCAN *ap_dronecan, const CanardRxTransfer& transfer, const uavcan_equipment_ahrs_MagneticFieldStrength2 &msg)
     174             : {
     175           0 :     WITH_SEMAPHORE(_sem_registry);
     176             : 
     177           0 :     Vector3f mag_vector;
     178           0 :     uint8_t sensor_id = msg.sensor_id;
     179           0 :     AP_Compass_DroneCAN* driver = get_dronecan_backend(ap_dronecan, transfer.source_node_id, sensor_id);
     180           0 :     if (driver != nullptr) {
     181           0 :         mag_vector[0] = msg.magnetic_field_ga[0];
     182           0 :         mag_vector[1] = msg.magnetic_field_ga[1];
     183           0 :         mag_vector[2] = msg.magnetic_field_ga[2];
     184           0 :         driver->handle_mag_msg(mag_vector);
     185             :     }
     186           0 : }
     187             : 
     188             : #if AP_COMPASS_DRONECAN_HIRES_ENABLED
     189             : /*
     190             :   just log hires magnetic field data for magnetic surveying
     191             :  */
     192             : void AP_Compass_DroneCAN::handle_magnetic_field_hires(AP_DroneCAN *ap_dronecan, const CanardRxTransfer& transfer,
     193             :                                                       const dronecan_sensors_magnetometer_MagneticFieldStrengthHiRes &msg)
     194             : {
     195             : // @LoggerMessage: MAGH
     196             : // @Description: Magnetometer high resolution data
     197             : // @Field: TimeUS: Time since system startup
     198             : // @Field: Node: CAN node
     199             : // @Field: Sensor: sensor ID on node
     200             : // @Field: Bus: CAN bus
     201             : // @Field: Mx: X axis field
     202             : // @Field: My: y axis field
     203             : // @Field: Mz: z axis field
     204             : 
     205             : #if HAL_LOGGING_ENABLED
     206             :     // just log it for now
     207             :     AP::logger().WriteStreaming("MAGH", "TimeUS,Node,Sensor,Bus,Mx,My,Mz", "s#-----", "F------", "QBBBfff",
     208             :                                 transfer.timestamp_usec,
     209             :                                 transfer.source_node_id,
     210             :                                 ap_dronecan->get_driver_index(),
     211             :                                 msg.sensor_id,
     212             :                                 msg.magnetic_field_ga[0]*1000,
     213             :                                 msg.magnetic_field_ga[1]*1000,
     214             :                                 msg.magnetic_field_ga[2]*1000);
     215             : #endif  // HAL_LOGGING_ENABLED
     216             : }
     217             : #endif  // AP_COMPASS_DRONECAN_HIRES_ENABLED
     218             : 
     219             : #endif  // AP_COMPASS_DRONECAN_ENABLED

Generated by: LCOV version 1.14