Line data Source code
1 : #include "AP_Camera.h"
2 :
3 : #if AP_CAMERA_ENABLED
4 :
5 : #include <GCS_MAVLink/GCS.h>
6 : #include <AP_Math/AP_Math.h>
7 : #include <AP_HAL/AP_HAL.h>
8 : #include <SRV_Channel/SRV_Channel.h>
9 : #include <AP_Vehicle/AP_Vehicle.h>
10 : #include "AP_Camera_Backend.h"
11 : #include "AP_Camera_Servo.h"
12 : #include "AP_Camera_Relay.h"
13 : #include "AP_Camera_SoloGimbal.h"
14 : #include "AP_Camera_Mount.h"
15 : #include "AP_Camera_MAVLink.h"
16 : #include "AP_Camera_MAVLinkCamV2.h"
17 : #include "AP_Camera_Scripting.h"
18 : #include "AP_RunCam.h"
19 :
20 : const AP_Param::GroupInfo AP_Camera::var_info[] = {
21 :
22 : // @Param: _MAX_ROLL
23 : // @DisplayName: Maximum photo roll angle.
24 : // @Description: Postpone shooting if roll is greater than limit. (0=Disable, will shoot regardless of roll).
25 : // @User: Standard
26 : // @Units: deg
27 : // @Range: 0 180
28 : AP_GROUPINFO("_MAX_ROLL", 7, AP_Camera, _max_roll, 0),
29 :
30 : // @Param: _AUTO_ONLY
31 : // @DisplayName: Distance-trigging in AUTO mode only
32 : // @Description: When enabled, trigging by distance is done in AUTO mode only.
33 : // @Values: 0:Always,1:Only when in AUTO
34 : // @User: Standard
35 : AP_GROUPINFO("_AUTO_ONLY", 10, AP_Camera, _auto_mode_only, 0),
36 :
37 : // @Group: 1
38 : // @Path: AP_Camera_Params.cpp
39 : AP_SUBGROUPINFO(_params[0], "1", 12, AP_Camera, AP_Camera_Params),
40 :
41 : #if AP_CAMERA_MAX_INSTANCES > 1
42 : // @Group: 2
43 : // @Path: AP_Camera_Params.cpp
44 : AP_SUBGROUPINFO(_params[1], "2", 13, AP_Camera, AP_Camera_Params),
45 : #endif
46 : #if AP_CAMERA_RUNCAM_ENABLED
47 : // @Group: 1_RC_
48 : // @Path: AP_RunCam.cpp
49 : AP_SUBGROUPVARPTR(_backends[0], "1_RC_", 14, AP_Camera, _backend_var_info[0]),
50 :
51 : #if AP_CAMERA_MAX_INSTANCES > 1
52 : // @Group: 2_RC_
53 : // @Path: AP_RunCam.cpp
54 : AP_SUBGROUPVARPTR(_backends[1], "2_RC_", 15, AP_Camera, _backend_var_info[1]),
55 : #endif
56 : #endif
57 : AP_GROUPEND
58 : };
59 :
60 : #if AP_CAMERA_RUNCAM_ENABLED
61 : const AP_Param::GroupInfo *AP_Camera::_backend_var_info[AP_CAMERA_MAX_INSTANCES];
62 : #endif
63 :
64 : extern const AP_HAL::HAL& hal;
65 :
66 2061 : AP_Camera::AP_Camera(uint32_t _log_camera_bit) :
67 6183 : log_camera_bit(_log_camera_bit)
68 : {
69 2061 : AP_Param::setup_object_defaults(this, var_info);
70 2061 : _singleton = this;
71 2061 : }
72 :
73 : // momentary switch to change camera between picture and video modes
74 0 : void AP_Camera::cam_mode_toggle()
75 : {
76 0 : WITH_SEMAPHORE(_rsem);
77 :
78 0 : if (primary == nullptr) {
79 0 : return;
80 : }
81 0 : primary->cam_mode_toggle();
82 0 : }
83 :
84 : // take a picture
85 6 : bool AP_Camera::take_picture()
86 : {
87 6 : WITH_SEMAPHORE(_rsem);
88 :
89 : // call for each instance
90 6 : bool success = false;
91 18 : for (uint8_t i = 0; i < AP_CAMERA_MAX_INSTANCES; i++) {
92 12 : if (_backends[i] != nullptr) {
93 6 : success |= _backends[i]->take_picture();
94 : }
95 : }
96 :
97 : // return true if at least once pic taken
98 12 : return success;
99 6 : }
100 :
101 1 : bool AP_Camera::take_picture(uint8_t instance)
102 : {
103 1 : WITH_SEMAPHORE(_rsem);
104 :
105 1 : auto *backend = get_instance(instance);
106 1 : if (backend == nullptr) {
107 0 : return false;
108 : }
109 1 : return backend->take_picture();
110 1 : }
111 :
112 : // take multiple pictures, time_interval between two consecutive pictures is in miliseconds
113 : // if instance is not provided, all available cameras affected
114 : // time_interval_ms must be positive
115 : // total_num is number of pictures to be taken, -1 means capture forever
116 : // returns true if at least one camera is successful
117 0 : bool AP_Camera::take_multiple_pictures(uint32_t time_interval_ms, int16_t total_num)
118 : {
119 0 : WITH_SEMAPHORE(_rsem);
120 :
121 : // sanity check time interval
122 0 : if (time_interval_ms == 0) {
123 0 : return false;
124 : }
125 :
126 : // call for all instances
127 0 : bool success = false;
128 0 : for (uint8_t i = 0; i < AP_CAMERA_MAX_INSTANCES; i++) {
129 0 : if (_backends[i] != nullptr) {
130 0 : _backends[i]->take_multiple_pictures(time_interval_ms, total_num);
131 0 : success = true;
132 : }
133 : }
134 :
135 : // return true if at least once backend was successful
136 0 : return success;
137 0 : }
138 :
139 2 : bool AP_Camera::take_multiple_pictures(uint8_t instance, uint32_t time_interval_ms, int16_t total_num)
140 : {
141 2 : WITH_SEMAPHORE(_rsem);
142 :
143 : // sanity check time interval
144 2 : if (time_interval_ms == 0) {
145 0 : return false;
146 : }
147 :
148 2 : auto *backend = get_instance(instance);
149 2 : if (backend == nullptr) {
150 0 : return false;
151 : }
152 2 : backend->take_multiple_pictures(time_interval_ms, total_num);
153 2 : return true;
154 2 : }
155 :
156 : // stop capturing multiple image sequence
157 0 : void AP_Camera::stop_capture()
158 : {
159 0 : WITH_SEMAPHORE(_rsem);
160 :
161 : // call for each instance
162 0 : for (uint8_t i = 0; i < AP_CAMERA_MAX_INSTANCES; i++) {
163 0 : if (_backends[i] != nullptr) {
164 0 : _backends[i]->stop_capture();
165 : }
166 : }
167 0 : }
168 :
169 1 : bool AP_Camera::stop_capture(uint8_t instance)
170 : {
171 1 : WITH_SEMAPHORE(_rsem);
172 :
173 1 : auto *backend = get_instance(instance);
174 1 : if (backend == nullptr) {
175 0 : return false;
176 : }
177 1 : backend->stop_capture();
178 1 : return true;
179 1 : }
180 :
181 : // start/stop recording video
182 : // start_recording should be true to start recording, false to stop recording
183 0 : bool AP_Camera::record_video(bool start_recording)
184 : {
185 0 : WITH_SEMAPHORE(_rsem);
186 :
187 0 : if (primary == nullptr) {
188 0 : return false;
189 : }
190 0 : return primary->record_video(start_recording);
191 0 : }
192 :
193 : // detect and initialise backends
194 2059 : void AP_Camera::init()
195 : {
196 : // check init has not been called before
197 2059 : if (primary != nullptr) {
198 0 : return;
199 : }
200 :
201 : // perform any required parameter conversion
202 2059 : convert_params();
203 : #if AP_CAMERA_RUNCAM_ENABLED && (AP_CAMERA_MAX_INSTANCES > 1)
204 2059 : convert_runcam_params();
205 : #endif // AP_CAMERA_RUNCAM_ENABLED && (AP_CAMERA_MAX_INSTANCES > 1)
206 :
207 : // create each instance
208 6177 : for (uint8_t instance = 0; instance < AP_CAMERA_MAX_INSTANCES; instance++) {
209 4118 : switch ((CameraType)_params[instance].type.get()) {
210 : #if AP_CAMERA_SERVO_ENABLED
211 5 : case CameraType::SERVO:
212 5 : _backends[instance] = NEW_NOTHROW AP_Camera_Servo(*this, _params[instance], instance);
213 5 : break;
214 : #endif
215 : #if AP_CAMERA_RELAY_ENABLED
216 0 : case CameraType::RELAY:
217 0 : _backends[instance] = NEW_NOTHROW AP_Camera_Relay(*this, _params[instance], instance);
218 0 : break;
219 : #endif
220 : #if AP_CAMERA_SOLOGIMBAL_ENABLED
221 : // check for GoPro in Solo camera
222 0 : case CameraType::SOLOGIMBAL:
223 0 : _backends[instance] = NEW_NOTHROW AP_Camera_SoloGimbal(*this, _params[instance], instance);
224 0 : break;
225 : #endif
226 : #if AP_CAMERA_MOUNT_ENABLED
227 : // check for Mount camera
228 5 : case CameraType::MOUNT:
229 5 : _backends[instance] = NEW_NOTHROW AP_Camera_Mount(*this, _params[instance], instance);
230 5 : break;
231 : #endif
232 : #if AP_CAMERA_MAVLINK_ENABLED
233 : // check for MAVLink enabled camera driver
234 1 : case CameraType::MAVLINK:
235 1 : _backends[instance] = NEW_NOTHROW AP_Camera_MAVLink(*this, _params[instance], instance);
236 1 : break;
237 : #endif
238 : #if AP_CAMERA_MAVLINKCAMV2_ENABLED
239 : // check for MAVLink Camv2 driver
240 4 : case CameraType::MAVLINK_CAMV2:
241 4 : _backends[instance] = NEW_NOTHROW AP_Camera_MAVLinkCamV2(*this, _params[instance], instance);
242 4 : break;
243 : #endif
244 : #if AP_CAMERA_SCRIPTING_ENABLED
245 : // check for Scripting driver
246 0 : case CameraType::SCRIPTING:
247 0 : _backends[instance] = NEW_NOTHROW AP_Camera_Scripting(*this, _params[instance], instance);
248 0 : break;
249 : #endif
250 : #if AP_CAMERA_RUNCAM_ENABLED
251 : // check for RunCam driver
252 6 : case CameraType::RUNCAM:
253 6 : if (_backends[instance] == nullptr) { // may have already been created by the conversion code
254 6 : _backends[instance] = NEW_NOTHROW AP_RunCam(*this, _params[instance], instance, _runcam_instances);
255 6 : _backend_var_info[instance] = AP_RunCam::var_info;
256 6 : AP_Param::load_object_from_eeprom(_backends[instance], _backend_var_info[instance]);
257 6 : _runcam_instances++;
258 : }
259 6 : break;
260 : #endif
261 4097 : case CameraType::NONE:
262 4097 : break;
263 : }
264 :
265 : // set primary to first non-null instance
266 4118 : if (primary == nullptr) {
267 4099 : primary = _backends[instance];
268 : }
269 : }
270 :
271 : // init each instance, do it after all instances were created, so that they all know things
272 6177 : for (uint8_t instance = 0; instance < AP_CAMERA_MAX_INSTANCES; instance++) {
273 4118 : if (_backends[instance] != nullptr) {
274 21 : _backends[instance]->init();
275 : }
276 : }
277 : }
278 :
279 : // handle incoming mavlink messages
280 5 : void AP_Camera::handle_message(mavlink_channel_t chan, const mavlink_message_t &msg)
281 : {
282 5 : WITH_SEMAPHORE(_rsem);
283 :
284 5 : if (msg.msgid == MAVLINK_MSG_ID_DIGICAM_CONTROL) {
285 : // decode deprecated MavLink message that controls camera.
286 : __mavlink_digicam_control_t packet;
287 1 : mavlink_msg_digicam_control_decode(&msg, &packet);
288 1 : control(packet.session, packet.zoom_pos, packet.zoom_step, packet.focus_lock, packet.shot, packet.command_id);
289 1 : return;
290 : }
291 :
292 : // call each instance
293 12 : for (uint8_t instance = 0; instance < AP_CAMERA_MAX_INSTANCES; instance++) {
294 8 : if (_backends[instance] != nullptr) {
295 8 : _backends[instance]->handle_message(chan, msg);
296 : }
297 : }
298 5 : }
299 :
300 : #if HAL_MAVLINK_BINDINGS_ENABLED
301 : // a method which handles mavlink-style semantics for instance_id; if
302 : // instance_id is zero or matches backend instance ID code is run
303 15 : MAV_RESULT AP_Camera::handle_mav_DO_SET_CAM_TRIGG_DISTANCE(uint8_t instance_id, bool trigger, float dist_m)
304 : {
305 45 : for (uint8_t i=0; i<AP_CAMERA_MAX_INSTANCES; i++) {
306 30 : if (_backends[i] == nullptr) {
307 22 : continue;
308 : }
309 : // honour packet instance number:
310 8 : if (instance_id != 0 && i+1 != instance_id) {
311 2 : continue;
312 : }
313 6 : _backends[i]->set_trigger_distance(dist_m);
314 6 : if (trigger) {
315 4 : _backends[i]->take_picture();
316 : }
317 : }
318 :
319 15 : return MAV_RESULT_ACCEPTED;
320 : }
321 :
322 5 : MAV_RESULT AP_Camera::handle_mav_SET_CAMERA_ZOOM(uint8_t instance_id, CAMERA_ZOOM_TYPE mav_zoom_type, float zoom_value)
323 : {
324 : ZoomType zoom_type;
325 5 : switch (mav_zoom_type) {
326 0 : case ZOOM_TYPE_CONTINUOUS:
327 0 : zoom_type = ZoomType::RATE;
328 0 : break;
329 5 : case ZOOM_TYPE_RANGE:
330 5 : zoom_type = ZoomType::PCT;
331 5 : break;
332 0 : default:
333 : // invalid param1
334 0 : return MAV_RESULT_DENIED;
335 : }
336 :
337 5 : MAV_RESULT result = MAV_RESULT_ACCEPTED;
338 15 : for (uint8_t i=0; i<AP_CAMERA_MAX_INSTANCES; i++) {
339 10 : if (_backends[i] == nullptr) {
340 0 : continue;
341 : }
342 : // honour packet instance number:
343 10 : if (instance_id != 0 && i+1 != instance_id) {
344 4 : continue;
345 : }
346 : // all backends must succeed:
347 6 : if (!_backends[i]->set_zoom(zoom_type, zoom_value)) {
348 0 : result = MAV_RESULT_FAILED;
349 : }
350 : }
351 :
352 5 : return result;
353 : }
354 :
355 5 : MAV_RESULT AP_Camera::handle_mav_SET_CAMERA_FOCUS(uint8_t instance_id, SET_FOCUS_TYPE mav_focus_type, float focus_value)
356 : {
357 : // note: focus_value can be modified before it is used
358 :
359 : FocusType focus_type;
360 5 : switch (mav_focus_type) {
361 0 : case FOCUS_TYPE_AUTO:
362 : case FOCUS_TYPE_AUTO_SINGLE:
363 : case FOCUS_TYPE_AUTO_CONTINUOUS:
364 : // accept any of the auto focus types
365 0 : focus_type = FocusType::AUTO;
366 0 : focus_value = 0;
367 0 : break;
368 0 : case FOCUS_TYPE_CONTINUOUS:
369 : // accept continuous manual focus
370 0 : focus_type = FocusType::RATE;
371 0 : break;
372 5 : case FOCUS_TYPE_RANGE:
373 : // accept focus as percentage
374 5 : focus_type = FocusType::PCT;
375 5 : break;
376 0 : case SET_FOCUS_TYPE_ENUM_END:
377 : case FOCUS_TYPE_STEP:
378 : case FOCUS_TYPE_METERS:
379 : default: // mav_focus_type comes off the wire so could be anything
380 : // unsupported focus (bad parameter)
381 0 : return MAV_RESULT_DENIED;
382 : }
383 :
384 5 : MAV_RESULT result = MAV_RESULT_ACCEPTED;
385 15 : for (uint8_t i=0; i<AP_CAMERA_MAX_INSTANCES; i++) {
386 10 : if (_backends[i] == nullptr) {
387 0 : continue;
388 : }
389 : // honour packet instance number:
390 10 : if (instance_id != 0 && i+1 != instance_id) {
391 4 : continue;
392 : }
393 : // all backends must succeed. If any fail we pass back the
394 : // result from the last camera that failed.
395 6 : const SetFocusResult backend_result = _backends[i]->set_focus(focus_type, focus_value);
396 6 : if (backend_result != SetFocusResult::ACCEPTED) {
397 : // for now FocusResult can just be cast into MAV_RESULT:
398 0 : result = (MAV_RESULT)backend_result;
399 : }
400 : }
401 :
402 5 : return result;
403 : }
404 : #endif // HAL_MAVLINK_BINDINGS_ENABLED
405 :
406 : // handle command_long mavlink messages
407 13 : MAV_RESULT AP_Camera::handle_command(const mavlink_command_int_t &packet)
408 : {
409 13 : switch (packet.command) {
410 2 : case MAV_CMD_DO_DIGICAM_CONFIGURE:
411 2 : configure(packet.param1, packet.param2, packet.param3, packet.param4, packet.x, packet.y, packet.z);
412 2 : return MAV_RESULT_ACCEPTED;
413 2 : case MAV_CMD_DO_DIGICAM_CONTROL:
414 2 : control(packet.param1, packet.param2, packet.param3, packet.param4, packet.x, packet.y);
415 2 : return MAV_RESULT_ACCEPTED;
416 3 : case MAV_CMD_DO_SET_CAM_TRIGG_DIST:
417 3 : return handle_mav_DO_SET_CAM_TRIGG_DISTANCE(
418 3 : packet.param4, // instance
419 3 : is_equal(packet.param3, 1.0f), // trigger
420 3 : packet.param1 // distance
421 3 : );
422 3 : case MAV_CMD_SET_CAMERA_ZOOM:
423 3 : return handle_mav_SET_CAMERA_ZOOM(
424 3 : packet.param3, // instance
425 3 : CAMERA_ZOOM_TYPE(packet.param1), // zoom type
426 3 : packet.param2 // zoom level
427 3 : );
428 3 : case MAV_CMD_SET_CAMERA_FOCUS:
429 3 : return handle_mav_SET_CAMERA_FOCUS(
430 3 : packet.param3, // instance
431 3 : SET_FOCUS_TYPE(packet.param1), // focus type
432 3 : packet.param2 // focus value
433 3 : );
434 :
435 : #if AP_CAMERA_SET_CAMERA_SOURCE_ENABLED
436 0 : case MAV_CMD_SET_CAMERA_SOURCE:
437 : // sanity check instance
438 0 : if (is_negative(packet.param1) || packet.param1 > AP_CAMERA_MAX_INSTANCES) {
439 0 : return MAV_RESULT_DENIED;
440 : }
441 0 : if (is_zero(packet.param1)) {
442 : // set camera source for all backends
443 0 : bool accepted = false;
444 0 : for (uint8_t i = 0; i < ARRAY_SIZE(_backends); i++) {
445 0 : if (_backends[i] != nullptr) {
446 0 : accepted |= set_camera_source(i, (AP_Camera::CameraSource)packet.param2, (AP_Camera::CameraSource)packet.param3);
447 : }
448 : }
449 0 : return accepted ? MAV_RESULT_ACCEPTED : MAV_RESULT_DENIED;
450 : }
451 0 : if (set_camera_source(packet.param1-1, (AP_Camera::CameraSource)packet.param2, (AP_Camera::CameraSource)packet.param3)) {
452 0 : return MAV_RESULT_ACCEPTED;
453 : }
454 0 : return MAV_RESULT_DENIED;
455 : #endif
456 :
457 0 : case MAV_CMD_IMAGE_START_CAPTURE:
458 : // param1 : camera id
459 : // param2 : interval (in seconds)
460 : // param3 : total num images
461 : // sanity check instance
462 0 : if (is_negative(packet.param1)) {
463 0 : return MAV_RESULT_UNSUPPORTED;
464 : }
465 : // check if this is a single picture request (e.g. total images is 1 or interval and total images are zero)
466 0 : if (is_equal(packet.param3, 1.0f) ||
467 0 : (is_zero(packet.param2) && is_zero(packet.param3))) {
468 0 : if (is_zero(packet.param1)) {
469 : // take pictures for every backend
470 0 : return take_picture() ? MAV_RESULT_ACCEPTED : MAV_RESULT_FAILED;
471 : }
472 : // take picture for specified instance
473 0 : return take_picture(packet.param1-1) ? MAV_RESULT_ACCEPTED : MAV_RESULT_FAILED;
474 0 : } else if (is_zero(packet.param3)) {
475 : // multiple picture request, take pictures forever
476 0 : if (is_zero(packet.param1)) {
477 : // take pictures for every backend
478 0 : return take_multiple_pictures(packet.param2*1000, -1) ? MAV_RESULT_ACCEPTED : MAV_RESULT_FAILED;
479 : }
480 0 : return take_multiple_pictures(packet.param1-1, packet.param2*1000, -1) ? MAV_RESULT_ACCEPTED : MAV_RESULT_FAILED;
481 : } else {
482 : // take multiple pictures equal to the number specified in param3
483 0 : if (is_zero(packet.param1)) {
484 : // take pictures for every backend
485 0 : return take_multiple_pictures(packet.param2*1000, packet.param3) ? MAV_RESULT_ACCEPTED : MAV_RESULT_FAILED;
486 : }
487 0 : return take_multiple_pictures(packet.param1-1, packet.param2*1000, packet.param3) ? MAV_RESULT_ACCEPTED : MAV_RESULT_FAILED;
488 : }
489 0 : case MAV_CMD_IMAGE_STOP_CAPTURE:
490 : // param1 : camera id
491 0 : if (is_negative(packet.param1)) {
492 0 : return MAV_RESULT_UNSUPPORTED;
493 : }
494 0 : if (is_zero(packet.param1)) {
495 : // stop capture for every backend
496 0 : stop_capture();
497 0 : return MAV_RESULT_ACCEPTED;
498 : }
499 0 : if (stop_capture(packet.param1-1)) {
500 0 : return MAV_RESULT_ACCEPTED;
501 : }
502 0 : return MAV_RESULT_UNSUPPORTED;
503 0 : case MAV_CMD_CAMERA_TRACK_POINT:
504 0 : if (set_tracking(TrackingType::TRK_POINT, Vector2f{packet.param1, packet.param2}, Vector2f{})) {
505 0 : return MAV_RESULT_ACCEPTED;
506 : }
507 0 : return MAV_RESULT_UNSUPPORTED;
508 0 : case MAV_CMD_CAMERA_TRACK_RECTANGLE:
509 0 : if (set_tracking(TrackingType::TRK_RECTANGLE, Vector2f{packet.param1, packet.param2}, Vector2f{packet.param3, packet.param4})) {
510 0 : return MAV_RESULT_ACCEPTED;
511 : }
512 0 : return MAV_RESULT_UNSUPPORTED;
513 0 : case MAV_CMD_CAMERA_STOP_TRACKING:
514 0 : if (set_tracking(TrackingType::TRK_NONE, Vector2f{}, Vector2f{})) {
515 0 : return MAV_RESULT_ACCEPTED;
516 : }
517 0 : return MAV_RESULT_UNSUPPORTED;
518 0 : case MAV_CMD_VIDEO_START_CAPTURE:
519 : case MAV_CMD_VIDEO_STOP_CAPTURE:
520 : {
521 0 : bool success = false;
522 0 : const bool start_recording = (packet.command == MAV_CMD_VIDEO_START_CAPTURE);
523 0 : const uint8_t stream_id = packet.param1; // Stream ID
524 0 : if (stream_id == 0) {
525 : // stream id of 0 interpreted as primary camera
526 0 : success = record_video(start_recording);
527 : } else {
528 : // convert stream id to instance id
529 0 : success = record_video(stream_id - 1, start_recording);
530 : }
531 0 : if (success) {
532 0 : return MAV_RESULT_ACCEPTED;
533 : } else {
534 0 : return MAV_RESULT_FAILED;
535 : }
536 : }
537 0 : default:
538 0 : return MAV_RESULT_UNSUPPORTED;
539 : }
540 : }
541 :
542 : // send a mavlink message; returns false if there was not space to
543 : // send the message, true otherwise
544 2884 : bool AP_Camera::send_mavlink_message(GCS_MAVLINK &link, const enum ap_message msg_id)
545 : {
546 2884 : const auto chan = link.get_chan();
547 :
548 2884 : switch (msg_id) {
549 2869 : case MSG_CAMERA_FEEDBACK:
550 2869 : CHECK_PAYLOAD_SIZE2(CAMERA_FEEDBACK);
551 2869 : send_feedback(chan);
552 2869 : break;
553 13 : case MSG_CAMERA_INFORMATION:
554 13 : CHECK_PAYLOAD_SIZE2(CAMERA_INFORMATION);
555 13 : if (_camera_information_send_instance >= 0) {
556 8 : const int16_t instance = _camera_information_send_instance;
557 8 : _camera_information_send_instance = -1;
558 8 : send_camera_information((uint8_t)instance, chan);
559 : } else {
560 5 : send_camera_information(chan);
561 : }
562 13 : break;
563 0 : case MSG_CAMERA_SETTINGS:
564 0 : CHECK_PAYLOAD_SIZE2(CAMERA_SETTINGS);
565 0 : send_camera_settings(chan);
566 0 : break;
567 : #if AP_CAMERA_SEND_FOV_STATUS_ENABLED
568 0 : case MSG_CAMERA_FOV_STATUS:
569 0 : CHECK_PAYLOAD_SIZE2(CAMERA_FOV_STATUS);
570 0 : send_camera_fov_status(chan);
571 0 : break;
572 : #endif
573 2 : case MSG_CAMERA_CAPTURE_STATUS:
574 2 : CHECK_PAYLOAD_SIZE2(CAMERA_CAPTURE_STATUS);
575 2 : send_camera_capture_status(chan);
576 2 : break;
577 : #if AP_CAMERA_SEND_THERMAL_RANGE_ENABLED
578 0 : case MSG_CAMERA_THERMAL_RANGE:
579 0 : CHECK_PAYLOAD_SIZE2(CAMERA_THERMAL_RANGE);
580 0 : send_camera_thermal_range(chan);
581 0 : break;
582 : #endif
583 : #if AP_MAVLINK_MSG_VIDEO_STREAM_INFORMATION_ENABLED
584 0 : case MSG_VIDEO_STREAM_INFORMATION:
585 0 : CHECK_PAYLOAD_SIZE2(VIDEO_STREAM_INFORMATION);
586 0 : send_video_stream_information(chan);
587 0 : break;
588 : #endif // AP_MAVLINK_MSG_VIDEO_STREAM_INFORMATION_ENABLED
589 :
590 0 : default:
591 : // should not reach this; should only be called for specific IDs
592 0 : break;
593 : }
594 :
595 2884 : return true;
596 : }
597 :
598 : // set camera trigger distance in a mission
599 0 : void AP_Camera::set_trigger_distance(uint8_t instance, float distance_m)
600 : {
601 0 : WITH_SEMAPHORE(_rsem);
602 :
603 0 : auto *backend = get_instance(instance);
604 0 : if (backend == nullptr) {
605 0 : return;
606 : }
607 :
608 : // call backend
609 0 : backend->set_trigger_distance(distance_m);
610 0 : }
611 :
612 : // momentary switch to change camera between picture and video modes
613 0 : void AP_Camera::cam_mode_toggle(uint8_t instance)
614 : {
615 0 : WITH_SEMAPHORE(_rsem);
616 :
617 0 : auto *backend = get_instance(instance);
618 0 : if (backend == nullptr) {
619 0 : return;
620 : }
621 :
622 : // call backend
623 0 : backend->cam_mode_toggle();
624 0 : }
625 :
626 : // configure camera
627 2 : void AP_Camera::configure(float shooting_mode, float shutter_speed, float aperture, float ISO, int32_t exposure_type, int32_t cmd_id, float engine_cutoff_time)
628 : {
629 2 : WITH_SEMAPHORE(_rsem);
630 :
631 2 : if (primary == nullptr) {
632 0 : return;
633 : }
634 2 : primary->configure(shooting_mode, shutter_speed, aperture, ISO, exposure_type, cmd_id, engine_cutoff_time);
635 2 : }
636 :
637 0 : void AP_Camera::configure(uint8_t instance, float shooting_mode, float shutter_speed, float aperture, float ISO, int32_t exposure_type, int32_t cmd_id, float engine_cutoff_time)
638 : {
639 0 : WITH_SEMAPHORE(_rsem);
640 :
641 0 : auto *backend = get_instance(instance);
642 0 : if (backend == nullptr) {
643 0 : return;
644 : }
645 :
646 : // call backend
647 0 : backend->configure(shooting_mode, shutter_speed, aperture, ISO, exposure_type, cmd_id, engine_cutoff_time);
648 0 : }
649 :
650 : // handle camera control
651 4 : void AP_Camera::control(float session, float zoom_pos, float zoom_step, float focus_lock, int32_t shooting_cmd, int32_t cmd_id)
652 : {
653 4 : WITH_SEMAPHORE(_rsem);
654 :
655 4 : if (primary == nullptr) {
656 1 : return;
657 : }
658 3 : primary->control(session, zoom_pos, zoom_step, focus_lock, shooting_cmd, cmd_id);
659 4 : }
660 :
661 0 : void AP_Camera::control(uint8_t instance, float session, float zoom_pos, float zoom_step, float focus_lock, int32_t shooting_cmd, int32_t cmd_id)
662 : {
663 0 : WITH_SEMAPHORE(_rsem);
664 :
665 0 : auto *backend = get_instance(instance);
666 0 : if (backend == nullptr) {
667 0 : return;
668 : }
669 :
670 : // call backend
671 0 : backend->control(session, zoom_pos, zoom_step, focus_lock, shooting_cmd, cmd_id);
672 0 : }
673 :
674 : /*
675 : Send camera feedback to the GCS
676 : */
677 2869 : void AP_Camera::send_feedback(mavlink_channel_t chan)
678 : {
679 2869 : WITH_SEMAPHORE(_rsem);
680 :
681 : // call each instance
682 8607 : for (uint8_t instance = 0; instance < AP_CAMERA_MAX_INSTANCES; instance++) {
683 5738 : if (_backends[instance] != nullptr) {
684 2917 : _backends[instance]->send_camera_feedback(chan);
685 : }
686 : }
687 2869 : }
688 :
689 : // send camera information message to GCS
690 5 : void AP_Camera::send_camera_information(mavlink_channel_t chan)
691 : {
692 5 : WITH_SEMAPHORE(_rsem);
693 :
694 : // call each instance
695 15 : for (uint8_t instance = 0; instance < AP_CAMERA_MAX_INSTANCES; instance++) {
696 10 : if (_backends[instance] != nullptr) {
697 5 : _backends[instance]->send_camera_information(chan);
698 : }
699 : }
700 5 : }
701 :
702 : // send camera information for a specific instance to GCS
703 8 : void AP_Camera::send_camera_information(uint8_t instance, mavlink_channel_t chan)
704 : {
705 8 : WITH_SEMAPHORE(_rsem);
706 :
707 8 : auto *backend = get_instance(instance);
708 8 : if (backend == nullptr) {
709 0 : return;
710 : }
711 8 : backend->send_camera_information(chan);
712 8 : }
713 :
714 : #if AP_MAVLINK_MSG_VIDEO_STREAM_INFORMATION_ENABLED
715 : // send video stream information message to GCS
716 0 : void AP_Camera::send_video_stream_information(mavlink_channel_t chan)
717 : {
718 0 : WITH_SEMAPHORE(_rsem);
719 :
720 : // call each instance
721 0 : for (uint8_t instance = 0; instance < AP_CAMERA_MAX_INSTANCES; instance++) {
722 0 : if (_backends[instance] != nullptr) {
723 0 : _backends[instance]->send_video_stream_information(chan);
724 : }
725 : }
726 0 : }
727 : #endif // AP_MAVLINK_MSG_VIDEO_STREAM_INFORMATION_ENABLED
728 :
729 : // send camera settings message to GCS
730 0 : void AP_Camera::send_camera_settings(mavlink_channel_t chan)
731 : {
732 0 : WITH_SEMAPHORE(_rsem);
733 :
734 : // call each instance
735 0 : for (uint8_t instance = 0; instance < AP_CAMERA_MAX_INSTANCES; instance++) {
736 0 : if (_backends[instance] != nullptr) {
737 0 : _backends[instance]->send_camera_settings(chan);
738 : }
739 : }
740 0 : }
741 :
742 : #if AP_CAMERA_SEND_FOV_STATUS_ENABLED
743 : // send camera field of view status
744 0 : void AP_Camera::send_camera_fov_status(mavlink_channel_t chan)
745 : {
746 0 : WITH_SEMAPHORE(_rsem);
747 :
748 : // call each instance
749 0 : for (uint8_t instance = 0; instance < AP_CAMERA_MAX_INSTANCES; instance++) {
750 0 : if (_backends[instance] != nullptr) {
751 0 : _backends[instance]->send_camera_fov_status(chan);
752 : }
753 : }
754 0 : }
755 : #endif
756 :
757 : // send camera capture status message to GCS
758 2 : void AP_Camera::send_camera_capture_status(mavlink_channel_t chan)
759 : {
760 2 : WITH_SEMAPHORE(_rsem);
761 :
762 : // call each instance
763 6 : for (uint8_t instance = 0; instance < AP_CAMERA_MAX_INSTANCES; instance++) {
764 4 : if (_backends[instance] != nullptr) {
765 4 : _backends[instance]->send_camera_capture_status(chan);
766 : }
767 : }
768 2 : }
769 :
770 : #if AP_CAMERA_SEND_THERMAL_RANGE_ENABLED
771 : // send camera thermal range message to GCS
772 0 : void AP_Camera::send_camera_thermal_range(mavlink_channel_t chan)
773 : {
774 0 : WITH_SEMAPHORE(_rsem);
775 :
776 : // call each instance
777 0 : for (uint8_t instance = 0; instance < AP_CAMERA_MAX_INSTANCES; instance++) {
778 0 : if (_backends[instance] != nullptr) {
779 0 : _backends[instance]->send_camera_thermal_range(chan);
780 : }
781 : }
782 0 : }
783 : #endif
784 :
785 : /*
786 : update; triggers by distance moved and camera trigger
787 : */
788 7438628 : void AP_Camera::update()
789 : {
790 7438628 : WITH_SEMAPHORE(_rsem);
791 :
792 : // call each instance
793 22315884 : for (uint8_t instance = 0; instance < AP_CAMERA_MAX_INSTANCES; instance++) {
794 14877256 : if (_backends[instance] != nullptr) {
795 70356 : _backends[instance]->update();
796 : }
797 : }
798 7438628 : }
799 :
800 : // start/stop recording video. returns true on success
801 : // start_recording should be true to start recording, false to stop recording
802 0 : bool AP_Camera::record_video(uint8_t instance, bool start_recording)
803 : {
804 0 : WITH_SEMAPHORE(_rsem);
805 :
806 0 : auto *backend = get_instance(instance);
807 0 : if (backend == nullptr) {
808 0 : return false;
809 : }
810 :
811 : // call backend
812 0 : return backend->record_video(start_recording);
813 0 : }
814 :
815 : // zoom specified as a rate or percentage
816 0 : bool AP_Camera::set_zoom(ZoomType zoom_type, float zoom_value)
817 : {
818 0 : WITH_SEMAPHORE(_rsem);
819 :
820 0 : if (primary == nullptr) {
821 0 : return false;
822 : }
823 0 : return primary->set_zoom(zoom_type, zoom_value);
824 0 : }
825 :
826 : // zoom specified as a rate or percentage
827 0 : bool AP_Camera::set_zoom(uint8_t instance, ZoomType zoom_type, float zoom_value)
828 : {
829 0 : WITH_SEMAPHORE(_rsem);
830 :
831 0 : auto *backend = get_instance(instance);
832 0 : if (backend == nullptr) {
833 0 : return false;
834 : }
835 :
836 : // call each instance
837 0 : return backend->set_zoom(zoom_type, zoom_value);
838 0 : }
839 :
840 :
841 : // set focus specified as rate, percentage or auto
842 : // focus in = -1, focus hold = 0, focus out = 1
843 0 : SetFocusResult AP_Camera::set_focus(FocusType focus_type, float focus_value)
844 : {
845 0 : WITH_SEMAPHORE(_rsem);
846 :
847 0 : if (primary == nullptr) {
848 0 : return SetFocusResult::FAILED;
849 : }
850 0 : return primary->set_focus(focus_type, focus_value);
851 0 : }
852 :
853 : // set focus specified as rate, percentage or auto
854 : // focus in = -1, focus hold = 0, focus out = 1
855 0 : SetFocusResult AP_Camera::set_focus(uint8_t instance, FocusType focus_type, float focus_value)
856 : {
857 0 : WITH_SEMAPHORE(_rsem);
858 :
859 0 : auto *backend = get_instance(instance);
860 0 : if (backend == nullptr) {
861 0 : return SetFocusResult::FAILED;
862 : }
863 :
864 : // call each instance
865 0 : return backend->set_focus(focus_type, focus_value);
866 0 : }
867 :
868 : // set tracking to none, point or rectangle (see TrackingType enum)
869 : // if POINT only p1 is used, if RECTANGLE then p1 is top-left, p2 is bottom-right
870 : // p1,p2 are in range 0 to 1. 0 is left or top, 1 is right or bottom
871 0 : bool AP_Camera::set_tracking(TrackingType tracking_type, const Vector2f& p1, const Vector2f& p2)
872 : {
873 0 : WITH_SEMAPHORE(_rsem);
874 :
875 0 : if (primary == nullptr) {
876 0 : return false;
877 : }
878 0 : return primary->set_tracking(tracking_type, p1, p2);
879 0 : }
880 :
881 : // set tracking to none, point or rectangle (see TrackingType enum)
882 : // if POINT only p1 is used, if RECTANGLE then p1 is top-left, p2 is bottom-right
883 : // p1,p2 are in range 0 to 1. 0 is left or top, 1 is right or bottom
884 0 : bool AP_Camera::set_tracking(uint8_t instance, TrackingType tracking_type, const Vector2f& p1, const Vector2f& p2)
885 : {
886 0 : WITH_SEMAPHORE(_rsem);
887 :
888 0 : auto *backend = get_instance(instance);
889 0 : if (backend == nullptr) {
890 0 : return false;
891 : }
892 :
893 : // call each instance
894 0 : return backend->set_tracking(tracking_type, p1, p2);
895 0 : }
896 :
897 : #if AP_CAMERA_SET_CAMERA_SOURCE_ENABLED
898 : // set camera lens as a value from 0 to 5
899 0 : bool AP_Camera::set_lens(uint8_t lens)
900 : {
901 0 : WITH_SEMAPHORE(_rsem);
902 :
903 0 : if (primary == nullptr) {
904 0 : return false;
905 : }
906 0 : return primary->set_lens(lens);
907 0 : }
908 :
909 0 : bool AP_Camera::set_lens(uint8_t instance, uint8_t lens)
910 : {
911 0 : WITH_SEMAPHORE(_rsem);
912 :
913 0 : auto *backend = get_instance(instance);
914 0 : if (backend == nullptr) {
915 0 : return false;
916 : }
917 :
918 : // call instance
919 0 : return backend->set_lens(lens);
920 0 : }
921 :
922 : // set_camera_source is functionally the same as set_lens except primary and secondary lenses are specified by type
923 0 : bool AP_Camera::set_camera_source(uint8_t instance, CameraSource primary_source, CameraSource secondary_source)
924 : {
925 0 : WITH_SEMAPHORE(_rsem);
926 :
927 0 : auto *backend = get_instance(instance);
928 0 : if (backend == nullptr) {
929 0 : return false;
930 : }
931 :
932 : // call instance
933 0 : return backend->set_camera_source(primary_source, secondary_source);
934 0 : }
935 : #endif // AP_CAMERA_SET_CAMERA_SOURCE_ENABLED
936 :
937 : #if AP_CAMERA_SCRIPTING_ENABLED
938 : // accessor to allow scripting backend to retrieve state
939 : // returns true on success and cam_state is filled in
940 0 : bool AP_Camera::get_state(uint8_t instance, camera_state_t& cam_state)
941 : {
942 0 : WITH_SEMAPHORE(_rsem);
943 :
944 0 : auto *backend = get_instance(instance);
945 0 : if (backend == nullptr) {
946 0 : return false;
947 : }
948 0 : return backend->get_state(cam_state);
949 0 : }
950 :
951 : // change camera settings not normally used by autopilot
952 0 : bool AP_Camera::change_setting(uint8_t instance, CameraSetting setting, float value)
953 : {
954 0 : WITH_SEMAPHORE(_rsem);
955 :
956 0 : auto *backend = get_instance(instance);
957 0 : if (backend == nullptr) {
958 0 : return false;
959 : }
960 0 : return backend->change_setting(setting, value);
961 0 : }
962 :
963 : #endif // #if AP_CAMERA_SCRIPTING_ENABLED
964 :
965 :
966 : #if AP_CAMERA_INFO_FROM_SCRIPT_ENABLED
967 0 : void AP_Camera::set_camera_information(mavlink_camera_information_t camera_info)
968 : {
969 0 : WITH_SEMAPHORE(_rsem);
970 :
971 0 : if (primary == nullptr) {
972 0 : return;
973 : }
974 0 : return primary->set_camera_information(camera_info);
975 0 : }
976 :
977 0 : void AP_Camera::set_camera_information(uint8_t instance, mavlink_camera_information_t camera_info)
978 : {
979 0 : WITH_SEMAPHORE(_rsem);
980 :
981 0 : auto *backend = get_instance(instance);
982 0 : if (backend == nullptr) {
983 0 : return;
984 : }
985 :
986 : // call instance
987 0 : backend->set_camera_information(camera_info);
988 0 : }
989 :
990 0 : void AP_Camera::set_stream_information(mavlink_video_stream_information_t stream_info)
991 : {
992 0 : WITH_SEMAPHORE(_rsem);
993 :
994 0 : if (primary == nullptr) {
995 0 : return;
996 : }
997 0 : return primary->set_stream_information(stream_info);
998 0 : }
999 :
1000 0 : void AP_Camera::set_stream_information(uint8_t instance, mavlink_video_stream_information_t stream_info)
1001 : {
1002 0 : WITH_SEMAPHORE(_rsem);
1003 :
1004 0 : auto *backend = get_instance(instance);
1005 0 : if (backend == nullptr) {
1006 0 : return;
1007 : }
1008 :
1009 : // call instance
1010 0 : backend->set_stream_information(stream_info);
1011 0 : }
1012 : #endif // AP_CAMERA_INFO_FROM_SCRIPT_ENABLED
1013 :
1014 : // return backend for instance number
1015 12 : AP_Camera_Backend *AP_Camera::get_instance(uint8_t instance) const
1016 : {
1017 12 : if (instance >= ARRAY_SIZE(_backends)) {
1018 0 : return nullptr;
1019 : }
1020 12 : return _backends[instance];
1021 : }
1022 :
1023 : #if AP_CAMERA_RUNCAM_ENABLED && (AP_CAMERA_MAX_INSTANCES > 1)
1024 : // Convert to runcam specific backend
1025 2059 : void AP_Camera::convert_runcam_params()
1026 : {
1027 : // exit immediately if CAM2_TYPE has already been configured
1028 2059 : if (_params[1].type.configured()) {
1029 1768 : return;
1030 : }
1031 :
1032 : // PARAMETER_CONVERSION - Added: Dec-2024 for ArduPilot-4.7
1033 :
1034 : // Since slot 1 is essentially used by the trigger type, we will use slot 2 for runcam
1035 291 : int8_t rc_type = 0;
1036 : // find vehicle's top level key
1037 : uint16_t k_param_vehicle_key;
1038 291 : if (!AP_Param::find_top_level_key_by_pointer(AP::vehicle(), k_param_vehicle_key)) {
1039 0 : return;
1040 : }
1041 :
1042 : // RunCam protocol configured so set cam type to RunCam
1043 291 : bool rc_protocol_configured = false;
1044 291 : AP_SerialManager *serial_manager = AP_SerialManager::get_singleton();
1045 291 : if (serial_manager && serial_manager->find_serial(AP_SerialManager::SerialProtocol_RunCam, 0)) {
1046 0 : rc_protocol_configured = true;
1047 : }
1048 :
1049 291 : const AP_Param::ConversionInfo rc_type_info = {
1050 : k_param_vehicle_key, AP_GROUP_ELEM_IDX(1, 1), AP_PARAM_INT8, "CAM_RC_TYPE"
1051 291 : };
1052 291 : AP_Int8 rc_type_old;
1053 291 : const bool found_rc_type = AP_Param::find_old_parameter(&rc_type_info, &rc_type_old);
1054 :
1055 291 : if (rc_protocol_configured || (found_rc_type && rc_type_old.get() > 0)) {
1056 0 : rc_type = int8_t(CameraType::RUNCAM);
1057 0 : _backends[1] = NEW_NOTHROW AP_RunCam(*this, _params[1], 1, _runcam_instances);
1058 0 : _backend_var_info[1] = AP_RunCam::var_info;
1059 0 : AP_Param::convert_class(k_param_vehicle_key, &_backends[1], _backend_var_info[1], 1, false);
1060 0 : AP_Param::invalidate_count();
1061 0 : _runcam_instances++;
1062 : }
1063 :
1064 291 : _params[1].type.set_and_save(rc_type);
1065 :
1066 : }
1067 : #endif // AP_CAMERA_RUNCAM_ENABLED && (AP_CAMERA_MAX_INSTANCES > 1)
1068 :
1069 : // perform any required parameter conversion
1070 2059 : void AP_Camera::convert_params()
1071 : {
1072 : // exit immediately if CAM1_TYPE has already been configured
1073 2059 : if (_params[0].type.configured()) {
1074 1768 : return;
1075 : }
1076 :
1077 : // PARAMETER_CONVERSION - Added: Feb-2023 for ArduPilot-4.4
1078 :
1079 : // convert CAM_TRIGG_TYPE to CAM1_TYPE
1080 291 : int8_t cam_trigg_type = 0;
1081 291 : int8_t cam1_type = 0;
1082 291 : IGNORE_RETURN(AP_Param::get_param_by_index(this, 0, AP_PARAM_INT8, &cam_trigg_type));
1083 291 : if ((cam_trigg_type == 0) && SRV_Channels::function_assigned(SRV_Channel::k_cam_trigger)) {
1084 : // CAM_TRIGG_TYPE was 0 (Servo) and camera trigger servo function was assigned so set CAM1_TYPE = 1 (Servo)
1085 0 : cam1_type = 1;
1086 : }
1087 291 : if ((cam_trigg_type >= 1) && (cam_trigg_type <= 3)) {
1088 : // CAM_TRIGG_TYPE was set to Relay, GoPro or Mount
1089 0 : cam1_type = cam_trigg_type + 1;
1090 : }
1091 291 : _params[0].type.set_and_save(cam1_type);
1092 :
1093 : // convert CAM_DURATION (in deci-seconds) to CAM1_DURATION (in seconds)
1094 291 : int8_t cam_duration = 0;
1095 291 : if (AP_Param::get_param_by_index(this, 1, AP_PARAM_INT8, &cam_duration) && (cam_duration > 0)) {
1096 0 : _params[0].trigger_duration.set_and_save(cam_duration * 0.1);
1097 : }
1098 :
1099 : // convert CAM_MIN_INTERVAL (in milliseconds) to CAM1__INTRVAL_MIN (in seconds)
1100 291 : int16_t cam_min_interval = 0;
1101 291 : if (AP_Param::get_param_by_index(this, 6, AP_PARAM_INT16, &cam_min_interval) && (cam_min_interval > 0)) {
1102 0 : _params[0].interval_min.set_and_save(cam_min_interval * 0.001f);
1103 : }
1104 :
1105 : // find Camera's top level key
1106 : uint16_t k_param_camera_key;
1107 291 : if (!AP_Param::find_top_level_key_by_pointer(this, k_param_camera_key)) {
1108 0 : return;
1109 : }
1110 :
1111 : // table parameters to convert without scaling
1112 : static const AP_Param::ConversionInfo camera_param_conversion_info[] {
1113 : { k_param_camera_key, 2, AP_PARAM_INT16, "CAM1_SERVO_ON" },
1114 : { k_param_camera_key, 3, AP_PARAM_INT16, "CAM1_SERVO_OFF" },
1115 : { k_param_camera_key, 4, AP_PARAM_FLOAT, "CAM1_TRIGG_DIST" },
1116 : { k_param_camera_key, 5, AP_PARAM_INT8, "CAM1_RELAY_ON" },
1117 : { k_param_camera_key, 8, AP_PARAM_INT8, "CAM1_FEEDBAK_PIN" },
1118 : { k_param_camera_key, 9, AP_PARAM_INT8, "CAM1_FEEDBAK_POL" },
1119 291 : };
1120 291 : uint8_t table_size = ARRAY_SIZE(camera_param_conversion_info);
1121 2037 : for (uint8_t i=0; i<table_size; i++) {
1122 1746 : AP_Param::convert_old_parameter(&camera_param_conversion_info[i], 1.0f);
1123 : }
1124 : }
1125 :
1126 : #if AP_RELAY_ENABLED
1127 : // Return true and the relay index if relay camera backend is selected, used for conversion to relay functions
1128 2059 : bool AP_Camera::get_legacy_relay_index(int8_t &index) const
1129 : {
1130 : // PARAMETER_CONVERSION - Added: Dec-2023
1131 :
1132 : // Note that this assumes that the camera param conversion has already been done
1133 : // Copter, Plane, Sub and Rover all have both relay and camera and all init relay first
1134 : // This will only be a issue if the relay and camera conversion were done at once, if the user skipped 4.4
1135 6177 : for (uint8_t i = 0; i < AP_CAMERA_MAX_INSTANCES; i++) {
1136 : #if AP_CAMERA_RELAY_ENABLED
1137 4118 : if ((CameraType)_params[i].type.get() == CameraType::RELAY) {
1138 : // Camera was hard coded to relay 0
1139 0 : index = 0;
1140 0 : return true;
1141 : }
1142 : #endif
1143 : }
1144 2059 : return false;
1145 : }
1146 : #endif
1147 :
1148 : // singleton instance
1149 : AP_Camera *AP_Camera::_singleton;
1150 :
1151 : namespace AP {
1152 :
1153 4996 : AP_Camera *camera()
1154 : {
1155 4996 : return AP_Camera::get_singleton();
1156 : }
1157 :
1158 : }
1159 :
1160 : #endif
|