LCOV - code coverage report
Current view: top level - SITL - SIM_SoloGimbal.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 110 121 90.9 %
Date: 2026-09-09 11:02:08 Functions: 4 4 100.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             :   gimbal simulator class for MAVLink gimbal
      17             : */
      18             : 
      19             : #include "SIM_SoloGimbal.h"
      20             : 
      21             : #if AP_SIM_SOLOGIMBAL_ENABLED
      22             : 
      23             : #include <stdio.h>
      24             : 
      25             : #include "SIM_Aircraft.h"
      26             : 
      27             : extern const AP_HAL::HAL& hal;
      28             : 
      29             : #define GIMBAL_DEBUG 0
      30             : 
      31             : #if GIMBAL_DEBUG
      32             : #define debug(fmt, args...)  do { printf("GIMBAL: " fmt, ##args); } while(0)
      33             : #else
      34             : #define debug(fmt, args...)  do { } while(0)
      35             : #endif
      36             : 
      37             : namespace SITL {
      38             : 
      39             : /*
      40             :   update the gimbal state
      41             : */
      42      248542 : void SoloGimbal::update(const Aircraft &aircraft)
      43             : {
      44      248542 :     gimbal.update(aircraft);
      45             : 
      46             :     // see if we should do a report
      47      248542 :     send_report();
      48      248542 : }
      49             : 
      50             : static struct gimbal_param {
      51             :     const char *name;
      52             :     float value;
      53             : } gimbal_params[] = {
      54             :     {"GMB_OFF_ACC_X", 0},
      55             :     {"GMB_OFF_ACC_Y", 0},
      56             :     {"GMB_OFF_ACC_Z", 0},
      57             :     {"GMB_GN_ACC_X", 0},
      58             :     {"GMB_GN_ACC_Y", 0},
      59             :     {"GMB_GN_ACC_Z", 0},
      60             :     {"GMB_OFF_GYRO_X", 0},
      61             :     {"GMB_OFF_GYRO_Y", 0},
      62             :     {"GMB_OFF_GYRO_Z", 0},
      63             :     {"GMB_OFF_JNT_X", 0},
      64             :     {"GMB_OFF_JNT_Y", 0},
      65             :     {"GMB_OFF_JNT_Z", 0},
      66             :     {"GMB_K_RATE", 0},
      67             :     {"GMB_POS_HOLD", 0},
      68             :     {"GMB_MAX_TORQUE", 0},
      69             :     {"GMB_SND_TORQUE", 0},
      70             :     {"GMB_SYSID", 0},
      71             :     {"GMB_FLASH", 0},
      72             : };
      73             : 
      74             : /*
      75             :   find a parameter structure
      76             :  */
      77           8 : struct gimbal_param *SoloGimbal::param_find(const char *name)
      78             : {
      79         120 :     for (uint8_t i=0; i<ARRAY_SIZE(gimbal_params); i++) {
      80         120 :         if (strncmp(name, gimbal_params[i].name, 16) == 0) {
      81           8 :             return &gimbal_params[i];
      82             :         }
      83             :     }
      84           0 :     return nullptr;
      85             : }
      86             :     
      87             : /*
      88             :   send a parameter to flight board
      89             :  */
      90          26 : void SoloGimbal::param_send(const struct gimbal_param *p)
      91             : {
      92             :     mavlink_message_t msg;
      93          26 :     mavlink_param_value_t param_value{};
      94          26 :     strncpy_noterm(param_value.param_id, p->name, sizeof(param_value.param_id));
      95          26 :     param_value.param_value = p->value;
      96          26 :     param_value.param_count = 0;
      97          26 :     param_value.param_index = 0;
      98          26 :     param_value.param_type = MAV_PARAM_TYPE_REAL32;
      99             : 
     100          26 :     uint16_t len = mavlink_msg_param_value_encode_status(vehicle_system_id,
     101          26 :                                                          gimbal_component_id,
     102             :                                                          &mavlink.status,
     103             :                                                          &msg, &param_value);
     104             : 
     105          26 :     uint8_t msgbuf[len];
     106          26 :     len = mavlink_msg_to_send_buffer(msgbuf, &msg);
     107          26 :     if (len > 0) {
     108          26 :         mav_socket.send(msgbuf, len);
     109             :     }
     110          26 : }
     111             : 
     112             :     
     113             : /*
     114             :   send a report to the vehicle control code over MAVLink
     115             : */
     116      248542 : void SoloGimbal::send_report(void)
     117             : {
     118      248542 :     uint32_t now = AP_HAL::millis();
     119      248542 :     if (now < 10000) {
     120             :         // don't send gimbal reports until 10s after startup. This
     121             :         // avoids a windows threading issue with non-blocking sockets
     122             :         // and the initial wait on SERIAL0
     123       12008 :         return;
     124             :     }
     125      236537 :     if (!mavlink.connected) {
     126           1 :         if (target_path != nullptr && mav_socket.connect_unix(target_path)) {
     127           0 :             ::printf("SoloGimbal connected to Unix domain socket %s\n", target_path);
     128           0 :             mavlink.connected = true;
     129           1 :         } else if (target_path == nullptr && mav_socket.connect(target_address, target_port)) {
     130           1 :             ::printf("SoloGimbal connected to %s:%u\n", target_address, (unsigned)target_port);
     131           1 :             mavlink.connected = true;
     132             :         }
     133             :     }
     134      236537 :     if (!mavlink.connected) {
     135           0 :         return;
     136             :     }
     137             : 
     138      236537 :     if (param_send_last_ms && now - param_send_last_ms > 100) {
     139          18 :         param_send(&gimbal_params[param_send_idx]);
     140          18 :         if (++param_send_idx == ARRAY_SIZE(gimbal_params)) {
     141           1 :             printf("Finished sending parameters\n");
     142           1 :             param_send_last_ms = 0;
     143             :         }
     144             :     }
     145             : 
     146             :     // check for incoming MAVLink messages
     147             :     uint8_t buf[100];
     148             :     ssize_t ret;
     149             : 
     150      236749 :     while ((ret=mav_socket.recv(buf, sizeof(buf), 0)) > 0) {
     151        5083 :         for (uint8_t i=0; i<ret; i++) {
     152             :             mavlink_message_t msg;
     153             :             mavlink_status_t status;
     154        9742 :             if (mavlink_frame_char_buffer(&mavlink.rxmsg, &mavlink.status,
     155        4871 :                                           buf[i],
     156        4871 :                                           &msg, &status) == MAVLINK_FRAMING_OK) {
     157         218 :                 switch (msg.msgid) {
     158         201 :                 case MAVLINK_MSG_ID_HEARTBEAT: {
     159             :                     mavlink_heartbeat_t pkt;
     160         201 :                     mavlink_msg_heartbeat_decode(&msg, &pkt);
     161             :                     debug("got HB type=%u autopilot=%u base_mode=0x%x\n", pkt.type, pkt.autopilot, pkt.base_mode);
     162         201 :                     if (!seen_heartbeat) {
     163           1 :                         seen_heartbeat = true;
     164           1 :                         vehicle_component_id = msg.compid;
     165           1 :                         vehicle_system_id = msg.sysid;
     166           1 :                         ::printf("SoloGimbal using srcSystem %u\n", (unsigned)vehicle_system_id);
     167             :                     }
     168         201 :                     break;
     169             :                 }
     170           0 :                 case MAVLINK_MSG_ID_GIMBAL_CONTROL: {
     171             :                     static uint32_t counter;
     172           0 :                     if (counter++ % 100 == 0) {
     173           0 :                         printf("GIMBAL_CONTROL %u\n", counter);
     174             :                     }
     175             :                     mavlink_gimbal_control_t pkt;
     176           0 :                     mavlink_msg_gimbal_control_decode(&msg, &pkt);
     177           0 :                     gimbal.set_demanded_rates(Vector3f(pkt.demanded_rate_x,
     178             :                                                        pkt.demanded_rate_y,
     179             :                                                        pkt.demanded_rate_z));
     180           0 :                     seen_gimbal_control = true;
     181           0 :                     break;
     182             :                 }
     183           8 :                 case MAVLINK_MSG_ID_PARAM_SET: {
     184             :                     mavlink_param_set_t pkt;
     185           8 :                     mavlink_msg_param_set_decode(&msg, &pkt);
     186           8 :                     printf("SoloGimbal got PARAM_SET %.16s %f\n", pkt.param_id, pkt.param_value);
     187             : 
     188           8 :                     struct gimbal_param *p = param_find(pkt.param_id);
     189           8 :                     if (p) {
     190           8 :                         p->value = pkt.param_value;
     191           8 :                         param_send(p);
     192             :                     }
     193             : 
     194           8 :                     break;
     195             :                 }
     196           1 :                 case MAVLINK_MSG_ID_PARAM_REQUEST_LIST: {
     197             :                     mavlink_param_request_list_t pkt;
     198           1 :                     mavlink_msg_param_request_list_decode(&msg, &pkt);
     199           1 :                     if (pkt.target_system == 0 && pkt.target_component == MAV_COMP_ID_GIMBAL) {
     200             :                         // start param send
     201           1 :                         param_send_idx = 0;
     202           1 :                         param_send_last_ms = AP_HAL::millis();
     203             :                     }
     204           1 :                     printf("SoloGimbal sending %u parameters\n", (unsigned)ARRAY_SIZE(gimbal_params));
     205           1 :                     break;
     206             :                 }
     207           8 :                 default:
     208             :                     debug("got unexpected msg %u\n", msg.msgid);
     209           8 :                     break;
     210             :                 }
     211             :             }
     212             :         }
     213             :     }
     214             : 
     215      236537 :     if (!seen_heartbeat) {
     216           3 :         return;
     217             :     }
     218             :     mavlink_message_t msg;
     219             :     uint16_t len;
     220             : 
     221      236534 :     if (now - last_heartbeat_ms >= 1000) {
     222             :         mavlink_heartbeat_t heartbeat;
     223         198 :         heartbeat.type = MAV_TYPE_GIMBAL;
     224         198 :         heartbeat.autopilot = MAV_AUTOPILOT_ARDUPILOTMEGA;
     225         198 :         heartbeat.base_mode = 0;
     226         198 :         heartbeat.system_status = 0;
     227         198 :         heartbeat.mavlink_version = 0;
     228         198 :         heartbeat.custom_mode = 0;
     229             : 
     230         198 :         len = mavlink_msg_heartbeat_encode_status(vehicle_system_id,
     231         198 :                                                   gimbal_component_id,
     232             :                                                   &mavlink.status,
     233             :                                                   &msg, &heartbeat);
     234             : 
     235         198 :         mav_socket.send(&msg.magic, len);
     236         198 :         last_heartbeat_ms = now;
     237             :     }
     238             : 
     239             :     /*
     240             :       send a GIMBAL_REPORT message
     241             :      */
     242      236534 :     uint32_t now_us = AP_HAL::micros();
     243      236534 :     if (now_us - last_report_us > reporting_period_ms*1000UL) {
     244       18195 :         last_report_us = now_us;
     245             : 
     246             :         uint32_t delta_time_us;
     247       18195 :         Vector3f delta_angle;
     248       18195 :         Vector3f delta_velocity;
     249       18195 :         gimbal.get_deltas(delta_angle, delta_velocity, delta_time_us);
     250             : 
     251       18195 :         Vector3f joint_angles;
     252       18195 :         gimbal.get_joint_angles(joint_angles);
     253             : 
     254             :         mavlink_gimbal_report_t gimbal_report;
     255       18195 :         gimbal_report.target_system = vehicle_system_id;
     256       18195 :         gimbal_report.target_component = vehicle_component_id;
     257       18195 :         gimbal_report.delta_time = delta_time_us * 1e-6;
     258       18195 :         gimbal_report.delta_angle_x = delta_angle.x;
     259       18195 :         gimbal_report.delta_angle_y = delta_angle.y;
     260       18195 :         gimbal_report.delta_angle_z = delta_angle.z;
     261       18195 :         gimbal_report.delta_velocity_x = delta_velocity.x;
     262       18195 :         gimbal_report.delta_velocity_y = delta_velocity.y;
     263       18195 :         gimbal_report.delta_velocity_z = delta_velocity.z;
     264       18195 :         gimbal_report.joint_roll = joint_angles.x;
     265       18195 :         gimbal_report.joint_el = joint_angles.y;
     266       18195 :         gimbal_report.joint_az = joint_angles.z;
     267             : 
     268       18195 :         len = mavlink_msg_gimbal_report_encode_status(vehicle_system_id,
     269       18195 :                                                       gimbal_component_id,
     270             :                                                       &mavlink.status,
     271             :                                                       &msg, &gimbal_report);
     272             : 
     273       18195 :         uint8_t msgbuf[len];
     274       18195 :         len = mavlink_msg_to_send_buffer(msgbuf, &msg);
     275       18195 :         if (len > 0) {
     276       18195 :             mav_socket.send(msgbuf, len);
     277             :         }
     278             : 
     279       18195 :         delta_velocity.zero();
     280       18195 :         delta_angle.zero();
     281       18195 :     }
     282             : }
     283             : 
     284             : } // namespace SITL
     285             : 
     286             : #endif  // AP_SIM_SOLOGIMBAL_ENABLED

Generated by: LCOV version 1.14