2023-02-21 02:04:28 -04:00
# include "AP_Mount_Xacti.h"
# if HAL_MOUNT_XACTI_ENABLED
# include <AP_HAL/AP_HAL.h>
# include <AP_AHRS/AP_AHRS.h>
# include <GCS_MAVLink/GCS.h>
# include <AP_BoardConfig/AP_BoardConfig.h>
# include <AP_CANManager/AP_CANManager.h>
# include <AP_DroneCAN/AP_DroneCAN.h>
2023-09-17 21:56:17 -03:00
# include <AP_RTC/AP_RTC.h>
2023-02-21 02:04:28 -04:00
extern const AP_HAL : : HAL & hal ;
# define LOG_TAG "Mount"
2023-08-02 03:34:34 -03:00
# define XACTI_MSG_SEND_MIN_MS 20 // messages should not be sent to camera more often than 20ms
2023-09-28 03:30:47 -03:00
# define XACTI_DIGITAL_ZOOM_RATE_UPDATE_INTERVAL_MS 500 // digital zoom rate control updates 11% up or down every 0.5sec
# define XACTI_OPTICAL_ZOOM_RATE_UPDATE_INTERVAL_MS 250 // optical zoom rate control updates 6.6% up or down every 0.25sec
2023-09-15 08:29:57 -03:00
# define XACTI_STATUS_REQ_INTERVAL_MS 3000 // request status every 3 seconds
2023-09-20 05:11:46 -03:00
# define XACTI_SET_PARAM_QUEUE_SIZE 3 // three set-param requests may be queued
2023-02-21 02:04:28 -04:00
# define AP_MOUNT_XACTI_DEBUG 0
# define debug(fmt, args ...) do { if (AP_MOUNT_XACTI_DEBUG) { GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Xacti: " fmt, ## args); } } while (0)
bool AP_Mount_Xacti : : _subscribed = false ;
AP_Mount_Xacti : : DetectedModules AP_Mount_Xacti : : _detected_modules [ ] ;
HAL_Semaphore AP_Mount_Xacti : : _sem_registry ;
2023-08-02 03:34:34 -03:00
const char * AP_Mount_Xacti : : send_text_prefix = " Xacti: " ;
const char * AP_Mount_Xacti : : sensor_mode_str [ ] = { " RGB " , " IR " , " PIP " , " NDVI " } ;
2023-09-28 03:30:47 -03:00
const char * AP_Mount_Xacti : : _param_names [ ] = { " SingleShot " , " Recording " , " FocusMode " ,
" SensorMode " , " DigitalZoomMagnification " ,
" FirmwareVersion " , " Status " , " DateTime " ,
" OpticalZoomMagnification " } ;
2023-02-21 02:04:28 -04:00
// Constructor
AP_Mount_Xacti : : AP_Mount_Xacti ( class AP_Mount & frontend , class AP_Mount_Params & params , uint8_t instance ) :
AP_Mount_Backend ( frontend , params , instance )
{
register_backend ( ) ;
param_int_cb = FUNCTOR_BIND_MEMBER ( & AP_Mount_Xacti : : handle_param_get_set_response_int , bool , AP_DroneCAN * , const uint8_t , const char * , int32_t & ) ;
2023-09-15 08:29:57 -03:00
param_string_cb = FUNCTOR_BIND_MEMBER ( & AP_Mount_Xacti : : handle_param_get_set_response_string , bool , AP_DroneCAN * , const uint8_t , const char * , AP_DroneCAN : : string & ) ;
2023-02-21 02:04:28 -04:00
param_save_cb = FUNCTOR_BIND_MEMBER ( & AP_Mount_Xacti : : handle_param_save_response , void , AP_DroneCAN * , const uint8_t , bool ) ;
2023-09-20 05:11:46 -03:00
// static assert that Param enum matches parameter names array
static_assert ( ( uint8_t ) AP_Mount_Xacti : : Param : : LAST + 1 = = ARRAY_SIZE ( AP_Mount_Xacti : : _param_names ) , " AP_Mount_Xacti::_param_names array must match Param enum " ) ;
2023-02-21 02:04:28 -04:00
}
// init - performs any required initialisation for this instance
void AP_Mount_Xacti : : init ( )
{
2023-09-20 05:11:46 -03:00
// instantiate parameter queue, return on failure so init fails
_set_param_int32_queue = new ObjectArray < SetParamQueueItem > ( XACTI_SET_PARAM_QUEUE_SIZE ) ;
if ( _set_param_int32_queue = = nullptr ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_ERROR , " %s init failed " , send_text_prefix ) ;
2023-09-20 05:11:46 -03:00
return ;
}
2023-02-21 02:04:28 -04:00
_initialised = true ;
}
// update mount position - should be called periodically
void AP_Mount_Xacti : : update ( )
{
// exit immediately if not initialised
if ( ! _initialised ) {
return ;
}
2023-08-02 03:34:34 -03:00
// return immediately if any message sent is unlikely to be processed
2023-09-16 02:12:58 -03:00
const uint32_t now_ms = AP_HAL : : millis ( ) ;
if ( ! is_safe_to_send ( now_ms ) ) {
2023-08-02 03:34:34 -03:00
return ;
}
2023-09-15 08:29:57 -03:00
// get firmware version
2023-09-16 02:12:58 -03:00
if ( request_firmware_version ( now_ms ) ) {
2023-09-15 08:29:57 -03:00
return ;
}
2023-09-28 03:30:47 -03:00
// additional initial setup
if ( _firmware_version . received ) {
// set date and time
if ( set_datetime ( now_ms ) ) {
return ;
}
// request camera capabilities
if ( request_capabilities ( now_ms ) ) {
return ;
}
2023-09-17 21:56:17 -03:00
}
2023-09-15 08:29:57 -03:00
// request status
2023-09-16 02:12:58 -03:00
if ( request_status ( now_ms ) ) {
2023-09-15 08:29:57 -03:00
return ;
}
2023-09-20 05:11:46 -03:00
// process queue of set parameter items
if ( process_set_param_int32_queue ( ) ) {
return ;
}
2023-02-21 02:04:28 -04:00
// periodically send copter attitude and GPS status
2023-09-16 02:12:58 -03:00
if ( send_copter_att_status ( now_ms ) ) {
2023-08-02 03:34:34 -03:00
// if message sent avoid sending other messages
return ;
}
// update zoom rate control
2023-09-16 02:12:58 -03:00
if ( update_zoom_rate_control ( now_ms ) ) {
2023-08-02 03:34:34 -03:00
// if message sent avoid sending other messages
return ;
}
2023-02-21 02:04:28 -04:00
2023-12-29 03:10:52 -04:00
// change to RC_TARGETING mode if RC input has changed
set_rctargeting_on_rcinput_change ( ) ;
2023-02-21 02:04:28 -04:00
// update based on mount mode
switch ( get_mode ( ) ) {
// move mount to a "retracted" position. To-Do: remove support and replace with a relaxed mode?
case MAV_MOUNT_MODE_RETRACT : {
const Vector3f & angle_bf_target = _params . retract_angles . get ( ) ;
2023-07-21 03:35:42 -03:00
mnt_target . target_type = MountTargetType : : ANGLE ;
mnt_target . angle_rad . set ( angle_bf_target * DEG_TO_RAD , false ) ;
2023-02-21 02:04:28 -04:00
break ;
}
case MAV_MOUNT_MODE_NEUTRAL : {
const Vector3f & angle_bf_target = _params . neutral_angles . get ( ) ;
2023-07-21 03:35:42 -03:00
mnt_target . target_type = MountTargetType : : ANGLE ;
mnt_target . angle_rad . set ( angle_bf_target * DEG_TO_RAD , false ) ;
2023-02-21 02:04:28 -04:00
break ;
}
2023-07-21 03:35:42 -03:00
case MAV_MOUNT_MODE_MAVLINK_TARGETING : {
// mavlink targets are set while handling the incoming message
break ;
}
case MAV_MOUNT_MODE_RC_TARGETING : {
// update targets using pilot's RC inputs
MountTarget rc_target ;
get_rc_target ( mnt_target . target_type , rc_target ) ;
switch ( mnt_target . target_type ) {
2023-02-21 02:04:28 -04:00
case MountTargetType : : ANGLE :
2023-07-21 03:35:42 -03:00
mnt_target . angle_rad = rc_target ;
2023-02-21 02:04:28 -04:00
break ;
case MountTargetType : : RATE :
2023-07-21 03:35:42 -03:00
mnt_target . rate_rads = rc_target ;
2023-02-21 02:04:28 -04:00
break ;
}
break ;
}
// point mount to a GPS point given by the mission planner
2023-07-21 03:35:42 -03:00
case MAV_MOUNT_MODE_GPS_POINT :
if ( get_angle_target_to_roi ( mnt_target . angle_rad ) ) {
mnt_target . target_type = MountTargetType : : ANGLE ;
2023-02-21 02:04:28 -04:00
}
break ;
2023-07-21 03:35:42 -03:00
// point mount to Home location
case MAV_MOUNT_MODE_HOME_LOCATION :
if ( get_angle_target_to_home ( mnt_target . angle_rad ) ) {
mnt_target . target_type = MountTargetType : : ANGLE ;
2023-02-21 02:04:28 -04:00
}
break ;
2023-07-21 03:35:42 -03:00
// point mount to another vehicle
case MAV_MOUNT_MODE_SYSID_TARGET :
if ( get_angle_target_to_sysid ( mnt_target . angle_rad ) ) {
mnt_target . target_type = MountTargetType : : ANGLE ;
2023-02-21 02:04:28 -04:00
}
break ;
default :
// we do not know this mode so raise internal error
INTERNAL_ERROR ( AP_InternalError : : error_t : : flow_of_control ) ;
break ;
}
2023-07-21 03:35:42 -03:00
// send target angles or rates depending on the target type
switch ( mnt_target . target_type ) {
case MountTargetType : : ANGLE :
send_target_angles ( mnt_target . angle_rad . pitch , mnt_target . angle_rad . yaw , mnt_target . angle_rad . yaw_is_ef ) ;
break ;
case MountTargetType : : RATE :
send_target_rates ( mnt_target . rate_rads . pitch , mnt_target . rate_rads . yaw , mnt_target . rate_rads . yaw_is_ef ) ;
break ;
}
2023-02-21 02:04:28 -04:00
}
// return true if healthy
bool AP_Mount_Xacti : : healthy ( ) const
{
2023-09-15 08:29:57 -03:00
// unhealthy until gimbal has been found and replied with firmware version and no motor errors
if ( ! _initialised | | ! _firmware_version . received | | _motor_error ) {
2023-02-21 02:04:28 -04:00
return false ;
}
// unhealthy if attitude information NOT received recently
const uint32_t now_ms = AP_HAL : : millis ( ) ;
if ( now_ms - _last_current_attitude_quat_ms > 1000 ) {
return false ;
}
// if we get this far return healthy
return true ;
}
// take a picture. returns true on success
bool AP_Mount_Xacti : : take_picture ( )
{
2023-09-20 05:11:46 -03:00
// fail if camera errored
if ( _camera_error ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_ERROR , " %s unable to take pic " , send_text_prefix ) ;
2023-02-21 02:04:28 -04:00
return false ;
}
2023-09-20 05:11:46 -03:00
return set_param_int32 ( Param : : SingleShot , 0 ) ;
2023-02-21 02:04:28 -04:00
}
// start or stop video recording. returns true on success
// set start_recording = true to start record, false to stop recording
bool AP_Mount_Xacti : : record_video ( bool start_recording )
{
2023-09-20 05:11:46 -03:00
return set_param_int32 ( Param : : Recording , start_recording ? 1 : 0 ) ;
2023-08-02 03:34:34 -03:00
}
// set zoom specified as a rate or percentage
bool AP_Mount_Xacti : : set_zoom ( ZoomType zoom_type , float zoom_value )
{
// zoom rate
if ( zoom_type = = ZoomType : : RATE ) {
if ( is_zero ( zoom_value ) ) {
// stop zooming
_zoom_rate_control . enabled = false ;
} else {
// zoom in or out
_zoom_rate_control . enabled = true ;
2023-09-28 03:30:47 -03:00
_zoom_rate_control . dir = ( zoom_value < 0 ) ? - 1 : 1 ;
2023-08-02 03:34:34 -03:00
}
return true ;
}
// zoom percentage
if ( zoom_type = = ZoomType : : PCT ) {
2023-09-28 03:30:47 -03:00
if ( capabilities . optical_zoom = = Capability : : True ) {
// optical zoom capable cameras use combination of optical and digital zoom
// convert zoom percentage (0 ~ 100) to zoom times using linear interpolation
// optical zoom covers 1x to 2.5x, param values are in 100 to 250
// digital zoom covers 2.5x to 25x, param values are 100 to 1000
const float zoom_times = linear_interpolate ( 1 , 25 , zoom_value , 0 , 100 ) ;
const uint16_t optical_zoom_param = constrain_uint16 ( uint16_t ( zoom_times * 10 ) * 10 , 100 , 250 ) ;
const uint16_t digital_zoom_param = constrain_uint16 ( uint16_t ( zoom_times * 0.4 ) * 100 , 100 , 1000 ) ;
bool ret = true ;
if ( optical_zoom_param ! = _last_optical_zoom_param_value ) {
ret = set_param_int32 ( Param : : OpticalZoomMagnification , optical_zoom_param ) ;
}
if ( digital_zoom_param ! = _last_digital_zoom_param_value ) {
ret & = set_param_int32 ( Param : : DigitalZoomMagnification , digital_zoom_param ) ;
}
return ret ;
}
// digital only zoom
2023-08-02 03:34:34 -03:00
// convert zoom percentage (0 ~ 100) to zoom parameter value (100, 200, 300, ... 1000)
2023-09-29 06:07:01 -03:00
// 0~11pct:100, 12~22pct:200, 23~33pct:300, 34~44pct:400, 45~55pct:500, 56~66pct:600, 67~77pct:700, 78~88pct:800, 89~99pct:900, 100:1000
const uint16_t zoom_param_value = uint16_t ( linear_interpolate ( 1 , 10 , zoom_value , 0 , 100 ) ) * 100 ;
2023-09-20 05:11:46 -03:00
return set_param_int32 ( Param : : DigitalZoomMagnification , zoom_param_value ) ;
2023-02-21 02:04:28 -04:00
}
2023-08-02 03:34:34 -03:00
// unsupported zoom type
return false ;
2023-02-21 02:04:28 -04:00
}
// set focus specified as rate, percentage or auto
// focus in = -1, focus hold = 0, focus out = 1
2023-06-21 03:03:39 -03:00
SetFocusResult AP_Mount_Xacti : : set_focus ( FocusType focus_type , float focus_value )
2023-02-21 02:04:28 -04:00
{
// convert focus type and value to parameter value
uint8_t focus_param_value ;
switch ( focus_type ) {
case FocusType : : RATE :
case FocusType : : PCT :
// focus rate and percentage control not supported so simply switch to manual focus
// FocusMode of 0:Manual Focus
focus_param_value = 0 ;
break ;
case FocusType : : AUTO :
// FocusMode of 1:Single AutoFocus, 2:Continuous AutoFocus
focus_param_value = 2 ;
break ;
default :
// unsupported forucs mode
2023-06-21 03:03:39 -03:00
return SetFocusResult : : INVALID_PARAMETERS ;
2023-02-21 02:04:28 -04:00
}
// set FocusMode parameter
2023-09-20 05:11:46 -03:00
return set_param_int32 ( Param : : FocusMode , focus_param_value ) ? SetFocusResult : : ACCEPTED : SetFocusResult : : FAILED ;
2023-08-02 03:34:34 -03:00
}
// set camera lens as a value from 0 to 5
bool AP_Mount_Xacti : : set_lens ( uint8_t lens )
{
// sanity check
if ( lens > ( uint8_t ) SensorsMode : : NDVI ) {
return false ;
2023-06-21 03:03:39 -03:00
}
2023-08-02 03:34:34 -03:00
2023-09-20 05:11:46 -03:00
return set_param_int32 ( Param : : SensorMode , lens ) ;
2023-02-21 02:04:28 -04:00
}
// send camera information message to GCS
void AP_Mount_Xacti : : send_camera_information ( mavlink_channel_t chan ) const
{
// exit immediately if not initialised
if ( ! _initialised ) {
return ;
}
static const uint8_t vendor_name [ 32 ] = " Xacti " ;
static uint8_t model_name [ 32 ] = " CX-GB100 " ;
const char cam_definition_uri [ 140 ] { } ;
const float NaN = nanf ( " 0x4152 " ) ;
// capability flags
const uint32_t flags = CAMERA_CAP_FLAGS_CAPTURE_VIDEO |
CAMERA_CAP_FLAGS_CAPTURE_IMAGE |
CAMERA_CAP_FLAGS_HAS_BASIC_FOCUS ;
// send CAMERA_INFORMATION message
mavlink_msg_camera_information_send (
chan ,
AP_HAL : : millis ( ) , // time_boot_ms
vendor_name , // vendor_name uint8_t[32]
model_name , // model_name uint8_t[32]
2023-09-15 08:29:57 -03:00
_firmware_version . received ? _firmware_version . mav_ver : 0 , // firmware version uint32_t
2023-02-21 02:04:28 -04:00
NaN , // focal_length float (mm)
NaN , // sensor_size_h float (mm)
NaN , // sensor_size_v float (mm)
0 , // resolution_h uint16_t (pix)
0 , // resolution_v uint16_t (pix)
0 , // lens_id uint8_t
flags , // flags uint32_t (CAMERA_CAP_FLAGS)
0 , // cam_definition_version uint16_t
2023-07-19 10:50:21 -03:00
cam_definition_uri , // cam_definition_uri char[140]
_instance + 1 ) ; // gimbal_device_id uint8_t
2023-02-21 02:04:28 -04:00
}
// send camera settings message to GCS
void AP_Mount_Xacti : : send_camera_settings ( mavlink_channel_t chan ) const
{
const float NaN = nanf ( " 0x4152 " ) ;
// send CAMERA_SETTINGS message
mavlink_msg_camera_settings_send (
chan ,
AP_HAL : : millis ( ) , // time_boot_ms
_recording_video ? CAMERA_MODE_VIDEO : CAMERA_MODE_IMAGE , // camera mode (0:image, 1:video, 2:image survey)
0 , // zoomLevel float, percentage from 0 to 100, NaN if unknown
NaN ) ; // focusLevel float, percentage from 0 to 100, NaN if unknown
}
// get attitude as a quaternion. returns true on success
bool AP_Mount_Xacti : : get_attitude_quaternion ( Quaternion & att_quat )
{
att_quat = _current_attitude_quat ;
return true ;
}
// send target pitch and yaw rates to gimbal
// yaw_is_ef should be true if yaw_rads target is an earth frame rate, false if body_frame
void AP_Mount_Xacti : : send_target_rates ( float pitch_rads , float yaw_rads , bool yaw_is_ef )
{
// send gimbal rate target to gimbal
send_gimbal_control ( 3 , degrees ( pitch_rads ) * 100 , degrees ( yaw_rads ) * 100 ) ;
}
// send target pitch and yaw angles to gimbal
// yaw_is_ef should be true if yaw_rad target is an earth frame angle, false if body_frame
void AP_Mount_Xacti : : send_target_angles ( float pitch_rad , float yaw_rad , bool yaw_is_ef )
{
// convert yaw to body frame
2024-01-12 08:40:23 -04:00
const float yaw_bf_rad = yaw_is_ef ? wrap_PI ( yaw_rad - AP : : ahrs ( ) . get_yaw ( ) ) : yaw_rad ;
2023-02-21 02:04:28 -04:00
// send angle target to gimbal
send_gimbal_control ( 2 , degrees ( pitch_rad ) * 100 , degrees ( yaw_bf_rad ) * 100 ) ;
}
// subscribe to Xacti DroneCAN messages
void AP_Mount_Xacti : : subscribe_msgs ( AP_DroneCAN * ap_dronecan )
{
// return immediately if DroneCAN is unavailable
if ( ap_dronecan = = nullptr ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_CRITICAL , " %s DroneCAN subscribe failed " , send_text_prefix ) ;
2023-02-21 02:04:28 -04:00
return ;
}
_subscribed = true ;
if ( Canard : : allocate_sub_arg_callback ( ap_dronecan , & handle_gimbal_attitude_status , ap_dronecan - > get_driver_index ( ) ) = = nullptr ) {
AP_BoardConfig : : allocation_error ( " gimbal_attitude_status_sub " ) ;
_subscribed = false ;
}
if ( Canard : : allocate_sub_arg_callback ( ap_dronecan , & handle_gnss_status_req , ap_dronecan - > get_driver_index ( ) ) = = nullptr ) {
AP_BoardConfig : : allocation_error ( " gnss_status_req_sub " ) ;
_subscribed = false ;
}
}
// register backend in detected modules array used to map DroneCAN port and node id to backend
void AP_Mount_Xacti : : register_backend ( )
{
WITH_SEMAPHORE ( _sem_registry ) ;
// add this backend to _detected_modules array
_detected_modules [ _instance ] . driver = this ;
// return if devid is zero meaning this backend has not yet been associated with a mount
const uint32_t devid = ( uint32_t ) _params . dev_id . get ( ) ;
if ( devid = = 0 ) {
return ;
}
// get DroneCan port from device id
const uint8_t can_driver_index = AP_HAL : : Device : : devid_get_bus ( devid ) ;
const uint8_t can_num_drivers = AP : : can ( ) . get_num_drivers ( ) ;
for ( uint8_t i = 0 ; i < can_num_drivers ; i + + ) {
AP_DroneCAN * ap_dronecan = AP_DroneCAN : : get_dronecan ( i ) ;
if ( ap_dronecan ! = nullptr & & ap_dronecan - > get_driver_index ( ) = = can_driver_index ) {
_detected_modules [ _instance ] . ap_dronecan = ap_dronecan ;
}
}
// get node_id from device id
_detected_modules [ _instance ] . node_id = AP_HAL : : Device : : devid_get_address ( devid ) ;
}
// find backend associated with the given dronecan port and node_id. also associates backends with zero node ids
// returns pointer to backend on success, nullptr on failure
AP_Mount_Xacti * AP_Mount_Xacti : : get_dronecan_backend ( AP_DroneCAN * ap_dronecan , uint8_t node_id )
{
WITH_SEMAPHORE ( _sem_registry ) ;
// exit immediately if DroneCAN is unavailable or invalid node id
if ( ap_dronecan = = nullptr | | node_id = = 0 ) {
return nullptr ;
}
// search for backend with matching dronecan port and node id
for ( uint8_t i = 0 ; i < ARRAY_SIZE ( _detected_modules ) ; i + + ) {
if ( _detected_modules [ i ] . driver ! = nullptr & &
_detected_modules [ i ] . ap_dronecan = = ap_dronecan & &
_detected_modules [ i ] . node_id = = node_id ) {
return _detected_modules [ i ] . driver ;
}
}
// if we got this far, this dronecan port and node id are not associated with any backend
// associate with first backend with node id of zero
for ( uint8_t i = 0 ; i < ARRAY_SIZE ( _detected_modules ) ; i + + ) {
if ( _detected_modules [ i ] . driver ! = nullptr & &
_detected_modules [ i ] . node_id = = 0 ) {
_detected_modules [ i ] . ap_dronecan = ap_dronecan ;
_detected_modules [ i ] . node_id = node_id ;
const auto dev_id = AP_HAL : : Device : : make_bus_id ( AP_HAL : : Device : : BUS_TYPE_UAVCAN ,
ap_dronecan - > get_driver_index ( ) ,
node_id , 0 ) ;
_detected_modules [ i ] . driver - > set_dev_id ( dev_id ) ;
return _detected_modules [ i ] . driver ;
}
}
return nullptr ;
}
// handle xacti gimbal attitude status message
void AP_Mount_Xacti : : handle_gimbal_attitude_status ( AP_DroneCAN * ap_dronecan , const CanardRxTransfer & transfer , const com_xacti_GimbalAttitudeStatus & msg )
{
// fetch the matching backend driver, node id and gimbal id backend instance
AP_Mount_Xacti * driver = get_dronecan_backend ( ap_dronecan , transfer . source_node_id ) ;
if ( driver = = nullptr ) {
return ;
}
// convert body-frame Euler angles to Quaternion. Note yaw direction is reversed from normal
driver - > _current_attitude_quat . from_euler ( radians ( msg . gimbal_roll * 0.01 ) , radians ( msg . gimbal_pitch * 0.01 ) , radians ( - msg . gimbal_yaw * 0.01 ) ) ;
driver - > _last_current_attitude_quat_ms = AP_HAL : : millis ( ) ;
}
// handle xacti gnss status request message
void AP_Mount_Xacti : : handle_gnss_status_req ( AP_DroneCAN * ap_dronecan , const CanardRxTransfer & transfer , const com_xacti_GnssStatusReq & msg )
{
// sanity check dronecan port
if ( ap_dronecan = = nullptr ) {
return ;
}
// get current location
2023-07-11 07:56:10 -03:00
uint8_t gps_status = 2 ;
2023-02-21 02:04:28 -04:00
Location loc ;
if ( ! AP : : ahrs ( ) . get_location ( loc ) ) {
gps_status = 0 ;
}
// get date and time
2023-07-12 09:11:21 -03:00
uint16_t year , ms ;
2023-02-21 02:04:28 -04:00
uint8_t month , day , hour , min , sec ;
2023-10-05 04:40:34 -03:00
# if AP_RTC_ENABLED
2023-07-12 09:11:21 -03:00
if ( ! AP : : rtc ( ) . get_date_and_time_utc ( year , month , day , hour , min , sec , ms ) ) {
2023-02-21 02:04:28 -04:00
year = month = day = hour = min = sec = 0 ;
}
2023-10-05 04:40:34 -03:00
# else
year = month = day = hour = min = sec = 0 ;
( void ) ms ;
# endif
2023-02-21 02:04:28 -04:00
// send xacti specific gnss status message
com_xacti_GnssStatus xacti_gnss_status_msg { } ;
xacti_gnss_status_msg . gps_status = gps_status ;
xacti_gnss_status_msg . order = msg . requirement ;
xacti_gnss_status_msg . remain_buffer = 1 ;
xacti_gnss_status_msg . utc_year = year ;
2023-07-11 07:56:10 -03:00
xacti_gnss_status_msg . utc_month = month + 1 ;
2023-02-21 02:04:28 -04:00
xacti_gnss_status_msg . utc_day = day ;
xacti_gnss_status_msg . utc_hour = hour ;
xacti_gnss_status_msg . utc_minute = min ;
xacti_gnss_status_msg . utc_seconds = sec ;
xacti_gnss_status_msg . latitude = loc . lat * 1e-7 ;
xacti_gnss_status_msg . longitude = loc . lng * 1e-7 ;
xacti_gnss_status_msg . altitude = loc . alt * 1e-2 ;
ap_dronecan - > xacti_gnss_status . broadcast ( xacti_gnss_status_msg ) ;
}
// handle param get/set response
bool AP_Mount_Xacti : : handle_param_get_set_response_int ( AP_DroneCAN * ap_dronecan , uint8_t node_id , const char * name , int32_t & value )
{
2023-09-28 03:30:29 -03:00
// error string prefix to save on flash
2023-02-21 02:04:28 -04:00
const char * err_prefix_str = " Xacti: failed to " ;
2023-09-28 03:30:29 -03:00
// take picture
2023-09-20 05:11:46 -03:00
if ( strcmp ( name , get_param_name_str ( Param : : SingleShot ) ) = = 0 ) {
2023-02-21 02:04:28 -04:00
if ( value < 0 ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_ERROR , " %s take pic " , err_prefix_str ) ;
2023-02-21 02:04:28 -04:00
}
return false ;
}
2023-09-28 03:30:29 -03:00
// recording
2023-09-20 05:11:46 -03:00
if ( strcmp ( name , get_param_name_str ( Param : : Recording ) ) = = 0 ) {
2023-02-21 02:04:28 -04:00
if ( value < 0 ) {
_recording_video = false ;
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_ERROR , " %s record " , err_prefix_str ) ;
2023-02-21 02:04:28 -04:00
} else {
_recording_video = ( value = = 1 ) ;
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s recording %s " , send_text_prefix , _recording_video ? " ON " : " OFF " ) ;
2023-02-21 02:04:28 -04:00
}
return false ;
}
2023-09-28 03:30:29 -03:00
// focus
2023-09-20 05:11:46 -03:00
if ( strcmp ( name , get_param_name_str ( Param : : FocusMode ) ) = = 0 ) {
2023-02-21 02:04:28 -04:00
if ( value < 0 ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_ERROR , " %s change focus " , err_prefix_str ) ;
2023-02-21 02:04:28 -04:00
} else {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s %s focus " , send_text_prefix , value = = 0 ? " manual " : " auto " ) ;
2023-02-21 02:04:28 -04:00
}
return false ;
}
2023-09-28 03:30:29 -03:00
// camera lens (aka sensor mode)
2023-09-20 05:11:46 -03:00
if ( strcmp ( name , get_param_name_str ( Param : : SensorMode ) ) = = 0 ) {
2023-08-02 03:34:34 -03:00
if ( value < 0 ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_ERROR , " %s change lens " , err_prefix_str ) ;
2023-08-02 03:34:34 -03:00
} else if ( ( uint32_t ) value < ARRAY_SIZE ( sensor_mode_str ) ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s %s " , send_text_prefix , sensor_mode_str [ ( uint8_t ) value ] ) ;
2023-08-02 03:34:34 -03:00
}
return false ;
}
2023-09-28 03:30:29 -03:00
// digital zoom
2023-09-20 05:11:46 -03:00
if ( strcmp ( name , get_param_name_str ( Param : : DigitalZoomMagnification ) ) = = 0 ) {
2023-08-02 03:34:34 -03:00
if ( value < 0 ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_ERROR , " %s change zoom " , err_prefix_str ) ;
2023-08-02 03:34:34 -03:00
// disable zoom rate control (if active) to avoid repeated failures
_zoom_rate_control . enabled = false ;
} else if ( value > = 100 & & value < = 1000 ) {
2023-09-28 03:30:47 -03:00
_last_digital_zoom_param_value = value ;
}
return false ;
}
// optical zoom
if ( strcmp ( name , get_param_name_str ( Param : : OpticalZoomMagnification ) ) = = 0 ) {
if ( value < 0 ) {
GCS_SEND_TEXT ( MAV_SEVERITY_ERROR , " %s change optical zoom " , err_prefix_str ) ;
// disable zoom rate control (if active) to avoid repeated failures
_zoom_rate_control . enabled = false ;
} else if ( value > = 100 & & value < = 250 ) {
capabilities . optical_zoom = Capability : : True ;
capabilities . received = true ;
_last_optical_zoom_param_value = value ;
2023-08-02 03:34:34 -03:00
}
return false ;
}
2023-09-28 03:30:47 -03:00
2023-02-21 02:04:28 -04:00
// unhandled parameter get or set
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s get/set %s res:%ld " , send_text_prefix , name , ( long int ) value ) ;
2023-02-21 02:04:28 -04:00
return false ;
}
2023-09-15 08:29:57 -03:00
// handle param get/set response
bool AP_Mount_Xacti : : handle_param_get_set_response_string ( AP_DroneCAN * ap_dronecan , uint8_t node_id , const char * name , AP_DroneCAN : : string & value )
{
2023-09-20 05:11:46 -03:00
if ( strcmp ( name , get_param_name_str ( Param : : FirmwareVersion ) ) = = 0 ) {
2023-09-15 08:29:57 -03:00
_firmware_version . received = true ;
const uint8_t len = MIN ( value . len , ARRAY_SIZE ( _firmware_version . str ) - 1 ) ;
memcpy ( _firmware_version . str , ( const char * ) value . data , len ) ;
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " Mount: Xacti fw:%s " , _firmware_version . str ) ;
2023-09-15 08:29:57 -03:00
// firmware str from gimbal is of the format YYMMDD[b]xx. Convert to uint32 for reporting to GCS
if ( len > = 9 ) {
const char major_str [ 3 ] = { _firmware_version . str [ 0 ] , _firmware_version . str [ 1 ] , 0 } ;
const char minor_str [ 3 ] = { _firmware_version . str [ 2 ] , _firmware_version . str [ 3 ] , 0 } ;
const char patch_str [ 3 ] = { _firmware_version . str [ 4 ] , _firmware_version . str [ 5 ] , 0 } ;
const char dev_str [ 3 ] = { _firmware_version . str [ 7 ] , _firmware_version . str [ 8 ] , 0 } ;
const uint8_t major_ver_num = atoi ( major_str ) & 0xFF ;
const uint8_t minor_ver_num = atoi ( minor_str ) & 0xFF ;
const uint8_t patch_ver_num = atoi ( patch_str ) & 0xFF ;
const uint8_t dev_ver_num = atoi ( dev_str ) & 0xFF ;
_firmware_version . mav_ver = UINT32_VALUE ( dev_ver_num , patch_ver_num , minor_ver_num , major_ver_num ) ;
}
return false ;
2023-09-20 05:11:46 -03:00
} else if ( strcmp ( name , get_param_name_str ( Param : : DateTime ) ) = = 0 ) {
2023-09-17 21:56:17 -03:00
// display when time and date have been set
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s datetime set %s " , send_text_prefix , ( const char * ) value . data ) ;
2023-09-17 21:56:17 -03:00
return false ;
2023-09-20 05:11:46 -03:00
} else if ( strcmp ( name , get_param_name_str ( Param : : Status ) ) = = 0 ) {
2023-09-15 08:29:57 -03:00
// check for expected length
const char * error_str = " error " ;
if ( value . len ! = sizeof ( _status ) ) {
2023-09-17 21:56:17 -03:00
INTERNAL_ERROR ( AP_InternalError : : error_t : : invalid_arg_or_result ) ;
2023-09-15 08:29:57 -03:00
return false ;
}
// backup error status and copy to structure
const uint32_t last_error_status = _status . error_status ;
memcpy ( & _status , value . data , value . len ) ;
// report change in status
uint32_t changed_bits = last_error_status ^ _status . error_status ;
const char * ok_str = " OK " ;
if ( changed_bits & ( uint32_t ) ErrorStatus : : TIME_NOT_SET ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s time %sset " , send_text_prefix , _status . error_status & ( uint32_t ) ErrorStatus : : TIME_NOT_SET ? " not " : " " ) ;
2023-09-20 05:11:46 -03:00
if ( _status . error_status & ( uint32_t ) ErrorStatus : : TIME_NOT_SET ) {
// try to set time again
_datetime . set = false ;
}
2023-09-15 08:29:57 -03:00
}
if ( changed_bits & ( uint32_t ) ErrorStatus : : MEDIA_ERROR ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s media %s " , send_text_prefix , _status . error_status & ( uint32_t ) ErrorStatus : : MEDIA_ERROR ? error_str : ok_str ) ;
2023-09-15 08:29:57 -03:00
}
if ( changed_bits & ( uint32_t ) ErrorStatus : : LENS_ERROR ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s lens %s " , send_text_prefix , _status . error_status & ( uint32_t ) ErrorStatus : : LENS_ERROR ? error_str : ok_str ) ;
2023-09-15 08:29:57 -03:00
}
if ( changed_bits & ( uint32_t ) ErrorStatus : : MOTOR_INIT_ERROR ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s motor %s " , send_text_prefix , _status . error_status & ( uint32_t ) ErrorStatus : : MOTOR_INIT_ERROR ? " init error " : ok_str ) ;
2023-09-15 08:29:57 -03:00
}
if ( changed_bits & ( uint32_t ) ErrorStatus : : MOTOR_OPERATION_ERROR ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s motor op %s " , send_text_prefix , _status . error_status & ( uint32_t ) ErrorStatus : : MOTOR_OPERATION_ERROR ? error_str : ok_str ) ;
2023-09-15 08:29:57 -03:00
}
if ( changed_bits & ( uint32_t ) ErrorStatus : : GIMBAL_CONTROL_ERROR ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s control %s " , send_text_prefix , _status . error_status & ( uint32_t ) ErrorStatus : : GIMBAL_CONTROL_ERROR ? error_str : ok_str ) ;
2023-09-15 08:29:57 -03:00
}
if ( changed_bits & ( uint32_t ) ErrorStatus : : TEMP_WARNING ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s temp %s " , send_text_prefix , _status . error_status & ( uint32_t ) ErrorStatus : : TEMP_WARNING ? " warning " : ok_str ) ;
2023-09-15 08:29:57 -03:00
}
// set motor error for health reporting
_motor_error = _status . error_status & ( ( uint32_t ) ErrorStatus : : MOTOR_INIT_ERROR | ( uint32_t ) ErrorStatus : : MOTOR_OPERATION_ERROR | ( uint32_t ) ErrorStatus : : GIMBAL_CONTROL_ERROR ) ;
2023-09-20 05:11:46 -03:00
_camera_error = _status . error_status & ( ( uint32_t ) ErrorStatus : : LENS_ERROR | ( uint32_t ) ErrorStatus : : MEDIA_ERROR ) ;
2023-09-15 08:29:57 -03:00
return false ;
}
// unhandled parameter get or set
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " %s get/set string %s res:%s " , send_text_prefix , name , ( const char * ) value . data ) ;
2023-09-15 08:29:57 -03:00
return false ;
}
2023-02-21 02:04:28 -04:00
void AP_Mount_Xacti : : handle_param_save_response ( AP_DroneCAN * ap_dronecan , const uint8_t node_id , bool success )
{
// display failure to save parameter
if ( ! success ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_ERROR , " %s CAM%u failed to set param " , send_text_prefix , ( int ) _instance + 1 ) ;
2023-02-21 02:04:28 -04:00
}
}
2023-09-20 05:11:46 -03:00
// get parameter name for a particular param enum value
2023-10-02 08:10:16 -03:00
// returns an empty string if not found (which should never happen)
2023-09-20 05:11:46 -03:00
const char * AP_Mount_Xacti : : get_param_name_str ( Param param ) const
{
// check to avoid reading beyond end of array. This should never happen
if ( ( uint8_t ) param > ARRAY_SIZE ( _param_names ) ) {
INTERNAL_ERROR ( AP_InternalError : : error_t : : invalid_arg_or_result ) ;
2023-10-02 08:10:16 -03:00
return " " ;
2023-09-20 05:11:46 -03:00
}
return _param_names [ ( uint8_t ) param ] ;
}
2023-08-02 03:34:34 -03:00
// helper function to set integer parameters
2023-09-20 05:11:46 -03:00
bool AP_Mount_Xacti : : set_param_int32 ( Param param , int32_t param_value )
{
if ( _set_param_int32_queue = = nullptr ) {
return false ;
}
// set param request added to queue to be sent. throttling requests improves reliability
return _set_param_int32_queue - > push ( SetParamQueueItem { param , param_value } ) ;
}
// helper function to set string parameters
bool AP_Mount_Xacti : : set_param_string ( Param param , const AP_DroneCAN : : string & param_value )
2023-08-02 03:34:34 -03:00
{
if ( _detected_modules [ _instance ] . ap_dronecan = = nullptr ) {
return false ;
}
2023-09-20 05:11:46 -03:00
// convert param to string
const char * param_name_str = get_param_name_str ( param ) ;
if ( param_name_str = = nullptr ) {
return false ;
}
if ( _detected_modules [ _instance ] . ap_dronecan - > set_parameter_on_node ( _detected_modules [ _instance ] . node_id , param_name_str , param_value , & param_string_cb ) ) {
2023-09-15 08:29:57 -03:00
last_send_getset_param_ms = AP_HAL : : millis ( ) ;
return true ;
}
return false ;
}
2023-09-20 05:11:46 -03:00
// helper function to get string parameters
bool AP_Mount_Xacti : : get_param_string ( Param param )
2023-09-17 21:56:17 -03:00
{
if ( _detected_modules [ _instance ] . ap_dronecan = = nullptr ) {
return false ;
}
2023-09-20 05:11:46 -03:00
// convert param to string
const char * param_name_str = get_param_name_str ( param ) ;
if ( _detected_modules [ _instance ] . ap_dronecan - > get_parameter_on_node ( _detected_modules [ _instance ] . node_id , param_name_str , & param_string_cb ) ) {
2023-09-17 21:56:17 -03:00
last_send_getset_param_ms = AP_HAL : : millis ( ) ;
return true ;
}
return false ;
}
2023-09-20 05:11:46 -03:00
// process queue of set parameter items
bool AP_Mount_Xacti : : process_set_param_int32_queue ( )
2023-09-15 08:29:57 -03:00
{
2023-09-20 05:11:46 -03:00
if ( ( _set_param_int32_queue = = nullptr ) | | ( _detected_modules [ _instance ] . ap_dronecan = = nullptr ) ) {
2023-09-15 08:29:57 -03:00
return false ;
}
2023-09-20 05:11:46 -03:00
SetParamQueueItem param_to_set ;
if ( _set_param_int32_queue - > pop ( param_to_set ) ) {
// convert param to string
const char * param_name_str = get_param_name_str ( param_to_set . param ) ;
if ( _detected_modules [ _instance ] . ap_dronecan - > set_parameter_on_node ( _detected_modules [ _instance ] . node_id , param_name_str , param_to_set . value , & param_int_cb ) ) {
last_send_getset_param_ms = AP_HAL : : millis ( ) ;
return true ;
}
return false ;
2023-08-02 03:34:34 -03:00
}
return false ;
}
2023-02-21 02:04:28 -04:00
// send gimbal control message via DroneCAN
// mode is 2:angle control or 3:rate control
// pitch_cd is pitch angle in centi-degrees or pitch rate in cds
// yaw_cd is angle in centi-degrees or yaw rate in cds
void AP_Mount_Xacti : : send_gimbal_control ( uint8_t mode , int16_t pitch_cd , int16_t yaw_cd )
{
// exit immediately if no DroneCAN port
if ( _detected_modules [ _instance ] . ap_dronecan = = nullptr ) {
return ;
}
// send at no faster than 5hz
2023-08-21 22:41:17 -03:00
const uint32_t now_ms = AP_HAL : : millis ( ) ;
2023-02-21 02:04:28 -04:00
if ( now_ms - last_send_gimbal_control_ms < 200 ) {
return ;
}
last_send_gimbal_control_ms = now_ms ;
// send xacti specific gimbal control message
com_xacti_GimbalControlData gimbal_control_data_msg { } ;
gimbal_control_data_msg . pitch_cmd_type = mode ;
gimbal_control_data_msg . yaw_cmd_type = mode ;
gimbal_control_data_msg . pitch_cmd_value = pitch_cd ;
gimbal_control_data_msg . yaw_cmd_value = - yaw_cd ;
_detected_modules [ _instance ] . ap_dronecan - > xacti_gimbal_control_data . broadcast ( gimbal_control_data_msg ) ;
}
2023-09-16 02:12:58 -03:00
// send copter attitude status message to gimbal. now_ms is current system time
2023-08-02 03:34:34 -03:00
// returns true if sent so that we avoid immediately trying to also send other messages
2023-09-16 02:12:58 -03:00
bool AP_Mount_Xacti : : send_copter_att_status ( uint32_t now_ms )
2023-02-21 02:04:28 -04:00
{
// exit immediately if no DroneCAN port
if ( _detected_modules [ _instance ] . ap_dronecan = = nullptr ) {
2023-08-02 03:34:34 -03:00
return false ;
2023-02-21 02:04:28 -04:00
}
// send at no faster than 5hz
if ( now_ms - last_send_copter_att_status_ms < 100 ) {
2023-08-02 03:34:34 -03:00
return false ;
2023-02-21 02:04:28 -04:00
}
// send xacti specific vehicle attitude message
Quaternion veh_att ;
if ( ! AP : : ahrs ( ) . get_quaternion ( veh_att ) ) {
2023-08-02 03:34:34 -03:00
return false ;
2023-02-21 02:04:28 -04:00
}
last_send_copter_att_status_ms = now_ms ;
com_xacti_CopterAttStatus copter_att_status_msg { } ;
copter_att_status_msg . quaternion_wxyz_e4 [ 0 ] = veh_att . q1 * 1e4 ;
copter_att_status_msg . quaternion_wxyz_e4 [ 1 ] = veh_att . q2 * 1e4 ;
copter_att_status_msg . quaternion_wxyz_e4 [ 2 ] = veh_att . q3 * 1e4 ;
copter_att_status_msg . quaternion_wxyz_e4 [ 3 ] = veh_att . q4 * 1e4 ;
copter_att_status_msg . reserved . len = 2 ;
copter_att_status_msg . reserved . data [ 0 ] = 0 ;
copter_att_status_msg . reserved . data [ 1 ] = 0 ;
_detected_modules [ _instance ] . ap_dronecan - > xacti_copter_att_status . broadcast ( copter_att_status_msg ) ;
2023-08-02 03:34:34 -03:00
return true ;
}
2023-09-16 02:12:58 -03:00
// update zoom rate controller. now_ms is current system time
2023-08-02 03:34:34 -03:00
// returns true if sent so that we avoid immediately trying to also send other messages
2023-09-16 02:12:58 -03:00
bool AP_Mount_Xacti : : update_zoom_rate_control ( uint32_t now_ms )
2023-08-02 03:34:34 -03:00
{
// return immediately if zoom rate control is not enabled
if ( ! _zoom_rate_control . enabled ) {
return false ;
}
2023-09-28 03:30:47 -03:00
// we are controlling optical zoom if the camera has it and we are below the optical zoom upper limit
// or at the optical zoom upper limit, the lower digital zoom limit and are zooming out
bool optical_zoom_control = ( capabilities . optical_zoom = = Capability : : True ) & &
( ( _last_optical_zoom_param_value < 250 ) | |
( ( _last_optical_zoom_param_value = = 250 ) & & ( _last_digital_zoom_param_value = = 100 ) & & ( _zoom_rate_control . dir < 0 ) ) ) ;
// update every 0.25 or 0.5 sec
const uint32_t update_interval_ms = optical_zoom_control ? XACTI_OPTICAL_ZOOM_RATE_UPDATE_INTERVAL_MS : XACTI_DIGITAL_ZOOM_RATE_UPDATE_INTERVAL_MS ;
if ( now_ms - _zoom_rate_control . last_update_ms < update_interval_ms ) {
2023-08-02 03:34:34 -03:00
return false ;
}
_zoom_rate_control . last_update_ms = now_ms ;
2023-09-28 03:30:47 -03:00
// optical zoom
if ( optical_zoom_control ) {
const uint16_t optical_zoom_value = _last_optical_zoom_param_value + _zoom_rate_control . dir * 10 ;
// if reached lower limit of optical zoom, disable zoom control
if ( optical_zoom_value < 100 ) {
_zoom_rate_control . enabled = false ;
return false ;
}
2023-08-02 03:34:34 -03:00
2023-09-28 03:30:47 -03:00
// send desired optical zoom to camera
return set_param_int32 ( Param : : OpticalZoomMagnification , MIN ( optical_zoom_value , 250 ) ) ;
}
// digital zoom
const uint16_t digital_zoom_value = _last_digital_zoom_param_value + _zoom_rate_control . dir * 100 ;
2023-08-02 03:34:34 -03:00
// if reached limit then disable zoom
2023-09-28 03:30:47 -03:00
if ( ( ( capabilities . optical_zoom ! = Capability : : True ) & & ( digital_zoom_value < 100 ) ) | | ( digital_zoom_value > 1000 ) ) {
2023-08-02 03:34:34 -03:00
_zoom_rate_control . enabled = false ;
return false ;
}
2023-09-28 03:30:47 -03:00
// send desired digital zoom to camera
return set_param_int32 ( Param : : DigitalZoomMagnification , digital_zoom_value ) ;
2023-08-02 03:34:34 -03:00
}
2023-09-16 02:12:58 -03:00
// request firmware version. now_ms should be current system time (reduces calls to AP_HAL::millis)
2023-09-15 08:29:57 -03:00
// returns true if sent so that we avoid immediately trying to also send other messages
2023-09-16 02:12:58 -03:00
bool AP_Mount_Xacti : : request_firmware_version ( uint32_t now_ms )
2023-09-15 08:29:57 -03:00
{
// return immediately if already have version or no dronecan
if ( _firmware_version . received ) {
return false ;
}
// send request once per second until received
if ( now_ms - _firmware_version . last_request_ms < 1000 ) {
return false ;
}
_firmware_version . last_request_ms = now_ms ;
2023-09-20 05:11:46 -03:00
return get_param_string ( Param : : FirmwareVersion ) ;
2023-09-15 08:29:57 -03:00
}
2023-09-28 03:30:47 -03:00
// request parameters used to determine camera capabilities. now_ms is current system time
// returns true if a param get/set was sent so that we avoid sending other messages
bool AP_Mount_Xacti : : request_capabilities ( uint32_t now_ms )
{
// return immediately if we have already determined this models capabilities
if ( capabilities . received ) {
return false ;
}
// send requests once per second until received
if ( now_ms - capabilities . last_request_ms < 1000 ) {
return false ;
}
capabilities . last_request_ms = now_ms ;
// record start time
if ( capabilities . first_request_ms = = 0 ) {
capabilities . first_request_ms = now_ms ;
}
// timeout after 10 seconds
if ( now_ms - capabilities . first_request_ms > 10000 ) {
capabilities . optical_zoom = Capability : : False ;
capabilities . received = true ;
return false ;
}
// optical zoom: try setting optical zoom to 1x
// return is handled in handle_param_get_set_response_int
return set_param_int32 ( Param : : OpticalZoomMagnification , 100 ) ;
}
2023-09-17 21:56:17 -03:00
// set date and time. now_ms is current system time
bool AP_Mount_Xacti : : set_datetime ( uint32_t now_ms )
{
// return immediately if gimbal's date/time has been set
if ( _datetime . set ) {
return false ;
}
// attempt to set datetime once per second until received
if ( now_ms - _datetime . last_request_ms < 1000 ) {
return false ;
}
_datetime . last_request_ms = now_ms ;
// get date and time
uint16_t year , ms ;
uint8_t month , day , hour , min , sec ;
2023-10-05 04:40:34 -03:00
# if AP_RTC_ENABLED
2023-09-17 21:56:17 -03:00
if ( ! AP : : rtc ( ) . get_date_and_time_utc ( year , month , day , hour , min , sec , ms ) ) {
return false ;
}
2023-10-05 04:40:34 -03:00
# else
( void ) ms ;
return false ;
# endif
2023-09-17 21:56:17 -03:00
// date time is of the format YYYYMMDDHHMMSS (14 bytes)
// convert month from 0~11 to 1~12 range
AP_DroneCAN : : string datetime_string { } ;
const int num_bytes = snprintf ( ( char * ) datetime_string . data , sizeof ( AP_DroneCAN : : string : : data ) , " %04u%02u%02u%02u%02u%02u " ,
( unsigned ) year , ( unsigned ) month + 1 , ( unsigned ) day , ( unsigned ) hour , ( unsigned ) min , ( unsigned ) sec ) ;
// sanity check bytes to be sent
if ( num_bytes ! = 14 ) {
INTERNAL_ERROR ( AP_InternalError : : error_t : : invalid_arg_or_result ) ;
return false ;
}
datetime_string . len = num_bytes ;
2023-09-20 05:11:46 -03:00
_datetime . set = set_param_string ( Param : : DateTime , datetime_string ) ;
2023-09-17 21:56:17 -03:00
if ( ! _datetime . set ) {
2023-10-02 07:54:30 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_ERROR , " %s failed to set date/time " , send_text_prefix ) ;
2023-09-17 21:56:17 -03:00
}
return _datetime . set ;
}
2023-09-16 02:12:58 -03:00
// request status. now_ms should be current system time (reduces calls to AP_HAL::millis)
2023-09-15 08:29:57 -03:00
// returns true if sent so that we avoid immediately trying to also send other messages
2023-09-16 02:12:58 -03:00
bool AP_Mount_Xacti : : request_status ( uint32_t now_ms )
2023-09-15 08:29:57 -03:00
{
// return immediately if 3 seconds has not passed
if ( now_ms - _status_report . last_request_ms < XACTI_STATUS_REQ_INTERVAL_MS ) {
return false ;
}
_status_report . last_request_ms = now_ms ;
2023-09-20 05:11:46 -03:00
return get_param_string ( Param : : Status ) ;
2023-09-15 08:29:57 -03:00
}
2023-08-02 03:34:34 -03:00
// check if safe to send message (if messages sent too often camera will not respond)
2023-09-16 02:12:58 -03:00
// now_ms should be current system time (reduces calls to AP_HAL::millis)
bool AP_Mount_Xacti : : is_safe_to_send ( uint32_t now_ms ) const
2023-08-02 03:34:34 -03:00
{
// check time since last attitude sent
if ( now_ms - last_send_copter_att_status_ms < XACTI_MSG_SEND_MIN_MS ) {
return false ;
}
// check time since last angle target sent
if ( now_ms - last_send_gimbal_control_ms < XACTI_MSG_SEND_MIN_MS ) {
return false ;
}
// check time since last set param message sent
2023-09-15 08:29:57 -03:00
if ( now_ms - last_send_getset_param_ms < XACTI_MSG_SEND_MIN_MS ) {
2023-08-02 03:34:34 -03:00
return false ;
}
return true ;
2023-02-21 02:04:28 -04:00
}
# endif // HAL_MOUNT_XACTI_ENABLED