LCOV - code coverage report
Current view: top level - AP_Mount - AP_Mount_MAVLink.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 85 128 66.4 %
Date: 2026-09-09 11:02:08 Functions: 9 12 75.0 %

          Line data    Source code
       1             : #include "AP_Mount_config.h"
       2             : 
       3             : #if HAL_MOUNT_MAVLINK_ENABLED
       4             : 
       5             : #include "AP_Mount_MAVLink.h"
       6             : 
       7             : #include <AP_HAL/AP_HAL.h>
       8             : #include <GCS_MAVLink/GCS.h>
       9             : 
      10             : extern const AP_HAL::HAL& hal;
      11             : 
      12             : #define AP_MOUNT_MAVLINK_SEARCH_MS  60000    // search for gimbal for 1 minute after startup
      13             : #define AP_MOUNT_MAVLINK_ATTITUDE_INTERVAL_US    20000  // send ATTITUDE and AUTOPILOT_STATE_FOR_GIMBAL_DEVICE at 50hz
      14             : 
      15             : // update mount position
      16       13247 : void AP_Mount_MAVLink::update()
      17             : {
      18       13247 :     AP_Mount_Backend::update();
      19             : 
      20             :     // exit immediately if not initialised
      21       13247 :     if (!_initialised) {
      22        1645 :         find_gimbal();
      23        1645 :         return;
      24             :     }
      25             : 
      26       11602 :     update_mnt_target();
      27             : 
      28             :     // send target angles/rates/retract depending on the target type
      29       11602 :     send_target_to_gimbal();
      30             : }
      31             : 
      32             : // return true if healthy
      33         108 : bool AP_Mount_MAVLink::healthy() const
      34             : {
      35             :     // unhealthy until gimbal has been found and replied with device info
      36         108 :     if (_link == nullptr || !_got_device_info) {
      37          16 :         return false;
      38             :     }
      39             : 
      40             :     // unhealthy if attitude information NOT received within the last second
      41          92 :     if (AP_HAL::millis() - _last_attitude_status_ms > 1000) {
      42           0 :         return false;
      43             :     }
      44             : 
      45             :     // check failure flags
      46          92 :     uint32_t critical_failure_flags = GIMBAL_DEVICE_ERROR_FLAGS_ENCODER_ERROR |
      47             :                                       GIMBAL_DEVICE_ERROR_FLAGS_POWER_ERROR |
      48             :                                       GIMBAL_DEVICE_ERROR_FLAGS_MOTOR_ERROR |
      49             :                                       GIMBAL_DEVICE_ERROR_FLAGS_SOFTWARE_ERROR |
      50             :                                       GIMBAL_DEVICE_ERROR_FLAGS_COMMS_ERROR;
      51             : 
      52          92 :     if ((_gimbal_device_attitude_status.failure_flags & critical_failure_flags) > 0) {
      53           0 :         return false;
      54             :     }
      55             : 
      56             :     // if we get this far return mount is healthy
      57          92 :     return true;
      58             : }
      59             : 
      60             : // get attitude as a quaternion.  returns true on success
      61        3989 : bool AP_Mount_MAVLink::get_attitude_quaternion(Quaternion& att_quat)
      62             : {
      63        3989 :     att_quat = _gimbal_device_attitude_status.q;
      64        3989 :     return true;
      65             : }
      66             : 
      67             : // search for gimbal in GCS_MAVLink routing table
      68        1645 : void AP_Mount_MAVLink::find_gimbal()
      69             : {
      70             :     // do not look for gimbal for first 10 seconds so user may see banner
      71        1645 :     uint32_t now_ms = AP_HAL::millis();
      72        1645 :     if (now_ms < 10000) {
      73        1637 :         return;
      74             :     }
      75             : 
      76             :     // search for gimbal for 60 seconds or until armed
      77           8 :     if ((now_ms > AP_MOUNT_MAVLINK_SEARCH_MS) && hal.util->get_soft_armed()) {
      78           0 :         return;
      79             :     }
      80             : 
      81             :     // search for a mavlink enabled gimbal
      82           8 :     if (_link == nullptr) {
      83             :         // we expect that instance 0 has compid = MAV_COMP_ID_GIMBAL, instance 1 has compid = MAV_COMP_ID_GIMBAL2, etc
      84           4 :         uint8_t compid = (_instance == 0) ? MAV_COMP_ID_GIMBAL : MAV_COMP_ID_GIMBAL2 + (_instance - 1);
      85           4 :         _link = GCS_MAVLINK::find_by_mavtype_and_compid(MAV_TYPE_GIMBAL, compid, _sysid);
      86           4 :         if (_link == nullptr) {
      87             :             // have not yet found a gimbal so return
      88           0 :             return;
      89             :         }
      90             : 
      91           4 :         _compid = compid;
      92             :     }
      93             : 
      94             :     // request GIMBAL_DEVICE_INFORMATION
      95           8 :     if (!_got_device_info) {
      96           4 :         if (now_ms - _last_devinfo_req_ms > 1000) {
      97           4 :             _last_devinfo_req_ms = now_ms;
      98           4 :             request_gimbal_device_information();
      99             :         }
     100           4 :         return;
     101             :     }
     102             : 
     103             :     // start sending autopilot attitude to gimbal
     104           4 :     if (start_sending_attitude_to_gimbal()) {
     105           4 :         _initialised = true;
     106             :     }
     107             : }
     108             : 
     109             : // handle GIMBAL_DEVICE_INFORMATION message
     110           8 : void AP_Mount_MAVLink::handle_gimbal_device_information(const mavlink_message_t &msg)
     111             : {
     112             :     // exit immediately if this is not our message
     113           8 :     if (msg.sysid != _sysid || msg.compid != _compid) {
     114           4 :         return;
     115             :     }
     116             : 
     117             :     mavlink_gimbal_device_information_t info;
     118           4 :     mavlink_msg_gimbal_device_information_decode(&msg, &info);
     119             : 
     120             :     // set parameter defaults from gimbal information
     121           4 :     if (!isnan(info.roll_min) && !isnan(info.roll_max)) {
     122           0 :         _params.roll_angle_min.set_default(degrees(info.roll_min));
     123           0 :         _params.roll_angle_max.set_default(degrees(info.roll_max));
     124             :     }
     125           4 :     if (!isnan(info.pitch_min) && !isnan(info.pitch_max)) {
     126           4 :         _params.pitch_angle_min.set_default(degrees(info.pitch_min));
     127           4 :         _params.pitch_angle_max.set_default(degrees(info.pitch_max));
     128             :     }
     129           4 :     if (!isnan(info.yaw_min) && !isnan(info.yaw_max)) {
     130           4 :         _params.yaw_angle_min.set_default(degrees(info.yaw_min));
     131           4 :         _params.yaw_angle_max.set_default(degrees(info.yaw_max));
     132             :     }
     133             : 
     134           4 :     const uint8_t fw_ver_major = info.firmware_version & 0x000000FF;
     135           4 :     const uint8_t fw_ver_minor = (info.firmware_version & 0x0000FF00) >> 8;
     136           4 :     const uint8_t fw_ver_revision = (info.firmware_version & 0x00FF0000) >> 16;
     137           4 :     const uint8_t fw_ver_build = (info.firmware_version & 0xFF000000) >> 24;
     138             : 
     139           4 :     strncpy(vendor_name, info.vendor_name, ARRAY_SIZE(vendor_name));
     140           4 :     strncpy(model_name, info.model_name, ARRAY_SIZE(model_name));
     141             : 
     142             :     // prefer the 32-bit extension, fall back to the 16-bit field:
     143           4 :     device_capapability_flags = (info.cap_flags2 != 0) ? info.cap_flags2 : info.cap_flags;
     144             : 
     145             :     // display gimbal info to user
     146           4 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Mount: %s %s fw:%u.%u.%u.%u",
     147             :             info.vendor_name,
     148             :             info.model_name,
     149             :             (unsigned)fw_ver_major,
     150             :             (unsigned)fw_ver_minor,
     151             :             (unsigned)fw_ver_revision,
     152             :             (unsigned)fw_ver_build);
     153             : 
     154           4 :     _got_device_info = true;
     155             : }
     156             : 
     157             : // handle GIMBAL_DEVICE_ATTITUDE_STATUS message
     158        5400 : void AP_Mount_MAVLink::handle_gimbal_device_attitude_status(const mavlink_message_t &msg)
     159             : {
     160             :     // exit immediately if this is not our message
     161        5400 :     if (msg.sysid != _sysid || msg.compid != _compid) {
     162        3080 :         return;
     163             :     }
     164             : 
     165             :     // take copy of message so it can be forwarded onto GCS later
     166        2320 :     mavlink_msg_gimbal_device_attitude_status_decode(&msg, &_gimbal_device_attitude_status);
     167        2320 :     _last_attitude_status_ms = AP_HAL::millis();
     168             : }
     169             : 
     170             : // request GIMBAL_DEVICE_INFORMATION message
     171           4 : void AP_Mount_MAVLink::request_gimbal_device_information() const
     172             : {
     173           4 :     if (_link == nullptr) {
     174           0 :         return;
     175             :     }
     176             : 
     177           4 :     const mavlink_command_long_t pkt {
     178             :         MAVLINK_MSG_ID_GIMBAL_DEVICE_INFORMATION,  // param1
     179             :         0,  // param2
     180             :         0,  // param3
     181             :         0,  // param4
     182             :         0,  // param5
     183             :         0,  // param6
     184             :         0,  // param7
     185             :         MAV_CMD_REQUEST_MESSAGE,
     186           4 :         _sysid,
     187           4 :         _compid,
     188             :         0  // confirmation
     189           4 :     };
     190             : 
     191           4 :     _link->send_message(MAVLINK_MSG_ID_COMMAND_LONG, (const char*)&pkt);
     192             : }
     193             : 
     194             : // start sending ATTITUDE and AUTOPILOT_STATE_FOR_GIMBAL_DEVICE to gimbal
     195           4 : bool AP_Mount_MAVLink::start_sending_attitude_to_gimbal()
     196             : {
     197             :     // better safe than sorry:
     198           4 :     if (_link == nullptr) {
     199           0 :         return false;
     200             :     }
     201             :     // send AUTOPILOT_STATE_FOR_GIMBAL_DEVICE
     202           4 :     const MAV_RESULT res = _link->set_message_interval(MAVLINK_MSG_ID_AUTOPILOT_STATE_FOR_GIMBAL_DEVICE, AP_MOUNT_MAVLINK_ATTITUDE_INTERVAL_US);
     203             : 
     204             :     // return true on success
     205           4 :     return (res == MAV_RESULT_ACCEPTED);
     206             : }
     207             : 
     208             : // send GIMBAL_DEVICE_SET_ATTITUDE to gimbal to command gimbal to retract (aka relax)
     209           0 : void AP_Mount_MAVLink::send_target_retracted()
     210             : {
     211             :     // update the target angles.  These may be absolutely bogus, of
     212             :     // course, but may be useful in logs to see what the gimbal was
     213             :     // doing.  This is also preserving existing behaviour in a change...
     214           0 :     mnt_target.angle_rad.set(Vector3f{0,0,0}, false);
     215             : 
     216           0 :     const mavlink_gimbal_device_set_attitude_t pkt {
     217             :         {NAN, NAN, NAN, NAN},  // attitude
     218             :         0,   // angular velocity x
     219             :         0,  // angular velocity y
     220             :         0,    // angular velocity z
     221             :         GIMBAL_DEVICE_FLAGS_RETRACT,  // flags
     222           0 :         _sysid,
     223           0 :         _compid
     224           0 :     };
     225             : 
     226           0 :     _link->send_message(MAVLINK_MSG_ID_GIMBAL_DEVICE_SET_ATTITUDE, (const char*)&pkt);
     227           0 : }
     228             : 
     229             : // send GIMBAL_DEVICE_SET_ATTITUDE to gimbal to control rate
     230           0 : void AP_Mount_MAVLink::send_target_rates(const MountRateTarget &rate_rads)
     231             : {
     232           0 :     const float roll_rads = rate_rads.roll;
     233           0 :     const float pitch_rads = rate_rads.pitch;
     234           0 :     const float yaw_rads = rate_rads.yaw;
     235           0 :     const bool earth_frame = rate_rads.yaw_is_ef;
     236             : 
     237             :     // prepare flags
     238           0 :     const uint16_t flags = earth_frame ? (GIMBAL_DEVICE_FLAGS_ROLL_LOCK | GIMBAL_DEVICE_FLAGS_PITCH_LOCK | GIMBAL_DEVICE_FLAGS_YAW_LOCK) : 0;
     239             : 
     240           0 :     const mavlink_gimbal_device_set_attitude_t pkt {
     241             :         {NAN, NAN, NAN, NAN},  // attitude
     242             :         roll_rads,   // angular velocity x
     243             :         pitch_rads,  // angular velocity y
     244             :         yaw_rads,    // angular velocity z
     245             :         flags,
     246           0 :         _sysid,
     247           0 :         _compid
     248           0 :     };
     249             : 
     250           0 :     _link->send_message(MAVLINK_MSG_ID_GIMBAL_DEVICE_SET_ATTITUDE, (const char*)&pkt);
     251           0 : }
     252             : 
     253             : // send GIMBAL_DEVICE_SET_ATTITUDE to gimbal to control attitude
     254       11602 : void AP_Mount_MAVLink::send_target_angles(const MountAngleTarget &angle_rad)
     255             : {
     256       11602 :     const float roll_rad = angle_rad.roll;
     257       11602 :     const float pitch_rad = angle_rad.pitch;
     258       11602 :     const float yaw_rad = angle_rad.yaw;
     259       11602 :     const bool earth_frame = angle_rad.yaw_is_ef;
     260             : 
     261             :     // exit immediately if not initialised
     262       11602 :     if (!_initialised) {
     263           0 :         return;
     264             :     }
     265             : 
     266             :     // prepare flags
     267       11602 :     const uint16_t flags = earth_frame ? (GIMBAL_DEVICE_FLAGS_ROLL_LOCK | GIMBAL_DEVICE_FLAGS_PITCH_LOCK | GIMBAL_DEVICE_FLAGS_YAW_LOCK) : 0;
     268             : 
     269             :     // convert euler angles to quaternion
     270       11602 :     Quaternion q;
     271       11602 :     q.from_euler(roll_rad, pitch_rad, yaw_rad);
     272             : 
     273       11602 :     const mavlink_gimbal_device_set_attitude_t pkt {
     274       11602 :         {q.q1, q.q2, q.q3, q.q4},
     275             :         NAN,  // angular velocity x
     276             :         NAN,  // angular velocity y
     277             :         NAN,  // angular velocity z
     278             :         flags,
     279       11602 :         _sysid,
     280       11602 :         _compid
     281       11602 :     };
     282             : 
     283       11602 :     _link->send_message(MAVLINK_MSG_ID_GIMBAL_DEVICE_SET_ATTITUDE, (const char*)&pkt);
     284             : }
     285             : 
     286             : // Send MAV_CMD_DO_SET_ROI_LOCATION  to gimbal
     287           0 : void AP_Mount_MAVLink::send_target_location(const Location &roi_loc)
     288             : {
     289           0 :     if (_link == nullptr) {
     290           0 :         return;
     291             :     }
     292             : 
     293           0 :     mavlink_command_int_t pkt {};
     294           0 :     pkt.target_system = _sysid;
     295           0 :     pkt.target_component = _compid;
     296             : 
     297           0 :     if (roi_loc.initialised()) {
     298           0 :         pkt.command = MAV_CMD_DO_SET_ROI_LOCATION;
     299           0 :         pkt.x = roi_loc.lat,  // param5 / local: x position in meters * 1e4, global: latitude in degrees * 10^7
     300           0 :         pkt.y = roi_loc.lng,  // param6 / local: y position in meters * 1e4, global: longitude in degrees * 10^7
     301           0 :         pkt.z = roi_loc.alt  * 0.01f;  // param7 / z position: global: altitude in meters (relative or absolute, depending on frame).
     302           0 :         pkt.frame = (uint8_t)roi_loc.get_alt_frame();
     303             :     } else {
     304           0 :         pkt.command = MAV_CMD_DO_SET_ROI_NONE;
     305             :     }
     306             : 
     307           0 :     _link->send_message(MAVLINK_MSG_ID_COMMAND_INT, (const char*)&pkt);
     308             : }
     309             : 
     310             : #endif // HAL_MOUNT_MAVLINK_ENABLED

Generated by: LCOV version 1.14