Line data Source code
1 : /*
2 : Lead developer: Andrew Tridgell
3 :
4 : Authors: Doug Weibel, Jose Julio, Jordi Munoz, Jason Short, Randy Mackay, Pat Hickey, John Arne Birkeland, Olivier Adler, Amilcar Lucas, Gregory Fletcher, Paul Riseborough, Brandon Jones, Jon Challinger, Tom Pittenger
5 : Thanks to: Chris Anderson, Michael Oborne, Paul Mather, Bill Premerlani, James Cohen, JB from rotorFX, Automatik, Fefenin, Peter Meister, Remzibi, Yury Smirnov, Sandro Benigno, Max Levine, Roberto Navoni, Lorenz Meier, Yury MonZon
6 :
7 : Please contribute your ideas! See https://ardupilot.club/dev for details
8 :
9 : This program is free software: you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation, either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "Plane.h"
24 :
25 : #define FORCE_VERSION_H_INCLUDE
26 : #include "version.h"
27 : #undef FORCE_VERSION_H_INCLUDE
28 :
29 : const AP_HAL::HAL& hal = AP_HAL::get_HAL();
30 :
31 : #define SCHED_TASK(func, rate_hz, max_time_micros, priority) SCHED_TASK_CLASS(Plane, &plane, func, rate_hz, max_time_micros, priority)
32 : #define FAST_TASK(func) FAST_TASK_CLASS(Plane, &plane, func)
33 :
34 :
35 : /*
36 : scheduler table - all regular tasks should be listed here.
37 :
38 : All entries in this table must be ordered by priority.
39 :
40 : This table is interleaved with the table present in each of the
41 : vehicles to determine the order in which tasks are run. Convenience
42 : methods SCHED_TASK and SCHED_TASK_CLASS are provided to build
43 : entries in this structure:
44 :
45 : SCHED_TASK arguments:
46 : - name of static function to call
47 : - rate (in Hertz) at which the function should be called
48 : - expected time (in MicroSeconds) that the function should take to run
49 : - priority (0 through 255, lower number meaning higher priority)
50 :
51 : SCHED_TASK_CLASS arguments:
52 : - class name of method to be called
53 : - instance on which to call the method
54 : - method to call on that instance
55 : - rate (in Hertz) at which the method should be called
56 : - expected time (in MicroSeconds) that the method should take to run
57 : - priority (0 through 255, lower number meaning higher priority)
58 :
59 : FAST_TASK entries are run on every loop even if that means the loop
60 : overruns its allotted time
61 : */
62 : const AP_Scheduler::Task Plane::scheduler_tasks[] = {
63 : // Units: Hz us
64 : FAST_TASK(ahrs_update),
65 : FAST_TASK(update_control_mode),
66 : FAST_TASK(stabilize),
67 : FAST_TASK(set_servos),
68 : SCHED_TASK(read_radio, 50, 100, 6),
69 : SCHED_TASK(check_short_rc_failsafe, 50, 100, 9),
70 : SCHED_TASK(update_speed_height, 50, 200, 12),
71 : SCHED_TASK(update_throttle_hover, 100, 90, 24),
72 : SCHED_TASK_CLASS(RC_Channels, (RC_Channels*)&plane.g2.rc_channels, read_mode_switch, 7, 100, 27),
73 : SCHED_TASK(update_GPS_50Hz, 50, 300, 30),
74 : SCHED_TASK(update_GPS_10Hz, 10, 400, 33),
75 : SCHED_TASK(navigate, 10, 150, 36),
76 : SCHED_TASK(update_compass, 10, 200, 39),
77 : SCHED_TASK(calc_airspeed_errors, 10, 100, 42),
78 : SCHED_TASK(update_alt, 10, 200, 45),
79 : SCHED_TASK(adjust_altitude_target, 10, 200, 48),
80 : #if AP_ADVANCEDFAILSAFE_ENABLED
81 : SCHED_TASK(afs_fs_check, 10, 100, 51),
82 : #endif
83 : SCHED_TASK(ekf_check, 10, 75, 54),
84 : SCHED_TASK_CLASS(GCS, (GCS*)&plane._gcs, update_receive, 300, 500, 57),
85 : SCHED_TASK_CLASS(GCS, (GCS*)&plane._gcs, update_send, 300, 750, 60),
86 : #if AP_SERVORELAYEVENTS_ENABLED
87 : SCHED_TASK_CLASS(AP_ServoRelayEvents, &plane.ServoRelayEvents, update_events, 50, 150, 63),
88 : #endif
89 : SCHED_TASK_CLASS(AP_BattMonitor, &plane.battery, read, 10, 300, 66),
90 : #if AP_RANGEFINDER_ENABLED
91 : SCHED_TASK(read_rangefinder, 50, 100, 78),
92 : #endif
93 : #if AP_ICENGINE_ENABLED
94 : SCHED_TASK_CLASS(AP_ICEngine, &plane.g2.ice_control, update, 10, 100, 81),
95 : #endif
96 : #if AP_OPTICALFLOW_ENABLED
97 : SCHED_TASK_CLASS(AP_OpticalFlow, &plane.optflow, update, 50, 50, 87),
98 : #endif
99 : SCHED_TASK(one_second_loop, 1, 400, 90),
100 : SCHED_TASK(three_hz_loop, 3, 75, 93),
101 : SCHED_TASK(check_long_failsafe, 3, 400, 96),
102 : #if AP_AIRSPEED_AUTOCAL_ENABLE
103 : SCHED_TASK(airspeed_ratio_update, 1, 100, 102),
104 : #endif // AP_AIRSPEED_AUTOCAL_ENABLE
105 : #if HAL_MOUNT_ENABLED
106 : SCHED_TASK_CLASS(AP_Mount, &plane.camera_mount, update, 50, 100, 105),
107 : #endif // HAL_MOUNT_ENABLED
108 : #if AP_CAMERA_ENABLED
109 : SCHED_TASK_CLASS(AP_Camera, &plane.camera, update, 50, 100, 108),
110 : #endif // AP_CAMERA_ENABLED
111 : #if HAL_LOGGING_ENABLED
112 : SCHED_TASK_CLASS(AP_Scheduler, &plane.scheduler, update_logging, 0.2, 100, 111),
113 : #endif
114 : #if HAL_LOGGING_ENABLED
115 : SCHED_TASK(Log_Write_FullRate, 400, 300, 117),
116 : SCHED_TASK(update_logging10, 10, 300, 120),
117 : SCHED_TASK(update_logging25, 25, 300, 123),
118 : #endif
119 : #if HAL_SOARING_ENABLED
120 : SCHED_TASK(update_soaring, 50, 400, 126),
121 : #endif
122 : SCHED_TASK(parachute_check, 10, 200, 129),
123 : #if AP_TERRAIN_AVAILABLE
124 : SCHED_TASK_CLASS(AP_Terrain, &plane.terrain, update, 10, 200, 132),
125 : #endif // AP_TERRAIN_AVAILABLE
126 : SCHED_TASK(update_is_flying_5Hz, 5, 100, 135),
127 : #if HAL_LOGGING_ENABLED
128 : SCHED_TASK_CLASS(AP_Logger, &plane.logger, periodic_tasks, 400, 300, 138),
129 : #endif
130 : SCHED_TASK_CLASS(AP_InertialSensor, &plane.ins, periodic, 50, 50, 141),
131 : #if HAL_ADSB_ENABLED || AP_ADSB_AVOIDANCE_ENABLED
132 : SCHED_TASK(avoidance_adsb_update, 10, 100, 144),
133 : #endif // HAL_ADSB_ENABLED || AP_ADSB_AVOIDANCE_ENABLED
134 : SCHED_TASK_CLASS(RC_Channels, (RC_Channels*)&plane.g2.rc_channels, read_aux_all, 10, 200, 147),
135 : #if HAL_BUTTON_ENABLED
136 : SCHED_TASK_CLASS(AP_Button, &plane.button, update, 5, 100, 150),
137 : #endif
138 : #if AP_LANDINGGEAR_ENABLED
139 : SCHED_TASK(landing_gear_update, 5, 50, 159),
140 : #endif
141 : #if AC_PRECLAND_ENABLED
142 : SCHED_TASK(precland_update, 400, 50, 160),
143 : #endif
144 : #if AP_QUICKTUNE_ENABLED
145 : SCHED_TASK(update_quicktune, 40, 100, 163),
146 : #endif
147 : };
148 :
149 692 : void Plane::get_scheduler_tasks(const AP_Scheduler::Task *&tasks,
150 : uint8_t &task_count,
151 : uint32_t &log_bit)
152 : {
153 692 : tasks = &scheduler_tasks[0];
154 692 : task_count = ARRAY_SIZE(scheduler_tasks);
155 692 : log_bit = MASK_LOG_PM;
156 692 : }
157 :
158 : #if HAL_QUADPLANE_ENABLED
159 : constexpr int8_t Plane::_failsafe_priorities[7];
160 : #else
161 : constexpr int8_t Plane::_failsafe_priorities[6];
162 : #endif
163 :
164 : // update AHRS system
165 8377547 : void Plane::ahrs_update()
166 : {
167 8377547 : arming.update_soft_armed();
168 :
169 8377547 : ahrs.update();
170 :
171 : #if HAL_LOGGING_ENABLED
172 8377547 : if (should_log(MASK_LOG_IMU)) {
173 7831972 : AP::ins().Write_IMU();
174 : }
175 : #endif
176 :
177 : // calculate a scaled roll limit based on current pitch
178 8377547 : roll_limit_cd = aparm.roll_limit*100;
179 8377547 : pitch_limit_min = aparm.pitch_limit_min;
180 :
181 8377547 : bool rotate_limits = true;
182 : #if HAL_QUADPLANE_ENABLED
183 8377547 : if (quadplane.tailsitter.active()) {
184 96659 : rotate_limits = false;
185 : }
186 : #endif
187 8377547 : if (rotate_limits) {
188 8280888 : roll_limit_cd *= ahrs.cos_pitch();
189 8280888 : pitch_limit_min *= fabsf(ahrs.cos_roll());
190 : }
191 :
192 : // updated the summed gyro used for ground steering and
193 : // auto-takeoff. Dot product of DCM.c with gyro vector gives earth
194 : // frame yaw rate
195 8377547 : steer_state.locked_course_err += ahrs.get_yaw_rate_earth() * G_Dt;
196 8377547 : steer_state.locked_course_err = wrap_PI(steer_state.locked_course_err);
197 :
198 : // Check if there has been a change in attitude estimate which the attitude controllers should be told about
199 8377547 : check_ahrs_reset();
200 :
201 : #if HAL_QUADPLANE_ENABLED
202 : // update inertial_nav for quadplane
203 8377547 : quadplane.inertial_nav.update();
204 8377547 : if (quadplane.available()) {
205 6504054 : quadplane.pos_control->update_estimates();
206 : }
207 : #endif
208 : #if AP_SCRIPTING_ENABLED && AP_FOLLOW_ENABLED
209 8377547 : g2.follow.update_estimates();
210 : #endif
211 :
212 : #if HAL_LOGGING_ENABLED
213 8377547 : if (should_log(MASK_LOG_VIDEO_STABILISATION)) {
214 0 : ahrs.write_video_stabilisation();
215 : }
216 : #endif
217 8377547 : }
218 :
219 : /*
220 : update 50Hz speed/height controller
221 : */
222 2736769 : void Plane::update_speed_height(void)
223 : {
224 2736769 : bool should_run_tecs = control_mode->does_auto_throttle();
225 : #if HAL_QUADPLANE_ENABLED
226 2736769 : if (quadplane.should_disable_TECS()) {
227 149840 : should_run_tecs = false;
228 : }
229 : #endif
230 :
231 2736769 : if (auto_state.idle_mode) {
232 52328 : should_run_tecs = false;
233 : }
234 :
235 : #if AP_PLANE_GLIDER_PULLUP_ENABLED
236 2736769 : if (mode_auto.in_pullup()) {
237 405 : should_run_tecs = false;
238 : }
239 : #endif
240 :
241 2736769 : if (should_run_tecs) {
242 : // Call TECS 50Hz update. Note that we call this regardless of
243 : // throttle suppressed, as this needs to be running for
244 : // takeoff detection
245 1657292 : TECS_controller.update_50hz();
246 : }
247 :
248 : #if HAL_QUADPLANE_ENABLED
249 5016592 : if (quadplane.in_vtol_mode() ||
250 2279823 : quadplane.in_assisted_flight()) {
251 527359 : quadplane.update_throttle_mix();
252 : }
253 : #endif
254 2736769 : }
255 :
256 :
257 : /*
258 : read and update compass
259 : */
260 547097 : void Plane::update_compass(void)
261 : {
262 547097 : compass.read();
263 547097 : }
264 :
265 : #if HAL_LOGGING_ENABLED
266 : /*
267 : do 10Hz logging
268 : */
269 547089 : void Plane::update_logging10(void)
270 : {
271 547089 : const bool attitude_med = should_log(MASK_LOG_ATTITUDE_MED);
272 547089 : const bool attitude_faster = should_log(MASK_LOG_ATTITUDE_FULLRATE) || should_log(MASK_LOG_ATTITUDE_FAST);
273 :
274 : // Log attitude only if no faster logging is selected
275 547089 : if (attitude_med && !attitude_faster) {
276 196072 : Log_Write_Attitude();
277 196072 : AP::ahrs().Log_Write();
278 : }
279 :
280 : // If any attitude logging is enabled log AOA and SSA
281 547089 : if (attitude_med || attitude_faster) {
282 524212 : ahrs.Write_AOA_SSA();
283 : }
284 :
285 : #if HAL_MOUNT_ENABLED
286 547089 : if (should_log(MASK_LOG_CAMERA)) {
287 521283 : camera_mount.write_log();
288 : }
289 : #endif
290 : #if AP_RANGEFINDER_ENABLED
291 547089 : if (should_log(MASK_LOG_NTUN)) {
292 542410 : if (rangefinder.has_orientation(rangefinder_orientation()) &&
293 18198 : (g.rangefinder_landing.get() > 0)) {
294 5098 : Log_Write_RFNS();
295 : }
296 : }
297 : #endif
298 547089 : }
299 :
300 : /*
301 : do 25Hz logging
302 : */
303 1367994 : void Plane::update_logging25(void)
304 : {
305 1367994 : const bool attitude_fast = should_log(MASK_LOG_ATTITUDE_FAST);
306 1367994 : const bool attitude_full_rate = should_log(MASK_LOG_ATTITUDE_FULLRATE);
307 :
308 : // Log at fast rate if fast logging is the fastest enabled
309 1367994 : if (attitude_fast && !attitude_full_rate) {
310 820469 : Log_Write_Attitude();
311 : }
312 :
313 : // Log AHRS at fast rate if either fast or full rate is selected
314 1367994 : if (attitude_fast || attitude_full_rate) {
315 820469 : AP::ahrs().Log_Write();
316 : }
317 :
318 1367994 : if (should_log(MASK_LOG_CTUN)) {
319 1310707 : Log_Write_Control_Tuning();
320 : #if AP_INERTIALSENSOR_HARMONICNOTCH_ENABLED
321 1310707 : if (!should_log(MASK_LOG_NOTCH_FULLRATE)) {
322 1310707 : AP::ins().write_notch_log_messages();
323 : }
324 : #endif
325 : #if HAL_GYROFFT_ENABLED
326 1310707 : gyro_fft.write_log_messages();
327 : #endif
328 : }
329 :
330 1367994 : if (should_log(MASK_LOG_NTUN)) {
331 1310707 : Log_Write_Nav_Tuning();
332 1310707 : Log_Write_Guided();
333 : }
334 :
335 1367994 : if (should_log(MASK_LOG_RC))
336 1310707 : Log_Write_RC();
337 :
338 1367994 : if (should_log(MASK_LOG_IMU))
339 1310707 : AP::ins().Write_Vibration();
340 :
341 : #if AP_PLANE_BLACKBOX_LOGGING
342 : Log_Write_Blackbox();
343 : #endif
344 1367994 : }
345 : #endif // HAL_LOGGING_ENABLED
346 :
347 : /*
348 : check for AFS failsafe check
349 : */
350 : #if AP_ADVANCEDFAILSAFE_ENABLED
351 547097 : void Plane::afs_fs_check(void)
352 : {
353 547097 : afs.check(failsafe.AFS_last_valid_rc_ms);
354 547097 : }
355 : #endif
356 :
357 : #if HAL_WITH_IO_MCU
358 : #include <AP_IOMCU/AP_IOMCU.h>
359 : extern AP_IOMCU iomcu;
360 : #endif
361 :
362 54357 : void Plane::one_second_loop()
363 : {
364 : // make it possible to change control channel ordering at runtime
365 54357 : set_control_channels();
366 :
367 : #if HAL_WITH_IO_MCU
368 : iomcu.setup_mixing(g.override_channel.get(), g.mixing_gain, g2.manual_rc_mask);
369 : #endif
370 :
371 : #if HAL_ADSB_ENABLED
372 54357 : adsb.set_stall_speed_cm(aparm.airspeed_min * 100); // convert m/s to cm/s
373 54357 : adsb.set_max_speed(aparm.airspeed_max);
374 : #endif
375 :
376 54357 : if (flight_option_enabled(FlightOptions::ENABLE_DEFAULT_AIRSPEED)) {
377 : // use average of min and max airspeed as default airspeed fusion with high variance
378 0 : ahrs.writeDefaultAirSpeed((float)((aparm.airspeed_min + aparm.airspeed_max)/2),
379 0 : (float)((aparm.airspeed_max - aparm.airspeed_min)/2));
380 : }
381 :
382 54357 : AP::srv().enable_aux_servos();
383 :
384 : // update notify flags
385 54357 : AP_Notify::flags.pre_arm_check = arming.pre_arm_checks(false);
386 54357 : AP_Notify::flags.pre_arm_gps_check = true;
387 54357 : AP_Notify::flags.armed = arming.is_armed() || arming.arming_required() == AP_Arming::Required::NO;
388 :
389 : #if AP_TERRAIN_AVAILABLE && HAL_LOGGING_ENABLED
390 54357 : if (should_log(MASK_LOG_GPS)) {
391 52128 : terrain.log_terrain_data();
392 : }
393 : #endif
394 :
395 : // update home position if NOT armed and gps position has
396 : // changed. Update every 5s at most
397 54357 : if (!arming.is_armed() &&
398 56356 : gps.last_message_time_ms() - last_home_update_ms > 5000 &&
399 1999 : gps.status() >= AP_GPS_FixType::FIX_3D) {
400 1953 : last_home_update_ms = gps.last_message_time_ms();
401 1953 : update_home();
402 :
403 : // reset the landing altitude correction
404 1953 : landing.alt_offset = 0;
405 : }
406 :
407 54357 : const float loop_rate = AP::scheduler().get_filtered_loop_rate_hz();
408 : #if HAL_QUADPLANE_ENABLED
409 54357 : if (quadplane.available()) {
410 21571 : quadplane.attitude_control->set_notch_sample_rate(loop_rate);
411 : }
412 : #endif
413 54357 : rollController.set_notch_sample_rate(loop_rate);
414 54357 : pitchController.set_notch_sample_rate(loop_rate);
415 54357 : yawController.set_notch_sample_rate(loop_rate);
416 54357 : }
417 :
418 167812 : void Plane::three_hz_loop()
419 : {
420 : #if AP_FENCE_ENABLED
421 167812 : fence_check();
422 : #endif
423 167812 : }
424 :
425 : #if AP_AIRSPEED_AUTOCAL_ENABLE
426 : /*
427 : once a second update the airspeed calibration ratio
428 : */
429 54357 : void Plane::airspeed_ratio_update(void)
430 : {
431 54357 : if (!hal.util->get_soft_armed() ||
432 41445 : !ahrs.get_fly_forward() ||
433 31561 : !is_flying() ||
434 60858 : !airspeed.enabled() ||
435 126231 : gps.status() < AP_GPS_FixType::FIX_3D ||
436 30225 : gps.ground_speed() < 4) {
437 : // don't calibrate when not moving
438 24850 : return;
439 : }
440 30522 : if (airspeed.get_airspeed() < aparm.airspeed_min &&
441 1015 : gps.ground_speed() < (uint32_t)aparm.airspeed_min) {
442 : // don't calibrate when flying below the minimum airspeed. We
443 : // check both airspeed and ground speed to catch cases where
444 : // the airspeed ratio is way too low, which could lead to it
445 : // never coming up again
446 799 : return;
447 : }
448 85855 : if (labs(ahrs.roll_sensor) > roll_limit_cd ||
449 57120 : ahrs.get_pitch_deg() > aparm.pitch_limit_max ||
450 28412 : ahrs.get_pitch_deg() < pitch_limit_min) {
451 : // don't calibrate when going beyond normal flight envelope
452 453 : return;
453 : }
454 28255 : const Vector3f &vg = gps.velocity();
455 28255 : airspeed.update_calibration(vg, aparm.airspeed_max);
456 : }
457 : #endif // AP_AIRSPEED_AUTOCAL_ENABLE
458 :
459 : /*
460 : read the GPS and update position
461 : */
462 2736769 : void Plane::update_GPS_50Hz(void)
463 : {
464 2736769 : gps.update();
465 :
466 2736769 : update_current_loc();
467 2736769 : }
468 :
469 : /*
470 : read update GPS position - 10Hz update
471 : */
472 547097 : void Plane::update_GPS_10Hz(void)
473 : {
474 : static uint32_t last_gps_msg_ms;
475 547097 : if (gps.last_message_time_ms() != last_gps_msg_ms && gps.status() >= AP_GPS_FixType::FIX_3D) {
476 258716 : last_gps_msg_ms = gps.last_message_time_ms();
477 :
478 258716 : if (ground_start_count > 1) {
479 1412 : ground_start_count--;
480 257304 : } else if (ground_start_count == 1) {
481 : // We countdown N number of good GPS fixes
482 : // so that the altitude is more accurate
483 : // -------------------------------------
484 405 : if (current_loc.lat == 0 && current_loc.lng == 0) {
485 0 : ground_start_count = 5;
486 :
487 405 : } else if (!hal.util->was_watchdog_reset()) {
488 351 : if (!set_home_persistently(gps.location())) {
489 : // silently ignore failure...
490 : }
491 :
492 351 : next_WP_loc = prev_WP_loc = home;
493 :
494 351 : ground_start_count = 0;
495 : }
496 : }
497 288381 : } else if (gps.status() < AP_GPS_FixType::FIX_3D && ground_start_count != 0) {
498 : // lost 3D fix, start again
499 33000 : ground_start_count = 5;
500 : }
501 :
502 547097 : calc_gndspeed_undershoot();
503 547097 : }
504 :
505 : /*
506 : main control mode dependent update code
507 : */
508 8377547 : void Plane::update_control_mode(void)
509 : {
510 8377547 : if ((control_mode != &mode_auto) && (control_mode != &mode_takeoff)) {
511 : // hold_course is only used in takeoff and landing
512 4635334 : steer_state.hold_course_cd = -1;
513 : }
514 : // refresh the throttle limits, to avoid using stale values
515 : // they will be updated once takeoff_calc_throttle is called
516 8377547 : takeoff_state.throttle_lim_max = 100.0f;
517 8377547 : takeoff_state.throttle_lim_min = -100.0f;
518 :
519 8377547 : update_fly_forward();
520 :
521 8377547 : control_mode->update();
522 :
523 : #if MODE_AUTOLAND_ENABLED
524 8377547 : mode_autoland.check_takeoff_direction();
525 : #endif
526 8377547 : }
527 :
528 :
529 8377547 : void Plane::update_fly_forward(void)
530 : {
531 : // ensure we are fly-forward when we are flying as a pure fixed
532 : // wing aircraft. This helps the EKF produce better state
533 : // estimates as it can make stronger assumptions
534 : #if HAL_QUADPLANE_ENABLED
535 8377547 : if (quadplane.available()) {
536 6504054 : if (quadplane.tailsitter.is_in_fw_flight()) {
537 60753 : ahrs.set_fly_forward(true);
538 60753 : return;
539 : }
540 :
541 6443301 : if (quadplane.in_vtol_mode()) {
542 2741706 : ahrs.set_fly_forward(false);
543 2741706 : return;
544 : }
545 :
546 3701595 : if (quadplane.in_assisted_flight()) {
547 419556 : ahrs.set_fly_forward(false);
548 419556 : return;
549 : }
550 : }
551 : #endif
552 :
553 5155532 : if (auto_state.idle_mode) {
554 : // don't fuse airspeed when in balloon lift
555 199220 : ahrs.set_fly_forward(false);
556 199220 : return;
557 : }
558 :
559 4956312 : if (flight_stage == AP_FixedWing::FlightStage::LAND) {
560 197641 : ahrs.set_fly_forward(landing.is_flying_forward());
561 197641 : return;
562 : }
563 :
564 4758671 : ahrs.set_fly_forward(true);
565 : }
566 :
567 : /*
568 : set the flight stage
569 : */
570 627562 : void Plane::set_flight_stage(AP_FixedWing::FlightStage fs)
571 : {
572 627562 : if (fs == flight_stage) {
573 619477 : return;
574 : }
575 :
576 8085 : const bool is_landing = (fs == AP_FixedWing::FlightStage::LAND);
577 :
578 8085 : landing.handle_flight_stage_change(is_landing);
579 :
580 : #if AP_LANDINGGEAR_ENABLED
581 8085 : if (is_landing) {
582 232 : plane.g2.landing_gear.deploy_for_landing();
583 : }
584 :
585 8085 : const bool is_takeoff_complete = (flight_stage == AP_FixedWing::FlightStage::TAKEOFF &&
586 : fs == AP_FixedWing::FlightStage::NORMAL);
587 3521 : if (is_takeoff_complete &&
588 11606 : arming.is_armed_and_safety_off() &&
589 369 : is_flying()) {
590 122 : g2.landing_gear.retract_after_takeoff();
591 : }
592 : #endif
593 :
594 8085 : if (fs == AP_FixedWing::FlightStage::ABORT_LANDING) {
595 2 : gcs().send_text(MAV_SEVERITY_NOTICE, "Landing aborted, climbing to %dm",
596 2 : int(auto_state.takeoff_altitude_rel_cm/100));
597 : }
598 :
599 8085 : flight_stage = fs;
600 : #if HAL_LOGGING_ENABLED
601 8085 : Log_Write_Status();
602 : #endif
603 : }
604 :
605 547097 : void Plane::update_alt()
606 : {
607 547097 : barometer.update();
608 :
609 : // calculate the sink rate.
610 : float sink_rate;
611 547097 : Vector3f vel;
612 547097 : if (ahrs.get_velocity_NED(vel)) {
613 514955 : sink_rate = vel.z;
614 32142 : } else if (gps.status() >= AP_GPS_FixType::FIX_3D && gps.have_vertical_velocity()) {
615 2 : sink_rate = gps.velocity().z;
616 : } else {
617 32140 : sink_rate = -barometer.get_climb_rate();
618 : }
619 :
620 : // low pass the sink rate to take some of the noise out
621 547097 : auto_state.sink_rate = 0.8f * auto_state.sink_rate + 0.2f*sink_rate;
622 : #if HAL_PARACHUTE_ENABLED
623 547097 : parachute.set_sink_rate(auto_state.sink_rate);
624 : #endif
625 :
626 547097 : update_flight_stage();
627 :
628 : #if AP_SCRIPTING_ENABLED
629 547097 : if (nav_scripting_active()) {
630 : // don't call TECS while we are in a trick
631 1331 : return;
632 : }
633 : #endif
634 :
635 545766 : bool should_run_tecs = control_mode->does_auto_throttle();
636 : #if HAL_QUADPLANE_ENABLED
637 545766 : if (quadplane.should_disable_TECS()) {
638 29987 : should_run_tecs = false;
639 : }
640 : #endif
641 :
642 545766 : if (auto_state.idle_mode) {
643 10465 : should_run_tecs = false;
644 : }
645 :
646 : #if AP_PLANE_GLIDER_PULLUP_ENABLED
647 545766 : if (mode_auto.in_pullup()) {
648 81 : should_run_tecs = false;
649 : }
650 : #endif
651 :
652 545766 : if (should_run_tecs && !throttle_suppressed) {
653 :
654 313893 : float distance_beyond_land_wp = 0;
655 349999 : if (flight_stage == AP_FixedWing::FlightStage::LAND &&
656 36106 : current_loc.past_interval_finish_line(prev_WP_loc, next_WP_loc)) {
657 15770 : distance_beyond_land_wp = current_loc.get_distance(next_WP_loc);
658 : }
659 :
660 313893 : tecs_target_alt_cm = relative_target_altitude_cm();
661 :
662 313893 : if (control_mode == &mode_rtl && !rtl.done_climb && (g2.rtl_climb_min > 0 || (plane.flight_option_enabled(FlightOptions::CLIMB_BEFORE_TURN)))) {
663 : // ensure we do the initial climb in RTL. We add an extra
664 : // 10m in the demanded height to push TECS to climb
665 : // quickly
666 391 : tecs_target_alt_cm = MAX(tecs_target_alt_cm, prev_WP_loc.alt - home.alt) + (g2.rtl_climb_min+10)*100;
667 : }
668 :
669 627786 : TECS_controller.update_pitch_throttle(tecs_target_alt_cm,
670 : target_airspeed_cm,
671 : flight_stage,
672 : distance_beyond_land_wp,
673 313893 : get_takeoff_pitch_min_cd(),
674 313893 : throttle_nudge,
675 : tecs_hgt_afe(),
676 : aerodynamic_load_factor,
677 313893 : g.pitch_trim.get());
678 : }
679 : }
680 :
681 : /*
682 : recalculate the flight_stage
683 : */
684 553198 : void Plane::update_flight_stage(void)
685 : {
686 : // Update the speed & height controller states
687 553198 : if (control_mode->does_auto_throttle() && !throttle_suppressed) {
688 355708 : if (control_mode == &mode_auto) {
689 : #if HAL_QUADPLANE_ENABLED
690 238091 : if (quadplane.in_vtol_auto()) {
691 28627 : set_flight_stage(AP_FixedWing::FlightStage::VTOL);
692 28627 : return;
693 : }
694 : #endif
695 209464 : if (auto_state.takeoff_complete == false) {
696 3689 : set_flight_stage(AP_FixedWing::FlightStage::TAKEOFF);
697 3689 : return;
698 205775 : } else if (mission.get_current_nav_cmd().id == MAV_CMD_NAV_LAND) {
699 35416 : if (landing.is_commanded_go_around() || flight_stage == AP_FixedWing::FlightStage::ABORT_LANDING) {
700 : // abort mode is sticky, it must complete while executing NAV_LAND
701 247 : set_flight_stage(AP_FixedWing::FlightStage::ABORT_LANDING);
702 35169 : } else if (landing.get_abort_throttle_enable() && get_throttle_input() >= 90 &&
703 0 : landing.request_go_around()) {
704 0 : gcs().send_text(MAV_SEVERITY_INFO,"Landing aborted via throttle");
705 0 : set_flight_stage(AP_FixedWing::FlightStage::ABORT_LANDING);
706 : } else {
707 35169 : set_flight_stage(AP_FixedWing::FlightStage::LAND);
708 : }
709 35416 : return;
710 : }
711 : #if HAL_QUADPLANE_ENABLED
712 170359 : if (quadplane.in_assisted_flight()) {
713 4429 : set_flight_stage(AP_FixedWing::FlightStage::VTOL);
714 4429 : return;
715 : }
716 : #endif
717 165930 : set_flight_stage(AP_FixedWing::FlightStage::NORMAL);
718 117617 : } else if ((control_mode != &mode_takeoff)
719 : #if MODE_AUTOLAND_ENABLED
720 104768 : && (control_mode != &mode_autoland)
721 : #endif
722 : ) {
723 : // If not in AUTO then assume normal operation for normal TECS operation.
724 : // This prevents TECS from being stuck in the wrong stage if you switch from
725 : // AUTO to, say, FBWB during a landing, an aborted landing or takeoff.
726 99307 : set_flight_stage(AP_FixedWing::FlightStage::NORMAL);
727 : }
728 283547 : return;
729 : }
730 : #if HAL_QUADPLANE_ENABLED
731 345884 : if (quadplane.in_vtol_mode() ||
732 148394 : quadplane.in_assisted_flight()) {
733 51576 : set_flight_stage(AP_FixedWing::FlightStage::VTOL);
734 51576 : return;
735 : }
736 : #endif
737 145914 : set_flight_stage(AP_FixedWing::FlightStage::NORMAL);
738 : }
739 :
740 :
741 :
742 :
743 : /*
744 : If land_DisarmDelay is enabled (non-zero), check for a landing then auto-disarm after time expires
745 :
746 : only called from AP_Landing, when the landing library is ready to disarm
747 : */
748 26301 : void Plane::disarm_if_autoland_complete()
749 : {
750 26301 : if (landing.get_disarm_delay() > 0 &&
751 32333 : !is_flying() &&
752 58634 : arming.arming_required() != AP_Arming::Required::NO &&
753 6047 : arming.is_armed()) {
754 : /* we have auto disarm enabled. See if enough time has passed */
755 3285 : if (millis() - auto_state.last_flying_ms >= landing.get_disarm_delay()*1000UL) {
756 112 : if (arming.disarm(AP_Arming::Method::AUTOLANDED)) {
757 112 : gcs().send_text(MAV_SEVERITY_INFO,"Auto disarmed");
758 : }
759 : }
760 : }
761 26301 : }
762 :
763 2 : bool Plane::trigger_land_abort(const float climb_to_alt_m)
764 : {
765 2 : if (plane.control_mode != &plane.mode_auto) {
766 0 : return false;
767 : }
768 : #if HAL_QUADPLANE_ENABLED
769 2 : if (plane.quadplane.in_vtol_auto()) {
770 0 : return quadplane.abort_landing();
771 : }
772 : #endif
773 :
774 2 : uint16_t mission_id = plane.mission.get_current_nav_cmd().id;
775 2 : bool is_in_landing = (plane.flight_stage == AP_FixedWing::FlightStage::LAND) ||
776 0 : plane.is_land_command(mission_id);
777 2 : if (is_in_landing) {
778 : // fly a user planned abort pattern if available
779 2 : if (plane.have_position && plane.mission.jump_to_abort_landing_sequence(plane.current_loc)) {
780 0 : return true;
781 : }
782 :
783 : // only fly a fixed wing abort if we aren't doing quadplane stuff, or potentially
784 : // shooting a quadplane approach
785 : #if HAL_QUADPLANE_ENABLED
786 : const bool attempt_go_around =
787 2 : (!plane.quadplane.available()) ||
788 0 : ((!plane.quadplane.in_vtol_auto()) &&
789 0 : (!plane.quadplane.landing_with_fixed_wing_spiral_approach()));
790 : #else
791 : const bool attempt_go_around = true;
792 : #endif
793 2 : if (attempt_go_around) {
794 : // Initiate an aborted landing. This will trigger a pitch-up and
795 : // climb-out to a safe altitude holding heading then one of the
796 : // following actions will occur, check for in this order:
797 : // - If MAV_CMD_CONTINUE_AND_CHANGE_ALT is next command in mission,
798 : // increment mission index to execute it
799 : // - else if DO_LAND_START is available, jump to it
800 : // - else decrement the mission index to repeat the landing approach
801 :
802 2 : if (!is_zero(climb_to_alt_m)) {
803 2 : plane.auto_state.takeoff_altitude_rel_cm = climb_to_alt_m * 100;
804 : }
805 2 : if (plane.landing.request_go_around()) {
806 2 : plane.auto_state.next_wp_crosstrack = false;
807 2 : return true;
808 : }
809 : }
810 : }
811 0 : return false;
812 : }
813 :
814 :
815 : /*
816 : the height above field elevation that we pass to TECS
817 : */
818 313893 : float Plane::tecs_hgt_afe(void)
819 : {
820 : /*
821 : pass the height above field elevation as the height above
822 : the ground when in landing, which means that TECS gets the
823 : rangefinder information and thus can know when the flare is
824 : coming.
825 : */
826 : float hgt_afe;
827 :
828 313893 : if (flight_stage == AP_FixedWing::FlightStage::LAND) {
829 :
830 : #if AP_MAVLINK_MAV_CMD_SET_HAGL_ENABLED
831 : // if external HAGL is active use that
832 36106 : if (get_external_HAGL(hgt_afe)) {
833 0 : return hgt_afe;
834 : }
835 : #endif
836 :
837 36106 : hgt_afe = height_above_target();
838 : #if AP_RANGEFINDER_ENABLED
839 36106 : hgt_afe -= rangefinder_correction();
840 : #endif
841 : } else {
842 : // when in normal flight we pass the hgt_afe as relative
843 : // altitude to home
844 277787 : hgt_afe = relative_altitude;
845 : }
846 313893 : return hgt_afe;
847 : }
848 :
849 : // vehicle specific waypoint info helpers
850 54357 : bool Plane::get_wp_distance_m(float &distance) const
851 : {
852 : // see GCS_MAVLINK_Plane::send_nav_controller_output()
853 54357 : if (control_mode == &mode_manual) {
854 7563 : return false;
855 : }
856 : #if HAL_QUADPLANE_ENABLED
857 46794 : if (quadplane.in_vtol_mode()) {
858 9146 : distance = quadplane.using_wp_nav() ? quadplane.wp_nav->get_wp_distance_to_destination_m() : 0;
859 9146 : return true;
860 : }
861 : #endif
862 37648 : distance = auto_state.wp_distance;
863 37648 : return true;
864 : }
865 :
866 58161 : bool Plane::get_wp_bearing_deg(float &bearing) const
867 : {
868 : // see GCS_MAVLINK_Plane::send_nav_controller_output()
869 58161 : if (control_mode == &mode_manual) {
870 0 : return false;
871 : }
872 : #if HAL_QUADPLANE_ENABLED
873 58161 : if (quadplane.in_vtol_mode()) {
874 11960 : bearing = quadplane.using_wp_nav() ? quadplane.wp_nav->get_wp_bearing_to_destination_cd() : 0;
875 11960 : return true;
876 : }
877 : #endif
878 46201 : bearing = nav_controller->target_bearing_cd() * 0.01;
879 46201 : return true;
880 : }
881 :
882 46794 : bool Plane::get_wp_crosstrack_error_m(float &xtrack_error) const
883 : {
884 : // see GCS_MAVLINK_Plane::send_nav_controller_output()
885 46794 : if (control_mode == &mode_manual) {
886 0 : return false;
887 : }
888 : #if HAL_QUADPLANE_ENABLED
889 46794 : if (quadplane.in_vtol_mode()) {
890 9146 : xtrack_error = quadplane.using_wp_nav() ? quadplane.wp_nav->crosstrack_error_m() : 0;
891 9146 : return true;
892 : }
893 : #endif
894 37648 : xtrack_error = nav_controller->crosstrack_error_m();
895 37648 : return true;
896 : }
897 :
898 : #if AP_SCRIPTING_ENABLED || AP_EXTERNAL_CONTROL_ENABLED
899 : // set target location (for use by external control and scripting)
900 0 : bool Plane::set_target_location(const Location &target_loc)
901 : {
902 0 : Location loc{target_loc};
903 0 : fix_terrain_WP(loc, __AP_LINE__);
904 :
905 0 : if (plane.control_mode != &plane.mode_guided) {
906 : // only accept position updates when in GUIDED mode
907 0 : return false;
908 : }
909 : // add home alt if needed
910 0 : if (!loc.terrain_alt) {
911 0 : loc.change_alt_frame(Location::AltFrame::ABSOLUTE);
912 : }
913 0 : plane.set_guided_WP(loc);
914 0 : return true;
915 : }
916 : #endif //AP_SCRIPTING_ENABLED || AP_EXTERNAL_CONTROL_ENABLED
917 :
918 : #if AP_SCRIPTING_ENABLED
919 : // get target location (for use by scripting)
920 25262 : bool Plane::get_target_location(Location& target_loc)
921 : {
922 25262 : switch (control_mode->mode_number()) {
923 25032 : case Mode::Number::RTL:
924 : case Mode::Number::AVOID_ADSB:
925 : case Mode::Number::GUIDED:
926 : case Mode::Number::AUTO:
927 : case Mode::Number::LOITER:
928 : case Mode::Number::TAKEOFF:
929 : #if HAL_QUADPLANE_ENABLED
930 : case Mode::Number::QLOITER:
931 : case Mode::Number::QLAND:
932 : case Mode::Number::QRTL:
933 : #endif
934 25032 : target_loc = next_WP_loc;
935 25032 : return true;
936 : break;
937 230 : default:
938 230 : break;
939 : }
940 230 : return false;
941 : }
942 :
943 : /*
944 : update_target_location() works in all auto navigation modes
945 : */
946 3551 : bool Plane::update_target_location(const Location &old_loc, const Location &new_loc)
947 : {
948 : /*
949 : by checking the caller has provided the correct old target
950 : location we prevent a race condition where the user changes mode
951 : or commands a different target in the controlling lua script
952 : */
953 7102 : if (!old_loc.same_loc_as(next_WP_loc) ||
954 3551 : old_loc.get_alt_frame() != new_loc.get_alt_frame()) {
955 0 : return false;
956 : }
957 3551 : next_WP_loc = new_loc;
958 :
959 3551 : fix_terrain_WP(next_WP_loc, __AP_LINE__);
960 :
961 : #if HAL_QUADPLANE_ENABLED
962 3551 : if (control_mode == &mode_qland || control_mode == &mode_qloiter) {
963 0 : mode_qloiter.last_target_loc_set_ms = AP_HAL::millis();
964 : }
965 : #endif
966 :
967 3551 : return true;
968 : }
969 :
970 : // allow for velocity matching in VTOL
971 1745 : bool Plane::set_velocity_match(const Vector2f &velocity_ne_ms)
972 : {
973 : #if HAL_QUADPLANE_ENABLED
974 1745 : if (quadplane.in_vtol_mode() || quadplane.in_vtol_land_sequence()) {
975 1745 : quadplane.poscontrol.velocity_match_ms = velocity_ne_ms;
976 1745 : quadplane.poscontrol.last_velocity_match_ms = AP_HAL::millis();
977 1745 : return true;
978 : }
979 : #endif
980 0 : return false;
981 : }
982 :
983 : // allow for override of land descent rate
984 67 : bool Plane::set_land_descent_rate(float descent_rate_ms)
985 : {
986 : #if HAL_QUADPLANE_ENABLED
987 67 : if (quadplane.in_vtol_land_descent() ||
988 0 : control_mode == &mode_qland) {
989 67 : quadplane.poscontrol.override_descent_rate_ms = descent_rate_ms;
990 67 : quadplane.poscontrol.last_override_descent_ms = AP_HAL::millis();
991 67 : return true;
992 : }
993 : #endif
994 0 : return false;
995 : }
996 :
997 : // Allow for scripting to have control over the crosstracking when exiting and resuming missions or guided flight
998 : // It's up to the Lua script to ensure the provided location makes sense
999 18 : bool Plane::set_crosstrack_start(const Location &new_start_location)
1000 : {
1001 18 : prev_WP_loc = new_start_location;
1002 18 : auto_state.crosstrack = true;
1003 18 : return true;
1004 : }
1005 :
1006 : #endif // AP_SCRIPTING_ENABLED
1007 :
1008 : // returns true if vehicle is landing.
1009 7270 : bool Plane::is_landing() const
1010 : {
1011 : #if HAL_QUADPLANE_ENABLED
1012 7270 : if (plane.quadplane.in_vtol_land_descent()) {
1013 1392 : return true;
1014 : }
1015 : #endif
1016 5878 : return control_mode->is_landing();
1017 : }
1018 :
1019 : // returns true if vehicle is taking off.
1020 7683 : bool Plane::is_taking_off() const
1021 : {
1022 : #if HAL_QUADPLANE_ENABLED
1023 7683 : if (plane.quadplane.in_vtol_takeoff()) {
1024 949 : return true;
1025 : }
1026 : #endif
1027 6734 : return control_mode->is_taking_off();
1028 : }
1029 :
1030 : #if HAL_QUADPLANE_ENABLED
1031 0 : bool Plane::start_takeoff(const float alt_m) {
1032 0 : return plane.quadplane.available() && quadplane.do_user_takeoff(alt_m);
1033 : }
1034 : #endif
1035 :
1036 : // correct AHRS pitch for PTCH_TRIM_DEG in non-VTOL modes, and return VTOL view in VTOL
1037 150 : void Plane::get_osd_attitude_rad(float &roll, float &pitch, float &yaw)
1038 : {
1039 : // Take semaphore as this can be called from a thread
1040 150 : WITH_SEMAPHORE(ahrs.get_semaphore());
1041 :
1042 : #if HAL_QUADPLANE_ENABLED
1043 150 : if (quadplane.show_vtol_view()) {
1044 0 : pitch = quadplane.ahrs_view->get_pitch_rad();
1045 0 : roll = quadplane.ahrs_view->get_roll_rad();
1046 0 : yaw = quadplane.ahrs_view->get_yaw_rad();
1047 0 : return;
1048 : }
1049 : #endif
1050 :
1051 150 : pitch = ahrs.get_pitch_rad();
1052 150 : roll = ahrs.get_roll_rad();
1053 150 : if (!(flight_option_enabled(FlightOptions::OSD_REMOVE_TRIM_PITCH))) { // correct for PTCH_TRIM_DEG
1054 150 : pitch -= g.pitch_trim * DEG_TO_RAD;
1055 : }
1056 150 : yaw = ahrs.get_yaw_rad();
1057 150 : }
1058 :
1059 : /*
1060 : update current_loc Location
1061 : */
1062 2739278 : void Plane::update_current_loc(void)
1063 : {
1064 2739278 : have_position = plane.ahrs.get_location(plane.current_loc);
1065 :
1066 : // re-calculate relative altitude
1067 2739278 : ahrs.get_relative_position_D_home(plane.relative_altitude);
1068 2739278 : relative_altitude *= -1.0f;
1069 2739278 : }
1070 :
1071 : // check if FLIGHT_OPTION is enabled
1072 38120554 : bool Plane::flight_option_enabled(FlightOptions flight_option) const
1073 : {
1074 38120554 : return g2.flight_options & flight_option;
1075 : }
1076 :
1077 : #if AC_PRECLAND_ENABLED
1078 8376953 : void Plane::precland_update(void)
1079 : {
1080 : // alt will be unused if we pass false through as the second parameter:
1081 : #if AP_RANGEFINDER_ENABLED
1082 8376953 : return g2.precland.update(rangefinder_state.height_estimate*100, rangefinder_state.in_range);
1083 : #else
1084 : return g2.precland.update(0, false);
1085 : #endif
1086 : }
1087 : #endif
1088 :
1089 : #if AP_QUICKTUNE_ENABLED
1090 : /*
1091 : update AP_Quicktune object. We pass the supports_quicktune() method
1092 : in so that quicktune can detect if the user changes to a
1093 : non-quicktune capable mode while tuning and the gains can be reverted
1094 : */
1095 2566790 : void Plane::update_quicktune(void)
1096 : {
1097 2566790 : quicktune.update(control_mode->supports_quicktune());
1098 2566790 : }
1099 : #endif
1100 :
1101 : /*
1102 : constructor for main Plane class
1103 : */
1104 692 : Plane::Plane(void)
1105 : {
1106 : // C++11 doesn't allow in-class initialisation of bitfields
1107 692 : auto_state.takeoff_complete = true;
1108 692 : }
1109 :
1110 : Plane plane;
1111 : AP_Vehicle& vehicle = plane;
1112 :
1113 692 : AP_HAL_MAIN_CALLBACKS(&plane);
|