Line data Source code
1 : /// @file AP_Mission.cpp
2 : /// @brief Handles the MAVLINK command mission stack. Reads and writes mission to storage.
3 :
4 : #include "AP_Mission_config.h"
5 :
6 : #if AP_MISSION_ENABLED
7 :
8 : #include <AP_AHRS/AP_AHRS.h>
9 : #include <AP_BoardConfig/AP_BoardConfig.h>
10 : #include <AP_Camera/AP_Camera.h>
11 : #include <AP_Gripper/AP_Gripper_config.h>
12 : #include "AP_Mission.h"
13 : #include <AP_Scripting/AP_Scripting.h>
14 : #include <AP_ServoRelayEvents/AP_ServoRelayEvents_config.h>
15 : #include <AP_Terrain/AP_Terrain.h>
16 : #include <AP_Vehicle/AP_Vehicle_Type.h>
17 : #include <GCS_MAVLink/GCS.h>
18 : #include <RC_Channel/RC_Channel_config.h>
19 : #include <AC_Fence/AC_Fence.h>
20 : #include <AP_Logger/AP_Logger.h>
21 :
22 : const AP_Param::GroupInfo AP_Mission::var_info[] = {
23 :
24 : // @Param: TOTAL
25 : // @DisplayName: Total mission commands
26 : // @Description: The number of mission mission items that has been loaded by the ground station. Do not change this manually.
27 : // @Range: 0 32766
28 : // @Increment: 1
29 : // @User: Advanced
30 : // @ReadOnly: True
31 : AP_GROUPINFO_FLAGS("TOTAL", 0, AP_Mission, _cmd_total, 0, AP_PARAM_FLAG_INTERNAL_USE_ONLY),
32 :
33 : // @Param: RESTART
34 : // @DisplayName: Mission Restart when entering Auto mode
35 : // @Description: Controls mission starting point when entering Auto mode (either restart from beginning of mission or resume from last command run)
36 : // @Values: 0:Resume Mission, 1:Restart Mission
37 : // @User: Advanced
38 : AP_GROUPINFO("RESTART", 1, AP_Mission, _restart, AP_MISSION_RESTART_DEFAULT),
39 :
40 : // @Param: OPTIONS
41 : // @DisplayName: Mission options bitmask
42 : // @Description: Bitmask of what options to use in missions. If the DontZeroCounter counter option is set than on completion of a jump loop the counter is left at zero, so the jump will not happen again if the loop is re-entered.
43 : // @Bitmask: 0:Clear Mission on reboot, 1:Use distance to land calc on battery failsafe,2:ContinueAfterLand, 3:DontZeroCounter
44 : // @Bitmask{Copter}: 0:Clear Mission on reboot, 2:ContinueAfterLand
45 : // @Bitmask{Rover, Sub}: 0:Clear Mission on reboot
46 : // @User: Advanced
47 : AP_GROUPINFO("OPTIONS", 2, AP_Mission, _options, AP_MISSION_OPTIONS_DEFAULT),
48 :
49 : AP_GROUPEND
50 : };
51 :
52 : extern const AP_HAL::HAL& hal;
53 :
54 : // storage object
55 : StorageAccess AP_Mission::_storage(StorageManager::StorageMission);
56 :
57 : HAL_Semaphore AP_Mission::_rsem;
58 :
59 : ///
60 : /// public mission methods
61 : ///
62 :
63 : /// init - initialises this library including checks the version in eeprom matches this library
64 2054 : void AP_Mission::init()
65 : {
66 : #if AP_SDCARD_STORAGE_ENABLED
67 : // check for extra storage on microsd
68 2054 : const auto *bc = AP::boardConfig();
69 2054 : if (bc != nullptr) {
70 2054 : const auto size_kb = bc->get_sdcard_mission_kb();
71 2054 : if (size_kb > 0) {
72 2 : _failed_sdcard_storage = !_storage.attach_file(AP_MISSION_SDCARD_FILENAME, size_kb);
73 2 : if (_failed_sdcard_storage) {
74 : // wipe mission if storage not available, but don't save. This allows sdcard error to be fixed and reboot
75 0 : _cmd_total.set(0);
76 : }
77 : }
78 : }
79 : #endif
80 :
81 : // work out maximum index for our storage size
82 2054 : if (_storage.size() >= AP_MISSION_EEPROM_COMMAND_SIZE+4) {
83 2054 : _commands_max = (_storage.size()-4U) / AP_MISSION_EEPROM_COMMAND_SIZE;
84 : }
85 2054 : if (_cmd_total.get() > _commands_max) {
86 : // wipe mission if storage not available, but don't save. This allows sdcard error to be fixed and reboot
87 1 : _cmd_total.set(0);
88 : }
89 :
90 :
91 : // check_eeprom_version - checks version of missions stored in eeprom matches this library
92 : // command list will be cleared if they do not match
93 2054 : check_eeprom_version();
94 :
95 : // initialize the jump tracking array
96 2054 : init_jump_tracking();
97 :
98 : // If Mission Clear bit is set then it should clear the mission, otherwise retain the mission.
99 2054 : if (option_is_set(Option::CLEAR_ON_BOOT)) {
100 1 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Clearing Mission");
101 1 : clear();
102 : }
103 :
104 2054 : _last_change_time_ms = AP_HAL::millis();
105 2054 : }
106 :
107 : /// start - resets current commands to point to the beginning of the mission
108 : /// To-Do: should we validate the mission first and return true/false?
109 96 : void AP_Mission::start()
110 : {
111 96 : _flags.state = MISSION_RUNNING;
112 :
113 96 : reset(); // reset mission to the first command, resets jump tracking
114 :
115 : // advance to the first command
116 96 : if (!advance_current_nav_cmd()) {
117 : // on failure set mission complete
118 2 : complete();
119 : }
120 96 : }
121 :
122 : /// stop - stops mission execution. subsequent calls to update() will have no effect until the mission is started or resumed
123 178 : void AP_Mission::stop()
124 : {
125 178 : _flags.state = MISSION_STOPPED;
126 178 : }
127 :
128 : /// resume - continues the mission execution from where we last left off
129 : /// previous running commands will be re-initialized
130 365 : void AP_Mission::resume()
131 : {
132 : // if mission had completed then start it from the first command
133 365 : if (_flags.state == MISSION_COMPLETE) {
134 24 : start();
135 24 : return;
136 : }
137 :
138 : // if mission had stopped then restart it
139 341 : if (_flags.state == MISSION_STOPPED) {
140 317 : _flags.state = MISSION_RUNNING;
141 :
142 : // if no valid nav command index restart from beginning
143 317 : if (_nav_cmd.index == AP_MISSION_CMD_INDEX_NONE) {
144 62 : start();
145 62 : return;
146 : }
147 : }
148 :
149 : // ensure cache coherence
150 279 : if (!read_cmd_from_storage(_nav_cmd.index, _nav_cmd)) {
151 : // if we failed to read the command from storage, then the command may have
152 : // been from a previously loaded mission it is illogical to ever resume
153 : // flying to a command that has been excluded from the current mission
154 10 : start();
155 10 : return;
156 : }
157 :
158 : // rewind the mission wp if the repeat distance has been set via MAV_CMD_DO_SET_RESUME_REPEAT_DIST
159 269 : if (_repeat_dist > 0 && _wp_index_history[LAST_WP_PASSED] != AP_MISSION_CMD_INDEX_NONE) {
160 : // if not already in a resume state calculate the position to rewind to
161 0 : Mission_Command tmp_cmd;
162 0 : if (!_flags.resuming_mission && calc_rewind_pos(tmp_cmd)) {
163 0 : _resume_cmd = tmp_cmd;
164 : }
165 :
166 : // resume mission to rewound position
167 0 : if (_resume_cmd.index != AP_MISSION_CMD_INDEX_NONE && start_command(_resume_cmd)) {
168 0 : _nav_cmd = _resume_cmd;
169 0 : _flags.nav_cmd_loaded = true;
170 : // set flag to prevent history being re-written
171 0 : _flags.resuming_mission = true;
172 0 : return;
173 : }
174 : }
175 :
176 : // restart active navigation command. We run these on resume()
177 : // regardless of whether the mission was stopped, as we may be
178 : // re-entering AUTO mode and the nav_cmd callback needs to be run
179 : // to setup the current target waypoint
180 :
181 269 : if (_flags.do_cmd_loaded && _do_cmd.index != AP_MISSION_CMD_INDEX_NONE) {
182 : // restart the active do command, which will also load the nav command for us
183 61 : set_current_cmd(_do_cmd.index);
184 208 : } else if (_flags.nav_cmd_loaded) {
185 : // restart the active nav command
186 208 : set_current_cmd(_nav_cmd.index);
187 : }
188 :
189 : // Note: if there is no active command then the mission must have been stopped just after the previous nav command completed
190 : // update will take care of finding and starting the nav command
191 : }
192 :
193 : /// check if the next nav command is a takeoff, skipping delays
194 1356 : bool AP_Mission::is_takeoff_next(uint16_t cmd_index)
195 : {
196 1356 : Mission_Command cmd = {};
197 : // check a maximum of 16 items, remembering that missions can have
198 : // loops in them
199 1357 : for (uint8_t i=0; i<16; i++, cmd_index++) {
200 1357 : if (!get_next_nav_cmd(cmd_index, cmd)) {
201 27 : return false;
202 : }
203 1330 : switch (cmd.id) {
204 : // any of these are considered a takeoff command:
205 593 : case MAV_CMD_NAV_VTOL_TAKEOFF:
206 : case MAV_CMD_NAV_TAKEOFF:
207 : case MAV_CMD_NAV_TAKEOFF_LOCAL:
208 593 : return true;
209 : // any of these are considered "skippable" when determining if
210 : // we "start with a takeoff command"
211 1 : case MAV_CMD_DO_AUX_FUNCTION:
212 : case MAV_CMD_NAV_DELAY:
213 1 : continue;
214 736 : default:
215 736 : return false;
216 : }
217 : }
218 0 : return false;
219 : }
220 :
221 : /// check mission starts with a takeoff command
222 1356 : bool AP_Mission::starts_with_takeoff_cmd()
223 : {
224 1356 : uint16_t cmd_index = _restart ? AP_MISSION_CMD_INDEX_NONE : _nav_cmd.index;
225 1356 : if (cmd_index == AP_MISSION_CMD_INDEX_NONE) {
226 4 : cmd_index = AP_MISSION_FIRST_REAL_COMMAND;
227 : }
228 1356 : return is_takeoff_next(cmd_index);
229 : }
230 :
231 : /*
232 : return true if MIS_OPTIONS is set to allow continue of mission
233 : logic after a land and the next waypoint is a takeoff. If this
234 : is false then after a landing is complete the vehicle should
235 : disarm and mission logic should stop
236 : */
237 9 : bool AP_Mission::continue_after_land_check_for_takeoff()
238 : {
239 9 : if (!continue_after_land()) {
240 9 : return false;
241 : }
242 0 : if (_nav_cmd.index == AP_MISSION_CMD_INDEX_NONE) {
243 0 : return false;
244 : }
245 0 : return is_takeoff_next(_nav_cmd.index+1);
246 : }
247 :
248 : /// start_or_resume - if MIS_AUTORESTART=0 this will call resume(), otherwise it will call start()
249 339 : void AP_Mission::start_or_resume()
250 : {
251 339 : if (_restart == 1 && !_force_resume) {
252 0 : start();
253 : } else {
254 339 : resume();
255 339 : _force_resume = false;
256 : }
257 339 : }
258 :
259 : /// reset - reset mission to the first command
260 1037 : void AP_Mission::reset()
261 : {
262 1037 : _flags.nav_cmd_loaded = false;
263 1037 : _flags.do_cmd_loaded = false;
264 1037 : _flags.do_cmd_all_done = false;
265 1037 : _flags.in_landing_sequence = false;
266 1037 : _flags.in_return_path = false;
267 1037 : _nav_cmd.index = AP_MISSION_CMD_INDEX_NONE;
268 1037 : _do_cmd.index = AP_MISSION_CMD_INDEX_NONE;
269 1037 : _prev_nav_cmd_index = AP_MISSION_CMD_INDEX_NONE;
270 1037 : _prev_nav_cmd_wp_index = AP_MISSION_CMD_INDEX_NONE;
271 1037 : _prev_nav_cmd_id = AP_MISSION_CMD_ID_NONE;
272 1037 : init_jump_tracking();
273 1037 : reset_wp_history();
274 1037 : }
275 :
276 : /// clear - clears out mission
277 : /// returns false if mission was running so it could not be cleared
278 300 : bool AP_Mission::clear()
279 : {
280 : // do not allow clearing the mission while it is running unless disarmed
281 300 : if (hal.util->get_soft_armed() && _flags.state == MISSION_RUNNING) {
282 0 : return false;
283 : }
284 :
285 : // remove all commands
286 300 : truncate(0);
287 :
288 : // clear index to commands
289 300 : _nav_cmd.index = AP_MISSION_CMD_INDEX_NONE;
290 300 : _do_cmd.index = AP_MISSION_CMD_INDEX_NONE;
291 300 : _flags.nav_cmd_loaded = false;
292 300 : _flags.do_cmd_loaded = false;
293 300 : _flags.state = MISSION_STOPPED;
294 : // return success
295 300 : return true;
296 : }
297 :
298 :
299 : /// trucate - truncate any mission items beyond index
300 1584 : void AP_Mission::truncate(uint16_t index)
301 : {
302 1584 : if ((unsigned)_cmd_total > index) {
303 699 : _cmd_total.set_and_save(index);
304 699 : _last_change_time_ms = AP_HAL::millis();
305 : }
306 1584 : }
307 :
308 : /// update - ensures the command queues are loaded with the next command and calls main programs command_init and command_verify functions to progress the mission
309 : /// should be called at 10hz or higher
310 4363001 : void AP_Mission::update()
311 : {
312 : // exit immediately if not running or no mission commands
313 4363001 : if (_flags.state != MISSION_RUNNING || _cmd_total == 0) {
314 115761 : return;
315 : }
316 :
317 4247240 : update_exit_position();
318 :
319 : // mission_change events
320 4247240 : if (_last_change_time_prev_ms != _last_change_time_ms) {
321 340 : _last_change_time_prev_ms = _last_change_time_ms;
322 340 : on_mission_timestamp_change();
323 : }
324 :
325 : // save persistent waypoint_num for watchdog restore
326 4247240 : hal.util->persistent_data.waypoint_num = _nav_cmd.index;
327 :
328 : // check if we have an active nav command
329 4247240 : if (!_flags.nav_cmd_loaded || _nav_cmd.index == AP_MISSION_CMD_INDEX_NONE) {
330 : // advance in mission if no active nav command
331 19 : if (!advance_current_nav_cmd()) {
332 : // failure to advance nav command means mission has completed
333 0 : complete();
334 0 : return;
335 : }
336 : } else {
337 : // run the active nav command
338 4247221 : if (verify_command(_nav_cmd)) {
339 : // market _nav_cmd as complete (it will be started on the next iteration)
340 1436 : _flags.nav_cmd_loaded = false;
341 : // immediately advance to the next mission command
342 1436 : if (!advance_current_nav_cmd()) {
343 : // failure to advance nav command means mission has completed
344 82 : complete();
345 82 : return;
346 : }
347 : }
348 : }
349 :
350 : // check if we have an active do command
351 4247158 : if (!_flags.do_cmd_loaded) {
352 4236782 : advance_current_do_cmd();
353 : } else {
354 : // check the active do command
355 10376 : if (verify_command(_do_cmd)) {
356 : // mark _do_cmd as complete
357 151 : _flags.do_cmd_loaded = false;
358 : }
359 : }
360 : }
361 :
362 : // handle events for when the mission has been updated (but maybe not changed)
363 340 : void AP_Mission::on_mission_timestamp_change()
364 : {
365 340 : _jump_tag.age = 0;
366 340 : }
367 :
368 4257597 : bool AP_Mission::verify_command(const Mission_Command& cmd)
369 : {
370 4257597 : switch (cmd.id) {
371 : // do-commands always return true for verify:
372 : #if AP_GRIPPER_ENABLED
373 38 : case MAV_CMD_DO_GRIPPER:
374 : #endif
375 : case MAV_CMD_DO_SET_SERVO:
376 : case MAV_CMD_DO_SET_RELAY:
377 : case MAV_CMD_DO_REPEAT_SERVO:
378 : case MAV_CMD_DO_REPEAT_RELAY:
379 : case MAV_CMD_DO_DIGICAM_CONFIGURE:
380 : case MAV_CMD_DO_DIGICAM_CONTROL:
381 : case MAV_CMD_DO_SET_CAM_TRIGG_DIST:
382 : #if AP_MISSION_MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET_ENABLED
383 : case MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET:
384 : #endif // AP_MISSION_MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET_ENABLED
385 : case MAV_CMD_DO_PARACHUTE:
386 : case MAV_CMD_DO_SEND_SCRIPT_MESSAGE:
387 : case MAV_CMD_DO_SPRAYER:
388 : case MAV_CMD_DO_AUX_FUNCTION:
389 : case MAV_CMD_DO_SET_RESUME_REPEAT_DIST:
390 : case MAV_CMD_DO_GIMBAL_MANAGER_PITCHYAW:
391 : case MAV_CMD_JUMP_TAG:
392 : case MAV_CMD_IMAGE_START_CAPTURE:
393 : case MAV_CMD_IMAGE_STOP_CAPTURE:
394 : case MAV_CMD_SET_CAMERA_ZOOM:
395 : case MAV_CMD_SET_CAMERA_FOCUS:
396 : case MAV_CMD_SET_CAMERA_SOURCE:
397 : case MAV_CMD_VIDEO_START_CAPTURE:
398 : case MAV_CMD_VIDEO_STOP_CAPTURE:
399 38 : return true;
400 4257559 : default:
401 4257559 : return _cmd_verify_fn(cmd);
402 : }
403 : }
404 :
405 2030 : bool AP_Mission::start_command(const Mission_Command& cmd)
406 : {
407 : #if HAL_LOGGING_ENABLED
408 2030 : if (log_start_mission_item_bit != (uint32_t)-1) {
409 2030 : auto &logger = AP::logger();
410 2030 : if (logger.should_log(log_start_mission_item_bit)) {
411 2026 : logger.Write_MISE(*this, cmd);
412 : }
413 : }
414 : #endif
415 :
416 : // check for landing related commands and set flags
417 2030 : if (is_landing_type_cmd(cmd.id) || cmd.id == MAV_CMD_DO_LAND_START) {
418 234 : _flags.in_landing_sequence = true;
419 :
420 1796 : } else if (cmd.id == MAV_CMD_DO_RETURN_PATH_START) {
421 0 : _flags.in_return_path = true;
422 :
423 1796 : } else if (is_takeoff_type_cmd(cmd.id)) {
424 : // Clear landing and return path flags on takeoff
425 283 : _flags.in_landing_sequence = false;
426 283 : _flags.in_return_path = false;
427 :
428 : }
429 :
430 2030 : switch (cmd.id) {
431 1 : case MAV_CMD_DO_JUMP:
432 : case MAV_CMD_JUMP_TAG:
433 : case MAV_CMD_DO_JUMP_TAG:
434 1 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Mission: %u %s %u", cmd.index, cmd.type(), (unsigned)cmd.p1);
435 1 : break;
436 :
437 2029 : default:
438 2029 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Mission: %u %s", cmd.index, cmd.type());
439 2029 : break;
440 :
441 : }
442 :
443 2030 : switch (cmd.id) {
444 : #if AP_RC_CHANNEL_ENABLED
445 1 : case MAV_CMD_DO_AUX_FUNCTION:
446 1 : return start_command_do_aux_function(cmd);
447 : #endif
448 : #if AP_GRIPPER_ENABLED
449 12 : case MAV_CMD_DO_GRIPPER:
450 12 : return start_command_do_gripper(cmd);
451 : #endif
452 : #if AP_SERVORELAYEVENTS_ENABLED
453 0 : case MAV_CMD_DO_SET_SERVO:
454 : case MAV_CMD_DO_SET_RELAY:
455 : case MAV_CMD_DO_REPEAT_SERVO:
456 : case MAV_CMD_DO_REPEAT_RELAY:
457 0 : return start_command_do_servorelayevents(cmd);
458 : #endif
459 : #if AP_CAMERA_ENABLED
460 21 : case MAV_CMD_DO_DIGICAM_CONFIGURE:
461 : case MAV_CMD_DO_DIGICAM_CONTROL:
462 : case MAV_CMD_DO_SET_CAM_TRIGG_DIST:
463 : case MAV_CMD_IMAGE_START_CAPTURE:
464 : case MAV_CMD_IMAGE_STOP_CAPTURE:
465 : case MAV_CMD_SET_CAMERA_ZOOM:
466 : case MAV_CMD_SET_CAMERA_FOCUS:
467 : case MAV_CMD_SET_CAMERA_SOURCE:
468 : case MAV_CMD_VIDEO_START_CAPTURE:
469 : case MAV_CMD_VIDEO_STOP_CAPTURE:
470 21 : return start_command_camera(cmd);
471 : #endif
472 : #if AP_FENCE_ENABLED
473 0 : case MAV_CMD_DO_FENCE_ENABLE:
474 0 : return start_command_fence(cmd);
475 : #endif
476 3 : case MAV_CMD_DO_PARACHUTE:
477 3 : return start_command_parachute(cmd);
478 0 : case MAV_CMD_DO_SEND_SCRIPT_MESSAGE:
479 0 : return start_command_do_scripting(cmd);
480 1 : case MAV_CMD_DO_SPRAYER:
481 1 : return start_command_do_sprayer(cmd);
482 0 : case MAV_CMD_DO_SET_RESUME_REPEAT_DIST:
483 0 : return command_do_set_repeat_dist(cmd);
484 0 : case MAV_CMD_DO_GIMBAL_MANAGER_PITCHYAW:
485 0 : return start_command_do_gimbal_manager_pitchyaw(cmd);
486 : #if AP_MISSION_MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET_ENABLED
487 3 : case MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET:
488 3 : return start_command_do_set_roi_wpnext_offset(cmd);
489 : #endif // AP_MISSION_MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET_ENABLED
490 1 : case MAV_CMD_JUMP_TAG:
491 1 : _jump_tag.tag = cmd.content.jump.target;
492 1 : _jump_tag.age = 1;
493 : FALLTHROUGH; // fall through in case the vehicle handles tag events
494 1989 : default:
495 1989 : return _cmd_start_fn(cmd);
496 : }
497 : }
498 :
499 : ///
500 : /// public command methods
501 : ///
502 :
503 : /// add_cmd - adds a command to the end of the command list and writes to storage
504 : /// returns true if successfully added, false on failure
505 : /// cmd.index is updated with it's new position in the mission
506 7159 : bool AP_Mission::add_cmd(Mission_Command& cmd)
507 : {
508 : // Add home if its not already present
509 7159 : if (_cmd_total < 1) {
510 112 : write_home_to_storage();
511 : }
512 :
513 : // attempt to write the command to storage
514 7159 : bool ret = write_cmd_to_storage(_cmd_total, cmd);
515 :
516 7159 : if (ret) {
517 : // update command's index
518 7159 : cmd.index = _cmd_total;
519 : // increment total number of commands
520 7159 : _cmd_total.set_and_save(_cmd_total + 1);
521 : }
522 :
523 7159 : return ret;
524 : }
525 :
526 : /// replace_cmd - replaces the command at position 'index' in the command list with the provided cmd
527 : /// replacing the current active command will have no effect until the command is restarted
528 : /// returns true if successfully replaced, false on failure
529 1498 : bool AP_Mission::replace_cmd(uint16_t index, const Mission_Command& cmd)
530 : {
531 : // sanity check index
532 1498 : if (index >= (unsigned)_cmd_total) {
533 0 : return false;
534 : }
535 :
536 : // Writing index zero is not allowed, it must be home
537 1498 : if (index == 0) {
538 : // Really should be returning false in this case, but in order to not break things we return true
539 220 : return true;
540 : }
541 :
542 : // attempt to write the command to storage
543 1278 : return write_cmd_to_storage(index, cmd);
544 : }
545 :
546 : /// is_nav_cmd - returns true if the command's id is a "navigation" command, false if "do" or "conditional" command
547 29967 : bool AP_Mission::is_nav_cmd(const Mission_Command& cmd)
548 : {
549 : // NAV commands all have ids below MAV_CMD_NAV_LAST, plus some exceptions
550 30799 : return (cmd.id <= MAV_CMD_NAV_LAST ||
551 832 : cmd.id == MAV_CMD_NAV_SET_YAW_SPEED ||
552 31631 : cmd.id == MAV_CMD_NAV_SCRIPT_TIME ||
553 30799 : cmd.id == MAV_CMD_NAV_ATTITUDE_TIME);
554 : }
555 :
556 : /// get_next_nav_cmd - gets next "navigation" command found at or after start_index
557 : /// returns true if found, false if not found (i.e. reached end of mission command list)
558 : /// accounts for do_jump commands but never increments the jump's num_times_run (advance_current_nav_cmd is responsible for this)
559 25077 : bool AP_Mission::get_next_nav_cmd(uint16_t start_index, Mission_Command& cmd)
560 : {
561 : // search until the end of the mission command list
562 25459 : for (uint16_t cmd_index = start_index; cmd_index < (unsigned)_cmd_total; cmd_index++) {
563 : // get next command
564 24737 : if (!get_next_cmd(cmd_index, cmd, false)) {
565 : // no more commands so return failure
566 5 : return false;
567 : }
568 : // if found a "navigation" command then return it
569 24732 : if (is_nav_cmd(cmd)) {
570 24350 : return true;
571 : }
572 : }
573 :
574 : // if we got this far we did not find a navigation command
575 722 : return false;
576 : }
577 :
578 : /// get the ground course of the next navigation leg in centidegrees
579 : /// from 0 36000. Return default_angle if next navigation
580 : /// leg cannot be determined
581 4165 : int32_t AP_Mission::get_next_ground_course_cd(int32_t default_angle)
582 : {
583 4165 : Mission_Command cmd;
584 4165 : if (!get_next_nav_cmd(_nav_cmd.index+1, cmd)) {
585 252 : return default_angle;
586 : }
587 : // special handling for nav commands with no target location
588 3913 : switch (cmd.id) {
589 0 : case MAV_CMD_NAV_GUIDED_ENABLE:
590 : case MAV_CMD_NAV_DELAY:
591 0 : return default_angle;
592 :
593 0 : case MAV_CMD_NAV_SET_YAW_SPEED:
594 0 : return (_nav_cmd.content.set_yaw_speed.angle_deg * 100);
595 :
596 3913 : default:
597 3913 : break;
598 : }
599 :
600 3913 : return _nav_cmd.content.location.get_bearing_to(cmd.content.location);
601 : }
602 :
603 : // set_current_cmd - jumps to command specified by index
604 2612 : bool AP_Mission::set_current_cmd(uint16_t index)
605 : {
606 : // Clear flags
607 2612 : _flags.in_landing_sequence = false;
608 2612 : _flags.in_return_path = false;
609 :
610 : // read command to check for DO_LAND_START and DO_RETURN_PATH_START
611 2612 : Mission_Command cmd;
612 2612 : if (read_cmd_from_storage(index, cmd)) {
613 2560 : switch (cmd.id) {
614 65 : case MAV_CMD_DO_LAND_START:
615 65 : _flags.in_landing_sequence = true;
616 65 : break;
617 :
618 0 : case MAV_CMD_DO_RETURN_PATH_START:
619 0 : _flags.in_return_path = true;
620 0 : break;
621 :
622 2495 : default:
623 2495 : break;
624 :
625 : }
626 : }
627 :
628 : // mission command has been set, don't track history.
629 2612 : reset_wp_history();
630 :
631 : // sanity check index and that we have a mission
632 2612 : if (index >= (unsigned)_cmd_total || _cmd_total == 1) {
633 1926 : return false;
634 : }
635 :
636 : // stop the current running do command
637 686 : _do_cmd.index = AP_MISSION_CMD_INDEX_NONE;
638 686 : _flags.do_cmd_loaded = false;
639 686 : _flags.do_cmd_all_done = false;
640 :
641 : // stop current nav cmd
642 686 : _flags.nav_cmd_loaded = false;
643 :
644 : // if index is zero then the user wants to completely restart the mission
645 686 : if (index == 0 || _flags.state == MISSION_COMPLETE) {
646 275 : _prev_nav_cmd_id = AP_MISSION_CMD_ID_NONE;
647 275 : _prev_nav_cmd_index = AP_MISSION_CMD_INDEX_NONE;
648 275 : _prev_nav_cmd_wp_index = AP_MISSION_CMD_INDEX_NONE;
649 : // reset the jump tracking to zero
650 275 : init_jump_tracking();
651 275 : if (index == 0) {
652 274 : index = 1;
653 : }
654 : }
655 :
656 : // if the mission is stopped or completed move the nav_cmd index to the specified point and set the state to stopped
657 : // so that if the user resumes the mission it will begin at the specified index
658 686 : if (_flags.state != MISSION_RUNNING) {
659 : // search until we find next nav command or reach end of command list
660 688 : while (!_flags.nav_cmd_loaded) {
661 : // get next command
662 383 : if (!get_next_cmd(index, cmd, true)) {
663 0 : _nav_cmd.index = AP_MISSION_CMD_INDEX_NONE;
664 0 : return false;
665 : }
666 :
667 : // check if navigation or "do" command
668 383 : if (is_nav_cmd(cmd)) {
669 : // set current navigation command
670 305 : _nav_cmd = cmd;
671 305 : _flags.nav_cmd_loaded = true;
672 : } else {
673 : // set current do command
674 78 : if (!_flags.do_cmd_loaded) {
675 78 : _do_cmd = cmd;
676 78 : _flags.do_cmd_loaded = true;
677 : }
678 : }
679 : // move onto next command
680 383 : index = cmd.index+1;
681 : }
682 :
683 : // if we have not found a do command then set flag to show there are no do-commands to be run before nav command completes
684 305 : if (!_flags.do_cmd_loaded) {
685 227 : _flags.do_cmd_all_done = true;
686 : }
687 :
688 : // if we got this far then the mission can safely be "resumed" from the specified index so we set the state to "stopped"
689 305 : _flags.state = MISSION_STOPPED;
690 305 : return true;
691 : }
692 :
693 : // the state must be MISSION_RUNNING, allow advance_current_nav_cmd() to manage starting the item
694 381 : if (!advance_current_nav_cmd(index)) {
695 : // on failure set mission complete
696 2 : complete();
697 2 : return false;
698 : }
699 :
700 : // if we got this far we must have successfully advanced the nav command
701 379 : return true;
702 : }
703 :
704 : // restart current navigation command. Used to handle external changes to mission
705 : // returns true on success, false if mission is not running or current nav command is invalid
706 1 : bool AP_Mission::restart_current_nav_cmd()
707 : {
708 : // return immediately if mission is not running
709 1 : if (_flags.state != MISSION_RUNNING) {
710 0 : return false;
711 : }
712 :
713 : // return immediately if nav command index is invalid
714 1 : const uint16_t nav_cmd_index = get_current_nav_index();
715 1 : if ((nav_cmd_index == 0) || (nav_cmd_index >= num_commands())) {
716 1 : return false;
717 : }
718 :
719 0 : return set_current_cmd(_nav_cmd.index);
720 : }
721 :
722 : // returns false on any issue at all.
723 4373 : bool AP_Mission::set_item(uint16_t index, mavlink_mission_item_int_t& src_packet)
724 : {
725 : // this is the on-storage format
726 4373 : AP_Mission::Mission_Command cmd {};
727 :
728 : // can't handle request for anything bigger than the mission size+1...
729 4373 : if (index > num_commands()) {
730 0 : return false;
731 : }
732 :
733 : // convert from mavlink-ish format to storage format, if we can.
734 4373 : if (mavlink_int_to_mission_cmd(src_packet, cmd) != MAV_MISSION_ACCEPTED) {
735 0 : return false;
736 : }
737 :
738 : // Writing index zero is not allowed, it must be home
739 4373 : if (index == 0) {
740 : // If home is not already loaded add it so cmd_total is incremented to 1 as would be expected when the returning true
741 2 : if (_cmd_total < 1) {
742 2 : write_home_to_storage();
743 : }
744 : // Really should be returning false in this case, but in order to not break things we return true
745 2 : return true;
746 : }
747 :
748 : // A request to set the 'next' item after the end is how we add an extra
749 : // item to the list, thus allowing us to write entire missions if needed.
750 4371 : if (index == num_commands()) {
751 4371 : return add_cmd(cmd);
752 : }
753 :
754 : // replacing an existing mission item...
755 0 : return AP_Mission::replace_cmd(index, cmd);
756 : }
757 :
758 9435 : bool AP_Mission::get_item(uint16_t index, mavlink_mission_item_int_t& ret_packet) const
759 : {
760 : mavlink_mission_item_int_t tmp;
761 :
762 : // can't handle request for anything bigger than the mission size...
763 9435 : if (index >= num_commands()) {
764 5 : return false;
765 : }
766 :
767 : // retrieve mission from eeprom
768 9430 : AP_Mission::Mission_Command cmd {};
769 9430 : if (!read_cmd_from_storage(index, cmd)) {
770 0 : return false;
771 : }
772 : // convert into mavlink-ish format for lua and friends.
773 9430 : if (!mission_cmd_to_mavlink_int(cmd, tmp)) {
774 0 : return false;
775 : }
776 :
777 : // set packet's current field to 1 if this is the command being executed
778 9430 : if (cmd.id == (uint16_t)get_current_nav_cmd().index) {
779 0 : tmp.current = 1;
780 : }
781 :
782 9430 : ret_packet = tmp;
783 :
784 9430 : return true;
785 : }
786 :
787 :
788 : struct PACKED Packed_Location_Option_Flags {
789 : uint8_t relative_alt : 1; // 1 if altitude is relative to home
790 : uint8_t unused1 : 1; // unused flag (defined so that loiter_ccw uses the correct bit)
791 : uint8_t loiter_ccw : 1; // 0 if clockwise, 1 if counter clockwise
792 : uint8_t terrain_alt : 1; // this altitude is above terrain
793 : uint8_t origin_alt : 1; // this altitude is above ekf origin
794 : uint8_t loiter_xtrack : 1; // 0 to crosstrack from center of waypoint, 1 to crosstrack from tangent exit location
795 : uint8_t type_specific_bit_0 : 1; // each mission item type can use this for storing 1 bit of extra data
796 : uint8_t type_specific_bit_1 : 1; // each mission item type can use this for storing 1 bit of extra data
797 : };
798 :
799 : struct PACKED PackedLocation {
800 : union {
801 : Packed_Location_Option_Flags flags; ///< options bitmask (1<<0 = relative altitude)
802 : uint8_t options; /// allows writing all flags to eeprom as one byte
803 : };
804 : // by making alt 24 bit we can make p1 in a command 16 bit,
805 : // allowing an accurate angle in centi-degrees. This keeps the
806 : // storage cost per mission item at 15 bytes, and allows mission
807 : // altitudes of up to +/- 83km
808 : int32_t alt:24; ///< param 2 - Altitude in centimeters (meters * 100) see LOCATION_ALT_MAX_M
809 : int32_t lat; ///< param 3 - Latitude * 10**7
810 : int32_t lng; ///< param 4 - Longitude * 10**7
811 : };
812 :
813 : union PackedContent {
814 : // location
815 : PackedLocation location; // Waypoint location
816 :
817 : // raw bytes, for reading/writing to eeprom. Note that only 10
818 : // bytes are available if a 16 bit command ID is used
819 : uint8_t bytes[12];
820 :
821 : };
822 :
823 : /// load_cmd_from_storage - load command from storage
824 : /// true is return if successful
825 104916 : bool AP_Mission::read_cmd_from_storage(uint16_t index, Mission_Command& cmd) const
826 : {
827 : ASSERT_STORAGE_SIZE(PackedContent, 12);
828 :
829 104916 : WITH_SEMAPHORE(_rsem);
830 :
831 : // special handling for command #0 which is home
832 104916 : if (index == 0) {
833 4417 : cmd = {};
834 4417 : cmd.id = MAV_CMD_NAV_WAYPOINT;
835 4417 : cmd.content.location = AP::ahrs().get_home();
836 4417 : return true;
837 : }
838 100499 : if (index >= (unsigned)_cmd_total || index >= _commands_max) {
839 2699 : return false;
840 : }
841 :
842 : // ensure all bytes of cmd are zeroed
843 97800 : cmd = {};
844 :
845 : // Find out proper location in memory by using the start_byte position + the index
846 : // we can load a command, we don't process it yet
847 : // read WP position
848 97800 : const uint16_t pos_in_storage = 4 + (index * AP_MISSION_EEPROM_COMMAND_SIZE);
849 :
850 97800 : PackedContent packed_content {};
851 :
852 97800 : const uint8_t b1 = _storage.read_byte(pos_in_storage);
853 97800 : if (b1 == 0 || b1 == 1) {
854 259 : cmd.id = _storage.read_uint16(pos_in_storage+1);
855 259 : cmd.p1 = _storage.read_uint16(pos_in_storage+3);
856 259 : _storage.read_block(packed_content.bytes, pos_in_storage+5, 10);
857 259 : format_conversion(b1, cmd, packed_content);
858 : } else {
859 97541 : cmd.id = b1;
860 97541 : cmd.p1 = _storage.read_uint16(pos_in_storage+1);
861 97541 : _storage.read_block(packed_content.bytes, pos_in_storage+3, 12);
862 : }
863 :
864 97800 : if (stored_in_location(cmd.id)) {
865 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
866 : // NOTE! no 16-bit command may be stored_in_location as only
867 : // 10 bytes are available for storage and lat/lon/alt required
868 : // 4*sizeof(float) == 12 bytes of storage.
869 94098 : if (b1 == 0) {
870 0 : AP_HAL::panic("May not store location for 16-bit commands");
871 : }
872 : #endif
873 : // Location is not PACKED; field-wise copy it:
874 94098 : cmd.content.location.relative_alt = packed_content.location.flags.relative_alt;
875 94098 : cmd.content.location.loiter_ccw = packed_content.location.flags.loiter_ccw;
876 94098 : cmd.content.location.terrain_alt = packed_content.location.flags.terrain_alt;
877 94098 : cmd.content.location.origin_alt = packed_content.location.flags.origin_alt;
878 94098 : cmd.content.location.loiter_xtrack = packed_content.location.flags.loiter_xtrack;
879 94098 : cmd.content.location.alt = packed_content.location.alt;
880 94098 : cmd.content.location.lat = packed_content.location.lat;
881 94098 : cmd.content.location.lng = packed_content.location.lng;
882 :
883 94098 : if (packed_content.location.flags.type_specific_bit_0) {
884 143 : cmd.type_specific_bits |= 1U << 0;
885 : }
886 94098 : if (packed_content.location.flags.type_specific_bit_1) {
887 7 : cmd.type_specific_bits |= 1U << 1;
888 : }
889 : } else {
890 : // all other options in Content are assumed to be packed:
891 : static_assert(sizeof(cmd.content) >= 12,
892 : "content is big enough to take bytes");
893 : // (void *) cast to specify gcc that we know that we are copy byte into a non trivial type and leaving 4 bytes untouched
894 3702 : memcpy((void *)&cmd.content, packed_content.bytes, 12);
895 : }
896 :
897 : // set command's index to it's position in eeprom
898 97800 : cmd.index = index;
899 :
900 : // return success
901 97800 : return true;
902 104916 : }
903 :
904 176479 : bool AP_Mission::stored_in_location(uint16_t id)
905 : {
906 176479 : switch (id) {
907 170495 : case MAV_CMD_NAV_WAYPOINT:
908 : case MAV_CMD_NAV_LOITER_UNLIM:
909 : case MAV_CMD_NAV_LOITER_TURNS:
910 : case MAV_CMD_NAV_LOITER_TIME:
911 : case MAV_CMD_NAV_LAND:
912 : case MAV_CMD_NAV_TAKEOFF:
913 : case MAV_CMD_NAV_CONTINUE_AND_CHANGE_ALT:
914 : case MAV_CMD_NAV_LOITER_TO_ALT:
915 : case MAV_CMD_NAV_SPLINE_WAYPOINT:
916 : case MAV_CMD_NAV_GUIDED_ENABLE:
917 : case MAV_CMD_DO_SET_HOME:
918 : case MAV_CMD_DO_RETURN_PATH_START:
919 : case MAV_CMD_DO_LAND_START:
920 : case MAV_CMD_DO_GO_AROUND:
921 : case MAV_CMD_DO_SET_ROI_LOCATION:
922 : case MAV_CMD_DO_SET_ROI:
923 : case MAV_CMD_NAV_VTOL_TAKEOFF:
924 : case MAV_CMD_NAV_VTOL_LAND:
925 : case MAV_CMD_NAV_PAYLOAD_PLACE:
926 : case MAV_CMD_NAV_FENCE_POLYGON_VERTEX_INCLUSION:
927 : case MAV_CMD_NAV_FENCE_POLYGON_VERTEX_EXCLUSION:
928 : case MAV_CMD_NAV_FENCE_CIRCLE_INCLUSION:
929 : case MAV_CMD_NAV_FENCE_CIRCLE_EXCLUSION:
930 : case MAV_CMD_NAV_FENCE_RETURN_POINT:
931 : case MAV_CMD_NAV_RALLY_POINT:
932 : case MAV_CMD_NAV_ARC_WAYPOINT:
933 170495 : return true;
934 5984 : default:
935 5984 : return false;
936 : }
937 : }
938 :
939 : /// write_cmd_to_storage - write a command to storage
940 : /// index is used to calculate the storage location
941 : /// true is returned if successful
942 15313 : bool AP_Mission::write_cmd_to_storage(uint16_t index, const Mission_Command& cmd)
943 : {
944 15313 : WITH_SEMAPHORE(_rsem);
945 :
946 : // range check cmd's index
947 15313 : if (index >= num_commands_max()) {
948 29 : return false;
949 : }
950 :
951 15284 : PackedContent packed {};
952 15284 : if (stored_in_location(cmd.id)) {
953 : // Location is not PACKED; field-wise copy it:
954 14927 : packed.location.flags.relative_alt = cmd.content.location.relative_alt;
955 14927 : packed.location.flags.loiter_ccw = cmd.content.location.loiter_ccw;
956 14927 : packed.location.flags.terrain_alt = cmd.content.location.terrain_alt;
957 14927 : packed.location.flags.origin_alt = cmd.content.location.origin_alt;
958 14927 : packed.location.flags.loiter_xtrack = cmd.content.location.loiter_xtrack;
959 14927 : packed.location.flags.type_specific_bit_0 = (cmd.type_specific_bits & (1U<<0)) >> 0;
960 14927 : packed.location.flags.type_specific_bit_1 = (cmd.type_specific_bits & (1U<<1)) >> 1;
961 14927 : packed.location.alt = cmd.content.location.alt;
962 14927 : packed.location.lat = cmd.content.location.lat;
963 14927 : packed.location.lng = cmd.content.location.lng;
964 : } else {
965 : // all other options in Content are assumed to be packed:
966 : static_assert(sizeof(packed.bytes) >= 12,
967 : "packed.bytes is big enough to take content");
968 357 : memcpy(packed.bytes, &cmd.content, 12);
969 : }
970 :
971 : // calculate where in storage the command should be placed
972 15284 : uint16_t pos_in_storage = 4 + (index * AP_MISSION_EEPROM_COMMAND_SIZE);
973 :
974 15284 : if (cmd.id < 256) {
975 : // for commands below 256 we store up to 12 bytes
976 15271 : _storage.write_byte(pos_in_storage, cmd.id);
977 15271 : _storage.write_uint16(pos_in_storage+1, cmd.p1);
978 15271 : _storage.write_block(pos_in_storage+3, packed.bytes, 12);
979 : } else {
980 : // if the command ID is above 256 we store a tag byte followed
981 : // by the 16 bit command ID. The tag byte is 1 for commands
982 : // where we have changed the storage format (see
983 : // format_conversion), 0 otherwise
984 13 : uint8_t tag_byte = 0;
985 : // currently the only converted structure is NAV_SCRIPT_TIME
986 13 : if (cmd.id == MAV_CMD_NAV_SCRIPT_TIME) {
987 0 : tag_byte = 1;
988 : }
989 13 : _storage.write_byte(pos_in_storage, tag_byte);
990 13 : _storage.write_uint16(pos_in_storage+1, cmd.id);
991 13 : _storage.write_uint16(pos_in_storage+3, cmd.p1);
992 13 : _storage.write_block(pos_in_storage+5, packed.bytes, 10);
993 : }
994 :
995 : // remember when the mission last changed
996 15284 : if (index != 0) {
997 : // Update of home location is not a true change
998 8437 : _last_change_time_ms = AP_HAL::millis();
999 : }
1000 :
1001 : // return success
1002 15284 : return true;
1003 15313 : }
1004 :
1005 : /// write_home_to_storage - writes the special purpose cmd 0 (home) to storage
1006 : /// home is taken directly from ahrs
1007 6876 : void AP_Mission::write_home_to_storage()
1008 : {
1009 6876 : Mission_Command home_cmd = {};
1010 6876 : home_cmd.id = MAV_CMD_NAV_WAYPOINT;
1011 6876 : home_cmd.content.location = AP::ahrs().get_home();
1012 6876 : write_cmd_to_storage(0,home_cmd);
1013 :
1014 : // Make sure command total reflects that home has been added
1015 6876 : if (_cmd_total < 1) {
1016 932 : _cmd_total.set_and_save(1);
1017 : }
1018 6876 : }
1019 :
1020 8654 : MAV_MISSION_RESULT AP_Mission::sanity_check_params(const mavlink_mission_item_int_t& packet)
1021 : {
1022 : uint8_t nan_mask;
1023 8654 : switch (packet.command) {
1024 7641 : case MAV_CMD_NAV_WAYPOINT:
1025 7641 : nan_mask = ~(1 << 3); // param 4 can be nan
1026 7641 : break;
1027 14 : case MAV_CMD_NAV_LOITER_UNLIM:
1028 14 : nan_mask = ~(1 << 3); // param 4 can be nan
1029 14 : break;
1030 134 : case MAV_CMD_NAV_LAND:
1031 134 : nan_mask = ~(1 << 3); // param 4 can be nan
1032 134 : break;
1033 224 : case MAV_CMD_NAV_TAKEOFF:
1034 224 : nan_mask = ~(1 << 3); // param 4 can be nan
1035 224 : break;
1036 35 : case MAV_CMD_NAV_ARC_WAYPOINT:
1037 35 : nan_mask = ~((1 << 1) | (1 << 2) | (1 << 3)); // param 2,3 & 4 can be nan
1038 35 : break;
1039 24 : case MAV_CMD_NAV_VTOL_TAKEOFF:
1040 24 : nan_mask = ~(1 << 3); // param 4 can be nan
1041 24 : break;
1042 33 : case MAV_CMD_NAV_VTOL_LAND:
1043 33 : nan_mask = ~((1 << 2) | (1 << 3)); // param 3 and 4 can be nan
1044 33 : break;
1045 549 : default:
1046 549 : nan_mask = 0xff;
1047 549 : break;
1048 : }
1049 :
1050 17308 : if (((nan_mask & (1 << 0)) && isnan(packet.param1)) ||
1051 8654 : isinf(packet.param1)) {
1052 0 : return MAV_MISSION_INVALID_PARAM1;
1053 : }
1054 17308 : if (((nan_mask & (1 << 1)) && isnan(packet.param2)) ||
1055 8654 : isinf(packet.param2)) {
1056 0 : return MAV_MISSION_INVALID_PARAM2;
1057 : }
1058 17308 : if (((nan_mask & (1 << 2)) && isnan(packet.param3)) ||
1059 8654 : isinf(packet.param3)) {
1060 0 : return MAV_MISSION_INVALID_PARAM3;
1061 : }
1062 17308 : if (((nan_mask & (1 << 3)) && isnan(packet.param4)) ||
1063 8654 : isinf(packet.param4)) {
1064 0 : return MAV_MISSION_INVALID_PARAM4;
1065 : }
1066 8654 : return MAV_MISSION_ACCEPTED;
1067 : }
1068 :
1069 : // mavlink_int_to_mission_cmd - converts mavlink message to an AP_Mission::Mission_Command object which can be stored to eeprom
1070 : // return MAV_MISSION_ACCEPTED on success, MAV_MISSION_RESULT error on failure
1071 8654 : MAV_MISSION_RESULT AP_Mission::mavlink_int_to_mission_cmd(const mavlink_mission_item_int_t& packet, AP_Mission::Mission_Command& cmd)
1072 : {
1073 8654 : cmd = {};
1074 :
1075 : // command's position in mission list and mavlink id
1076 8654 : cmd.index = packet.seq;
1077 8654 : cmd.id = packet.command;
1078 8654 : cmd.content.location = {};
1079 :
1080 8654 : MAV_MISSION_RESULT param_check = sanity_check_params(packet);
1081 8654 : if (param_check != MAV_MISSION_ACCEPTED) {
1082 0 : return param_check;
1083 : }
1084 :
1085 : // command specific conversions from mavlink packet to mission command
1086 8654 : switch (cmd.id) {
1087 :
1088 0 : case 0 ... 1:
1089 : // these are reserved for storing 16 bit command IDs
1090 0 : return MAV_MISSION_INVALID;
1091 :
1092 7641 : case MAV_CMD_NAV_WAYPOINT: { // MAV ID: 16
1093 : /*
1094 : the 15 byte limit means we can't fit both delay and radius
1095 : in the cmd structure. When we expand the mission structure
1096 : we can do this properly
1097 : */
1098 : #if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
1099 : // acceptance radius in meters and pass by distance in meters
1100 7160 : uint16_t acp = packet.param2; // param 2 is acceptance radius in meters is held in low p1
1101 7160 : uint16_t passby = packet.param3; // param 3 is pass by distance in meters is held in high p1
1102 :
1103 : // limit to 255 so it does not wrap during the shift or mask operation
1104 7160 : passby = MIN(0xFF,passby);
1105 7160 : acp = MIN(0xFF,acp);
1106 :
1107 7160 : cmd.p1 = (passby << 8) | (acp & 0x00FF);
1108 : #else
1109 : // delay at waypoint in seconds (this is for copters???)
1110 : // reject invalid param1 values to prevent floating point exception
1111 481 : if (packet.param1 < 0 || packet.param1 > UINT16_MAX) {
1112 0 : return MAV_MISSION_INVALID_PARAM1;
1113 : }
1114 481 : cmd.p1 = (uint16_t)packet.param1;
1115 : #endif
1116 : }
1117 7641 : break;
1118 :
1119 14 : case MAV_CMD_NAV_LOITER_UNLIM: // MAV ID: 17
1120 14 : cmd.p1 = fabsf(packet.param3); // store radius as 16bit since no other params are competing for space
1121 14 : cmd.content.location.loiter_ccw = (packet.param3 < 0); // -1 = counter clockwise, +1 = clockwise
1122 14 : break;
1123 :
1124 12 : case MAV_CMD_NAV_LOITER_TURNS: { // MAV ID: 18
1125 : // number of turns is stored in the lowest bits. Number of
1126 : // turns 0 < N < 1 are stored multiplied by 256 and a bit set
1127 : // in storage so that on retrieval they are divided by 256.
1128 : // Radii below 255m are stored in the top 8 bits as an 8-bit integer.
1129 : // Radii above 255m are stored divided by 10 and a bit set in
1130 : // storage so that on retrieval they are multiplied by 10
1131 12 : float param1_stored = packet.param1;
1132 12 : if (param1_stored > 0 && param1_stored < 1) {
1133 1 : param1_stored *= 256.0;
1134 1 : cmd.type_specific_bits |= (1U << 1);
1135 : }
1136 12 : cmd.p1 = MIN(255, param1_stored); // store number of times to circle in low p1
1137 : uint8_t radius_m;
1138 12 : const float abs_radius = fabsf(packet.param3);
1139 12 : if (abs_radius <= 255) {
1140 10 : radius_m = abs_radius;
1141 : } else {
1142 2 : radius_m = MIN(255, abs_radius * 0.1);
1143 2 : cmd.type_specific_bits |= (1U << 0);
1144 : }
1145 12 : cmd.p1 |= (radius_m<<8); // store radius in high byte of p1
1146 12 : cmd.content.location.loiter_ccw = (packet.param3 < 0);
1147 12 : cmd.content.location.loiter_xtrack = (packet.param4 > 0); // 0 to xtrack from center of waypoint, 1 to xtrack from tangent exit location
1148 : }
1149 12 : break;
1150 :
1151 15 : case MAV_CMD_NAV_LOITER_TIME: // MAV ID: 19
1152 15 : cmd.p1 = packet.param1; // loiter time in seconds uses all 16 bits, 8bit seconds is too small. No room for radius.
1153 15 : cmd.content.location.loiter_ccw = (packet.param3 < 0);
1154 15 : cmd.content.location.loiter_xtrack = (packet.param4 > 0); // 0 to xtrack from center of waypoint, 1 to xtrack from tangent exit location
1155 15 : break;
1156 :
1157 95 : case MAV_CMD_NAV_RETURN_TO_LAUNCH: // MAV ID: 20
1158 95 : break;
1159 :
1160 134 : case MAV_CMD_NAV_LAND: // MAV ID: 21
1161 134 : cmd.p1 = packet.param1; // abort target altitude(m) (plane only)
1162 134 : if (!isnan(packet.param4)) {
1163 134 : cmd.content.location.loiter_ccw = is_negative(packet.param4); // yaw direction, (plane deepstall only)
1164 : }
1165 134 : break;
1166 :
1167 224 : case MAV_CMD_NAV_TAKEOFF: // MAV ID: 22
1168 224 : cmd.p1 = packet.param1; // minimum pitch (plane only)
1169 224 : break;
1170 :
1171 0 : case MAV_CMD_NAV_CONTINUE_AND_CHANGE_ALT: // MAV ID: 30
1172 0 : cmd.p1 = packet.param1; // Climb/Descend
1173 : // 0 = Neutral, cmd complete at +/- 5 of indicated alt.
1174 : // 1 = Climb, cmd complete at or above indicated alt.
1175 : // 2 = Descend, cmd complete at or below indicated alt.
1176 0 : break;
1177 :
1178 15 : case MAV_CMD_NAV_LOITER_TO_ALT: // MAV ID: 31
1179 15 : cmd.p1 = fabsf(packet.param2); // param2 is radius in meters
1180 15 : cmd.content.location.loiter_ccw = (packet.param2 < 0);
1181 15 : cmd.content.location.loiter_xtrack = (packet.param4 > 0); // 0 to xtrack from center of waypoint, 1 to xtrack from tangent exit location
1182 15 : break;
1183 :
1184 35 : case MAV_CMD_NAV_ARC_WAYPOINT: // MAV ID: 36
1185 35 : cmd.content.location.loiter_ccw = is_negative(packet.param1); // re-use loiter_cw for arc direction
1186 35 : cmd.p1 = fabsf(packet.param1); // arc angle in deg
1187 35 : break;
1188 :
1189 16 : case MAV_CMD_NAV_SPLINE_WAYPOINT: // MAV ID: 82
1190 : #if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
1191 0 : return MAV_MISSION_UNSUPPORTED;
1192 : #else
1193 16 : cmd.p1 = packet.param1; // delay at waypoint in seconds
1194 16 : break;
1195 : #endif
1196 :
1197 1 : case MAV_CMD_NAV_GUIDED_ENABLE: // MAV ID: 92
1198 1 : cmd.p1 = packet.param1; // on/off. >0.5 means "on", hand-over control to external controller
1199 1 : break;
1200 :
1201 7 : case MAV_CMD_NAV_DELAY: // MAV ID: 93
1202 7 : cmd.content.nav_delay.seconds = packet.param1; // delay in seconds
1203 7 : cmd.content.nav_delay.hour_utc = packet.param2;// absolute time's hour (utc)
1204 7 : cmd.content.nav_delay.min_utc = packet.param3;// absolute time's min (utc)
1205 7 : cmd.content.nav_delay.sec_utc = packet.param4; // absolute time's second (utc)
1206 7 : break;
1207 :
1208 0 : case MAV_CMD_CONDITION_DELAY: // MAV ID: 112
1209 0 : cmd.content.delay.seconds = packet.param1; // delay in seconds
1210 0 : break;
1211 :
1212 0 : case MAV_CMD_CONDITION_DISTANCE: // MAV ID: 114
1213 0 : cmd.content.distance.meters = packet.param1; // distance in meters from next waypoint
1214 0 : break;
1215 :
1216 6 : case MAV_CMD_CONDITION_YAW: // MAV ID: 115
1217 6 : cmd.content.yaw.angle_deg = packet.param1; // target angle in degrees
1218 6 : cmd.content.yaw.turn_rate_dps = packet.param2; // 0 = use default turn rate otherwise specific turn rate in deg/sec
1219 6 : cmd.content.yaw.direction = packet.param3; // -1 = ccw, +1 = cw
1220 6 : cmd.content.yaw.relative_angle = packet.param4; // lng=0: absolute angle provided, lng=1: relative angle provided
1221 6 : break;
1222 :
1223 131 : case MAV_CMD_DO_JUMP: // MAV ID: 177
1224 : case MAV_CMD_DO_JUMP_TAG: // MAV ID: 601
1225 131 : cmd.content.jump.target = packet.param1; // jump-to command/tag number
1226 131 : cmd.content.jump.num_times = packet.param2; // repeat count
1227 131 : break;
1228 :
1229 2 : case MAV_CMD_JUMP_TAG: // MAV ID: 600
1230 2 : cmd.content.jump.target = packet.param1; // jump-to tag number
1231 2 : break;
1232 :
1233 41 : case MAV_CMD_DO_CHANGE_SPEED: // MAV ID: 178
1234 41 : cmd.content.speed.speed_type = packet.param1; // 0 = airspeed, 1 = ground speed
1235 41 : cmd.content.speed.target_ms = packet.param2; // target speed in m/s
1236 41 : cmd.content.speed.throttle_pct = packet.param3; // throttle as a percentage from 1 ~ 100%
1237 41 : break;
1238 :
1239 0 : case MAV_CMD_DO_SET_HOME:
1240 0 : cmd.p1 = packet.param1; // p1=0 means use current location, p=1 means use provided location
1241 0 : break;
1242 :
1243 0 : case MAV_CMD_DO_SET_RELAY: // MAV ID: 181
1244 0 : cmd.content.relay.num = packet.param1; // relay number
1245 0 : cmd.content.relay.state = packet.param2; // 0:off, 1:on
1246 0 : break;
1247 :
1248 0 : case MAV_CMD_DO_REPEAT_RELAY: // MAV ID: 182
1249 0 : cmd.content.repeat_relay.num = packet.param1; // relay number
1250 0 : cmd.content.repeat_relay.repeat_count = packet.param2; // count
1251 0 : cmd.content.repeat_relay.cycle_time = packet.param3; // time converted from seconds to milliseconds
1252 0 : break;
1253 :
1254 2 : case MAV_CMD_DO_SET_SERVO: // MAV ID: 183
1255 2 : cmd.content.servo.channel = packet.param1; // channel
1256 2 : cmd.content.servo.pwm = packet.param2; // PWM
1257 2 : break;
1258 :
1259 0 : case MAV_CMD_DO_REPEAT_SERVO: // MAV ID: 184
1260 0 : cmd.content.repeat_servo.channel = packet.param1; // channel
1261 0 : cmd.content.repeat_servo.pwm = packet.param2; // PWM
1262 0 : cmd.content.repeat_servo.repeat_count = packet.param3; // count
1263 0 : cmd.content.repeat_servo.cycle_time = packet.param4; // time in seconds
1264 0 : break;
1265 :
1266 131 : case MAV_CMD_DO_RETURN_PATH_START: // MAV ID: 188
1267 : case MAV_CMD_DO_LAND_START: // MAV ID: 189
1268 131 : break;
1269 :
1270 0 : case MAV_CMD_DO_GO_AROUND: // MAV ID: 191
1271 0 : break;
1272 :
1273 0 : case MAV_CMD_DO_SET_ROI_LOCATION: // MAV ID: 195
1274 : case MAV_CMD_DO_SET_ROI_NONE: // MAV ID: 197
1275 0 : cmd.p1 = packet.param1; // gimbal device id
1276 0 : break;
1277 :
1278 0 : case MAV_CMD_DO_SET_ROI: // MAV ID: 201
1279 0 : cmd.p1 = packet.param1; // 0 = no roi, 1 = next waypoint, 2 = waypoint number, 3 = fixed location, 4 = given target (not supported)
1280 0 : break;
1281 :
1282 0 : case MAV_CMD_DO_DIGICAM_CONFIGURE: // MAV ID: 202
1283 0 : cmd.content.digicam_configure.shooting_mode = packet.param1;
1284 0 : cmd.content.digicam_configure.shutter_speed = packet.param2;
1285 0 : cmd.content.digicam_configure.aperture = packet.param3;
1286 0 : cmd.content.digicam_configure.ISO = packet.param4;
1287 0 : cmd.content.digicam_configure.exposure_type = packet.x;
1288 0 : cmd.content.digicam_configure.cmd_id = packet.y;
1289 0 : cmd.content.digicam_configure.engine_cutoff_time = packet.z;
1290 0 : break;
1291 :
1292 1 : case MAV_CMD_DO_DIGICAM_CONTROL: // MAV ID: 203
1293 1 : cmd.content.digicam_control.session = packet.param1;
1294 1 : cmd.content.digicam_control.zoom_pos = packet.param2;
1295 1 : cmd.content.digicam_control.zoom_step = packet.param3;
1296 1 : cmd.content.digicam_control.focus_lock = packet.param4;
1297 1 : cmd.content.digicam_control.shooting_cmd = packet.x;
1298 1 : cmd.content.digicam_control.cmd_id = packet.y;
1299 1 : break;
1300 :
1301 0 : case MAV_CMD_DO_MOUNT_CONTROL: // MAV ID: 205
1302 : // TODO: this is only valid if packet.z == MAV_MOUNT_MODE_MAVLINK_TARGETING
1303 0 : cmd.content.mount_control.pitch = packet.param1;
1304 0 : cmd.content.mount_control.roll = packet.param2;
1305 0 : cmd.content.mount_control.yaw = packet.param3;
1306 0 : break;
1307 :
1308 12 : case MAV_CMD_DO_SET_CAM_TRIGG_DIST: // MAV ID: 206
1309 12 : cmd.content.cam_trigg_dist.meters = packet.param1; // distance between camera shots in meters
1310 12 : cmd.content.cam_trigg_dist.trigger = packet.param3; // when enabled, camera triggers once immediately
1311 12 : cmd.content.cam_trigg_dist.camera_id = packet.param4; // which camera to trigger
1312 12 : break;
1313 :
1314 0 : case MAV_CMD_DO_FENCE_ENABLE: // MAV ID: 207
1315 0 : cmd.p1 = packet.param1; // action 0=disable, 1=enable, 2=disable floor
1316 0 : break;
1317 :
1318 1 : case MAV_CMD_DO_AUX_FUNCTION:
1319 1 : cmd.content.auxfunction.function = packet.param1;
1320 1 : cmd.content.auxfunction.switchpos = packet.param2;
1321 1 : break;
1322 :
1323 4 : case MAV_CMD_DO_PARACHUTE: // MAV ID: 208
1324 4 : cmd.p1 = packet.param1; // action 0=disable, 1=enable, 2=release. See PARACHUTE_ACTION enum
1325 4 : break;
1326 :
1327 2 : case MAV_CMD_DO_INVERTED_FLIGHT: // MAV ID: 210
1328 2 : cmd.p1 = packet.param1; // normal=0 inverted=1
1329 2 : break;
1330 :
1331 : #if AP_GRIPPER_ENABLED
1332 14 : case MAV_CMD_DO_GRIPPER: // MAV ID: 211
1333 14 : cmd.content.gripper.num = packet.param1; // gripper number
1334 14 : cmd.content.gripper.action = packet.param2; // action 0=release, 1=grab. See GRIPPER_ACTION enum
1335 14 : break;
1336 : #endif
1337 :
1338 1 : case MAV_CMD_DO_GUIDED_LIMITS: // MAV ID: 222
1339 1 : cmd.p1 = packet.param1; // max time in seconds the external controller will be allowed to control the vehicle
1340 1 : cmd.content.guided_limits.alt_min = packet.param2; // min alt below which the command will be aborted. 0 for no lower alt limit
1341 1 : cmd.content.guided_limits.alt_max = packet.param3; // max alt above which the command will be aborted. 0 for no upper alt limit
1342 1 : cmd.content.guided_limits.horiz_max = packet.param4;// max horizontal distance the vehicle can move before the command will be aborted. 0 for no horizontal limit
1343 1 : break;
1344 :
1345 0 : case MAV_CMD_DO_AUTOTUNE_ENABLE: // MAV ID: 211
1346 0 : cmd.p1 = packet.param1; // disable=0 enable=1
1347 0 : break;
1348 :
1349 2 : case MAV_CMD_NAV_ALTITUDE_WAIT: // MAV ID: 83
1350 2 : cmd.content.altitude_wait.altitude = packet.param1;
1351 2 : cmd.content.altitude_wait.descent_rate = packet.param2;
1352 2 : cmd.content.altitude_wait.wiggle_time = packet.param3;
1353 2 : break;
1354 :
1355 24 : case MAV_CMD_NAV_VTOL_TAKEOFF:
1356 24 : break;
1357 :
1358 33 : case MAV_CMD_NAV_VTOL_LAND:
1359 33 : cmd.p1 = (NAV_VTOL_LAND_OPTIONS)packet.param1;
1360 33 : break;
1361 :
1362 2 : case MAV_CMD_DO_VTOL_TRANSITION:
1363 2 : cmd.content.do_vtol_transition.target_state = packet.param1;
1364 2 : break;
1365 :
1366 0 : case MAV_CMD_DO_SET_REVERSE:
1367 0 : cmd.p1 = packet.param1; // 0 = forward, 1 = reverse
1368 0 : break;
1369 :
1370 21 : case MAV_CMD_DO_ENGINE_CONTROL:
1371 21 : cmd.content.do_engine_control.start_control = (packet.param1>0);
1372 21 : cmd.content.do_engine_control.cold_start = (packet.param2>0);
1373 21 : cmd.content.do_engine_control.height_delay_cm = packet.param3*100;
1374 21 : cmd.content.do_engine_control.allow_disarmed_start = (((uint32_t)packet.param4) & ENGINE_CONTROL_OPTIONS_ALLOW_START_WHILE_DISARMED) != 0;
1375 21 : break;
1376 :
1377 : #if AP_MISSION_NAV_PAYLOAD_PLACE_ENABLED
1378 2 : case MAV_CMD_NAV_PAYLOAD_PLACE:
1379 2 : cmd.p1 = packet.param1*100; // copy max-descend parameter (m->cm)
1380 2 : break;
1381 : #endif
1382 :
1383 0 : case MAV_CMD_NAV_SET_YAW_SPEED:
1384 0 : cmd.content.set_yaw_speed.angle_deg = packet.param1; // target angle in degrees
1385 0 : cmd.content.set_yaw_speed.speed = packet.param2; // speed in meters/second
1386 0 : cmd.content.set_yaw_speed.relative_angle = packet.param3; // 0 = absolute angle, 1 = relative angle
1387 0 : break;
1388 :
1389 0 : case MAV_CMD_DO_WINCH: // MAV ID: 42600
1390 0 : cmd.content.winch.num = packet.param1; // winch number
1391 0 : cmd.content.winch.action = packet.param2; // action (0 = relax, 1 = length control, 2 = rate control). See WINCH_ACTION enum
1392 0 : cmd.content.winch.release_length = packet.param3; // cable distance to unwind in meters, negative numbers to wind in cable
1393 0 : cmd.content.winch.release_rate = packet.param4; // release rate in meters/second
1394 0 : break;
1395 :
1396 0 : case MAV_CMD_DO_SET_RESUME_REPEAT_DIST:
1397 0 : cmd.p1 = packet.param1; // Resume repeat distance (m)
1398 0 : break;
1399 :
1400 2 : case MAV_CMD_DO_SPRAYER:
1401 2 : cmd.p1 = packet.param1; // action 0=disable, 1=enable
1402 2 : break;
1403 :
1404 0 : case MAV_CMD_DO_SEND_SCRIPT_MESSAGE:
1405 0 : cmd.p1 = packet.param1;
1406 0 : cmd.content.scripting.p1 = packet.param2;
1407 0 : cmd.content.scripting.p2 = packet.param3;
1408 0 : cmd.content.scripting.p3 = packet.param4;
1409 0 : break;
1410 :
1411 : #if AP_SCRIPTING_ENABLED
1412 0 : case MAV_CMD_NAV_SCRIPT_TIME:
1413 0 : cmd.content.nav_script_time.command = packet.param1;
1414 0 : cmd.content.nav_script_time.timeout_s = packet.param2;
1415 0 : cmd.content.nav_script_time.arg1.set(packet.param3);
1416 0 : cmd.content.nav_script_time.arg2.set(packet.param4);
1417 0 : cmd.content.nav_script_time.arg3 = int16_t(packet.x);
1418 0 : cmd.content.nav_script_time.arg4 = int16_t(packet.y);
1419 0 : break;
1420 : #endif
1421 :
1422 0 : case MAV_CMD_NAV_ATTITUDE_TIME:
1423 0 : cmd.content.nav_attitude_time.time_sec = constrain_float(packet.param1, 0, UINT16_MAX);
1424 0 : cmd.content.nav_attitude_time.roll_deg = (fabsf(packet.param2) <= 180) ? packet.param2 : 0;
1425 0 : cmd.content.nav_attitude_time.pitch_deg = (fabsf(packet.param3) <= 90) ? packet.param3 : 0;
1426 0 : cmd.content.nav_attitude_time.yaw_deg = ((packet.param4 >= -180) && (packet.param4 <= 360)) ? packet.param4 : 0;
1427 0 : cmd.content.nav_attitude_time.climb_rate = packet.x;
1428 0 : break;
1429 :
1430 0 : case MAV_CMD_DO_PAUSE_CONTINUE:
1431 0 : cmd.p1 = packet.param1;
1432 0 : break;
1433 :
1434 0 : case MAV_CMD_DO_GIMBAL_MANAGER_PITCHYAW:
1435 0 : cmd.content.gimbal_manager_pitchyaw.pitch_angle_deg = packet.param1;
1436 0 : cmd.content.gimbal_manager_pitchyaw.yaw_angle_deg = packet.param2;
1437 0 : cmd.content.gimbal_manager_pitchyaw.pitch_rate_degs = packet.param3;
1438 0 : cmd.content.gimbal_manager_pitchyaw.yaw_rate_degs = packet.param4;
1439 0 : cmd.content.gimbal_manager_pitchyaw.flags = packet.x;
1440 0 : cmd.content.gimbal_manager_pitchyaw.gimbal_id = packet.z;
1441 0 : break;
1442 :
1443 3 : case MAV_CMD_IMAGE_START_CAPTURE:
1444 3 : cmd.content.image_start_capture.instance = packet.param1;
1445 3 : cmd.content.image_start_capture.interval_s = packet.param2;
1446 3 : cmd.content.image_start_capture.total_num_images = packet.param3;
1447 3 : cmd.content.image_start_capture.start_seq_number = packet.param4;
1448 3 : break;
1449 :
1450 1 : case MAV_CMD_IMAGE_STOP_CAPTURE:
1451 1 : cmd.p1 = packet.param1;
1452 1 : break;
1453 :
1454 2 : case MAV_CMD_SET_CAMERA_ZOOM:
1455 2 : cmd.content.set_camera_zoom.zoom_type = packet.param1;
1456 2 : cmd.content.set_camera_zoom.zoom_value = packet.param2;
1457 2 : cmd.content.set_camera_zoom.camera_id = packet.param3;
1458 2 : break;
1459 :
1460 2 : case MAV_CMD_SET_CAMERA_FOCUS:
1461 2 : cmd.content.set_camera_focus.focus_type = packet.param1;
1462 2 : cmd.content.set_camera_focus.focus_value = packet.param2;
1463 2 : cmd.content.set_camera_focus.camera_id = packet.param3;
1464 2 : break;
1465 :
1466 0 : case MAV_CMD_SET_CAMERA_SOURCE:
1467 0 : cmd.content.set_camera_source.instance = packet.param1;
1468 0 : cmd.content.set_camera_source.primary_source = packet.param2;
1469 0 : cmd.content.set_camera_source.secondary_source = packet.param3;
1470 0 : break;
1471 :
1472 0 : case MAV_CMD_VIDEO_START_CAPTURE:
1473 0 : cmd.content.video_start_capture.video_stream_id = packet.param1;
1474 0 : break;
1475 :
1476 0 : case MAV_CMD_VIDEO_STOP_CAPTURE:
1477 0 : cmd.content.video_stop_capture.video_stream_id = packet.param1;
1478 0 : break;
1479 :
1480 : #if AP_MISSION_MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET_ENABLED
1481 3 : case MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET:
1482 3 : cmd.content.wpnext_offset.gimbal_id = packet.param1;
1483 3 : cmd.content.wpnext_offset.roll_offset_cd = packet.y * 100;
1484 3 : cmd.content.wpnext_offset.pitch_offset_cd = packet.x * 100;
1485 3 : cmd.content.wpnext_offset.yaw_offset_cd = packet.z * 100;
1486 3 : break;
1487 : #endif // AP_MISSION_MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET_ENABLED
1488 :
1489 0 : default:
1490 : // unrecognised command
1491 0 : return MAV_MISSION_UNSUPPORTED;
1492 : }
1493 :
1494 : // copy location from mavlink to command
1495 8654 : if (stored_in_location(cmd.id)) {
1496 :
1497 : // sanity check location
1498 8297 : if (!check_lat(packet.x)) {
1499 0 : return MAV_MISSION_INVALID_PARAM5_X;
1500 : }
1501 8297 : if (!check_lng(packet.y)) {
1502 0 : return MAV_MISSION_INVALID_PARAM6_Y;
1503 : }
1504 8297 : if (isnan(packet.z) || fabsf(packet.z) >= LOCATION_ALT_MAX_M) {
1505 0 : return MAV_MISSION_INVALID_PARAM7;
1506 : }
1507 :
1508 8297 : cmd.content.location.lat = packet.x;
1509 8297 : cmd.content.location.lng = packet.y;
1510 :
1511 8297 : cmd.content.location.alt = packet.z * 100.0f; // convert packet's alt (m) to cmd alt (cm)
1512 :
1513 8297 : switch (packet.frame) {
1514 :
1515 254 : case MAV_FRAME_MISSION:
1516 : case MAV_FRAME_GLOBAL:
1517 : case MAV_FRAME_GLOBAL_INT:
1518 254 : cmd.content.location.relative_alt = 0;
1519 254 : break;
1520 :
1521 6899 : case MAV_FRAME_GLOBAL_RELATIVE_ALT:
1522 : case MAV_FRAME_GLOBAL_RELATIVE_ALT_INT:
1523 6899 : cmd.content.location.relative_alt = 1;
1524 6899 : break;
1525 :
1526 : #if AP_TERRAIN_AVAILABLE
1527 1144 : case MAV_FRAME_GLOBAL_TERRAIN_ALT:
1528 : case MAV_FRAME_GLOBAL_TERRAIN_ALT_INT:
1529 : // we mark it as a relative altitude, as it doesn't have
1530 : // home alt added
1531 1144 : cmd.content.location.relative_alt = 1;
1532 : // mark altitude as above terrain, not above home
1533 1144 : cmd.content.location.terrain_alt = 1;
1534 1144 : break;
1535 : #endif
1536 :
1537 0 : default:
1538 0 : return MAV_MISSION_UNSUPPORTED_FRAME;
1539 : }
1540 : }
1541 :
1542 : // if we got this far then it must have been successful
1543 8654 : return MAV_MISSION_ACCEPTED;
1544 : }
1545 :
1546 9 : MAV_MISSION_RESULT AP_Mission::convert_MISSION_ITEM_to_MISSION_ITEM_INT(const mavlink_mission_item_t &packet,
1547 : mavlink_mission_item_int_t &mav_cmd)
1548 : {
1549 : // TODO: rename mav_cmd to mission_item_int
1550 : // TODO: rename packet to mission_item
1551 9 : mav_cmd.param1 = packet.param1;
1552 9 : mav_cmd.param2 = packet.param2;
1553 9 : mav_cmd.param3 = packet.param3;
1554 9 : mav_cmd.param4 = packet.param4;
1555 9 : mav_cmd.z = packet.z;
1556 9 : mav_cmd.seq = packet.seq;
1557 9 : mav_cmd.command = packet.command;
1558 9 : mav_cmd.target_system = packet.target_system;
1559 9 : mav_cmd.target_component = packet.target_component;
1560 9 : mav_cmd.frame = packet.frame;
1561 9 : mav_cmd.current = packet.current;
1562 9 : mav_cmd.autocontinue = packet.autocontinue;
1563 9 : mav_cmd.mission_type = packet.mission_type;
1564 :
1565 : /*
1566 : the strategy for handling both MISSION_ITEM and MISSION_ITEM_INT
1567 : is to pass the lat/lng in MISSION_ITEM_INT straight through, and
1568 : for MISSION_ITEM multiply by 1e7 here. We need an exception for
1569 : any commands which use the x and y fields not as
1570 : latitude/longitude.
1571 : */
1572 9 : if (!cmd_has_location(packet.command)) {
1573 0 : mav_cmd.x = packet.x;
1574 0 : mav_cmd.y = packet.y;
1575 :
1576 : } else {
1577 : //these commands use x and y as lat/lon. We need to
1578 : // multiply by 1e7 to convert to int32_t
1579 9 : if (!check_lat(packet.x)) {
1580 0 : return MAV_MISSION_INVALID_PARAM5_X;
1581 : }
1582 9 : if (!check_lng(packet.y)) {
1583 0 : return MAV_MISSION_INVALID_PARAM6_Y;
1584 : }
1585 9 : mav_cmd.x = packet.x * 1.0e7f;
1586 9 : mav_cmd.y = packet.y * 1.0e7f;
1587 : }
1588 :
1589 9 : return MAV_MISSION_ACCEPTED;
1590 : }
1591 :
1592 0 : MAV_MISSION_RESULT AP_Mission::convert_MISSION_ITEM_INT_to_MISSION_ITEM(const mavlink_mission_item_int_t &item_int,
1593 : mavlink_mission_item_t &item)
1594 : {
1595 0 : item.param1 = item_int.param1;
1596 0 : item.param2 = item_int.param2;
1597 0 : item.param3 = item_int.param3;
1598 0 : item.param4 = item_int.param4;
1599 0 : item.z = item_int.z;
1600 0 : item.seq = item_int.seq;
1601 0 : item.command = item_int.command;
1602 0 : item.target_system = item_int.target_system;
1603 0 : item.target_component = item_int.target_component;
1604 0 : item.frame = item_int.frame;
1605 0 : item.current = item_int.current;
1606 0 : item.autocontinue = item_int.autocontinue;
1607 0 : item.mission_type = item_int.mission_type;
1608 :
1609 0 : if (!cmd_has_location(item_int.command)) {
1610 0 : item.x = item_int.x;
1611 0 : item.y = item_int.y;
1612 :
1613 : } else {
1614 : // These commands use x and y as lat/lon. We need to
1615 : // multiply by 1e-7 to convert to float
1616 0 : item.x = item_int.x * 1.0e-7f;
1617 0 : item.y = item_int.y * 1.0e-7f;
1618 0 : if (!check_lat(item.x)) {
1619 0 : return MAV_MISSION_INVALID_PARAM5_X;
1620 : }
1621 0 : if (!check_lng(item.y)) {
1622 0 : return MAV_MISSION_INVALID_PARAM6_Y;
1623 : }
1624 : }
1625 :
1626 0 : return MAV_MISSION_ACCEPTED;
1627 : }
1628 :
1629 : // mission_cmd_to_mavlink_int - converts an AP_Mission::Mission_Command object to a mavlink message which can be sent to the GCS
1630 : // return true on success, false on failure
1631 : // NOTE: callers to this method current fill parts of "packet" in before calling this method, so do NOT attempt to zero the entire packet in here
1632 26892 : bool AP_Mission::mission_cmd_to_mavlink_int(const AP_Mission::Mission_Command& cmd, mavlink_mission_item_int_t& packet)
1633 : {
1634 : // Make sure return packed is zeroed
1635 : // Strictly params 1 to 4 and alt should be defaulted to NaN
1636 : // x and y should be defaulted to INT32_MAX
1637 26892 : packet = {
1638 26892 : seq: cmd.index,
1639 26892 : command: cmd.id,
1640 : autocontinue: 1,
1641 : };
1642 :
1643 : // command specific conversions from mission command to mavlink packet
1644 26892 : switch (cmd.id) {
1645 0 : case 0:
1646 : // this is reserved for 16 bit command IDs
1647 0 : return false;
1648 :
1649 23333 : case MAV_CMD_NAV_WAYPOINT: // MAV ID: 16
1650 : #if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
1651 : // acceptance radius in meters
1652 :
1653 21007 : packet.param2 = LOWBYTE(cmd.p1); // param 2 is acceptance radius in meters is held in low p1
1654 21007 : packet.param3 = HIGHBYTE(cmd.p1); // param 3 is pass by distance in meters is held in high p1
1655 : #else
1656 : // delay at waypoint in seconds
1657 2326 : packet.param1 = cmd.p1;
1658 : #endif
1659 23333 : break;
1660 :
1661 38 : case MAV_CMD_NAV_LOITER_UNLIM: // MAV ID: 17
1662 38 : packet.param3 = (float)cmd.p1;
1663 38 : if (cmd.content.location.loiter_ccw) {
1664 0 : packet.param3 *= -1;
1665 : }
1666 38 : break;
1667 :
1668 51 : case MAV_CMD_NAV_LOITER_TURNS: // MAV ID: 18
1669 51 : packet.param1 = LOWBYTE(cmd.p1); // number of times to circle is held in low byte of p1
1670 51 : packet.param3 = HIGHBYTE(cmd.p1); // radius is held in high byte of p1
1671 51 : if (cmd.content.location.loiter_ccw) {
1672 13 : packet.param3 = -packet.param3;
1673 : }
1674 51 : if (cmd.type_specific_bits & (1U<<0)) {
1675 6 : packet.param3 *= 10;
1676 : }
1677 51 : if (cmd.type_specific_bits & (1U<<1)) {
1678 3 : packet.param1 /= 256.0;
1679 : }
1680 51 : packet.param4 = cmd.content.location.loiter_xtrack; // 0 to xtrack from center of waypoint, 1 to xtrack from tangent exit location
1681 51 : break;
1682 :
1683 43 : case MAV_CMD_NAV_LOITER_TIME: // MAV ID: 19
1684 43 : packet.param1 = cmd.p1; // loiter time in seconds
1685 43 : if (cmd.content.location.loiter_ccw) {
1686 0 : packet.param3 = -1;
1687 : } else {
1688 43 : packet.param3 = 1;
1689 : }
1690 43 : packet.param4 = cmd.content.location.loiter_xtrack; // 0 to xtrack from center of waypoint, 1 to xtrack from tangent exit location
1691 43 : break;
1692 :
1693 303 : case MAV_CMD_NAV_RETURN_TO_LAUNCH: // MAV ID: 20
1694 303 : break;
1695 :
1696 519 : case MAV_CMD_NAV_LAND: // MAV ID: 21
1697 519 : packet.param1 = cmd.p1; // abort target altitude(m) (plane only)
1698 519 : packet.param4 = cmd.content.location.loiter_ccw ? -1 : 1; // yaw direction, (plane deepstall only)
1699 519 : break;
1700 :
1701 895 : case MAV_CMD_NAV_TAKEOFF: // MAV ID: 22
1702 895 : packet.param1 = cmd.p1; // minimum pitch (plane only)
1703 895 : break;
1704 :
1705 0 : case MAV_CMD_NAV_CONTINUE_AND_CHANGE_ALT: // MAV ID: 30
1706 0 : packet.param1 = cmd.p1; // Climb/Descend
1707 : // 0 = Neutral, cmd complete at +/- 5 of indicated alt.
1708 : // 1 = Climb, cmd complete at or above indicated alt.
1709 : // 2 = Descend, cmd complete at or below indicated alt.
1710 0 : break;
1711 :
1712 55 : case MAV_CMD_NAV_LOITER_TO_ALT: // MAV ID: 31
1713 55 : packet.param2 = cmd.p1; // loiter radius(m)
1714 55 : if (cmd.content.location.loiter_ccw) {
1715 3 : packet.param2 = -packet.param2;
1716 : }
1717 55 : packet.param4 = cmd.content.location.loiter_xtrack; // 0 to xtrack from center of waypoint, 1 to xtrack from tangent exit location
1718 55 : break;
1719 :
1720 123 : case MAV_CMD_NAV_ARC_WAYPOINT: { // MAV ID: 36
1721 123 : const float sign = cmd.content.location.loiter_ccw == 0 ? 1.0 : -1.0; // Mavlink command specifies positiive is CW
1722 123 : packet.param1 = float(cmd.p1) * sign;
1723 123 : break;
1724 : }
1725 57 : case MAV_CMD_NAV_SPLINE_WAYPOINT: // MAV ID: 82
1726 57 : packet.param1 = cmd.p1; // delay at waypoint in seconds
1727 57 : break;
1728 :
1729 4 : case MAV_CMD_NAV_GUIDED_ENABLE: // MAV ID: 92
1730 4 : packet.param1 = cmd.p1; // on/off. >0.5 means "on", hand-over control to external controller
1731 4 : break;
1732 :
1733 20 : case MAV_CMD_NAV_DELAY: // MAV ID: 93
1734 20 : packet.param1 = cmd.content.nav_delay.seconds; // delay in seconds
1735 20 : packet.param2 = cmd.content.nav_delay.hour_utc; // absolute time's day of week (utc)
1736 20 : packet.param3 = cmd.content.nav_delay.min_utc; // absolute time's hour (utc)
1737 20 : packet.param4 = cmd.content.nav_delay.sec_utc; // absolute time's min (utc)
1738 20 : break;
1739 :
1740 0 : case MAV_CMD_CONDITION_DELAY: // MAV ID: 112
1741 0 : packet.param1 = cmd.content.delay.seconds; // delay in seconds
1742 0 : break;
1743 :
1744 0 : case MAV_CMD_CONDITION_DISTANCE: // MAV ID: 114
1745 0 : packet.param1 = cmd.content.distance.meters; // distance in meters from next waypoint
1746 0 : break;
1747 :
1748 23 : case MAV_CMD_CONDITION_YAW: // MAV ID: 115
1749 23 : packet.param1 = cmd.content.yaw.angle_deg; // target angle in degrees
1750 23 : packet.param2 = cmd.content.yaw.turn_rate_dps; // 0 = use default turn rate otherwise specific turn rate in deg/sec
1751 23 : packet.param3 = cmd.content.yaw.direction; // -1 = ccw, +1 = cw
1752 23 : packet.param4 = cmd.content.yaw.relative_angle; // 0 = absolute angle provided, 1 = relative angle provided
1753 23 : break;
1754 :
1755 372 : case MAV_CMD_DO_JUMP: // MAV ID: 177
1756 : case MAV_CMD_DO_JUMP_TAG: // MAV ID: 601
1757 372 : packet.param1 = cmd.content.jump.target; // jump-to command/tag number
1758 372 : packet.param2 = cmd.content.jump.num_times; // repeat count
1759 372 : break;
1760 :
1761 5 : case MAV_CMD_JUMP_TAG: // MAV ID: 600
1762 5 : packet.param1 = cmd.content.jump.target; // jump-to tag number
1763 5 : break;
1764 :
1765 124 : case MAV_CMD_DO_CHANGE_SPEED: // MAV ID: 178
1766 124 : packet.param1 = cmd.content.speed.speed_type; // 0 = airspeed, 1 = ground speed
1767 124 : packet.param2 = cmd.content.speed.target_ms; // speed in m/s
1768 124 : packet.param3 = cmd.content.speed.throttle_pct; // throttle as a percentage from 1 ~ 100%
1769 124 : break;
1770 :
1771 0 : case MAV_CMD_DO_SET_HOME: // MAV ID: 179
1772 0 : packet.param1 = cmd.p1; // p1=0 means use current location, p=1 means use provided location
1773 0 : break;
1774 :
1775 0 : case MAV_CMD_DO_SET_RELAY: // MAV ID: 181
1776 0 : packet.param1 = cmd.content.relay.num; // relay number
1777 0 : packet.param2 = cmd.content.relay.state; // 0:off, 1:on
1778 0 : break;
1779 :
1780 0 : case MAV_CMD_DO_REPEAT_RELAY: // MAV ID: 182
1781 0 : packet.param1 = cmd.content.repeat_relay.num; // relay number
1782 0 : packet.param2 = cmd.content.repeat_relay.repeat_count; // count
1783 0 : packet.param3 = cmd.content.repeat_relay.cycle_time; // time in seconds
1784 0 : break;
1785 :
1786 4 : case MAV_CMD_DO_SET_SERVO: // MAV ID: 183
1787 4 : packet.param1 = cmd.content.servo.channel; // channel
1788 4 : packet.param2 = cmd.content.servo.pwm; // PWM
1789 4 : break;
1790 :
1791 0 : case MAV_CMD_DO_REPEAT_SERVO: // MAV ID: 184
1792 0 : packet.param1 = cmd.content.repeat_servo.channel; // channel
1793 0 : packet.param2 = cmd.content.repeat_servo.pwm; // PWM
1794 0 : packet.param3 = cmd.content.repeat_servo.repeat_count; // count
1795 0 : packet.param4 = cmd.content.repeat_servo.cycle_time; // time in milliseconds converted to seconds
1796 0 : break;
1797 :
1798 442 : case MAV_CMD_DO_RETURN_PATH_START: // MAV ID: 188
1799 : case MAV_CMD_DO_LAND_START: // MAV ID: 189
1800 442 : break;
1801 :
1802 0 : case MAV_CMD_DO_GO_AROUND: // MAV ID: 191
1803 0 : break;
1804 :
1805 0 : case MAV_CMD_DO_SET_ROI_LOCATION: // MAV ID: 195
1806 : case MAV_CMD_DO_SET_ROI_NONE: // MAV ID: 197
1807 0 : packet.param1 = cmd.p1; // gimbal device id
1808 0 : break;
1809 :
1810 0 : case MAV_CMD_DO_SET_ROI: // MAV ID: 201
1811 0 : packet.param1 = cmd.p1; // 0 = no roi, 1 = next waypoint, 2 = waypoint number, 3 = fixed location, 4 = given target (not supported)
1812 0 : break;
1813 :
1814 0 : case MAV_CMD_DO_DIGICAM_CONFIGURE: // MAV ID: 202
1815 0 : packet.param1 = cmd.content.digicam_configure.shooting_mode;
1816 0 : packet.param2 = cmd.content.digicam_configure.shutter_speed;
1817 0 : packet.param3 = cmd.content.digicam_configure.aperture;
1818 0 : packet.param4 = cmd.content.digicam_configure.ISO;
1819 0 : packet.x = cmd.content.digicam_configure.exposure_type;
1820 0 : packet.y = cmd.content.digicam_configure.cmd_id;
1821 0 : packet.z = cmd.content.digicam_configure.engine_cutoff_time;
1822 0 : break;
1823 :
1824 3 : case MAV_CMD_DO_DIGICAM_CONTROL: // MAV ID: 203
1825 3 : packet.param1 = cmd.content.digicam_control.session;
1826 3 : packet.param2 = cmd.content.digicam_control.zoom_pos;
1827 3 : packet.param3 = cmd.content.digicam_control.zoom_step;
1828 3 : packet.param4 = cmd.content.digicam_control.focus_lock;
1829 3 : packet.x = cmd.content.digicam_control.shooting_cmd;
1830 3 : packet.y = cmd.content.digicam_control.cmd_id;
1831 3 : break;
1832 :
1833 0 : case MAV_CMD_DO_MOUNT_CONTROL: // MAV ID: 205
1834 0 : packet.param1 = cmd.content.mount_control.pitch;
1835 0 : packet.param2 = cmd.content.mount_control.roll;
1836 0 : packet.param3 = cmd.content.mount_control.yaw;
1837 0 : packet.z = MAV_MOUNT_MODE_MAVLINK_TARGETING;
1838 0 : break;
1839 :
1840 68 : case MAV_CMD_DO_SET_CAM_TRIGG_DIST: // MAV ID: 206
1841 68 : packet.param1 = cmd.content.cam_trigg_dist.meters; // distance between camera shots in meters
1842 68 : packet.param3 = cmd.content.cam_trigg_dist.trigger; // when enabled, camera triggers once immediately
1843 68 : packet.param4 = cmd.content.cam_trigg_dist.camera_id; // which camera to trigger
1844 68 : break;
1845 :
1846 0 : case MAV_CMD_DO_FENCE_ENABLE: // MAV ID: 207
1847 0 : packet.param1 = cmd.p1; // action 0=disable, 1=enable, 2=disable floor, 3=enable except floor
1848 0 : break;
1849 :
1850 34 : case MAV_CMD_DO_PARACHUTE: // MAV ID: 208
1851 34 : packet.param1 = cmd.p1; // action 0=disable, 1=enable, 2=release. See PARACHUTE_ACTION enum
1852 34 : break;
1853 :
1854 7 : case MAV_CMD_DO_SPRAYER:
1855 7 : packet.param1 = cmd.p1; // action 0=disable, 1=enable
1856 7 : break;
1857 :
1858 3 : case MAV_CMD_DO_AUX_FUNCTION:
1859 3 : packet.param1 = cmd.content.auxfunction.function;
1860 3 : packet.param2 = cmd.content.auxfunction.switchpos;
1861 3 : break;
1862 :
1863 6 : case MAV_CMD_DO_INVERTED_FLIGHT: // MAV ID: 210
1864 6 : packet.param1 = cmd.p1; // normal=0 inverted=1
1865 6 : break;
1866 :
1867 : #if AP_GRIPPER_ENABLED
1868 51 : case MAV_CMD_DO_GRIPPER: // MAV ID: 211
1869 51 : packet.param1 = cmd.content.gripper.num; // gripper number
1870 51 : packet.param2 = cmd.content.gripper.action; // action 0=release, 1=grab. See GRIPPER_ACTION enum
1871 51 : break;
1872 : #endif
1873 :
1874 4 : case MAV_CMD_DO_GUIDED_LIMITS: // MAV ID: 222
1875 4 : packet.param1 = cmd.p1; // max time in seconds the external controller will be allowed to control the vehicle
1876 4 : packet.param2 = cmd.content.guided_limits.alt_min; // min alt below which the command will be aborted. 0 for no lower alt limit
1877 4 : packet.param3 = cmd.content.guided_limits.alt_max; // max alt above which the command will be aborted. 0 for no upper alt limit
1878 4 : packet.param4 = cmd.content.guided_limits.horiz_max;// max horizontal distance the vehicle can move before the command will be aborted. 0 for no horizontal limit
1879 4 : break;
1880 :
1881 0 : case MAV_CMD_DO_AUTOTUNE_ENABLE:
1882 0 : packet.param1 = cmd.p1; // disable=0 enable=1
1883 0 : break;
1884 :
1885 0 : case MAV_CMD_DO_SET_REVERSE:
1886 0 : packet.param1 = cmd.p1; // 0 = forward, 1 = reverse
1887 0 : break;
1888 :
1889 6 : case MAV_CMD_NAV_ALTITUDE_WAIT: // MAV ID: 83
1890 6 : packet.param1 = cmd.content.altitude_wait.altitude;
1891 6 : packet.param2 = cmd.content.altitude_wait.descent_rate;
1892 6 : packet.param3 = cmd.content.altitude_wait.wiggle_time;
1893 6 : break;
1894 :
1895 89 : case MAV_CMD_NAV_VTOL_TAKEOFF:
1896 89 : break;
1897 :
1898 97 : case MAV_CMD_NAV_VTOL_LAND:
1899 97 : packet.param1 = cmd.p1;
1900 97 : break;
1901 :
1902 6 : case MAV_CMD_DO_VTOL_TRANSITION:
1903 6 : packet.param1 = cmd.content.do_vtol_transition.target_state;
1904 6 : break;
1905 :
1906 63 : case MAV_CMD_DO_ENGINE_CONTROL:
1907 63 : packet.param1 = cmd.content.do_engine_control.start_control?1:0;
1908 63 : packet.param2 = cmd.content.do_engine_control.cold_start?1:0;
1909 63 : packet.param3 = cmd.content.do_engine_control.height_delay_cm*0.01f;
1910 63 : if (cmd.content.do_engine_control.allow_disarmed_start) {
1911 0 : packet.param4 = ENGINE_CONTROL_OPTIONS_ALLOW_START_WHILE_DISARMED;
1912 : }
1913 63 : break;
1914 :
1915 : #if AP_MISSION_NAV_PAYLOAD_PLACE_ENABLED
1916 8 : case MAV_CMD_NAV_PAYLOAD_PLACE:
1917 8 : packet.param1 = cmd.p1*0.01f; // copy max-descend parameter (cm->m)
1918 8 : break;
1919 : #endif
1920 :
1921 0 : case MAV_CMD_NAV_SET_YAW_SPEED:
1922 0 : packet.param1 = cmd.content.set_yaw_speed.angle_deg; // target angle in degrees
1923 0 : packet.param2 = cmd.content.set_yaw_speed.speed; // speed in meters/second
1924 0 : packet.param3 = cmd.content.set_yaw_speed.relative_angle; // 0 = absolute angle, 1 = relative angle
1925 0 : break;
1926 :
1927 0 : case MAV_CMD_DO_WINCH:
1928 0 : packet.param1 = cmd.content.winch.num; // winch number
1929 0 : packet.param2 = cmd.content.winch.action; // action (0 = relax, 1 = length control, 2 = rate control). See WINCH_ACTION enum
1930 0 : packet.param3 = cmd.content.winch.release_length; // cable distance to unwind in meters, negative numbers to wind in cable
1931 0 : packet.param4 = cmd.content.winch.release_rate; // release rate in meters/second
1932 0 : break;
1933 :
1934 0 : case MAV_CMD_DO_SET_RESUME_REPEAT_DIST:
1935 0 : packet.param1 = cmd.p1; // Resume repeat distance (m)
1936 0 : break;
1937 :
1938 0 : case MAV_CMD_DO_SEND_SCRIPT_MESSAGE:
1939 0 : packet.param1 = cmd.p1;
1940 0 : packet.param2 = cmd.content.scripting.p1;
1941 0 : packet.param3 = cmd.content.scripting.p2;
1942 0 : packet.param4 = cmd.content.scripting.p3;
1943 0 : break;
1944 :
1945 : #if AP_SCRIPTING_ENABLED
1946 0 : case MAV_CMD_NAV_SCRIPT_TIME:
1947 0 : packet.param1 = cmd.content.nav_script_time.command;
1948 0 : packet.param2 = cmd.content.nav_script_time.timeout_s;
1949 0 : packet.param3 = cmd.content.nav_script_time.arg1.get();
1950 0 : packet.param4 = cmd.content.nav_script_time.arg2.get();
1951 0 : packet.x = cmd.content.nav_script_time.arg3;
1952 0 : packet.y = cmd.content.nav_script_time.arg4;
1953 0 : break;
1954 : #endif
1955 :
1956 0 : case MAV_CMD_NAV_ATTITUDE_TIME:
1957 0 : packet.param1 = cmd.content.nav_attitude_time.time_sec;
1958 0 : packet.param2 = cmd.content.nav_attitude_time.roll_deg;
1959 0 : packet.param3 = cmd.content.nav_attitude_time.pitch_deg;
1960 0 : packet.param4 = cmd.content.nav_attitude_time.yaw_deg;
1961 0 : packet.x = cmd.content.nav_attitude_time.climb_rate;
1962 0 : break;
1963 :
1964 0 : case MAV_CMD_DO_PAUSE_CONTINUE:
1965 0 : packet.param1 = cmd.p1;
1966 0 : break;
1967 :
1968 0 : case MAV_CMD_DO_GIMBAL_MANAGER_PITCHYAW:
1969 0 : packet.param1 = cmd.content.gimbal_manager_pitchyaw.pitch_angle_deg;
1970 0 : packet.param2 = cmd.content.gimbal_manager_pitchyaw.yaw_angle_deg;
1971 0 : packet.param3 = cmd.content.gimbal_manager_pitchyaw.pitch_rate_degs;
1972 0 : packet.param4 = cmd.content.gimbal_manager_pitchyaw.yaw_rate_degs;
1973 0 : packet.x = cmd.content.gimbal_manager_pitchyaw.flags;
1974 0 : packet.z = cmd.content.gimbal_manager_pitchyaw.gimbal_id;
1975 0 : break;
1976 :
1977 9 : case MAV_CMD_IMAGE_START_CAPTURE:
1978 9 : packet.param1 = cmd.content.image_start_capture.instance;
1979 9 : packet.param2 = cmd.content.image_start_capture.interval_s;
1980 9 : packet.param3 = cmd.content.image_start_capture.total_num_images;
1981 9 : packet.param4 = cmd.content.image_start_capture.start_seq_number;
1982 9 : break;
1983 :
1984 3 : case MAV_CMD_IMAGE_STOP_CAPTURE:
1985 3 : packet.param1 = cmd.p1;
1986 3 : break;
1987 :
1988 6 : case MAV_CMD_SET_CAMERA_ZOOM:
1989 6 : packet.param1 = cmd.content.set_camera_zoom.zoom_type;
1990 6 : packet.param2 = cmd.content.set_camera_zoom.zoom_value;
1991 6 : packet.param3 = cmd.content.set_camera_zoom.camera_id;
1992 6 : break;
1993 :
1994 6 : case MAV_CMD_SET_CAMERA_FOCUS:
1995 6 : packet.param1 = cmd.content.set_camera_focus.focus_type;
1996 6 : packet.param2 = cmd.content.set_camera_focus.focus_value;
1997 6 : packet.param3 = cmd.content.set_camera_focus.camera_id;
1998 6 : break;
1999 :
2000 0 : case MAV_CMD_SET_CAMERA_SOURCE:
2001 0 : packet.param1 = cmd.content.set_camera_source.instance;
2002 0 : packet.param2 = cmd.content.set_camera_source.primary_source;
2003 0 : packet.param3 = cmd.content.set_camera_source.secondary_source;
2004 0 : break;
2005 :
2006 0 : case MAV_CMD_VIDEO_START_CAPTURE:
2007 0 : packet.param1 = cmd.content.video_start_capture.video_stream_id;
2008 0 : break;
2009 :
2010 0 : case MAV_CMD_VIDEO_STOP_CAPTURE:
2011 0 : packet.param1 = cmd.content.video_stop_capture.video_stream_id;
2012 0 : break;
2013 :
2014 : #if AP_MISSION_MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET_ENABLED
2015 12 : case MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET:
2016 12 : packet.param1 = cmd.content.wpnext_offset.gimbal_id;
2017 12 : packet.y = cmd.content.wpnext_offset.roll_offset_cd * 0.01;
2018 12 : packet.x = cmd.content.wpnext_offset.pitch_offset_cd * 0.01;
2019 12 : packet.z = cmd.content.wpnext_offset.yaw_offset_cd * 0.01;
2020 12 : break;
2021 : #endif // AP_MISSION_MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET_ENABLED
2022 :
2023 0 : default:
2024 : // unrecognised command
2025 0 : return false;
2026 : }
2027 :
2028 : // copy location from mavlink to command
2029 26892 : if (stored_in_location(cmd.id)) {
2030 25754 : packet.x = cmd.content.location.lat;
2031 25754 : packet.y = cmd.content.location.lng;
2032 :
2033 25754 : packet.z = cmd.content.location.alt * 0.01f; // cmd alt in cm to m
2034 25754 : if (cmd.content.location.relative_alt) {
2035 23687 : packet.frame = MAV_FRAME_GLOBAL_RELATIVE_ALT;
2036 : } else {
2037 2067 : packet.frame = MAV_FRAME_GLOBAL;
2038 : }
2039 : #if AP_TERRAIN_AVAILABLE
2040 25754 : if (cmd.content.location.terrain_alt) {
2041 : // this is a above-terrain altitude
2042 2510 : if (!cmd.content.location.relative_alt) {
2043 : // refuse to return non-relative terrain mission
2044 : // items. Internally we do have these, and they
2045 : // have home.alt added, but we should never be
2046 : // returning them to the GCS, as the GCS doesn't know
2047 : // our home.alt, so it would have no way to properly
2048 : // interpret it
2049 0 : return false;
2050 : }
2051 2510 : packet.z = cmd.content.location.alt * 0.01f;
2052 2510 : packet.frame = MAV_FRAME_GLOBAL_TERRAIN_ALT;
2053 : }
2054 : #else
2055 : // don't ever return terrain mission items if no terrain support
2056 : if (cmd.content.location.terrain_alt) {
2057 : return false;
2058 : }
2059 : #endif
2060 : }
2061 :
2062 : // if we got this far then it must have been successful
2063 26892 : return true;
2064 : }
2065 :
2066 : ///
2067 : /// private methods
2068 : ///
2069 :
2070 : /// complete - mission is marked complete and clean-up performed including calling the mission_complete_fn
2071 86 : void AP_Mission::complete()
2072 : {
2073 : // flag mission as complete
2074 86 : _flags.state = MISSION_COMPLETE;
2075 86 : _flags.in_landing_sequence = false;
2076 86 : _flags.in_return_path = false;
2077 :
2078 : // callback to main program's mission complete function
2079 86 : _mission_complete_fn();
2080 86 : }
2081 :
2082 : /// advance_current_nav_cmd - moves current nav command forward
2083 : /// do command will also be loaded
2084 : /// accounts for do-jump commands
2085 : // returns true if command is advanced, false if failed (i.e. mission completed)
2086 1932 : bool AP_Mission::advance_current_nav_cmd(uint16_t starting_index)
2087 : {
2088 : // exit immediately if we're not running
2089 1932 : if (_flags.state != MISSION_RUNNING) {
2090 0 : return false;
2091 : }
2092 :
2093 : // exit immediately if current nav command has not completed
2094 1932 : if (_flags.nav_cmd_loaded) {
2095 0 : return false;
2096 : }
2097 :
2098 : // stop the current running do command
2099 1932 : _do_cmd.index = AP_MISSION_CMD_INDEX_NONE;
2100 1932 : _flags.do_cmd_loaded = false;
2101 1932 : _flags.do_cmd_all_done = false;
2102 :
2103 : // get starting point for search
2104 1932 : uint16_t cmd_index = starting_index > 0 ? starting_index - 1 : _nav_cmd.index;
2105 1932 : if (cmd_index == AP_MISSION_CMD_INDEX_NONE) {
2106 : // start from beginning of the mission command list
2107 115 : cmd_index = AP_MISSION_FIRST_REAL_COMMAND;
2108 : } else {
2109 : // start from one position past the current nav command
2110 1817 : cmd_index++;
2111 : }
2112 :
2113 : // avoid endless loops
2114 1932 : uint8_t max_loops = 255;
2115 :
2116 : // search until we find next nav command or reach end of command list
2117 3962 : while (!_flags.nav_cmd_loaded && max_loops-- > 0) {
2118 : // get next command
2119 2116 : Mission_Command cmd;
2120 2116 : if (!get_next_cmd(cmd_index, cmd, true)) {
2121 86 : return false;
2122 : }
2123 :
2124 : // check if navigation or "do" command
2125 2030 : if (is_nav_cmd(cmd)) {
2126 : // save previous nav command index
2127 1853 : _prev_nav_cmd_id = _nav_cmd.id;
2128 1853 : _prev_nav_cmd_index = _nav_cmd.index;
2129 : // save separate previous nav command index if it contains lat,long,alt
2130 1853 : if (!(cmd.content.location.lat == 0 && cmd.content.location.lng == 0)) {
2131 1615 : _prev_nav_cmd_wp_index = _nav_cmd.index;
2132 : }
2133 : // set current navigation command and start it
2134 1853 : _nav_cmd = cmd;
2135 1853 : if (start_command(_nav_cmd)) {
2136 1846 : _flags.nav_cmd_loaded = true;
2137 1846 : if (_jump_tag.age > 0 && _jump_tag.age < UINT16_MAX) {
2138 : // we're tracking a tag so increase it's age on every new NAV item
2139 1 : _jump_tag.age++;
2140 : }
2141 : }
2142 : // save a loaded wp index in history array for when _repeat_dist is set via MAV_CMD_DO_SET_RESUME_REPEAT_DIST
2143 : // and prevent history being re-written until vehicle returns to interrupted position
2144 1853 : if (_repeat_dist > 0 && !_flags.resuming_mission && _nav_cmd.index != AP_MISSION_CMD_INDEX_NONE && !(_nav_cmd.content.location.lat == 0 && _nav_cmd.content.location.lng == 0)) {
2145 : // update mission history. last index position is always the most recent wp loaded.
2146 0 : for (uint8_t i=0; i<AP_MISSION_MAX_WP_HISTORY-1; i++) {
2147 0 : _wp_index_history[i] = _wp_index_history[i+1];
2148 : }
2149 0 : _wp_index_history[AP_MISSION_MAX_WP_HISTORY-1] = _nav_cmd.index;
2150 : }
2151 : // check if the vehicle is resuming and has returned to where it was interrupted
2152 1853 : if (_flags.resuming_mission && _nav_cmd.index == _wp_index_history[AP_MISSION_MAX_WP_HISTORY-1]) {
2153 : // vehicle has resumed previous position
2154 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Mission: Returned to interrupted WP");
2155 0 : _flags.resuming_mission = false;
2156 : }
2157 :
2158 : } else {
2159 : // set current do command and start it (if not already set)
2160 177 : if (!_flags.do_cmd_loaded) {
2161 166 : _do_cmd = cmd;
2162 166 : _flags.do_cmd_loaded = true;
2163 166 : start_command(_do_cmd);
2164 : }
2165 : }
2166 : // move onto next command
2167 2030 : cmd_index = cmd.index+1;
2168 : }
2169 :
2170 1846 : if (max_loops == 0) {
2171 : // infinite loop. This can happen if there's a loop involving
2172 : // only nav commands (no DO commands) which won't start()
2173 0 : return false;
2174 : }
2175 :
2176 : // if we have not found a do command then set flag to show there are no do-commands to be run before nav command completes
2177 1846 : if (!_flags.do_cmd_loaded) {
2178 1684 : _flags.do_cmd_all_done = true;
2179 : }
2180 :
2181 : // if we got this far we must have successfully advanced the nav command
2182 1846 : return true;
2183 : }
2184 :
2185 : /// advance_current_do_cmd - moves current do command forward
2186 : /// accounts for do-jump commands
2187 4236782 : void AP_Mission::advance_current_do_cmd()
2188 : {
2189 : // exit immediately if we're not running or we've completed all possible "do" commands
2190 4236782 : if (_flags.state != MISSION_RUNNING || _flags.do_cmd_all_done) {
2191 4236771 : return;
2192 : }
2193 :
2194 : // get starting point for search
2195 157 : uint16_t cmd_index = _do_cmd.index;
2196 157 : if (cmd_index == AP_MISSION_CMD_INDEX_NONE) {
2197 9 : cmd_index = AP_MISSION_FIRST_REAL_COMMAND;
2198 : } else {
2199 : // start from one position past the current do command
2200 148 : cmd_index = _do_cmd.index + 1;
2201 : }
2202 :
2203 : // find next do command
2204 157 : Mission_Command cmd;
2205 157 : if (!get_next_do_cmd(cmd_index, cmd)) {
2206 : // set flag to stop unnecessarily searching for do commands
2207 146 : _flags.do_cmd_all_done = true;
2208 146 : return;
2209 : }
2210 :
2211 : // set current do command and start it
2212 11 : _do_cmd = cmd;
2213 11 : _flags.do_cmd_loaded = true;
2214 11 : start_command(_do_cmd);
2215 : }
2216 :
2217 : /// get_next_cmd - gets next command found at or after start_index
2218 : /// returns true if found, false if not found (i.e. mission complete)
2219 : /// accounts for do_jump commands
2220 : /// increment_jump_num_times_if_found should be set to true if advancing the active navigation command
2221 27416 : bool AP_Mission::get_next_cmd(uint16_t start_index, Mission_Command& cmd, bool increment_jump_num_times_if_found, bool send_gcs_msg)
2222 : {
2223 27416 : uint16_t cmd_index = start_index;
2224 27416 : Mission_Command temp_cmd;
2225 27416 : uint16_t jump_index = AP_MISSION_CMD_INDEX_NONE;
2226 :
2227 : // search until the end of the mission command list
2228 27416 : uint8_t max_loops = 64;
2229 27941 : while (cmd_index < (unsigned)_cmd_total) {
2230 : // load the next command
2231 27854 : if (!read_cmd_from_storage(cmd_index, temp_cmd)) {
2232 : // this should never happen because of check above but just in case
2233 0 : return false;
2234 : }
2235 :
2236 : // check for do-jump-tag command and convert target tag to do-jump target index and do-jump to it
2237 27854 : if (temp_cmd.id == MAV_CMD_DO_JUMP_TAG) {
2238 : // convert tmp_cmd target from a target tag to a target index
2239 4 : temp_cmd.content.jump.target = get_index_of_jump_tag(temp_cmd.content.jump.target);
2240 4 : temp_cmd.id = MAV_CMD_DO_JUMP;
2241 : }
2242 :
2243 : // check for do-jump command
2244 27854 : if (temp_cmd.id == MAV_CMD_DO_JUMP) {
2245 :
2246 529 : if (max_loops-- == 0) {
2247 0 : return false;
2248 : }
2249 :
2250 : // check for invalid target
2251 529 : if ((temp_cmd.content.jump.target >= (unsigned)_cmd_total) || (temp_cmd.content.jump.target == 0)) {
2252 : // To-Do: log an error?
2253 4 : return false;
2254 : }
2255 :
2256 : // check for endless loops
2257 525 : if (!increment_jump_num_times_if_found && jump_index == cmd_index) {
2258 : // we have somehow reached this jump command twice and there is no chance it will complete
2259 : // To-Do: log an error?
2260 0 : return false;
2261 : }
2262 :
2263 : // record this command so we can check for endless loops
2264 525 : if (jump_index == AP_MISSION_CMD_INDEX_NONE) {
2265 525 : jump_index = cmd_index;
2266 : }
2267 :
2268 : // get number of times jump command has already been run
2269 1039 : if (temp_cmd.content.jump.num_times == AP_MISSION_JUMP_REPEAT_FOREVER ||
2270 514 : get_jump_times_run(temp_cmd) < temp_cmd.content.jump.num_times) {
2271 : // update the record of the number of times run
2272 413 : if (increment_jump_num_times_if_found && !_flags.resuming_mission) {
2273 50 : increment_jump_times_run(temp_cmd, send_gcs_msg);
2274 : }
2275 : // continue searching from jump target
2276 413 : cmd_index = temp_cmd.content.jump.target;
2277 : } else {
2278 112 : if (increment_jump_num_times_if_found && !option_is_set(Option::DONT_ZERO_COUNTER)) {
2279 : /*
2280 : when we have completed a jump loop reset the counter
2281 : so if the user changes WP back to restart the loop
2282 : they get the count again
2283 : */
2284 34 : for (uint8_t i=0; i<AP_MISSION_MAX_NUM_DO_JUMP_COMMANDS; i++) {
2285 34 : if (_jump_tracking[i].index == cmd_index) {
2286 34 : _jump_tracking[i].num_times_run = 0;
2287 34 : break;
2288 : }
2289 : }
2290 : }
2291 : // jump has been run specified number of times so move search to next command in mission
2292 112 : cmd_index++;
2293 : }
2294 : } else {
2295 : // this is a non-jump command so return it
2296 27325 : cmd = temp_cmd;
2297 27325 : return true;
2298 : }
2299 : }
2300 :
2301 : // if we got this far we did not find a navigation command
2302 87 : return false;
2303 : }
2304 :
2305 : /// get_next_do_cmd - gets next "do" or "conditional" command after start_index
2306 : /// returns true if found, false if not found
2307 : /// stops and returns false if it hits another navigation command before it finds the first do or conditional command
2308 : /// accounts for do_jump commands but never increments the jump's num_times_run (advance_current_nav_cmd is responsible for this)
2309 157 : bool AP_Mission::get_next_do_cmd(uint16_t start_index, Mission_Command& cmd)
2310 : {
2311 157 : Mission_Command temp_cmd;
2312 :
2313 : // check we have not passed the end of the mission list
2314 157 : if (start_index >= (unsigned)_cmd_total) {
2315 1 : return false;
2316 : }
2317 :
2318 : // get next command
2319 156 : if (!get_next_cmd(start_index, temp_cmd, false)) {
2320 : // no more commands so return failure
2321 0 : return false;
2322 156 : } else if (is_nav_cmd(temp_cmd)) {
2323 : // if it's a "navigation" command then return false because we do not progress past nav commands
2324 145 : return false;
2325 : } else {
2326 : // this must be a "do" or "conditional" and is not a do-jump command so return it
2327 11 : cmd = temp_cmd;
2328 11 : return true;
2329 : }
2330 : }
2331 :
2332 : ///
2333 : /// jump handling methods
2334 : ///
2335 :
2336 : // Set the mission index to the first JUMP_TAG with this tag.
2337 : // Returns true on success, else false if no appropriate JUMP_TAG match can be found or if setting the index failed
2338 4 : bool AP_Mission::jump_to_tag(const uint16_t tag)
2339 : {
2340 4 : const uint16_t index = get_index_of_jump_tag(tag);
2341 4 : if (index == 0) {
2342 2 : return false;
2343 : }
2344 2 : return set_current_cmd(index);
2345 : }
2346 :
2347 : // find the first JUMP_TAG with this tag and return its index.
2348 : // Returns 0 if no appropriate JUMP_TAG match can be found.
2349 8 : uint16_t AP_Mission::get_index_of_jump_tag(const uint16_t tag) const
2350 : {
2351 8 : const auto count = num_commands();
2352 28 : for (uint16_t i = 1; i < count; i++) {
2353 22 : if (get_command_id(i) != uint16_t(MAV_CMD_JUMP_TAG)) {
2354 19 : continue;
2355 : }
2356 3 : Mission_Command tmp;
2357 3 : if (!read_cmd_from_storage(i, tmp)) {
2358 0 : continue;
2359 : }
2360 3 : if (tmp.id == MAV_CMD_JUMP_TAG && tmp.content.jump.target == tag) {
2361 2 : return i;
2362 : }
2363 : }
2364 6 : return 0;
2365 : }
2366 :
2367 : #if AP_SCRIPTING_ENABLED
2368 0 : bool AP_Mission::get_last_jump_tag(uint16_t &tag, uint16_t &age) const
2369 : {
2370 0 : if (_jump_tag.age == 0) {
2371 0 : return false;
2372 : }
2373 0 : tag = _jump_tag.tag;
2374 0 : age = _jump_tag.age;
2375 0 : return true;
2376 : }
2377 : #endif
2378 :
2379 : // init_jump_tracking - initialise jump_tracking variables
2380 3369 : void AP_Mission::init_jump_tracking()
2381 : {
2382 340269 : for (uint8_t i=0; i<AP_MISSION_MAX_NUM_DO_JUMP_COMMANDS; i++) {
2383 336900 : _jump_tracking[i].index = AP_MISSION_CMD_INDEX_NONE;
2384 336900 : _jump_tracking[i].num_times_run = 0;
2385 : }
2386 3369 : }
2387 :
2388 : /// get_jump_times_run - returns number of times the jump command has been run
2389 514 : int16_t AP_Mission::get_jump_times_run(const Mission_Command& cmd)
2390 : {
2391 : // exit immediately if cmd is not a do-jump command or target is invalid
2392 514 : if ((cmd.id != MAV_CMD_DO_JUMP) || (cmd.content.jump.target >= (unsigned)_cmd_total) || (cmd.content.jump.target == 0)) {
2393 : // To-Do: log an error?
2394 0 : return AP_MISSION_JUMP_TIMES_MAX;
2395 : }
2396 :
2397 : // search through jump_tracking array for this cmd
2398 514 : for (uint8_t i=0; i<AP_MISSION_MAX_NUM_DO_JUMP_COMMANDS; i++) {
2399 514 : if (_jump_tracking[i].index == cmd.index) {
2400 477 : return _jump_tracking[i].num_times_run;
2401 37 : } else if (_jump_tracking[i].index == AP_MISSION_CMD_INDEX_NONE) {
2402 : // we've searched through all known jump commands and haven't found it so allocate new space in _jump_tracking array
2403 37 : _jump_tracking[i].index = cmd.index;
2404 37 : _jump_tracking[i].num_times_run = 0;
2405 37 : return 0;
2406 : }
2407 : }
2408 :
2409 : // if we've gotten this far then the _jump_tracking array must be full
2410 : // To-Do: log an error?
2411 0 : return AP_MISSION_JUMP_TIMES_MAX;
2412 : }
2413 :
2414 : /// increment_jump_times_run - increments the recorded number of times the jump command has been run
2415 50 : void AP_Mission::increment_jump_times_run(Mission_Command& cmd, bool send_gcs_msg)
2416 : {
2417 : // exit immediately if cmd is not a do-jump command
2418 50 : if (cmd.id != MAV_CMD_DO_JUMP) {
2419 : // To-Do: log an error?
2420 0 : return;
2421 : }
2422 :
2423 : // search through jump_tracking array for this cmd
2424 50 : for (uint8_t i=0; i<AP_MISSION_MAX_NUM_DO_JUMP_COMMANDS; i++) {
2425 50 : if (_jump_tracking[i].index == cmd.index) {
2426 46 : _jump_tracking[i].num_times_run++;
2427 46 : if (send_gcs_msg) {
2428 46 : if (cmd.content.jump.num_times == AP_MISSION_JUMP_REPEAT_FOREVER) {
2429 1 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Mission: %u Jump %i/unlimited", _jump_tracking[i].index, _jump_tracking[i].num_times_run);
2430 : } else {
2431 45 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Mission: %u Jump %i/%i", _jump_tracking[i].index, _jump_tracking[i].num_times_run, cmd.content.jump.num_times);
2432 : }
2433 : }
2434 46 : return;
2435 4 : } else if (_jump_tracking[i].index == AP_MISSION_CMD_INDEX_NONE) {
2436 : // we've searched through all known jump commands and haven't found it so allocate new space in _jump_tracking array
2437 4 : _jump_tracking[i].index = cmd.index;
2438 4 : _jump_tracking[i].num_times_run = 1;
2439 4 : return;
2440 : }
2441 : }
2442 :
2443 : // if we've gotten this far then the _jump_tracking array must be full
2444 : // To-Do: log an error
2445 0 : return;
2446 : }
2447 :
2448 : // check_eeprom_version - checks version of missions stored in eeprom matches this library
2449 : // command list will be cleared if they do not match
2450 2054 : void AP_Mission::check_eeprom_version()
2451 : {
2452 2054 : uint32_t eeprom_version = _storage.read_uint32(0);
2453 :
2454 : // if eeprom version does not match, clear the command list and update the eeprom version
2455 2054 : if (eeprom_version != AP_MISSION_EEPROM_VERSION) {
2456 291 : if (clear()) {
2457 291 : _storage.write_uint32(0, AP_MISSION_EEPROM_VERSION);
2458 : }
2459 : }
2460 2054 : }
2461 :
2462 : // find the nearest landing sequence starting point (DO_LAND_START) and
2463 : // return its index. Returns 0 if no appropriate DO_LAND_START point can
2464 : // be found.
2465 97 : uint16_t AP_Mission::get_landing_sequence_start(const Location ¤t_loc)
2466 : {
2467 97 : uint16_t landing_start_index = 0;
2468 97 : float min_distance = -1;
2469 :
2470 : // Go through mission looking for nearest landing start command
2471 97 : const auto count = num_commands();
2472 265 : for (uint16_t i = 1; i < count; i++) {
2473 168 : if (get_command_id(i) != uint16_t(MAV_CMD_DO_LAND_START)) {
2474 140 : continue;
2475 : }
2476 28 : Mission_Command tmp;
2477 28 : if (!read_cmd_from_storage(i, tmp)) {
2478 0 : continue;
2479 : }
2480 28 : if (tmp.id == MAV_CMD_DO_LAND_START) {
2481 28 : if (!tmp.content.location.initialised() && !get_next_nav_cmd(i, tmp)) {
2482 : // command does not have a valid location and cannot get next valid
2483 0 : continue;
2484 : }
2485 :
2486 28 : const float tmp_distance = tmp.content.location.get_distance_NED_alt_frame(current_loc).length();
2487 28 : if (min_distance < 0 || tmp_distance < min_distance) {
2488 28 : min_distance = tmp_distance;
2489 28 : landing_start_index = i;
2490 : }
2491 : }
2492 : }
2493 :
2494 97 : return landing_start_index;
2495 : }
2496 :
2497 : /*
2498 : find the nearest landing sequence starting point (DO_LAND_START) and
2499 : switch to that mission item. Returns false if no DO_LAND_START
2500 : available.
2501 : */
2502 39 : bool AP_Mission::jump_to_landing_sequence(const Location ¤t_loc)
2503 : {
2504 39 : uint16_t land_idx = get_landing_sequence_start(current_loc);
2505 39 : if (land_idx != 0 && set_current_cmd(land_idx)) {
2506 :
2507 : //if the mission has ended it has to be restarted
2508 22 : if (state() == AP_Mission::MISSION_STOPPED) {
2509 17 : resume();
2510 : }
2511 :
2512 22 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Landing sequence start");
2513 22 : return true;
2514 : }
2515 :
2516 17 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Unable to start landing sequence");
2517 17 : return false;
2518 : }
2519 :
2520 : /*
2521 : find the closest point on the mission after a DO_RETURN_PATH_START and before DO_LAND_START or landing
2522 : */
2523 18 : bool AP_Mission::jump_to_closest_mission_leg(const Location ¤t_loc)
2524 : {
2525 18 : if (_flags.state == MISSION_RUNNING) {
2526 : // if mission is already running don't switch away from a active landing or return path
2527 0 : if (_flags.in_landing_sequence) {
2528 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Landing sequence active");
2529 0 : return true;
2530 :
2531 0 : } else if (_flags.in_return_path) {
2532 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Return path active");
2533 0 : return true;
2534 : }
2535 : }
2536 :
2537 18 : uint16_t landing_start_index = 0;
2538 18 : float min_distance = -1;
2539 :
2540 : // This defines the maximum number of waypoints that will be searched, this limits the worst case runtime
2541 18 : uint16_t search_remaining = 1000;
2542 :
2543 : // Go through mission and check each DO_RETURN_PATH_START
2544 68 : for (uint16_t i = 1; i < num_commands(); i++) {
2545 50 : if (get_command_id(i) == uint16_t(MAV_CMD_DO_RETURN_PATH_START)) {
2546 : uint16_t tmp_index;
2547 : float tmp_distance;
2548 4 : if (distance_to_mission_leg(i, search_remaining, tmp_distance, tmp_index, current_loc) && (min_distance < 0 || tmp_distance <= min_distance)){
2549 4 : min_distance = tmp_distance;
2550 4 : landing_start_index = tmp_index;
2551 : }
2552 4 : if (search_remaining == 0) {
2553 : // Run out of time to search, stop and return the best so far
2554 0 : break;
2555 : }
2556 : }
2557 : }
2558 :
2559 18 : if (landing_start_index != 0 && set_current_cmd(landing_start_index)) {
2560 :
2561 : // if the mission has ended it has to be restarted
2562 4 : if (state() == AP_Mission::MISSION_STOPPED) {
2563 4 : resume();
2564 : }
2565 :
2566 4 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Return path started");
2567 4 : _flags.in_return_path = true;
2568 4 : return true;
2569 : }
2570 :
2571 : // Failed to find do land start
2572 14 : return false;
2573 : }
2574 :
2575 : // jumps the mission to the closest landing abort that is planned, returns false if unable to find a valid abort
2576 2 : bool AP_Mission::jump_to_abort_landing_sequence(const Location ¤t_loc)
2577 : {
2578 2 : uint16_t abort_index = 0;
2579 2 : float min_distance = FLT_MAX;
2580 :
2581 2 : const auto count = num_commands();
2582 14 : for (uint16_t i = 1; i < count; i++) {
2583 12 : if (get_command_id(i) != uint16_t(MAV_CMD_DO_GO_AROUND)) {
2584 12 : continue;
2585 : }
2586 0 : Mission_Command tmp;
2587 0 : if (!read_cmd_from_storage(i, tmp)) {
2588 0 : continue;
2589 : }
2590 0 : if (tmp.id == MAV_CMD_DO_GO_AROUND) {
2591 0 : float tmp_distance = tmp.content.location.get_distance(current_loc);
2592 0 : if (tmp_distance < min_distance) {
2593 0 : min_distance = tmp_distance;
2594 0 : abort_index = i;
2595 : }
2596 : }
2597 : }
2598 :
2599 2 : if (abort_index != 0 && set_current_cmd(abort_index)) {
2600 :
2601 : //if the mission has ended it has to be restarted
2602 0 : if (state() == AP_Mission::MISSION_STOPPED) {
2603 0 : resume();
2604 : }
2605 :
2606 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Landing abort sequence start");
2607 0 : return true;
2608 : }
2609 :
2610 2 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Unable to start find a landing abort sequence");
2611 2 : return false;
2612 : }
2613 :
2614 : // check which is the shortest route to landing an RTL via a DO_LAND_START or continuing on the current mission plan
2615 0 : bool AP_Mission::is_best_land_sequence(const Location ¤t_loc)
2616 : {
2617 : // check if there is even a running mission to interupt
2618 0 : if (_flags.state != MISSION_RUNNING) {
2619 0 : return false;
2620 : }
2621 :
2622 : // check if aircraft has already jumped to a landing sequence
2623 0 : if (_flags.in_landing_sequence) {
2624 0 : return true;
2625 : }
2626 :
2627 : // check if MIS_OPTIONS bit set to allow distance calculation to be done
2628 0 : if (!option_is_set(Option::FAILSAFE_TO_BEST_LANDING)) {
2629 0 : return false;
2630 : }
2631 :
2632 : // The decision to allow a failsafe to interupt a potential landing approach
2633 : // is a distance travelled minimization problem. Look forward in
2634 : // mission to evaluate the shortest remaining distance to land.
2635 :
2636 : // go through the mission for the nearest DO_LAND_START first as this is the most probable route
2637 : // to a landing with the minimum number of WP.
2638 0 : uint16_t do_land_start_index = get_landing_sequence_start(current_loc);
2639 0 : if (do_land_start_index == 0) {
2640 : // then no DO_LAND_START commands are in mission and normal failsafe behaviour should be maintained
2641 0 : return false;
2642 : }
2643 :
2644 : // get distance to landing if travelled to nearest DO_LAND_START via RTL
2645 : float dist_via_do_land;
2646 0 : if (!distance_to_landing(do_land_start_index, dist_via_do_land, current_loc)) {
2647 : // cant get a valid distance to landing
2648 0 : return false;
2649 : }
2650 :
2651 : // get distance to landing if continue along current mission path
2652 : float dist_continue_to_land;
2653 0 : if (!distance_to_landing(_nav_cmd.index, dist_continue_to_land, current_loc)) {
2654 : // cant get a valid distance to landing
2655 0 : return false;
2656 : }
2657 :
2658 : // compare distances
2659 0 : if (dist_via_do_land >= dist_continue_to_land) {
2660 : // then the mission should carry on uninterrupted as that is the shorter distance
2661 0 : GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Rejecting RTL: closer land if mis continued");
2662 0 : return true;
2663 : } else {
2664 : // allow failsafes to interrupt the current mission
2665 0 : return false;
2666 : }
2667 : }
2668 :
2669 : // Approximate the distance travelled to get to a landing. DO_JUMP commands are observed in look forward.
2670 0 : bool AP_Mission::distance_to_landing(uint16_t index, float &tot_distance, Location prev_loc)
2671 : {
2672 0 : Mission_Command temp_cmd;
2673 0 : tot_distance = 0.0f;
2674 0 : bool ret = false; // reached end of loop without getting to a landing
2675 :
2676 : // back up jump tracking to reset after distance calculation
2677 : jump_tracking_struct _jump_tracking_backup[AP_MISSION_MAX_NUM_DO_JUMP_COMMANDS];
2678 0 : for (uint8_t i=0; i<AP_MISSION_MAX_NUM_DO_JUMP_COMMANDS; i++) {
2679 0 : _jump_tracking_backup[i] = _jump_tracking[i];
2680 : }
2681 :
2682 : // run through remainder of mission to approximate a distance to landing
2683 0 : for (uint8_t i=0; i<UINT8_MAX; i++) {
2684 : // search until the end of the mission command list
2685 0 : for (uint16_t cmd_index = index; cmd_index < (unsigned)_cmd_total; cmd_index++) {
2686 : // get next command
2687 0 : if (!get_next_cmd(cmd_index, temp_cmd, true, false)) {
2688 : // we got to the end of the mission
2689 0 : goto reset_do_jump_tracking;
2690 : }
2691 0 : if (temp_cmd.id == MAV_CMD_NAV_WAYPOINT || temp_cmd.id == MAV_CMD_NAV_SPLINE_WAYPOINT || is_landing_type_cmd(temp_cmd.id)) {
2692 0 : break;
2693 0 : } else if (is_nav_cmd(temp_cmd) || temp_cmd.id == MAV_CMD_CONDITION_DELAY) {
2694 : // if we receive a nav command that we dont handle then give up as cant measure the distance e.g. MAV_CMD_NAV_LOITER_UNLIM
2695 0 : goto reset_do_jump_tracking;
2696 : }
2697 : }
2698 0 : index = temp_cmd.index+1;
2699 :
2700 0 : if (!(temp_cmd.content.location.lat == 0 && temp_cmd.content.location.lng == 0)) {
2701 : // add distance to running total
2702 0 : float disttemp = prev_loc.get_distance(temp_cmd.content.location);
2703 0 : tot_distance = tot_distance + disttemp;
2704 :
2705 : // store wp location as previous
2706 0 : prev_loc = temp_cmd.content.location;
2707 : }
2708 :
2709 0 : if (is_landing_type_cmd(temp_cmd.id)) {
2710 : // reached a landing!
2711 0 : ret = true;
2712 0 : goto reset_do_jump_tracking;
2713 : }
2714 : }
2715 :
2716 0 : reset_do_jump_tracking:
2717 0 : for (uint8_t i=0; i<AP_MISSION_MAX_NUM_DO_JUMP_COMMANDS; i++) {
2718 0 : _jump_tracking[i] = _jump_tracking_backup[i];
2719 : }
2720 :
2721 0 : return ret;
2722 : }
2723 :
2724 : // Approximate the distance travelled to return to the mission path. DO_JUMP commands are observed in look forward.
2725 : // Stop searching once reaching a landing or do-land-start
2726 4 : bool AP_Mission::distance_to_mission_leg(uint16_t start_index, uint16_t &search_remaining, float &rejoin_distance, uint16_t &rejoin_index, const Location& current_loc)
2727 : {
2728 4 : Location prev_loc;
2729 4 : Mission_Command temp_cmd;
2730 4 : rejoin_distance = -1;
2731 4 : rejoin_index = -1;
2732 4 : bool ret = false;
2733 :
2734 : // back up jump tracking to reset after distance calculation
2735 : jump_tracking_struct _jump_tracking_backup[AP_MISSION_MAX_NUM_DO_JUMP_COMMANDS];
2736 404 : for (uint8_t i=0; i<AP_MISSION_MAX_NUM_DO_JUMP_COMMANDS; i++) {
2737 400 : _jump_tracking_backup[i] = _jump_tracking[i];
2738 : }
2739 :
2740 : // run through remainder of mission to approximate a distance to landing
2741 4 : uint16_t index = start_index;
2742 24 : for (; search_remaining > 0; search_remaining--) {
2743 : // search until the end of the mission command list
2744 24 : for (uint16_t cmd_index = index; cmd_index <= (unsigned)_cmd_total; cmd_index++) {
2745 24 : if (get_next_cmd(cmd_index, temp_cmd, true, false)) {
2746 24 : break;
2747 : } else {
2748 : // got to the end of the mission
2749 0 : goto reset_do_jump_tracking;
2750 : }
2751 : }
2752 24 : index = temp_cmd.index + 1;
2753 :
2754 24 : if (stored_in_location(temp_cmd.id) && temp_cmd.content.location.initialised()) {
2755 16 : if (prev_loc.lat == 0 && prev_loc.lng == 0) {
2756 : // Need a valid previous location to do distance to leg calculation
2757 4 : prev_loc = temp_cmd.content.location;
2758 :
2759 : // single point dist calc
2760 4 : rejoin_distance = prev_loc.get_distance_NED_alt_frame(current_loc).length();
2761 4 : rejoin_index = temp_cmd.index;
2762 4 : ret = true;
2763 :
2764 : } else {
2765 : // Calculate the distance to rejoin
2766 12 : const Vector3f mission_vector = prev_loc.get_distance_NED_alt_frame(temp_cmd.content.location);
2767 12 : if (!mission_vector.is_zero()) {
2768 12 : Vector3f pos = prev_loc.get_distance_NED_alt_frame(current_loc);
2769 :
2770 : // project pos vector on to mission vector
2771 12 : Vector3f p = pos.projected(mission_vector);
2772 :
2773 : // constrain to mission line
2774 12 : p.x = constrain_float(p.x, MIN(0,mission_vector.x), MAX(0,mission_vector.x));
2775 12 : p.y = constrain_float(p.y, MIN(0,mission_vector.y), MAX(0,mission_vector.y));
2776 12 : p.z = constrain_float(p.z, MIN(0,mission_vector.z), MAX(0,mission_vector.z));
2777 :
2778 12 : const float disttemp = (p - pos).length();
2779 :
2780 : // store wp location as previous
2781 12 : prev_loc = temp_cmd.content.location;
2782 :
2783 12 : if (disttemp < rejoin_distance || is_negative(rejoin_distance)) {
2784 5 : rejoin_distance = disttemp;
2785 5 : rejoin_index = temp_cmd.index;
2786 : }
2787 12 : ret = true;
2788 : }
2789 : }
2790 : }
2791 :
2792 24 : if (is_landing_type_cmd(temp_cmd.id) || (temp_cmd.id == MAV_CMD_DO_LAND_START)) {
2793 : // reached a landing!
2794 4 : goto reset_do_jump_tracking;
2795 : }
2796 : }
2797 : // reached end of loop without getting to a landing or DO_LAND_START
2798 0 : ret = false;
2799 :
2800 4 : reset_do_jump_tracking:
2801 404 : for (uint8_t i=0; i<AP_MISSION_MAX_NUM_DO_JUMP_COMMANDS; i++) {
2802 400 : _jump_tracking[i] = _jump_tracking_backup[i];
2803 : }
2804 :
2805 4 : return ret;
2806 : }
2807 :
2808 : // check if command is a landing type command.
2809 2054 : bool AP_Mission::is_landing_type_cmd(uint16_t id) const
2810 : {
2811 2054 : switch (id) {
2812 157 : case MAV_CMD_NAV_LAND:
2813 : case MAV_CMD_NAV_VTOL_LAND:
2814 : case MAV_CMD_DO_PARACHUTE:
2815 157 : return true;
2816 1897 : default:
2817 1897 : return false;
2818 : }
2819 : }
2820 :
2821 : // check if command is a takeoff type command.
2822 307237 : bool AP_Mission::is_takeoff_type_cmd(uint16_t id) const
2823 : {
2824 307237 : switch (id) {
2825 5190 : case MAV_CMD_NAV_TAKEOFF:
2826 : case MAV_CMD_NAV_VTOL_TAKEOFF:
2827 5190 : return true;
2828 302047 : default:
2829 302047 : return false;
2830 : }
2831 : }
2832 :
2833 2030 : const char *AP_Mission::Mission_Command::type() const
2834 : {
2835 2030 : switch (id) {
2836 1232 : case MAV_CMD_NAV_WAYPOINT:
2837 1232 : return "WP";
2838 24 : case MAV_CMD_NAV_SPLINE_WAYPOINT:
2839 24 : return "SplineWP";
2840 37 : case MAV_CMD_NAV_ARC_WAYPOINT:
2841 37 : return "ArcWP";
2842 70 : case MAV_CMD_NAV_RETURN_TO_LAUNCH:
2843 70 : return "RTL";
2844 9 : case MAV_CMD_NAV_LOITER_UNLIM:
2845 9 : return "LoitUnlim";
2846 6 : case MAV_CMD_NAV_LOITER_TIME:
2847 6 : return "LoitTime";
2848 1 : case MAV_CMD_NAV_GUIDED_ENABLE:
2849 1 : return "GuidedEnable";
2850 15 : case MAV_CMD_NAV_LOITER_TURNS:
2851 15 : return "LoitTurns";
2852 13 : case MAV_CMD_NAV_LOITER_TO_ALT:
2853 13 : return "LoitAltitude";
2854 0 : case MAV_CMD_NAV_SET_YAW_SPEED:
2855 0 : return "SetYawSpd";
2856 0 : case MAV_CMD_CONDITION_DELAY:
2857 0 : return "CondDelay";
2858 0 : case MAV_CMD_CONDITION_DISTANCE:
2859 0 : return "CondDist";
2860 28 : case MAV_CMD_DO_CHANGE_SPEED:
2861 28 : return "ChangeSpeed";
2862 0 : case MAV_CMD_DO_SET_HOME:
2863 0 : return "SetHome";
2864 0 : case MAV_CMD_DO_SET_SERVO:
2865 0 : return "SetServo";
2866 0 : case MAV_CMD_DO_SET_RELAY:
2867 0 : return "SetRelay";
2868 0 : case MAV_CMD_DO_REPEAT_SERVO:
2869 0 : return "RepeatServo";
2870 0 : case MAV_CMD_DO_REPEAT_RELAY:
2871 0 : return "RepeatRelay";
2872 0 : case MAV_CMD_DO_DIGICAM_CONFIGURE:
2873 0 : return "DigiCamCfg";
2874 1 : case MAV_CMD_DO_DIGICAM_CONTROL:
2875 1 : return "DigiCamCtrl";
2876 12 : case MAV_CMD_DO_SET_CAM_TRIGG_DIST:
2877 12 : return "SetCamTrigDst";
2878 0 : case MAV_CMD_DO_SET_ROI_LOCATION:
2879 0 : return "SetROILocation";
2880 0 : case MAV_CMD_DO_SET_ROI_NONE:
2881 0 : return "SetROINone";
2882 0 : case MAV_CMD_DO_SET_ROI:
2883 0 : return "SetROI";
2884 0 : case MAV_CMD_DO_SET_REVERSE:
2885 0 : return "SetReverse";
2886 0 : case MAV_CMD_DO_SET_RESUME_REPEAT_DIST:
2887 0 : return "SetRepeatDist";
2888 1 : case MAV_CMD_DO_GUIDED_LIMITS:
2889 1 : return "GuidedLimits";
2890 247 : case MAV_CMD_NAV_TAKEOFF:
2891 247 : return "Takeoff";
2892 125 : case MAV_CMD_NAV_LAND:
2893 125 : return "Land";
2894 0 : case MAV_CMD_NAV_CONTINUE_AND_CHANGE_ALT:
2895 0 : return "ContinueAndChangeAlt";
2896 2 : case MAV_CMD_NAV_ALTITUDE_WAIT:
2897 2 : return "AltitudeWait";
2898 36 : case MAV_CMD_NAV_VTOL_TAKEOFF:
2899 36 : return "VTOLTakeoff";
2900 29 : case MAV_CMD_NAV_VTOL_LAND:
2901 29 : return "VTOLLand";
2902 2 : case MAV_CMD_DO_INVERTED_FLIGHT:
2903 2 : return "InvertedFlight";
2904 0 : case MAV_CMD_DO_FENCE_ENABLE:
2905 0 : return "FenceEnable";
2906 0 : case MAV_CMD_DO_AUTOTUNE_ENABLE:
2907 0 : return "AutoTuneEnable";
2908 2 : case MAV_CMD_DO_VTOL_TRANSITION:
2909 2 : return "VTOLTransition";
2910 21 : case MAV_CMD_DO_ENGINE_CONTROL:
2911 21 : return "EngineControl";
2912 4 : case MAV_CMD_CONDITION_YAW:
2913 4 : return "CondYaw";
2914 0 : case MAV_CMD_DO_RETURN_PATH_START:
2915 0 : return "ReturnPathStart";
2916 77 : case MAV_CMD_DO_LAND_START:
2917 77 : return "LandStart";
2918 5 : case MAV_CMD_NAV_DELAY:
2919 5 : return "Delay";
2920 : #if AP_GRIPPER_ENABLED
2921 12 : case MAV_CMD_DO_GRIPPER:
2922 12 : return "Gripper";
2923 : #endif
2924 : #if AP_MISSION_NAV_PAYLOAD_PLACE_ENABLED
2925 2 : case MAV_CMD_NAV_PAYLOAD_PLACE:
2926 2 : return "PayloadPlace";
2927 : #endif
2928 3 : case MAV_CMD_DO_PARACHUTE:
2929 3 : return "Parachute";
2930 1 : case MAV_CMD_DO_SPRAYER:
2931 1 : return "Sprayer";
2932 1 : case MAV_CMD_DO_AUX_FUNCTION:
2933 1 : return "AuxFunction";
2934 0 : case MAV_CMD_DO_MOUNT_CONTROL:
2935 0 : return "MountControl";
2936 0 : case MAV_CMD_DO_WINCH:
2937 0 : return "Winch";
2938 0 : case MAV_CMD_DO_SEND_SCRIPT_MESSAGE:
2939 0 : return "Scripting";
2940 0 : case MAV_CMD_DO_JUMP:
2941 0 : return "Jump";
2942 0 : case MAV_CMD_DO_JUMP_TAG:
2943 0 : return "JumpToTag";
2944 1 : case MAV_CMD_JUMP_TAG:
2945 1 : return "Tag";
2946 0 : case MAV_CMD_DO_GO_AROUND:
2947 0 : return "Go Around";
2948 : #if AP_SCRIPTING_ENABLED
2949 0 : case MAV_CMD_NAV_SCRIPT_TIME:
2950 0 : return "NavScriptTime";
2951 : #endif
2952 0 : case MAV_CMD_NAV_ATTITUDE_TIME:
2953 0 : return "NavAttitudeTime";
2954 0 : case MAV_CMD_DO_PAUSE_CONTINUE:
2955 0 : return "PauseContinue";
2956 0 : case MAV_CMD_DO_GIMBAL_MANAGER_PITCHYAW:
2957 0 : return "GimbalPitchYaw";
2958 3 : case MAV_CMD_IMAGE_START_CAPTURE:
2959 3 : return "ImageStartCapture";
2960 1 : case MAV_CMD_IMAGE_STOP_CAPTURE:
2961 1 : return "ImageStopCapture";
2962 2 : case MAV_CMD_SET_CAMERA_ZOOM:
2963 2 : return "SetCameraZoom";
2964 2 : case MAV_CMD_SET_CAMERA_FOCUS:
2965 2 : return "SetCameraFocus";
2966 0 : case MAV_CMD_SET_CAMERA_SOURCE:
2967 0 : return "SetCameraSource";
2968 0 : case MAV_CMD_VIDEO_START_CAPTURE:
2969 0 : return "VideoStartCapture";
2970 0 : case MAV_CMD_VIDEO_STOP_CAPTURE:
2971 0 : return "VideoStopCapture";
2972 : #if AP_MISSION_MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET_ENABLED
2973 3 : case MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET:
2974 3 : return "ROIWPNextOffset";
2975 : #endif // AP_MISSION_MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET_ENABLED
2976 0 : default:
2977 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
2978 0 : AP_HAL::panic("Mission command with ID %u has no string", id);
2979 : #endif
2980 0 : return "?";
2981 : }
2982 : }
2983 :
2984 : /*
2985 : get the command ID of a mission index. Caller should have checked the index is in range
2986 : */
2987 883564 : uint16_t AP_Mission::get_command_id(uint16_t index) const
2988 : {
2989 883564 : const uint16_t pos_in_storage = 4 + (index * AP_MISSION_EEPROM_COMMAND_SIZE);
2990 883564 : uint8_t b[3] {};
2991 883564 : if (!_storage.read_block(b, pos_in_storage, sizeof(b))) {
2992 0 : return 0U;
2993 : }
2994 883564 : uint16_t id = 0;
2995 883564 : if (b[0] == 0 || b[0] == 1) {
2996 750 : memcpy((void*)&id, (void*)&b[1], 2);
2997 : } else {
2998 882814 : id = b[0];
2999 : }
3000 883564 : return id;
3001 : }
3002 :
3003 : /*
3004 : see if the mission contains a particular item
3005 : */
3006 50272 : bool AP_Mission::contains_item(MAV_CMD command) const
3007 : {
3008 50272 : const auto count = num_commands();
3009 613284 : for (uint16_t i = 1; i < count; i++) {
3010 563748 : if (get_command_id(i) != uint16_t(command)) {
3011 563012 : continue;
3012 : }
3013 : // confirm with full read
3014 736 : Mission_Command tmp;
3015 736 : if (!read_cmd_from_storage(i, tmp)) {
3016 0 : continue;
3017 : }
3018 736 : if (tmp.id == command) {
3019 736 : return true;
3020 : }
3021 : }
3022 49536 : return false;
3023 : }
3024 :
3025 : /*
3026 : return true if the mission item has a location
3027 : */
3028 :
3029 9 : bool AP_Mission::cmd_has_location(const uint16_t command)
3030 : {
3031 9 : return stored_in_location(command);
3032 : }
3033 :
3034 : /*
3035 : return true if the mission has a terrain relative item. ~2200us for 530 items on H7
3036 : */
3037 130880 : bool AP_Mission::contains_terrain_alt_items(void)
3038 : {
3039 130880 : if (_last_contains_relative_calculated_ms != _last_change_time_ms) {
3040 2796 : _contains_terrain_alt_items = calculate_contains_terrain_alt_items();
3041 2796 : _last_contains_relative_calculated_ms = _last_change_time_ms;
3042 : }
3043 130880 : return _contains_terrain_alt_items;
3044 : }
3045 :
3046 2796 : bool AP_Mission::calculate_contains_terrain_alt_items(void) const
3047 : {
3048 2796 : const auto count = num_commands();
3049 16873 : for (uint16_t i = 1; i < count; i++) {
3050 14123 : if (!stored_in_location(get_command_id(i))) {
3051 430 : continue;
3052 : }
3053 13693 : Mission_Command tmp;
3054 13693 : if (!read_cmd_from_storage(i, tmp)) {
3055 0 : continue;
3056 : }
3057 13693 : if (stored_in_location(tmp.id) && tmp.content.location.terrain_alt) {
3058 46 : return true;
3059 : }
3060 : }
3061 2750 : return false;
3062 : }
3063 :
3064 : // reset the mission history to prevent recalling previous mission histories after a mission restart.
3065 3649 : void AP_Mission::reset_wp_history(void)
3066 : {
3067 29192 : for (uint8_t i = 0; i<AP_MISSION_MAX_WP_HISTORY; i++) {
3068 25543 : _wp_index_history[i] = AP_MISSION_CMD_INDEX_NONE;
3069 : }
3070 3649 : _resume_cmd.index = AP_MISSION_CMD_INDEX_NONE;
3071 3649 : _flags.resuming_mission = false;
3072 3649 : _repeat_dist = 0;
3073 3649 : }
3074 :
3075 : // store the latest reported position incase of mission exit and rewind resume
3076 4247240 : void AP_Mission::update_exit_position(void)
3077 : {
3078 4247240 : if (!AP::ahrs().get_location(_exit_position)) {
3079 0 : _exit_position.lat = 0;
3080 0 : _exit_position.lng = 0;
3081 : }
3082 4247240 : }
3083 :
3084 : // calculate the location of the _resume_cmd wp and set as current
3085 0 : bool AP_Mission::calc_rewind_pos(Mission_Command& rewind_cmd)
3086 : {
3087 : // check for a recent history
3088 0 : if (_wp_index_history[LAST_WP_PASSED] == AP_MISSION_CMD_INDEX_NONE) {
3089 : // no saved history so can't rewind
3090 0 : return false;
3091 : }
3092 :
3093 : // check that we have a valid exit position
3094 0 : if (_exit_position.lat == 0 && _exit_position.lng == 0) {
3095 0 : return false;
3096 : }
3097 :
3098 0 : Mission_Command temp_cmd;
3099 0 : float rewind_distance = _repeat_dist; //(m)
3100 : uint16_t resume_index;
3101 0 : Location prev_loc = _exit_position;
3102 :
3103 0 : for (int8_t i = (LAST_WP_PASSED); i>=0; i--) {
3104 :
3105 : // to get this far there has to be at least one 'passed wp' stored in history. This is to check incase
3106 : // of history array no being completely filled with valid waypoints upon resume.
3107 0 : if (_wp_index_history[i] == AP_MISSION_CMD_INDEX_NONE) {
3108 : // no more stored history
3109 0 : resume_index = i+1;
3110 0 : break;
3111 : }
3112 :
3113 0 : if (!read_cmd_from_storage(_wp_index_history[i], temp_cmd)) {
3114 : // if read from storage failed then don't use rewind
3115 0 : return false;
3116 : }
3117 :
3118 : // calculate distance
3119 0 : float disttemp = prev_loc.get_distance(temp_cmd.content.location); //(m)
3120 0 : rewind_distance -= disttemp;
3121 0 : resume_index = i;
3122 :
3123 0 : if (rewind_distance <= 0.0f) {
3124 : // history rewound enough distance to meet _repeat_dist requirement
3125 0 : rewind_cmd = temp_cmd;
3126 0 : break;
3127 : }
3128 :
3129 : // store wp location as previous
3130 0 : prev_loc = temp_cmd.content.location;
3131 : }
3132 :
3133 0 : if (rewind_distance > 0.0f) {
3134 : // then the history array was rewound all of the way without finding a wp distance > _repeat_dist distance.
3135 : // the last read temp_cmd will be the furthest cmd back in the history array so resume to that.
3136 0 : rewind_cmd = temp_cmd;
3137 0 : return true;
3138 : }
3139 :
3140 : // if we have got this far the desired rewind distance lies between two waypoints stored in history array.
3141 : // calculate the location for the mission to resume
3142 :
3143 : // the last wp read from storage is the wp that is before the resume wp in the mission order
3144 0 : Location passed_wp_loc = temp_cmd.content.location;
3145 :
3146 : // fetch next destination wp
3147 0 : if (!read_cmd_from_storage(_wp_index_history[resume_index+1], temp_cmd)) {
3148 : // if read from storage failed then don't use rewind
3149 0 : return false;
3150 : }
3151 :
3152 : // determine the length of the mission leg that the resume wp lies in
3153 0 : float leg_length = passed_wp_loc.get_distance(temp_cmd.content.location); //(m)
3154 :
3155 : // calculate the percentage along the leg that resume wp will be positioned
3156 0 : float leg_percent = fabsf(rewind_distance)/leg_length;
3157 :
3158 : // calculate difference vector of mission leg
3159 0 : Vector3f dist_vec = passed_wp_loc.get_distance_NED(temp_cmd.content.location);
3160 :
3161 : // calculate the resume wp position
3162 0 : rewind_cmd.content.location.offset(dist_vec.x * leg_percent, dist_vec.y * leg_percent);
3163 0 : rewind_cmd.content.location.offset_up_m(-dist_vec.z * leg_percent);
3164 :
3165 : // The rewind_cmd.index has the index of the 'last passed wp' from the history array. This ensures that the mission order
3166 : // continues as planned without further intervention. The resume wp is not written to memory so will not perminantely change the mission.
3167 :
3168 : // if we got this far then mission rewind position was successfully calculated
3169 0 : return true;
3170 : }
3171 :
3172 : /*
3173 : handle format conversion of storage format to allow us to update
3174 : format to take advantage of new packing. This is particularly useful
3175 : for conversion to Float16 to get extra parameter space
3176 : */
3177 259 : void AP_Mission::format_conversion(uint8_t tag_byte, const Mission_Command &cmd, PackedContent &packed_content) const
3178 : {
3179 : // currently only one conversion needed, more can be added
3180 : #if AP_SCRIPTING_ENABLED
3181 259 : if (tag_byte == 0 && cmd.id == MAV_CMD_NAV_SCRIPT_TIME) {
3182 : // PARAMETER_CONVERSION - Added: Oct-2022 for ArduPilot-4.4
3183 : struct nav_script_time_Command_tag0 old_fmt;
3184 : struct nav_script_time_Command new_fmt;
3185 0 : memcpy((void*)&old_fmt, packed_content.bytes, sizeof(old_fmt));
3186 0 : new_fmt.command = old_fmt.command;
3187 0 : new_fmt.timeout_s = old_fmt.timeout_s;
3188 0 : new_fmt.arg1.set(old_fmt.arg1);
3189 0 : new_fmt.arg2.set(old_fmt.arg2);
3190 0 : new_fmt.arg3 = 0;
3191 0 : new_fmt.arg4 = 0;
3192 0 : memcpy(packed_content.bytes, (void*)&new_fmt, sizeof(new_fmt));
3193 : }
3194 : #endif
3195 259 : }
3196 :
3197 : // Helpers to fill in location for scripting
3198 : #if AP_SCRIPTING_ENABLED
3199 0 : bool AP_Mission::jump_to_landing_sequence(void)
3200 : {
3201 0 : Location loc;
3202 0 : if (AP::ahrs().get_location(loc)) {
3203 0 : return jump_to_landing_sequence(loc);
3204 : }
3205 0 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Unable to start landing sequence");
3206 0 : return false;
3207 : }
3208 :
3209 0 : bool AP_Mission::jump_to_abort_landing_sequence(void)
3210 : {
3211 0 : Location loc;
3212 0 : if (AP::ahrs().get_location(loc)) {
3213 0 : return jump_to_abort_landing_sequence(loc);
3214 : }
3215 0 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Unable to start find a landing abort sequence");
3216 0 : return false;
3217 : }
3218 : #endif // AP_SCRIPTING_ENABLED
3219 :
3220 :
3221 : // singleton instance
3222 : AP_Mission *AP_Mission::_singleton;
3223 :
3224 : namespace AP
3225 : {
3226 :
3227 6405083 : AP_Mission &mission()
3228 : {
3229 6405083 : return *(AP_Mission::get_singleton());
3230 : }
3231 :
3232 : }
3233 :
3234 : #endif // AP_MISSION_ENABLED
|