2013-05-29 20:54:53 -03:00
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2012-06-13 16:00:20 -03:00
2015-08-11 03:28:42 -03:00
# include "AP_Camera.h"
# include <AP_Relay/AP_Relay.h>
# include <AP_Math/AP_Math.h>
# include <RC_Channel/RC_Channel.h>
# include <AP_HAL/AP_HAL.h>
2012-06-13 16:00:20 -03:00
// ------------------------------
# define CAM_DEBUG DISABLED
2015-10-25 14:03:46 -03:00
const AP_Param : : GroupInfo AP_Camera : : var_info [ ] = {
2012-08-17 03:09:29 -03:00
// @Param: TRIGG_TYPE
// @DisplayName: Camera shutter (trigger) type
// @Description: how to trigger the camera to take a picture
2013-07-14 20:55:38 -03:00
// @Values: 0:Servo,1:Relay
2012-08-17 03:09:29 -03:00
// @User: Standard
2012-12-06 04:46:09 -04:00
AP_GROUPINFO ( " TRIGG_TYPE " , 0 , AP_Camera , _trigger_type , AP_CAMERA_TRIGGER_DEFAULT_TRIGGER_TYPE ) ,
// @Param: DURATION
// @DisplayName: Duration that shutter is held open
// @Description: How long the shutter will be held open in 10ths of a second (i.e. enter 10 for 1second, 50 for 5seconds)
2014-10-14 00:42:57 -03:00
// @Units: seconds
2012-12-06 04:46:09 -04:00
// @Range: 0 50
// @User: Standard
AP_GROUPINFO ( " DURATION " , 1 , AP_Camera , _trigger_duration , AP_CAMERA_TRIGGER_DEFAULT_DURATION ) ,
// @Param: SERVO_ON
// @DisplayName: Servo ON PWM value
// @Description: PWM value to move servo to when shutter is activated
2014-10-14 00:42:57 -03:00
// @Units: pwm
2012-12-06 04:46:09 -04:00
// @Range: 1000 2000
// @User: Standard
AP_GROUPINFO ( " SERVO_ON " , 2 , AP_Camera , _servo_on_pwm , AP_CAMERA_SERVO_ON_PWM ) ,
// @Param: SERVO_OFF
// @DisplayName: Servo OFF PWM value
// @Description: PWM value to move servo to when shutter is deactivated
2014-10-14 00:42:57 -03:00
// @Units: pwm
2012-12-06 04:46:09 -04:00
// @Range: 1000 2000
// @User: Standard
AP_GROUPINFO ( " SERVO_OFF " , 3 , AP_Camera , _servo_off_pwm , AP_CAMERA_SERVO_OFF_PWM ) ,
2013-06-24 09:39:50 -03:00
// @Param: TRIGG_DIST
// @DisplayName: Camera trigger distance
2013-10-14 20:09:39 -03:00
// @Description: Distance in meters between camera triggers. If this value is non-zero then the camera will trigger whenever the GPS position changes by this number of meters regardless of what mode the APM is in. Note that this parameter can also be set in an auto mission using the DO_SET_CAM_TRIGG_DIST command, allowing you to enable/disable the triggering of the camera during the flight.
2013-06-24 09:39:50 -03:00
// @User: Standard
2014-10-14 00:42:57 -03:00
// @Units: meters
2013-06-24 09:39:50 -03:00
// @Range: 0 1000
AP_GROUPINFO ( " TRIGG_DIST " , 4 , AP_Camera , _trigg_dist , 0 ) ,
2015-09-05 00:44:07 -03:00
// @Param: RELAY_ON
// @DisplayName: Relay ON value
// @Description: This sets whether the relay goes high or low when it triggers. Note that you should also set RELAY_DEFAULT appropriately for your camera
// @Values: 0:Low,1:High
// @User: Standard
AP_GROUPINFO ( " RELAY_ON " , 5 , AP_Camera , _relay_on , 1 ) ,
2015-12-18 03:16:11 -04:00
// @Param: MIN_INTERVAL
// @DisplayName: Minimum time between photos
// @Description: Postpone shooting if previous picture was taken less than preset time(ms) ago.
// @User: Standard
// @Units: milliseconds
// @Range: 0 10000
AP_GROUPINFO ( " MIN_INTERVAL " , 6 , AP_Camera , _min_interval , 0 ) ,
// @Param: MAX_ROLL
// @DisplayName: Maximum photo roll angle.
// @Description: Postpone shooting if roll is greater than limit. (0=Disable, will shoot regardless of roll).
// @User: Standard
// @Units: Degrees
// @Range: 0 180
AP_GROUPINFO ( " MAX_ROLL " , 7 , AP_Camera , _max_roll , 0 ) ,
2012-08-17 03:09:29 -03:00
AP_GROUPEND
2012-06-13 16:00:20 -03:00
} ;
2015-12-18 03:16:11 -04:00
extern const AP_HAL : : HAL & hal ;
2012-06-13 16:00:20 -03:00
2012-06-17 17:53:54 -03:00
/// Servo operated camera
2012-06-13 16:00:20 -03:00
void
2012-06-17 17:53:54 -03:00
AP_Camera : : servo_pic ( )
2012-06-13 16:00:20 -03:00
{
2012-12-06 04:46:09 -04:00
RC_Channel_aux : : set_radio ( RC_Channel_aux : : k_cam_trigger , _servo_on_pwm ) ;
// leave a message that it should be active for this many loops (assumes 50hz loops)
2012-12-18 22:36:35 -04:00
_trigger_counter = constrain_int16 ( _trigger_duration * 5 , 0 , 255 ) ;
2012-06-13 16:00:20 -03:00
}
2012-06-17 17:53:54 -03:00
/// basic relay activation
2012-06-13 16:00:20 -03:00
void
2012-06-17 17:53:54 -03:00
AP_Camera : : relay_pic ( )
2012-06-13 16:00:20 -03:00
{
2015-09-05 00:44:07 -03:00
if ( _relay_on ) {
_apm_relay - > on ( 0 ) ;
} else {
_apm_relay - > off ( 0 ) ;
}
2012-12-06 04:46:09 -04:00
// leave a message that it should be active for this many loops (assumes 50hz loops)
2012-12-18 22:36:35 -04:00
_trigger_counter = constrain_int16 ( _trigger_duration * 5 , 0 , 255 ) ;
2012-06-13 16:00:20 -03:00
}
2012-06-17 17:53:54 -03:00
/// single entry point to take pictures
2015-03-28 22:36:43 -03:00
/// set send_mavlink_msg to true to send DO_DIGICAM_CONTROL message to all components
2012-06-13 16:00:20 -03:00
void
2015-03-28 22:36:43 -03:00
AP_Camera : : trigger_pic ( bool send_mavlink_msg )
2012-06-13 16:00:20 -03:00
{
2014-08-26 18:08:54 -03:00
_image_index + + ;
2012-12-06 04:46:09 -04:00
switch ( _trigger_type )
2012-08-17 03:09:29 -03:00
{
2012-12-06 04:46:09 -04:00
case AP_CAMERA_TRIGGER_TYPE_SERVO :
2012-08-17 03:09:29 -03:00
servo_pic ( ) ; // Servo operated camera
break ;
2012-12-06 04:46:09 -04:00
case AP_CAMERA_TRIGGER_TYPE_RELAY :
2012-08-17 03:09:29 -03:00
relay_pic ( ) ; // basic relay activation
break ;
}
2015-03-28 22:36:43 -03:00
if ( send_mavlink_msg ) {
// create command long mavlink message
mavlink_command_long_t cmd_msg ;
memset ( & cmd_msg , 0 , sizeof ( cmd_msg ) ) ;
cmd_msg . command = MAV_CMD_DO_DIGICAM_CONTROL ;
2015-04-18 03:37:06 -03:00
cmd_msg . param5 = 1 ;
2015-03-28 22:36:43 -03:00
// create message
mavlink_message_t msg ;
mavlink_msg_command_long_encode ( 0 , 0 , & msg , & cmd_msg ) ;
// forward to all components
GCS_MAVLINK : : send_to_components ( & msg ) ;
}
2012-06-13 16:00:20 -03:00
}
2012-06-17 17:53:54 -03:00
/// de-activate the trigger after some delay, but without using a delay() function
2012-12-06 04:46:09 -04:00
/// should be called at 50hz
2012-06-13 16:00:20 -03:00
void
AP_Camera : : trigger_pic_cleanup ( )
{
2012-12-06 04:46:09 -04:00
if ( _trigger_counter ) {
_trigger_counter - - ;
} else {
switch ( _trigger_type ) {
case AP_CAMERA_TRIGGER_TYPE_SERVO :
RC_Channel_aux : : set_radio ( RC_Channel_aux : : k_cam_trigger , _servo_off_pwm ) ;
break ;
case AP_CAMERA_TRIGGER_TYPE_RELAY :
2015-09-05 00:44:07 -03:00
if ( _relay_on ) {
_apm_relay - > off ( 0 ) ;
} else {
_apm_relay - > on ( 0 ) ;
}
2012-12-06 04:46:09 -04:00
break ;
2012-08-17 03:09:29 -03:00
}
}
2012-06-13 16:00:20 -03:00
}
2012-06-17 17:53:54 -03:00
2015-09-06 17:41:47 -03:00
/// decode deprecated MavLink message that controls camera.
2012-06-13 16:00:20 -03:00
void
AP_Camera : : control_msg ( mavlink_message_t * msg )
{
2012-08-17 03:09:29 -03:00
__mavlink_digicam_control_t packet ;
mavlink_msg_digicam_control_decode ( msg , & packet ) ;
2014-12-09 23:04:19 -04:00
2015-09-06 17:41:47 -03:00
control ( packet . session , packet . zoom_pos , packet . zoom_step , packet . focus_lock , packet . shot , packet . command_id ) ;
2012-06-13 16:00:20 -03:00
}
2015-09-06 17:41:47 -03:00
void AP_Camera : : configure ( float shooting_mode , float shutter_speed , float aperture , float ISO , float exposure_type , float cmd_id , float engine_cutoff_time )
2015-04-18 10:30:03 -03:00
{
2015-04-18 03:37:06 -03:00
// we cannot process the configure command so convert to mavlink message
// and send to all components in case they and process it
mavlink_message_t msg ;
mavlink_command_long_t mav_cmd_long = { } ;
// convert mission command to mavlink command_long
2015-09-16 00:53:47 -03:00
mav_cmd_long . command = MAV_CMD_DO_DIGICAM_CONFIGURE ;
2015-09-06 17:41:47 -03:00
mav_cmd_long . param1 = shooting_mode ;
mav_cmd_long . param2 = shutter_speed ;
mav_cmd_long . param3 = aperture ;
mav_cmd_long . param4 = ISO ;
mav_cmd_long . param5 = exposure_type ;
mav_cmd_long . param6 = cmd_id ;
mav_cmd_long . param7 = engine_cutoff_time ;
2015-04-18 03:37:06 -03:00
// Encode Command long into MAVLINK msg
mavlink_msg_command_long_encode ( 0 , 0 , & msg , & mav_cmd_long ) ;
// send to all components
GCS_MAVLINK : : send_to_components ( & msg ) ;
2015-04-18 10:30:03 -03:00
}
2015-09-06 17:41:47 -03:00
void AP_Camera : : control ( float session , float zoom_pos , float zoom_step , float focus_lock , float shooting_cmd , float cmd_id )
2015-04-18 10:30:03 -03:00
{
2015-04-18 10:43:23 -03:00
// take picture
2015-09-16 00:53:47 -03:00
if ( is_equal ( shooting_cmd , 1.0f ) ) {
2015-09-06 17:41:47 -03:00
trigger_pic ( false ) ;
}
2015-04-18 10:43:23 -03:00
2015-04-18 03:37:06 -03:00
mavlink_message_t msg ;
mavlink_command_long_t mav_cmd_long = { } ;
// convert command to mavlink command long
mav_cmd_long . command = MAV_CMD_DO_DIGICAM_CONTROL ;
2015-09-06 17:41:47 -03:00
mav_cmd_long . param1 = session ;
mav_cmd_long . param2 = zoom_pos ;
mav_cmd_long . param3 = zoom_step ;
mav_cmd_long . param4 = focus_lock ;
mav_cmd_long . param5 = shooting_cmd ;
mav_cmd_long . param6 = cmd_id ;
2015-04-18 03:37:06 -03:00
// Encode Command long into MAVLINK msg
mavlink_msg_command_long_encode ( 0 , 0 , & msg , & mav_cmd_long ) ;
// send to all components
GCS_MAVLINK : : send_to_components ( & msg ) ;
2015-04-18 10:30:03 -03:00
}
2012-06-13 16:00:20 -03:00
2014-10-31 02:40:27 -03:00
/*
Send camera feedback to the GCS
*/
void AP_Camera : : send_feedback ( mavlink_channel_t chan , AP_GPS & gps , const AP_AHRS & ahrs , const Location & current_loc )
{
float altitude , altitude_rel ;
if ( current_loc . flags . relative_alt ) {
altitude = current_loc . alt + ahrs . get_home ( ) . alt ;
altitude_rel = current_loc . alt ;
} else {
altitude = current_loc . alt ;
altitude_rel = current_loc . alt - ahrs . get_home ( ) . alt ;
}
mavlink_msg_camera_feedback_send ( chan ,
gps . time_epoch_usec ( ) ,
2014-08-26 18:08:54 -03:00
0 , 0 , _image_index ,
2014-10-31 02:40:27 -03:00
current_loc . lat , current_loc . lng ,
2014-10-31 02:38:16 -03:00
altitude / 100.0f , altitude_rel / 100.0f ,
ahrs . roll_sensor / 100.0f , ahrs . pitch_sensor / 100.0f , ahrs . yaw_sensor / 100.0f ,
2015-04-24 00:27:55 -03:00
0.0f , CAMERA_FEEDBACK_PHOTO ) ;
2014-10-31 02:40:27 -03:00
}
2013-07-09 19:31:25 -03:00
/* update location, for triggering by GPS distance moved
This function returns true if a picture should be taken
The caller is responsible for taking the picture based on the return value of this function .
The caller is also responsible for logging the details about the photo
*/
2015-12-18 03:16:11 -04:00
bool AP_Camera : : update_location ( const struct Location & loc , const AP_AHRS & ahrs )
2013-06-24 09:39:50 -03:00
{
2015-05-04 23:35:15 -03:00
if ( is_zero ( _trigg_dist ) ) {
2013-07-09 19:31:25 -03:00
return false ;
2013-06-24 09:39:50 -03:00
}
if ( _last_location . lat = = 0 & & _last_location . lng = = 0 ) {
_last_location = loc ;
2013-07-09 19:31:25 -03:00
return false ;
2013-06-24 09:39:50 -03:00
}
if ( _last_location . lat = = loc . lat & & _last_location . lng = = loc . lng ) {
// we haven't moved - this can happen as update_location() may
// be called without a new GPS fix
2013-07-09 19:31:25 -03:00
return false ;
2013-06-24 09:39:50 -03:00
}
2015-12-18 03:16:11 -04:00
2013-08-04 21:16:00 -03:00
if ( get_distance ( loc , _last_location ) < _trigg_dist ) {
2013-07-09 19:31:25 -03:00
return false ;
2013-06-24 09:39:50 -03:00
}
2015-12-18 03:16:11 -04:00
if ( _max_roll > 0 & & labs ( ahrs . roll_sensor / 100 ) > _max_roll ) {
return false ;
}
uint32_t tnow = AP_HAL : : millis ( ) ;
if ( tnow - _last_photo_time < ( unsigned ) _min_interval ) {
return false ;
} else {
_last_location = loc ;
_last_photo_time = tnow ;
return true ;
}
2015-04-28 02:42:35 -03:00
}