1 #include "GCS_Mavlink.h"
4 #include <AP_RPM/AP_RPM_config.h>
5 #include <AP_Airspeed/AP_Airspeed_config.h>
6 #include <AP_EFI/AP_EFI_config.h>
8 MAV_TYPE
GCS_Plane::frame_type() const
10 #if HAL_QUADPLANE_ENABLED
11 return plane
.quadplane
.get_mav_type();
13 return MAV_TYPE_FIXED_WING
;
17 MAV_MODE
GCS_MAVLINK_Plane::base_mode() const
19 uint8_t _base_mode
= MAV_MODE_FLAG_CUSTOM_MODE_ENABLED
;
21 // work out the base_mode. This value is not very useful
22 // for APM, but we calculate it as best we can so a generic
23 // MAVLink enabled ground station can work out something about
24 // what the MAV is up to. The actual bit values are highly
25 // ambiguous for most of the APM flight modes. In practice, you
26 // only get useful information from the custom_mode, which maps to
27 // the APM flight mode and has a well defined meaning in the
28 // ArduPlane documentation
29 switch (plane
.control_mode
->mode_number()) {
30 case Mode::Number::MANUAL
:
31 case Mode::Number::TRAINING
:
32 case Mode::Number::ACRO
:
33 #if HAL_QUADPLANE_ENABLED
34 case Mode::Number::QACRO
:
35 _base_mode
= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED
;
38 case Mode::Number::STABILIZE
:
39 case Mode::Number::FLY_BY_WIRE_A
:
40 case Mode::Number::AUTOTUNE
:
41 case Mode::Number::FLY_BY_WIRE_B
:
42 #if HAL_QUADPLANE_ENABLED
43 case Mode::Number::QSTABILIZE
:
44 case Mode::Number::QHOVER
:
45 case Mode::Number::QLOITER
:
46 case Mode::Number::QLAND
:
48 case Mode::Number::QAUTOTUNE
:
50 #endif // HAL_QUADPLANE_ENABLED
51 case Mode::Number::CRUISE
:
52 _base_mode
= MAV_MODE_FLAG_STABILIZE_ENABLED
;
54 case Mode::Number::AUTO
:
55 case Mode::Number::RTL
:
56 case Mode::Number::LOITER
:
57 case Mode::Number::THERMAL
:
58 case Mode::Number::AVOID_ADSB
:
59 case Mode::Number::GUIDED
:
60 case Mode::Number::CIRCLE
:
61 case Mode::Number::TAKEOFF
:
62 #if HAL_QUADPLANE_ENABLED
63 case Mode::Number::QRTL
:
64 case Mode::Number::LOITER_ALT_QLAND
:
66 _base_mode
= MAV_MODE_FLAG_GUIDED_ENABLED
|
67 MAV_MODE_FLAG_STABILIZE_ENABLED
;
68 // note that MAV_MODE_FLAG_AUTO_ENABLED does not match what
69 // APM does in any mode, as that is defined as "system finds its own goal
70 // positions", which APM does not currently do
72 case Mode::Number::INITIALISING
:
76 if (!plane
.training_manual_pitch
|| !plane
.training_manual_roll
) {
77 _base_mode
|= MAV_MODE_FLAG_STABILIZE_ENABLED
;
80 if (plane
.control_mode
!= &plane
.mode_manual
&& plane
.control_mode
!= &plane
.mode_initializing
) {
81 // stabiliser of some form is enabled
82 _base_mode
|= MAV_MODE_FLAG_STABILIZE_ENABLED
;
85 if (plane
.g
.stick_mixing
!= StickMixing::NONE
&& plane
.control_mode
!= &plane
.mode_initializing
) {
86 if ((plane
.g
.stick_mixing
!= StickMixing::VTOL_YAW
) || (plane
.control_mode
== &plane
.mode_auto
)) {
87 // all modes except INITIALISING have some form of manual
88 // override if stick mixing is enabled
89 _base_mode
|= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED
;
93 // we are armed if we are not initialising
94 if (plane
.control_mode
!= &plane
.mode_initializing
&& plane
.arming
.is_armed()) {
95 _base_mode
|= MAV_MODE_FLAG_SAFETY_ARMED
;
98 // indicate we have set a custom mode
99 _base_mode
|= MAV_MODE_FLAG_CUSTOM_MODE_ENABLED
;
101 return (MAV_MODE
)_base_mode
;
104 uint32_t GCS_Plane::custom_mode() const
106 return plane
.control_mode
->mode_number();
109 MAV_STATE
GCS_MAVLINK_Plane::vehicle_system_status() const
111 if (plane
.control_mode
== &plane
.mode_initializing
) {
112 return MAV_STATE_CALIBRATING
;
114 if (plane
.any_failsafe_triggered()) {
115 return MAV_STATE_CRITICAL
;
117 if (plane
.crash_state
.is_crashed
) {
118 return MAV_STATE_EMERGENCY
;
120 if (plane
.is_flying()) {
121 return MAV_STATE_ACTIVE
;
124 return MAV_STATE_STANDBY
;
128 void GCS_MAVLINK_Plane::send_attitude() const
130 const AP_AHRS
&ahrs
= AP::ahrs();
132 float r
= ahrs
.get_roll();
133 float p
= ahrs
.get_pitch();
134 float y
= ahrs
.get_yaw();
136 if (!(plane
.flight_option_enabled(FlightOptions::GCS_REMOVE_TRIM_PITCH
))) {
137 p
-= radians(plane
.g
.pitch_trim
);
140 #if HAL_QUADPLANE_ENABLED
141 if (plane
.quadplane
.show_vtol_view()) {
142 r
= plane
.quadplane
.ahrs_view
->roll
;
143 p
= plane
.quadplane
.ahrs_view
->pitch
;
144 y
= plane
.quadplane
.ahrs_view
->yaw
;
148 const Vector3f
&omega
= ahrs
.get_gyro();
149 mavlink_msg_attitude_send(
160 void GCS_MAVLINK_Plane::send_attitude_target()
162 #if HAL_QUADPLANE_ENABLED
163 // Check if the attitude target is valid for reporting
164 const uint32_t now
= AP_HAL::millis();
165 if (now
- plane
.quadplane
.last_att_control_ms
> 100) {
169 const Quaternion quat
= plane
.quadplane
.attitude_control
->get_attitude_target_quat();
170 const Vector3f
& ang_vel
= plane
.quadplane
.attitude_control
->get_attitude_target_ang_vel();
171 const float throttle
= plane
.quadplane
.attitude_control
->get_throttle_in();
173 const float quat_out
[4] {quat
.q1
, quat
.q2
, quat
.q3
, quat
.q4
};
175 const uint16_t typemask
= 0;
177 mavlink_msg_attitude_target_send(
179 now
, // time since boot (ms)
180 typemask
, // Bitmask that tells the system what control dimensions should be ignored by the vehicle
181 quat_out
, // Target attitude quaternion [w, x, y, z] order, zero-rotation is [1, 0, 0, 0], unit-length
182 ang_vel
.x
, // bodyframe target roll rate (rad/s)
183 ang_vel
.y
, // bodyframe target pitch rate (rad/s)
184 ang_vel
.z
, // bodyframe yaw rate (rad/s)
185 throttle
); // Collective thrust, normalized to 0 .. 1
187 #endif // HAL_QUADPLANE_ENABLED
190 void GCS_MAVLINK_Plane::send_aoa_ssa()
192 AP_AHRS
&ahrs
= AP::ahrs();
194 mavlink_msg_aoa_ssa_send(
201 void GCS_MAVLINK_Plane::send_nav_controller_output() const
203 if (plane
.control_mode
== &plane
.mode_manual
) {
206 #if HAL_QUADPLANE_ENABLED
207 const QuadPlane
&quadplane
= plane
.quadplane
;
208 if (quadplane
.show_vtol_view() && quadplane
.using_wp_nav()) {
209 const Vector3f
&targets
= quadplane
.attitude_control
->get_att_target_euler_cd();
211 const Vector2f
& curr_pos
= quadplane
.inertial_nav
.get_position_xy_cm();
212 const Vector2f
& target_pos
= quadplane
.pos_control
->get_pos_target_cm().xy().tofloat();
213 const Vector2f error
= (target_pos
- curr_pos
) * 0.01;
215 mavlink_msg_nav_controller_output_send(
220 degrees(error
.angle()),
221 MIN(error
.length(), UINT16_MAX
),
222 (plane
.control_mode
!= &plane
.mode_qstabilize
) ? quadplane
.pos_control
->get_pos_error_z_cm() * 0.01 : 0,
223 plane
.airspeed_error
* 100, // incorrect units; see PR#7933
224 quadplane
.wp_nav
->crosstrack_error());
229 const AP_Navigation
*nav_controller
= plane
.nav_controller
;
230 mavlink_msg_nav_controller_output_send(
232 plane
.nav_roll_cd
* 0.01,
233 plane
.nav_pitch_cd
* 0.01,
234 nav_controller
->nav_bearing_cd() * 0.01,
235 nav_controller
->target_bearing_cd() * 0.01,
236 MIN(plane
.auto_state
.wp_distance
, UINT16_MAX
),
237 plane
.calc_altitude_error_cm() * 0.01,
238 plane
.airspeed_error
* 100, // incorrect units; see PR#7933
239 nav_controller
->crosstrack_error());
243 void GCS_MAVLINK_Plane::send_position_target_global_int()
245 if (plane
.control_mode
== &plane
.mode_manual
) {
248 Location
&next_WP_loc
= plane
.next_WP_loc
;
249 static constexpr uint16_t POSITION_TARGET_TYPEMASK_LAST_BYTE
= 0xF000;
250 static constexpr uint16_t TYPE_MASK
= POSITION_TARGET_TYPEMASK_VX_IGNORE
| POSITION_TARGET_TYPEMASK_VY_IGNORE
| POSITION_TARGET_TYPEMASK_VZ_IGNORE
|
251 POSITION_TARGET_TYPEMASK_AX_IGNORE
| POSITION_TARGET_TYPEMASK_AY_IGNORE
| POSITION_TARGET_TYPEMASK_AZ_IGNORE
|
252 POSITION_TARGET_TYPEMASK_YAW_IGNORE
| POSITION_TARGET_TYPEMASK_YAW_RATE_IGNORE
| POSITION_TARGET_TYPEMASK_LAST_BYTE
;
254 if (!next_WP_loc
.is_zero()) {
255 UNUSED_RESULT(next_WP_loc
.get_alt_cm(Location::AltFrame::ABSOLUTE
, alt
));
258 mavlink_msg_position_target_global_int_send(
260 AP_HAL::millis(), // time_boot_ms
261 MAV_FRAME_GLOBAL
, // targets are always global altitude
262 TYPE_MASK
, // ignore everything except the x/y/z components
263 next_WP_loc
.lat
, // latitude as 1e7
264 next_WP_loc
.lng
, // longitude as 1e7
265 alt
* 0.01, // altitude is sent as a float
277 float GCS_MAVLINK_Plane::vfr_hud_airspeed() const
279 // airspeed sensors are best. While the AHRS airspeed_estimate
280 // will use an airspeed sensor, that value is constrained by the
281 // ground speed. When reporting we should send the true airspeed
282 // value if possible:
283 #if AP_AIRSPEED_ENABLED
284 if (plane
.airspeed
.enabled() && plane
.airspeed
.healthy()) {
285 return plane
.airspeed
.get_airspeed();
289 // airspeed estimates are OK:
291 if (AP::ahrs().airspeed_estimate(aspeed
)) {
299 int16_t GCS_MAVLINK_Plane::vfr_hud_throttle() const
301 return plane
.throttle_percentage();
304 float GCS_MAVLINK_Plane::vfr_hud_climbrate() const
306 #if HAL_SOARING_ENABLED
307 if (plane
.g2
.soaring_controller
.is_active()) {
308 return plane
.g2
.soaring_controller
.get_vario_reading();
311 return GCS_MAVLINK::vfr_hud_climbrate();
314 void GCS_MAVLINK_Plane::send_wind() const
316 const Vector3f wind
= AP::ahrs().wind_estimate();
317 mavlink_msg_wind_send(
319 degrees(atan2f(-wind
.y
, -wind
.x
)), // use negative, to give
320 // direction wind is coming from
325 // sends a single pid info over the provided channel
326 void GCS_MAVLINK_Plane::send_pid_info(const AP_PIDInfo
*pid_info
,
327 const uint8_t axis
, const float achieved
)
329 if (pid_info
== nullptr) {
332 if (!HAVE_PAYLOAD_SPACE(chan
, PID_TUNING
)) {
335 mavlink_msg_pid_tuning_send(chan
, axis
,
347 send PID tuning message
349 void GCS_MAVLINK_Plane::send_pid_tuning()
351 if (plane
.control_mode
== &plane
.mode_manual
) {
352 // no PIDs should be used in manual
356 const Parameters
&g
= plane
.g
;
358 const AP_PIDInfo
*pid_info
;
359 if (g
.gcs_pid_mask
& TUNING_BITS_ROLL
) {
360 pid_info
= &plane
.rollController
.get_pid_info();
361 #if HAL_QUADPLANE_ENABLED
362 if (plane
.quadplane
.in_vtol_mode()) {
363 pid_info
= &plane
.quadplane
.attitude_control
->get_rate_roll_pid().get_pid_info();
366 send_pid_info(pid_info
, PID_TUNING_ROLL
, pid_info
->actual
);
368 if (g
.gcs_pid_mask
& TUNING_BITS_PITCH
) {
369 pid_info
= &plane
.pitchController
.get_pid_info();
370 #if HAL_QUADPLANE_ENABLED
371 if (plane
.quadplane
.in_vtol_mode()) {
372 pid_info
= &plane
.quadplane
.attitude_control
->get_rate_pitch_pid().get_pid_info();
375 send_pid_info(pid_info
, PID_TUNING_PITCH
, pid_info
->actual
);
377 if (g
.gcs_pid_mask
& TUNING_BITS_YAW
) {
378 pid_info
= &plane
.yawController
.get_pid_info();
379 #if HAL_QUADPLANE_ENABLED
380 if (plane
.quadplane
.in_vtol_mode()) {
381 pid_info
= &plane
.quadplane
.attitude_control
->get_rate_yaw_pid().get_pid_info();
384 send_pid_info(pid_info
, PID_TUNING_YAW
, pid_info
->actual
);
386 if (g
.gcs_pid_mask
& TUNING_BITS_STEER
) {
387 pid_info
= &plane
.steerController
.get_pid_info();
388 send_pid_info(pid_info
, PID_TUNING_STEER
, pid_info
->actual
);
390 if ((g
.gcs_pid_mask
& TUNING_BITS_LAND
) && (plane
.flight_stage
== AP_FixedWing::FlightStage::LAND
)) {
391 AP_AHRS
&ahrs
= AP::ahrs();
392 const Vector3f
&gyro
= ahrs
.get_gyro();
393 send_pid_info(plane
.landing
.get_pid_info(), PID_TUNING_LANDING
, degrees(gyro
.z
));
395 #if HAL_QUADPLANE_ENABLED
396 if (g
.gcs_pid_mask
& TUNING_BITS_ACCZ
&& plane
.quadplane
.in_vtol_mode()) {
397 pid_info
= &plane
.quadplane
.pos_control
->get_accel_z_pid().get_pid_info();
398 send_pid_info(pid_info
, PID_TUNING_ACCZ
, pid_info
->actual
);
403 uint8_t GCS_MAVLINK_Plane::sysid_my_gcs() const
405 return plane
.g
.sysid_my_gcs
;
407 bool GCS_MAVLINK_Plane::sysid_enforce() const
409 return plane
.g2
.sysid_enforce
;
412 uint32_t GCS_MAVLINK_Plane::telem_delay() const
414 return (uint32_t)(plane
.g
.telem_delay
);
417 // try to send a message, return false if it won't fit in the serial tx buffer
418 bool GCS_MAVLINK_Plane::try_send_message(enum ap_message id
)
427 #if AP_TERRAIN_AVAILABLE
428 CHECK_PAYLOAD_SIZE(TERRAIN_REQUEST
);
429 plane
.terrain
.send_request(chan
);
434 CHECK_PAYLOAD_SIZE(WIND
);
438 case MSG_ADSB_VEHICLE
:
440 CHECK_PAYLOAD_SIZE(ADSB_VEHICLE
);
441 plane
.adsb
.send_adsb_vehicle(chan
);
446 CHECK_PAYLOAD_SIZE(AOA_SSA
);
450 plane
.landing
.send_landing_message(chan
);
454 #if AP_AIRSPEED_HYGROMETER_ENABLE
455 CHECK_PAYLOAD_SIZE(HYGROMETER_SENSOR
);
461 return GCS_MAVLINK::try_send_message(id
);
466 #if AP_AIRSPEED_HYGROMETER_ENABLE
467 void GCS_MAVLINK_Plane::send_hygrometer()
469 if (!HAVE_PAYLOAD_SPACE(chan
, HYGROMETER_SENSOR
)) {
473 const auto *airspeed
= AP::airspeed();
474 if (airspeed
== nullptr) {
477 const uint32_t now
= AP_HAL::millis();
479 for (uint8_t i
=0; i
<AIRSPEED_MAX_SENSORS
; i
++) {
480 uint8_t idx
= (i
+last_hygrometer_send_idx
+1) % AIRSPEED_MAX_SENSORS
;
481 float temperature
, humidity
;
482 uint32_t last_sample_ms
;
483 if (!airspeed
->get_hygrometer(idx
, last_sample_ms
, temperature
, humidity
)) {
486 if (now
- last_sample_ms
> 2000) {
487 // not updating, stop sending
490 if (!HAVE_PAYLOAD_SPACE(chan
, HYGROMETER_SENSOR
)) {
494 mavlink_msg_hygrometer_sensor_send(
497 int16_t(temperature
*100),
498 uint16_t(humidity
*100));
499 last_hygrometer_send_idx
= idx
;
502 #endif // AP_AIRSPEED_HYGROMETER_ENABLE
506 default stream rates to 1Hz
508 const AP_Param::GroupInfo
GCS_MAVLINK_Parameters::var_info
[] = {
510 // @DisplayName: Raw sensor stream rate
511 // @Description: MAVLink Stream rate of RAW_IMU, SCALED_IMU2, SCALED_IMU3, SCALED_PRESSURE, SCALED_PRESSURE2, and SCALED_PRESSURE3
515 // @RebootRequired: True
517 AP_GROUPINFO("RAW_SENS", 0, GCS_MAVLINK_Parameters
, streamRates
[0], 1),
520 // @DisplayName: Extended status stream rate
521 // @Description: MAVLink Stream rate of SYS_STATUS, POWER_STATUS, MCU_STATUS, MEMINFO, CURRENT_WAYPOINT, GPS_RAW_INT, GPS_RTK (if available), GPS2_RAW_INT (if available), GPS2_RTK (if available), NAV_CONTROLLER_OUTPUT, FENCE_STATUS, and GLOBAL_TARGET_POS_INT
525 // @RebootRequired: True
527 AP_GROUPINFO("EXT_STAT", 1, GCS_MAVLINK_Parameters
, streamRates
[1], 1),
530 // @DisplayName: RC Channel stream rate
531 // @Description: MAVLink Stream rate of SERVO_OUTPUT_RAW and RC_CHANNELS
535 // @RebootRequired: True
537 AP_GROUPINFO("RC_CHAN", 2, GCS_MAVLINK_Parameters
, streamRates
[2], 1),
540 // @DisplayName: Raw Control stream rate
541 // @Description: MAVLink Raw Control stream rate of SERVO_OUT
545 // @RebootRequired: True
547 AP_GROUPINFO("RAW_CTRL", 3, GCS_MAVLINK_Parameters
, streamRates
[3], 1),
550 // @DisplayName: Position stream rate
551 // @Description: MAVLink Stream rate of GLOBAL_POSITION_INT and LOCAL_POSITION_NED
555 // @RebootRequired: True
557 AP_GROUPINFO("POSITION", 4, GCS_MAVLINK_Parameters
, streamRates
[4], 1),
560 // @DisplayName: Extra data type 1 stream rate
561 // @Description: MAVLink Stream rate of ATTITUDE, SIMSTATE (SIM only), AHRS2, RPM, AOA_SSA, LANDING,ESC_TELEMETRY,EFI_STATUS, and PID_TUNING
565 // @RebootRequired: True
567 AP_GROUPINFO("EXTRA1", 5, GCS_MAVLINK_Parameters
, streamRates
[5], 1),
570 // @DisplayName: Extra data type 2 stream rate
571 // @Description: MAVLink Stream rate of VFR_HUD
574 // @RebootRequired: True
576 AP_GROUPINFO("EXTRA2", 6, GCS_MAVLINK_Parameters
, streamRates
[6], 1),
579 // @DisplayName: Extra data type 3 stream rate
580 // @Description: MAVLink Stream rate of AHRS, SYSTEM_TIME, WIND, RANGEFINDER, DISTANCE_SENSOR, TERRAIN_REQUEST, BATTERY2, GIMBAL_DEVICE_ATTITUDE_STATUS, OPTICAL_FLOW, MAG_CAL_REPORT, MAG_CAL_PROGRESS, EKF_STATUS_REPORT, VIBRATION, and BATTERY_STATUS
584 // @RebootRequired: True
586 AP_GROUPINFO("EXTRA3", 7, GCS_MAVLINK_Parameters
, streamRates
[7], 1),
589 // @DisplayName: Parameter stream rate
590 // @Description: MAVLink Stream rate of PARAM_VALUE
594 // @RebootRequired: True
596 AP_GROUPINFO("PARAMS", 8, GCS_MAVLINK_Parameters
, streamRates
[8], 10),
599 // @DisplayName: ADSB stream rate
600 // @Description: MAVLink ADSB stream rate
604 // @RebootRequired: True
606 AP_GROUPINFO("ADSB", 9, GCS_MAVLINK_Parameters
, streamRates
[9], 5),
610 static const ap_message STREAM_RAW_SENSORS_msgs
[] = {
615 MSG_SCALED_PRESSURE2
,
616 MSG_SCALED_PRESSURE3
,
618 static const ap_message STREAM_EXTENDED_STATUS_msgs
[] = {
621 #if HAL_WITH_MCU_MONITORING
625 MSG_CURRENT_WAYPOINT
,
628 #if GPS_MAX_RECEIVERS > 1
632 MSG_NAV_CONTROLLER_OUTPUT
,
636 MSG_POSITION_TARGET_GLOBAL_INT
,
638 static const ap_message STREAM_POSITION_msgs
[] = {
642 static const ap_message STREAM_RAW_CONTROLLER_msgs
[] = {
645 static const ap_message STREAM_RC_CHANNELS_msgs
[] = {
646 MSG_SERVO_OUTPUT_RAW
,
648 MSG_RC_CHANNELS_RAW
, // only sent on a mavlink1 connection
650 static const ap_message STREAM_EXTRA1_msgs
[] = {
662 #if HAL_WITH_ESC_TELEM
668 #if AP_AIRSPEED_HYGROMETER_ENABLE
672 static const ap_message STREAM_EXTRA2_msgs
[] = {
675 static const ap_message STREAM_EXTRA3_msgs
[] = {
678 #if AP_RANGEFINDER_ENABLED
683 #if AP_TERRAIN_AVAILABLE
686 #if AP_BATTERY_ENABLED
689 #if HAL_MOUNT_ENABLED
690 MSG_GIMBAL_DEVICE_ATTITUDE_STATUS
,
692 #if AP_OPTICALFLOW_ENABLED
695 #if COMPASS_CAL_ENABLED
697 MSG_MAG_CAL_PROGRESS
,
699 MSG_EKF_STATUS_REPORT
,
702 static const ap_message STREAM_PARAMS_msgs
[] = {
705 static const ap_message STREAM_ADSB_msgs
[] = {
712 const struct GCS_MAVLINK::stream_entries
GCS_MAVLINK::all_stream_entries
[] = {
713 MAV_STREAM_ENTRY(STREAM_RAW_SENSORS
),
714 MAV_STREAM_ENTRY(STREAM_EXTENDED_STATUS
),
715 MAV_STREAM_ENTRY(STREAM_POSITION
),
716 MAV_STREAM_ENTRY(STREAM_RAW_CONTROLLER
),
717 MAV_STREAM_ENTRY(STREAM_RC_CHANNELS
),
718 MAV_STREAM_ENTRY(STREAM_EXTRA1
),
719 MAV_STREAM_ENTRY(STREAM_EXTRA2
),
720 MAV_STREAM_ENTRY(STREAM_EXTRA3
),
721 MAV_STREAM_ENTRY(STREAM_PARAMS
),
722 MAV_STREAM_ENTRY(STREAM_ADSB
),
723 MAV_STREAM_TERMINATOR
// must have this at end of stream_entries
727 handle a request to switch to guided mode. This happens via a
728 callback from handle_mission_item()
730 bool GCS_MAVLINK_Plane::handle_guided_request(AP_Mission::Mission_Command
&cmd
)
732 return plane
.control_mode
->handle_guided_request(cmd
.content
.location
);
736 handle a request to change current WP altitude. This happens via a
737 callback from handle_mission_item()
739 void GCS_MAVLINK_Plane::handle_change_alt_request(AP_Mission::Mission_Command
&cmd
)
741 plane
.next_WP_loc
.alt
= cmd
.content
.location
.alt
;
742 if (cmd
.content
.location
.relative_alt
) {
743 plane
.next_WP_loc
.alt
+= plane
.home
.alt
;
745 plane
.next_WP_loc
.relative_alt
= false;
746 plane
.next_WP_loc
.terrain_alt
= cmd
.content
.location
.terrain_alt
;
747 plane
.reset_offset_altitude();
752 handle a LANDING_TARGET command. The timestamp has been jitter corrected
754 void GCS_MAVLINK_Plane::handle_landing_target(const mavlink_landing_target_t
&packet
, uint32_t timestamp_ms
)
756 #if AC_PRECLAND_ENABLED
757 plane
.g2
.precland
.handle_msg(packet
, timestamp_ms
);
761 MAV_RESULT
GCS_MAVLINK_Plane::handle_command_preflight_calibration(const mavlink_command_int_t
&packet
, const mavlink_message_t
&msg
)
763 plane
.in_calibration
= true;
764 MAV_RESULT ret
= GCS_MAVLINK::handle_command_preflight_calibration(packet
, msg
);
765 plane
.in_calibration
= false;
770 void GCS_MAVLINK_Plane::packetReceived(const mavlink_status_t
&status
,
771 const mavlink_message_t
&msg
)
774 plane
.avoidance_adsb
.handle_msg(msg
);
776 #if AP_SCRIPTING_ENABLED && AP_FOLLOW_ENABLED
777 // pass message to follow library
778 plane
.g2
.follow
.handle_msg(msg
);
780 GCS_MAVLINK::packetReceived(status
, msg
);
784 bool Plane::set_home_to_current_location(bool _lock
)
786 if (!set_home_persistently(AP::gps().location())) {
790 AP::ahrs().lock_home();
792 if ((control_mode
== &mode_rtl
)
793 #if HAL_QUADPLANE_ENABLED
794 || (control_mode
== &mode_qrtl
)
797 // if in RTL head to the updated home location
798 control_mode
->enter();
802 bool Plane::set_home(const Location
& loc
, bool _lock
)
804 if (!AP::ahrs().set_home(loc
)) {
808 AP::ahrs().lock_home();
810 if ((control_mode
== &mode_rtl
)
811 #if HAL_QUADPLANE_ENABLED
812 || (control_mode
== &mode_qrtl
)
815 // if in RTL head to the updated home location
816 control_mode
->enter();
821 MAV_RESULT
GCS_MAVLINK_Plane::handle_command_int_do_reposition(const mavlink_command_int_t
&packet
)
823 // sanity check location
824 if (!check_latlng(packet
.x
, packet
.y
)) {
825 return MAV_RESULT_DENIED
;
828 Location requested_position
;
829 if (!location_from_command_t(packet
, requested_position
)) {
830 return MAV_RESULT_DENIED
;
833 if (isnan(packet
.param4
) || is_zero(packet
.param4
)) {
834 requested_position
.loiter_ccw
= 0;
836 requested_position
.loiter_ccw
= 1;
839 if (requested_position
.sanitize(plane
.current_loc
)) {
840 // if the location wasn't already sane don't load it
841 return MAV_RESULT_DENIED
;
844 // location is valid load and set
845 if (((int32_t)packet
.param2
& MAV_DO_REPOSITION_FLAGS_CHANGE_MODE
) ||
846 (plane
.control_mode
== &plane
.mode_guided
)) {
847 plane
.set_mode(plane
.mode_guided
, ModeReason::GCS_COMMAND
);
849 // add home alt if needed
850 if (requested_position
.relative_alt
) {
851 requested_position
.alt
+= plane
.home
.alt
;
852 requested_position
.relative_alt
= 0;
855 plane
.set_guided_WP(requested_position
);
857 // Loiter radius for planes. Positive radius in meters, direction is controlled by Yaw (param4) value, parsed above
858 if (!isnan(packet
.param3
) && packet
.param3
> 0) {
859 plane
.mode_guided
.set_radius_and_direction(packet
.param3
, requested_position
.loiter_ccw
);
862 return MAV_RESULT_ACCEPTED
;
864 return MAV_RESULT_FAILED
;
867 // these are GUIDED mode commands that are RATE or slew enabled, so you can have more powerful control than default controls.
868 MAV_RESULT
GCS_MAVLINK_Plane::handle_command_int_guided_slew_commands(const mavlink_command_int_t
&packet
)
870 switch(packet
.command
) {
872 #if OFFBOARD_GUIDED == ENABLED
873 case MAV_CMD_GUIDED_CHANGE_SPEED
: {
874 // command is only valid in guided mode
875 if (plane
.control_mode
!= &plane
.mode_guided
) {
876 return MAV_RESULT_FAILED
;
879 // only airspeed commands are supported right now...
880 if (int(packet
.param1
) != SPEED_TYPE_AIRSPEED
) { // since SPEED_TYPE is int in range 0-1 and packet.param1 is a *float* this works.
881 return MAV_RESULT_DENIED
;
884 // reject airspeeds that are outside of the tuning envelope
885 if (packet
.param2
> plane
.aparm
.airspeed_max
|| packet
.param2
< plane
.aparm
.airspeed_min
) {
886 return MAV_RESULT_DENIED
;
889 // no need to process any new packet/s with the
890 // same airspeed any further, if we are already doing it.
891 float new_target_airspeed_cm
= packet
.param2
* 100;
892 if ( is_equal(new_target_airspeed_cm
,plane
.guided_state
.target_airspeed_cm
)) {
893 return MAV_RESULT_ACCEPTED
;
895 plane
.guided_state
.target_airspeed_cm
= new_target_airspeed_cm
;
896 plane
.guided_state
.target_airspeed_time_ms
= AP_HAL::millis();
898 if (is_zero(packet
.param3
)) {
899 // the user wanted /maximum acceleration, pick a large value as close enough
900 plane
.guided_state
.target_airspeed_accel
= 1000.0f
;
902 plane
.guided_state
.target_airspeed_accel
= fabsf(packet
.param3
);
905 // assign an acceleration direction
906 if (plane
.guided_state
.target_airspeed_cm
< plane
.target_airspeed_cm
) {
907 plane
.guided_state
.target_airspeed_accel
*= -1.0f
;
909 return MAV_RESULT_ACCEPTED
;
912 case MAV_CMD_GUIDED_CHANGE_ALTITUDE
: {
913 // command is only valid in guided
914 if (plane
.control_mode
!= &plane
.mode_guided
) {
915 return MAV_RESULT_FAILED
;
918 // disallow default value of -1 and dangerous value of zero
919 if (is_equal(packet
.z
, -1.0f
) || is_equal(packet
.z
, 0.0f
)){
920 return MAV_RESULT_DENIED
;
923 // the requested alt data might be relative or absolute
924 float new_target_alt
= packet
.z
* 100;
925 float new_target_alt_rel
= packet
.z
* 100 + plane
.home
.alt
;
927 // only global/relative/terrain frames are supported
928 switch(packet
.frame
) {
929 case MAV_FRAME_GLOBAL_RELATIVE_ALT
: {
930 if (is_equal(plane
.guided_state
.target_alt
,new_target_alt_rel
) ) { // compare two floats as near-enough
931 // no need to process any new packet/s with the same ALT any further, if we are already doing it.
932 return MAV_RESULT_ACCEPTED
;
934 plane
.guided_state
.target_alt
= new_target_alt_rel
;
937 case MAV_FRAME_GLOBAL
: {
938 if (is_equal(plane
.guided_state
.target_alt
,new_target_alt
) ) { // compare two floats as near-enough
939 // no need to process any new packet/s with the same ALT any further, if we are already doing it.
940 return MAV_RESULT_ACCEPTED
;
942 plane
.guided_state
.target_alt
= new_target_alt
;
946 // MAV_RESULT_DENIED means Command is invalid (is supported but has invalid parameters).
947 return MAV_RESULT_DENIED
;
950 plane
.guided_state
.target_alt_frame
= packet
.frame
;
951 plane
.guided_state
.last_target_alt
= plane
.current_loc
.alt
; // FIXME: Reference frame is not corrected for here
952 plane
.guided_state
.target_alt_time_ms
= AP_HAL::millis();
954 if (is_zero(packet
.param3
)) {
955 // the user wanted /maximum acceleration, pick a large value as close enough
956 plane
.guided_state
.target_alt_accel
= 1000.0;
958 plane
.guided_state
.target_alt_accel
= fabsf(packet
.param3
);
961 // assign an acceleration direction
962 if (plane
.guided_state
.target_alt
< plane
.current_loc
.alt
) {
963 plane
.guided_state
.target_alt_accel
*= -1.0f
;
965 return MAV_RESULT_ACCEPTED
;
968 case MAV_CMD_GUIDED_CHANGE_HEADING
: {
970 // command is only valid in guided mode
971 if (plane
.control_mode
!= &plane
.mode_guided
) {
972 return MAV_RESULT_FAILED
;
975 // don't accept packets outside of [0-360] degree range
976 if (packet
.param2
< 0.0f
|| packet
.param2
>= 360.0f
) {
977 return MAV_RESULT_DENIED
;
980 float new_target_heading
= radians(wrap_180(packet
.param2
));
982 // course over ground
983 if ( int(packet
.param1
) == HEADING_TYPE_COURSE_OVER_GROUND
) { // compare as nearest int
984 plane
.guided_state
.target_heading_type
= GUIDED_HEADING_COG
;
985 plane
.prev_WP_loc
= plane
.current_loc
;
986 // normal vehicle heading
987 } else if (int(packet
.param1
) == HEADING_TYPE_HEADING
) { // compare as nearest int
988 plane
.guided_state
.target_heading_type
= GUIDED_HEADING_HEADING
;
990 // MAV_RESULT_DENIED means Command is invalid (is supported but has invalid parameters).
991 return MAV_RESULT_DENIED
;
994 plane
.g2
.guidedHeading
.reset_I();
996 plane
.guided_state
.target_heading
= new_target_heading
;
997 plane
.guided_state
.target_heading_accel_limit
= MAX(packet
.param3
, 0.05f
);
998 plane
.guided_state
.target_heading_time_ms
= AP_HAL::millis();
999 return MAV_RESULT_ACCEPTED
;
1001 #endif // OFFBOARD_GUIDED == ENABLED
1005 // anything else ...
1006 return MAV_RESULT_UNSUPPORTED
;
1010 MAV_RESULT
GCS_MAVLINK_Plane::handle_command_int_packet(const mavlink_command_int_t
&packet
, const mavlink_message_t
&msg
)
1012 switch(packet
.command
) {
1014 case MAV_CMD_DO_AUTOTUNE_ENABLE
:
1015 return handle_MAV_CMD_DO_AUTOTUNE_ENABLE(packet
);
1017 case MAV_CMD_DO_REPOSITION
:
1018 return handle_command_int_do_reposition(packet
);
1020 // special 'slew-enabled' guided commands here... for speed,alt, and direction commands
1021 case MAV_CMD_GUIDED_CHANGE_SPEED
:
1022 case MAV_CMD_GUIDED_CHANGE_ALTITUDE
:
1023 case MAV_CMD_GUIDED_CHANGE_HEADING
:
1024 return handle_command_int_guided_slew_commands(packet
);
1026 #if AP_SCRIPTING_ENABLED && AP_FOLLOW_ENABLED
1027 case MAV_CMD_DO_FOLLOW
:
1028 // param1: sysid of target to follow
1029 if ((packet
.param1
> 0) && (packet
.param1
<= 255)) {
1030 plane
.g2
.follow
.set_target_sysid((uint8_t)packet
.param1
);
1031 return MAV_RESULT_ACCEPTED
;
1033 return MAV_RESULT_DENIED
;
1036 #if AP_ICENGINE_ENABLED
1037 case MAV_CMD_DO_ENGINE_CONTROL
:
1038 if (!plane
.g2
.ice_control
.engine_control(packet
.param1
, packet
.param2
, packet
.param3
, (uint32_t)packet
.param4
)) {
1039 return MAV_RESULT_FAILED
;
1041 return MAV_RESULT_ACCEPTED
;
1044 case MAV_CMD_DO_CHANGE_SPEED
:
1045 return handle_command_DO_CHANGE_SPEED(packet
);
1047 #if PARACHUTE == ENABLED
1048 case MAV_CMD_DO_PARACHUTE
:
1049 return handle_MAV_CMD_DO_PARACHUTE(packet
);
1052 #if HAL_QUADPLANE_ENABLED
1053 case MAV_CMD_DO_MOTOR_TEST
:
1054 return handle_MAV_CMD_DO_MOTOR_TEST(packet
);
1056 case MAV_CMD_DO_VTOL_TRANSITION
:
1057 return handle_command_DO_VTOL_TRANSITION(packet
);
1059 case MAV_CMD_NAV_TAKEOFF
:
1060 return handle_command_MAV_CMD_NAV_TAKEOFF(packet
);
1063 case MAV_CMD_DO_GO_AROUND
:
1064 return plane
.trigger_land_abort(packet
.param1
) ? MAV_RESULT_ACCEPTED
: MAV_RESULT_FAILED
;
1066 case MAV_CMD_DO_LAND_START
:
1067 // attempt to switch to next DO_LAND_START command in the mission
1068 if (plane
.have_position
&& plane
.mission
.jump_to_landing_sequence(plane
.current_loc
)) {
1069 plane
.set_mode(plane
.mode_auto
, ModeReason::GCS_COMMAND
);
1070 return MAV_RESULT_ACCEPTED
;
1072 return MAV_RESULT_FAILED
;
1074 case MAV_CMD_MISSION_START
:
1075 plane
.set_mode(plane
.mode_auto
, ModeReason::GCS_COMMAND
);
1076 return MAV_RESULT_ACCEPTED
;
1078 case MAV_CMD_NAV_LOITER_UNLIM
:
1079 plane
.set_mode(plane
.mode_loiter
, ModeReason::GCS_COMMAND
);
1080 return MAV_RESULT_ACCEPTED
;
1082 case MAV_CMD_NAV_RETURN_TO_LAUNCH
:
1083 plane
.set_mode(plane
.mode_rtl
, ModeReason::GCS_COMMAND
);
1084 return MAV_RESULT_ACCEPTED
;
1087 return GCS_MAVLINK::handle_command_int_packet(packet
, msg
);
1091 MAV_RESULT
GCS_MAVLINK_Plane::handle_command_DO_CHANGE_SPEED(const mavlink_command_int_t
&packet
)
1093 // if we're in failsafe modes (e.g., RTL, LOITER) or in pilot
1094 // controlled modes (e.g., MANUAL, TRAINING)
1095 // this command should be ignored since it comes in from GCS
1096 // or a companion computer:
1097 if ((!plane
.control_mode
->is_guided_mode()) &&
1098 (plane
.control_mode
!= &plane
.mode_auto
)) {
1100 return MAV_RESULT_FAILED
;
1103 if (plane
.do_change_speed(packet
.param1
, packet
.param2
, packet
.param3
)) {
1104 return MAV_RESULT_ACCEPTED
;
1106 return MAV_RESULT_FAILED
;
1109 #if HAL_QUADPLANE_ENABLED
1110 #if AP_MAVLINK_COMMAND_LONG_ENABLED
1111 void GCS_MAVLINK_Plane::convert_MAV_CMD_NAV_TAKEOFF_to_COMMAND_INT(const mavlink_command_long_t
&in
, mavlink_command_int_t
&out
)
1113 // convert to MAV_FRAME_LOCAL_OFFSET_NED, "NED local tangent frame
1114 // with origin that travels with the vehicle"
1116 out
.target_system
= in
.target_system
;
1117 out
.target_component
= in
.target_component
;
1118 out
.frame
= MAV_FRAME_LOCAL_OFFSET_NED
;
1119 out
.command
= in
.command
;
1121 // out.autocontinue = 0;
1122 // out.param1 = in.param1; // we only use the "z" parameter in this command:
1123 // out.param2 = in.param2;
1124 // out.param3 = in.param3;
1125 // out.param4 = in.param4;
1126 // out.x = 0; // we don't handle positioning when doing takeoffs
1128 out
.z
= -in
.param7
; // up -> down
1131 void GCS_MAVLINK_Plane::convert_COMMAND_LONG_to_COMMAND_INT(const mavlink_command_long_t
&in
, mavlink_command_int_t
&out
, MAV_FRAME frame
)
1133 switch (in
.command
) {
1134 case MAV_CMD_NAV_TAKEOFF
:
1135 convert_MAV_CMD_NAV_TAKEOFF_to_COMMAND_INT(in
, out
);
1138 return GCS_MAVLINK::convert_COMMAND_LONG_to_COMMAND_INT(in
, out
, frame
);
1140 #endif // AP_MAVLINK_COMMAND_LONG_ENABLED
1142 MAV_RESULT
GCS_MAVLINK_Plane::handle_command_MAV_CMD_NAV_TAKEOFF(const mavlink_command_int_t
&packet
)
1144 float takeoff_alt
= packet
.z
;
1145 switch (packet
.frame
) {
1146 case MAV_FRAME_LOCAL_OFFSET_NED
: // "NED local tangent frame with origin that travels with the vehicle"
1147 takeoff_alt
= -takeoff_alt
; // down -> up
1150 return MAV_RESULT_DENIED
; // "is supported but has invalid parameters"
1152 if (!plane
.quadplane
.available()) {
1153 return MAV_RESULT_FAILED
;
1155 if (!plane
.quadplane
.do_user_takeoff(takeoff_alt
)) {
1156 return MAV_RESULT_FAILED
;
1158 return MAV_RESULT_ACCEPTED
;
1162 MAV_RESULT
GCS_MAVLINK_Plane::handle_MAV_CMD_DO_AUTOTUNE_ENABLE(const mavlink_command_int_t
&packet
)
1164 // param1 : enable/disable
1165 plane
.autotune_enable(!is_zero(packet
.param1
));
1166 return MAV_RESULT_ACCEPTED
;
1169 #if PARACHUTE == ENABLED
1170 MAV_RESULT
GCS_MAVLINK_Plane::handle_MAV_CMD_DO_PARACHUTE(const mavlink_command_int_t
&packet
)
1172 // configure or release parachute
1173 switch ((uint16_t)packet
.param1
) {
1174 case PARACHUTE_DISABLE
:
1175 plane
.parachute
.enabled(false);
1176 return MAV_RESULT_ACCEPTED
;
1177 case PARACHUTE_ENABLE
:
1178 plane
.parachute
.enabled(true);
1179 return MAV_RESULT_ACCEPTED
;
1180 case PARACHUTE_RELEASE
:
1181 // treat as a manual release which performs some additional check of altitude
1182 if (plane
.parachute
.released()) {
1183 gcs().send_text(MAV_SEVERITY_NOTICE
, "Parachute already released");
1184 return MAV_RESULT_FAILED
;
1186 if (!plane
.parachute
.enabled()) {
1187 gcs().send_text(MAV_SEVERITY_NOTICE
, "Parachute not enabled");
1188 return MAV_RESULT_FAILED
;
1190 if (!plane
.parachute_manual_release()) {
1191 return MAV_RESULT_FAILED
;
1193 return MAV_RESULT_ACCEPTED
;
1197 return MAV_RESULT_FAILED
;
1202 #if HAL_QUADPLANE_ENABLED
1203 MAV_RESULT
GCS_MAVLINK_Plane::handle_MAV_CMD_DO_MOTOR_TEST(const mavlink_command_int_t
&packet
)
1205 // param1 : motor sequence number (a number from 1 to max number of motors on the vehicle)
1206 // param2 : throttle type (0=throttle percentage, 1=PWM, 2=pilot throttle channel pass-through. See MOTOR_TEST_THROTTLE_TYPE enum)
1207 // param3 : throttle (range depends upon param2)
1208 // param4 : timeout (in seconds)
1209 // param5 : motor count (number of motors to test in sequence)
1210 return plane
.quadplane
.mavlink_motor_test_start(chan
,
1211 (uint8_t)packet
.param1
,
1212 (uint8_t)packet
.param2
,
1213 (uint16_t)packet
.param3
,
1218 MAV_RESULT
GCS_MAVLINK_Plane::handle_command_DO_VTOL_TRANSITION(const mavlink_command_int_t
&packet
)
1220 if (!plane
.quadplane
.handle_do_vtol_transition((enum MAV_VTOL_STATE
)packet
.param1
)) {
1221 return MAV_RESULT_FAILED
;
1223 return MAV_RESULT_ACCEPTED
;
1227 // this is called on receipt of a MANUAL_CONTROL packet and is
1228 // expected to call manual_override to override RC input on desired
1230 void GCS_MAVLINK_Plane::handle_manual_control_axes(const mavlink_manual_control_t
&packet
, const uint32_t tnow
)
1232 manual_override(plane
.channel_roll
, packet
.y
, 1000, 2000, tnow
);
1233 manual_override(plane
.channel_pitch
, packet
.x
, 1000, 2000, tnow
, true);
1234 manual_override(plane
.channel_throttle
, packet
.z
, 0, 1000, tnow
);
1235 manual_override(plane
.channel_rudder
, packet
.r
, 1000, 2000, tnow
);
1238 void GCS_MAVLINK_Plane::handle_message(const mavlink_message_t
&msg
)
1240 switch (msg
.msgid
) {
1242 case MAVLINK_MSG_ID_TERRAIN_DATA
:
1243 case MAVLINK_MSG_ID_TERRAIN_CHECK
:
1244 #if AP_TERRAIN_AVAILABLE
1245 plane
.terrain
.handle_data(chan
, msg
);
1249 case MAVLINK_MSG_ID_SET_ATTITUDE_TARGET
:
1250 handle_set_attitude_target(msg
);
1253 case MAVLINK_MSG_ID_SET_POSITION_TARGET_LOCAL_NED
:
1254 handle_set_position_target_local_ned(msg
);
1257 case MAVLINK_MSG_ID_SET_POSITION_TARGET_GLOBAL_INT
:
1258 handle_set_position_target_global_int(msg
);
1262 GCS_MAVLINK::handle_message(msg
);
1265 } // end handle mavlink
1267 void GCS_MAVLINK_Plane::handle_set_attitude_target(const mavlink_message_t
&msg
)
1269 // Only allow companion computer (or other external controller) to
1270 // control attitude in GUIDED mode. We DON'T want external control
1271 // in e.g., RTL, CICLE. Specifying a single mode for companion
1272 // computer control is more safe (even more so when using
1273 // FENCE_ACTION = 4 for geofence failures).
1274 if (plane
.control_mode
!= &plane
.mode_guided
) { // don't screw up failsafes
1278 mavlink_set_attitude_target_t att_target
;
1279 mavlink_msg_set_attitude_target_decode(&msg
, &att_target
);
1281 // Mappings: If any of these bits are set, the corresponding input should be ignored.
1282 // NOTE, when parsing the bits we invert them for easier interpretation but transport has them inverted
1283 // bit 1: body roll rate
1284 // bit 2: body pitch rate
1285 // bit 3: body yaw rate
1292 // if not setting all Quaternion values, use _rate flags to indicate which fields.
1294 // Extract the Euler roll angle from the Quaternion.
1295 Quaternion
q(att_target
.q
[0], att_target
.q
[1],
1296 att_target
.q
[2], att_target
.q
[3]);
1298 // NOTE: att_target.type_mask is inverted for easier interpretation
1299 att_target
.type_mask
= att_target
.type_mask
^ 0xFF;
1301 uint8_t attitude_mask
= att_target
.type_mask
& 0b10000111; // q plus rpy
1303 uint32_t now
= AP_HAL::millis();
1304 if ((attitude_mask
& 0b10000001) || // partial, including roll
1305 (attitude_mask
== 0b10000000)) { // all angles
1306 plane
.guided_state
.forced_rpy_cd
.x
= degrees(q
.get_euler_roll()) * 100.0f
;
1308 // Update timer for external roll to the nav control
1309 plane
.guided_state
.last_forced_rpy_ms
.x
= now
;
1312 if ((attitude_mask
& 0b10000010) || // partial, including pitch
1313 (attitude_mask
== 0b10000000)) { // all angles
1314 plane
.guided_state
.forced_rpy_cd
.y
= degrees(q
.get_euler_pitch()) * 100.0f
;
1316 // Update timer for external pitch to the nav control
1317 plane
.guided_state
.last_forced_rpy_ms
.y
= now
;
1320 if ((attitude_mask
& 0b10000100) || // partial, including yaw
1321 (attitude_mask
== 0b10000000)) { // all angles
1322 plane
.guided_state
.forced_rpy_cd
.z
= degrees(q
.get_euler_yaw()) * 100.0f
;
1324 // Update timer for external yaw to the nav control
1325 plane
.guided_state
.last_forced_rpy_ms
.z
= now
;
1327 if (att_target
.type_mask
& 0b01000000) { // throttle
1328 plane
.guided_state
.forced_throttle
= att_target
.thrust
* 100.0f
;
1330 // Update timer for external throttle
1331 plane
.guided_state
.last_forced_throttle_ms
= now
;
1335 void GCS_MAVLINK_Plane::handle_set_position_target_local_ned(const mavlink_message_t
&msg
)
1338 mavlink_set_position_target_local_ned_t packet
;
1339 mavlink_msg_set_position_target_local_ned_decode(&msg
, &packet
);
1341 // exit if vehicle is not in Guided mode
1342 if (plane
.control_mode
!= &plane
.mode_guided
) {
1346 // only local moves for now
1347 if (packet
.coordinate_frame
!= MAV_FRAME_LOCAL_OFFSET_NED
) {
1351 // just do altitude for now
1352 plane
.next_WP_loc
.alt
+= -packet
.z
*100.0;
1353 gcs().send_text(MAV_SEVERITY_INFO
, "Change alt to %.1f",
1354 (double)((plane
.next_WP_loc
.alt
- plane
.home
.alt
)*0.01));
1357 void GCS_MAVLINK_Plane::handle_set_position_target_global_int(const mavlink_message_t
&msg
)
1359 // Only want to allow companion computer position control when
1360 // in a certain mode to avoid inadvertently sending these
1361 // kinds of commands when the autopilot is responding to problems
1362 // in modes such as RTL, CIRCLE, etc. Specifying ONLY one mode
1363 // for companion computer control is more safe (provided
1364 // one uses the FENCE_ACTION = 4 (RTL) for geofence failures).
1365 if (plane
.control_mode
!= &plane
.mode_guided
) {
1366 //don't screw up failsafes
1370 mavlink_set_position_target_global_int_t pos_target
;
1371 mavlink_msg_set_position_target_global_int_decode(&msg
, &pos_target
);
1372 // Unexpectedly, the mask is expecting "ones" for dimensions that should
1373 // be IGNORNED rather than INCLUDED. See mavlink documentation of the
1374 // SET_POSITION_TARGET_GLOBAL_INT message, type_mask field.
1375 const uint16_t alt_mask
= 0b1111111111111011; // (z mask at bit 3)
1377 bool msg_valid
= true;
1378 AP_Mission::Mission_Command cmd
= {0};
1380 if (pos_target
.type_mask
& alt_mask
)
1382 cmd
.content
.location
.alt
= pos_target
.alt
* 100;
1383 cmd
.content
.location
.relative_alt
= false;
1384 cmd
.content
.location
.terrain_alt
= false;
1385 switch (pos_target
.coordinate_frame
)
1387 case MAV_FRAME_GLOBAL
:
1388 case MAV_FRAME_GLOBAL_INT
:
1389 break; //default to MSL altitude
1390 case MAV_FRAME_GLOBAL_RELATIVE_ALT
:
1391 case MAV_FRAME_GLOBAL_RELATIVE_ALT_INT
:
1392 cmd
.content
.location
.relative_alt
= true;
1394 case MAV_FRAME_GLOBAL_TERRAIN_ALT
:
1395 case MAV_FRAME_GLOBAL_TERRAIN_ALT_INT
:
1396 cmd
.content
.location
.relative_alt
= true;
1397 cmd
.content
.location
.terrain_alt
= true;
1400 gcs().send_text(MAV_SEVERITY_WARNING
, "Invalid coord frame in SET_POSTION_TARGET_GLOBAL_INT");
1406 handle_change_alt_request(cmd
);
1408 } // end if alt_mask
1411 MAV_RESULT
GCS_MAVLINK_Plane::handle_command_do_set_mission_current(const mavlink_command_int_t
&packet
)
1413 const MAV_RESULT result
= GCS_MAVLINK::handle_command_do_set_mission_current(packet
);
1414 if (result
!= MAV_RESULT_ACCEPTED
) {
1418 // if you change this you must change handle_mission_set_current
1419 plane
.auto_state
.next_wp_crosstrack
= false;
1420 if (plane
.control_mode
== &plane
.mode_auto
&& plane
.mission
.state() == AP_Mission::MISSION_STOPPED
) {
1421 plane
.mission
.resume();
1427 #if AP_MAVLINK_MISSION_SET_CURRENT_ENABLED
1428 void GCS_MAVLINK_Plane::handle_mission_set_current(AP_Mission
&mission
, const mavlink_message_t
&msg
)
1430 // if you change this you must change handle_command_do_set_mission_current
1431 plane
.auto_state
.next_wp_crosstrack
= false;
1432 GCS_MAVLINK::handle_mission_set_current(mission
, msg
);
1433 if (plane
.control_mode
== &plane
.mode_auto
&& plane
.mission
.state() == AP_Mission::MISSION_STOPPED
) {
1434 plane
.mission
.resume();
1439 uint64_t GCS_MAVLINK_Plane::capabilities() const
1441 return (MAV_PROTOCOL_CAPABILITY_MISSION_FLOAT
|
1442 MAV_PROTOCOL_CAPABILITY_COMMAND_INT
|
1443 MAV_PROTOCOL_CAPABILITY_MISSION_INT
|
1444 MAV_PROTOCOL_CAPABILITY_SET_POSITION_TARGET_GLOBAL_INT
|
1445 MAV_PROTOCOL_CAPABILITY_SET_ATTITUDE_TARGET
|
1446 #if AP_TERRAIN_AVAILABLE
1447 (plane
.terrain
.enabled() ? MAV_PROTOCOL_CAPABILITY_TERRAIN
: 0) |
1449 GCS_MAVLINK::capabilities());
1452 #if HAL_HIGH_LATENCY2_ENABLED
1453 int16_t GCS_MAVLINK_Plane::high_latency_target_altitude() const
1455 AP_AHRS
&ahrs
= AP::ahrs();
1456 Location global_position_current
;
1457 UNUSED_RESULT(ahrs
.get_location(global_position_current
));
1459 #if HAL_QUADPLANE_ENABLED
1460 const QuadPlane
&quadplane
= plane
.quadplane
;
1461 //return units are m
1462 if (quadplane
.show_vtol_view()) {
1463 return (plane
.control_mode
!= &plane
.mode_qstabilize
) ? 0.01 * (global_position_current
.alt
+ quadplane
.pos_control
->get_pos_error_z_cm()) : 0;
1466 return 0.01 * (global_position_current
.alt
+ plane
.calc_altitude_error_cm());
1469 uint8_t GCS_MAVLINK_Plane::high_latency_tgt_heading() const
1471 // return units are deg/2
1472 #if HAL_QUADPLANE_ENABLED
1473 const QuadPlane
&quadplane
= plane
.quadplane
;
1474 if (quadplane
.show_vtol_view()) {
1475 const Vector3f
&targets
= quadplane
.attitude_control
->get_att_target_euler_cd();
1476 return ((uint16_t)(targets
.z
* 0.01)) / 2;
1479 const AP_Navigation
*nav_controller
= plane
.nav_controller
;
1480 // need to convert -18000->18000 to 0->360/2
1481 return wrap_360_cd(nav_controller
->target_bearing_cd() ) / 200;
1484 // return units are dm
1485 uint16_t GCS_MAVLINK_Plane::high_latency_tgt_dist() const
1487 #if HAL_QUADPLANE_ENABLED
1488 const QuadPlane
&quadplane
= plane
.quadplane
;
1489 if (quadplane
.show_vtol_view()) {
1490 bool wp_nav_valid
= quadplane
.using_wp_nav();
1491 return (wp_nav_valid
? MIN(quadplane
.wp_nav
->get_wp_distance_to_destination(), UINT16_MAX
) : 0) / 10;
1495 return MIN(plane
.auto_state
.wp_distance
, UINT16_MAX
) / 10;
1498 uint8_t GCS_MAVLINK_Plane::high_latency_tgt_airspeed() const
1500 // return units are m/s*5
1501 return plane
.target_airspeed_cm
* 0.05;
1504 uint8_t GCS_MAVLINK_Plane::high_latency_wind_speed() const
1507 wind
= AP::ahrs().wind_estimate();
1509 // return units are m/s*5
1510 return MIN(wind
.length() * 5, UINT8_MAX
);
1513 uint8_t GCS_MAVLINK_Plane::high_latency_wind_direction() const
1515 const Vector3f wind
= AP::ahrs().wind_estimate();
1517 // return units are deg/2
1518 // need to convert -180->180 to 0->360/2
1519 return wrap_360(degrees(atan2f(-wind
.y
, -wind
.x
))) / 2;
1521 #endif // HAL_HIGH_LATENCY2_ENABLED
1523 MAV_VTOL_STATE
GCS_MAVLINK_Plane::vtol_state() const
1525 #if !HAL_QUADPLANE_ENABLED
1526 return MAV_VTOL_STATE_UNDEFINED
;
1528 if (!plane
.quadplane
.available()) {
1529 return MAV_VTOL_STATE_UNDEFINED
;
1532 return plane
.quadplane
.transition
->get_mav_vtol_state();
1536 MAV_LANDED_STATE
GCS_MAVLINK_Plane::landed_state() const
1538 if (plane
.is_flying()) {
1539 // note that Q-modes almost always consider themselves as flying
1540 return MAV_LANDED_STATE_IN_AIR
;
1543 return MAV_LANDED_STATE_ON_GROUND
;