LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduPlane - GCS_MAVLink_Plane.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 438 607 72.2 %
Date: 2026-09-09 11:02:08 Functions: 48 53 90.6 %

          Line data    Source code
       1             : #include "GCS_MAVLink_Plane.h"
       2             : 
       3             : #include "Plane.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>
       7             : 
       8       60885 : MAV_TYPE GCS_Plane::frame_type() const
       9             : {
      10             : #if HAL_QUADPLANE_ENABLED
      11       60885 :     return plane.quadplane.get_mav_type();
      12             : #else
      13             :     return MAV_TYPE_FIXED_WING;
      14             : #endif
      15             : }
      16             : 
      17       60877 : uint8_t GCS_MAVLINK_Plane::base_mode() const
      18             : {
      19       60877 :     uint8_t _base_mode = MAV_MODE_FLAG_CUSTOM_MODE_ENABLED;
      20             : 
      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       60877 :     switch (plane.control_mode->mode_number()) {
      30       10713 :     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       10713 :         _base_mode = MAV_MODE_FLAG_MANUAL_INPUT_ENABLED;
      36       10713 :         break;
      37             : #endif
      38       11698 :     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:
      47             : #if QAUTOTUNE_ENABLED
      48             :     case Mode::Number::QAUTOTUNE:
      49             : #endif
      50             : #endif  // HAL_QUADPLANE_ENABLED
      51             :     case Mode::Number::CRUISE:
      52       11698 :         _base_mode = MAV_MODE_FLAG_STABILIZE_ENABLED;
      53       11698 :         break;
      54       36585 :     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 MODE_AUTOLAND_ENABLED
      63             :     case Mode::Number::AUTOLAND:
      64             : #endif
      65             : #if HAL_QUADPLANE_ENABLED
      66             :     case Mode::Number::QRTL:
      67             :     case Mode::Number::LOITER_ALT_QLAND:
      68             : #endif
      69       36585 :         _base_mode = MAV_MODE_FLAG_GUIDED_ENABLED |
      70             :                      MAV_MODE_FLAG_STABILIZE_ENABLED;
      71             :         // note that MAV_MODE_FLAG_AUTO_ENABLED does not match what
      72             :         // APM does in any mode, as that is defined as "system finds its own goal
      73             :         // positions", which APM does not currently do
      74       36585 :         break;
      75        1881 :     case Mode::Number::INITIALISING:
      76        1881 :         break;
      77             :     }
      78             : 
      79       60877 :     if (!plane.training_manual_pitch || !plane.training_manual_roll) {
      80       60329 :         _base_mode |= MAV_MODE_FLAG_STABILIZE_ENABLED;        
      81             :     }
      82             : 
      83       60877 :     if (plane.control_mode != &plane.mode_manual && plane.control_mode != &plane.mode_initializing) {
      84             :         // stabiliser of some form is enabled
      85       48361 :         _base_mode |= MAV_MODE_FLAG_STABILIZE_ENABLED;
      86             :     }
      87             : 
      88       60877 :     if (plane.g.stick_mixing != StickMixing::NONE && plane.control_mode != &plane.mode_initializing) {
      89       58782 :         if ((plane.g.stick_mixing != StickMixing::VTOL_YAW) || (plane.control_mode == &plane.mode_auto)) {
      90             :             // all modes except INITIALISING have some form of manual
      91             :             // override if stick mixing is enabled
      92       58782 :             _base_mode |= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED;
      93             :         }
      94             :     }
      95             : 
      96             :     // we are armed if we are not initialising
      97       60877 :     if (plane.control_mode != &plane.mode_initializing && plane.arming.is_armed()) {
      98       42165 :         _base_mode |= MAV_MODE_FLAG_SAFETY_ARMED;
      99             :     }
     100             : 
     101             :     // indicate we have set a custom mode
     102       60877 :     _base_mode |= MAV_MODE_FLAG_CUSTOM_MODE_ENABLED;
     103             : 
     104       60877 :     return _base_mode;
     105             : }
     106             : 
     107       61020 : uint32_t GCS_Plane::custom_mode() const
     108             : {
     109       61020 :     return plane.control_mode->mode_number();
     110             : }
     111             : 
     112       60148 : MAV_STATE GCS_MAVLINK_Plane::vehicle_system_status() const
     113             : {
     114       60148 :     if (plane.control_mode == &plane.mode_initializing) {
     115        1881 :         return MAV_STATE_CALIBRATING;
     116             :     }
     117       58267 :     if (plane.any_failsafe_triggered()) {
     118         197 :         return MAV_STATE_CRITICAL;
     119             :     }
     120       58070 :     if (plane.crash_state.is_crashed) {
     121           0 :         return MAV_STATE_EMERGENCY;
     122             :     }
     123       58070 :     if (plane.is_flying()) {
     124       39816 :         return MAV_STATE_ACTIVE;
     125             :     }
     126             : 
     127       18254 :     return MAV_STATE_STANDBY;
     128             : }
     129             : 
     130             : 
     131      552408 : void GCS_MAVLINK_Plane::send_attitude() const
     132             : {
     133      552408 :     const AP_AHRS &ahrs = AP::ahrs();
     134             : 
     135      552408 :     float r = ahrs.get_roll_rad();
     136      552408 :     float p = ahrs.get_pitch_rad();
     137      552408 :     float y = ahrs.get_yaw_rad();
     138             : 
     139      552408 :     if (!(plane.flight_option_enabled(FlightOptions::GCS_REMOVE_TRIM_PITCH))) {
     140      552408 :         p -= radians(plane.g.pitch_trim);
     141             :     }
     142             : 
     143             : #if HAL_QUADPLANE_ENABLED
     144      552408 :     if (plane.quadplane.show_vtol_view()) {
     145       91693 :         r = plane.quadplane.ahrs_view->roll;
     146       91693 :         p = plane.quadplane.ahrs_view->pitch;
     147       91693 :         y = plane.quadplane.ahrs_view->yaw;
     148             :     }
     149             : #endif
     150             : 
     151      552408 :     const Vector3f &omega = ahrs.get_gyro();
     152     1104816 :     mavlink_msg_attitude_send(
     153      552408 :         chan,
     154             :         millis(),
     155             :         r,
     156             :         p,
     157             :         y,
     158      552408 :         omega.x,
     159      552408 :         omega.y,
     160      552408 :         omega.z);
     161      552408 : }
     162             : 
     163           0 : void GCS_MAVLINK_Plane::send_attitude_target() 
     164             : {
     165             : #if HAL_QUADPLANE_ENABLED
     166             :     // Check if the attitude target is valid for reporting
     167           0 :     const uint32_t now = AP_HAL::millis();
     168           0 :     if (now  - plane.quadplane.last_att_control_ms > 100) {
     169           0 :         return;
     170             :     }
     171             : 
     172           0 :     const Quaternion quat  = plane.quadplane.attitude_control->get_attitude_target_quat();
     173           0 :     const Vector3f& ang_vel = plane.quadplane.attitude_control->get_attitude_target_ang_vel();
     174           0 :     const float throttle = plane.quadplane.attitude_control->get_throttle_in();
     175             : 
     176           0 :     const float quat_out[4] {quat.q1, quat.q2, quat.q3, quat.q4};
     177             : 
     178           0 :     const uint16_t typemask = 0; 
     179             : 
     180           0 :     mavlink_msg_attitude_target_send(
     181             :         chan,
     182             :         now,                    // time since boot (ms)
     183             :         typemask,               // Bitmask that tells the system what control dimensions should be ignored by the vehicle
     184             :         quat_out,               // Target attitude quaternion [w, x, y, z] order, zero-rotation is [1, 0, 0, 0], unit-length
     185           0 :         ang_vel.x,              // bodyframe target roll rate (rad/s)
     186           0 :         ang_vel.y,              // bodyframe target pitch rate (rad/s)
     187           0 :         ang_vel.z,              // bodyframe yaw rate (rad/s)
     188             :         throttle);              // Collective thrust, normalized to 0 .. 1
     189             : 
     190             : #endif // HAL_QUADPLANE_ENABLED 
     191             : }
     192             : 
     193      552407 : void GCS_MAVLINK_Plane::send_aoa_ssa()
     194             : {
     195      552407 :     AP_AHRS &ahrs = AP::ahrs();
     196             : 
     197     1104814 :     mavlink_msg_aoa_ssa_send(
     198             :         chan,
     199      552407 :         micros(),
     200             :         ahrs.getAOA(),
     201             :         ahrs.getSSA());
     202      552407 : }
     203             : 
     204      552407 : void GCS_MAVLINK_Plane::send_nav_controller_output() const
     205             : {
     206      552407 :     if (plane.control_mode == &plane.mode_manual) {
     207       86132 :         return;
     208             :     }
     209             : #if HAL_QUADPLANE_ENABLED
     210      466275 :     const QuadPlane &quadplane = plane.quadplane;
     211      466275 :     if (quadplane.show_vtol_view() && quadplane.using_wp_nav()) {
     212       24800 :         const Vector3f &targets = quadplane.attitude_control->get_att_target_euler_cd();
     213             : 
     214       24800 :         const Vector2f& curr_pos_m = quadplane.inertial_nav.get_position_xy_cm() * 0.01;
     215       24800 :         const Vector2f& target_pos_m = quadplane.pos_control->get_pos_target_NED_m().xy().tofloat();
     216       24800 :         const Vector2f error_m = (target_pos_m - curr_pos_m);
     217             : 
     218       24800 :         mavlink_msg_nav_controller_output_send(
     219       24800 :             chan,
     220       24800 :             targets.x * 0.01,
     221       24800 :             targets.y * 0.01,
     222       24800 :             targets.z * 0.01,
     223       24800 :             degrees(error_m.angle()),
     224       24800 :             MIN(error_m.length(), UINT16_MAX),
     225       24800 :             (plane.control_mode != &plane.mode_qstabilize) ? -quadplane.pos_control->get_pos_error_D_m() : 0,
     226       24800 :             plane.airspeed_error * 100,  // incorrect units; see PR#7933
     227       24800 :             quadplane.wp_nav->crosstrack_error_m());
     228       24800 :         return;
     229             :     }
     230             : #endif
     231             :     {
     232      441475 :         const AP_Navigation *nav_controller = plane.nav_controller;
     233     2648850 :         mavlink_msg_nav_controller_output_send(
     234      441475 :             chan,
     235      441475 :             plane.nav_roll_cd * 0.01,
     236      441475 :             plane.nav_pitch_cd * 0.01,
     237      441475 :             nav_controller->nav_bearing_cd() * 0.01,
     238      441475 :             nav_controller->target_bearing_cd() * 0.01,
     239      882950 :             MIN(plane.auto_state.wp_distance, UINT16_MAX),
     240      441475 :             plane.calc_altitude_error_cm() * 0.01,
     241      441475 :             plane.airspeed_error * 100,  // incorrect units; see PR#7933
     242      441475 :             nav_controller->crosstrack_error_m());
     243             :     }
     244             : }
     245             : 
     246      552411 : bool GCS_MAVLINK_Plane::get_target_location(Location &loc) const
     247             : {
     248      552411 :     if (plane.control_mode == &plane.mode_manual) {
     249       86133 :         return false;
     250             :     }
     251      466278 :     loc = plane.next_WP_loc;
     252      466278 :     return true;
     253             : }
     254             : 
     255             : 
     256      553136 : float GCS_MAVLINK_Plane::vfr_hud_airspeed() const
     257             : {
     258             :     // airspeed sensors are best.  While the AHRS airspeed_estimate
     259             :     // will use an airspeed sensor, that value is constrained by the
     260             :     // ground speed.  When reporting we should send the true airspeed
     261             :     // value if possible:
     262             : #if AP_AIRSPEED_ENABLED
     263      553136 :     if (plane.airspeed.enabled() && plane.airspeed.healthy()) {
     264      549150 :         return plane.airspeed.get_airspeed();
     265             :     }
     266             : #endif
     267             : 
     268             :     // airspeed estimates are OK:
     269             :     float aspeed;
     270        3986 :     if (AP::ahrs().airspeed_EAS(aspeed)) {
     271         177 :         return aspeed;
     272             :     }
     273             : 
     274             :     // lying is worst:
     275        3809 :     return 0;
     276             : }
     277             : 
     278      553156 : int16_t GCS_MAVLINK_Plane::vfr_hud_throttle() const
     279             : {
     280      553156 :     return plane.throttle_percentage();
     281             : }
     282             : 
     283      552407 : float GCS_MAVLINK_Plane::vfr_hud_climbrate() const
     284             : {
     285             : #if HAL_SOARING_ENABLED
     286      552407 :     if (plane.g2.soaring_controller.is_active()) {
     287       13750 :         return plane.g2.soaring_controller.get_vario_reading();
     288             :     }
     289             : #endif
     290      538657 :     return GCS_MAVLINK::vfr_hud_climbrate();
     291             : }
     292             : 
     293      552407 : void GCS_MAVLINK_Plane::send_wind() const
     294             : {
     295      552407 :     Vector3f wind;
     296             :     // send the estimate even if it is not marked valid, to preserve
     297             :     // existing behaviour
     298      552407 :     IGNORE_RETURN(AP::ahrs().get_wind(wind));
     299      552407 :     mavlink_msg_wind_send(
     300      552407 :         chan,
     301      552407 :         degrees(atan2f(-wind.y, -wind.x)), // use negative, to give
     302             :                                           // direction wind is coming from
     303      552407 :         wind.xy().length(),               // ground-plane (horizontal) speed only
     304             :         wind.z);
     305      552407 : }
     306             : 
     307             : // sends a single pid info over the provided channel
     308          17 : void GCS_MAVLINK_Plane::send_pid_info(const AP_PIDInfo *pid_info,
     309             :                           const uint8_t axis, const float achieved)
     310             : {
     311          17 :     if (pid_info == nullptr) {
     312           0 :         return;
     313             :     }
     314          17 :     if (!HAVE_PAYLOAD_SPACE(chan, PID_TUNING)) {
     315           0 :         return;
     316             :     }
     317          17 :      mavlink_msg_pid_tuning_send(chan, axis,
     318          17 :                                  pid_info->target,
     319             :                                  achieved,
     320          17 :                                  pid_info->FF,
     321          17 :                                  pid_info->P,
     322          17 :                                  pid_info->I,
     323          17 :                                  pid_info->D,
     324          17 :                                  pid_info->slew_rate,
     325          17 :                                  pid_info->Dmod);
     326             : }
     327             : 
     328             : /*
     329             :   send PID tuning message
     330             :  */
     331      552407 : void GCS_MAVLINK_Plane::send_pid_tuning()
     332             : {
     333      552407 :     if (plane.control_mode == &plane.mode_manual) {
     334             :         // no PIDs should be used in manual
     335       86132 :         return;
     336             :     }
     337             : 
     338      466275 :     const Parameters &g = plane.g;
     339             : 
     340             :     const AP_PIDInfo *pid_info;
     341      466275 :     if (g.gcs_pid_mask & TUNING_BITS_ROLL) {
     342          17 :         pid_info = &plane.rollController.get_pid_info();
     343             : #if HAL_QUADPLANE_ENABLED
     344          17 :         if (plane.quadplane.in_vtol_mode()) {
     345           0 :             pid_info = &plane.quadplane.attitude_control->get_rate_roll_pid().get_pid_info();
     346             :         }
     347             : #endif
     348          17 :         send_pid_info(pid_info, PID_TUNING_ROLL, pid_info->actual);
     349             :     }
     350      466275 :     if (g.gcs_pid_mask & TUNING_BITS_PITCH) {
     351           0 :         pid_info = &plane.pitchController.get_pid_info();
     352             : #if HAL_QUADPLANE_ENABLED
     353           0 :         if (plane.quadplane.in_vtol_mode()) {
     354           0 :             pid_info = &plane.quadplane.attitude_control->get_rate_pitch_pid().get_pid_info();
     355             :         }
     356             : #endif
     357           0 :         send_pid_info(pid_info, PID_TUNING_PITCH, pid_info->actual);
     358             :     }
     359      466275 :     if (g.gcs_pid_mask & TUNING_BITS_YAW) {
     360           0 :         pid_info = &plane.yawController.get_pid_info();
     361             : #if HAL_QUADPLANE_ENABLED
     362           0 :         if (plane.quadplane.in_vtol_mode()) {
     363           0 :             pid_info = &plane.quadplane.attitude_control->get_rate_yaw_pid().get_pid_info();
     364             :         }
     365             : #endif
     366           0 :         send_pid_info(pid_info, PID_TUNING_YAW, pid_info->actual);
     367             :     }
     368      466275 :     if (g.gcs_pid_mask & TUNING_BITS_STEER) {
     369           0 :         pid_info = &plane.steerController.get_pid_info();
     370           0 :         send_pid_info(pid_info, PID_TUNING_STEER, pid_info->actual);
     371             :     }
     372      466275 :     if ((g.gcs_pid_mask & TUNING_BITS_LAND) && (plane.flight_stage == AP_FixedWing::FlightStage::LAND)) {
     373           0 :         AP_AHRS &ahrs = AP::ahrs();
     374           0 :         const Vector3f &gyro = ahrs.get_gyro();
     375           0 :         send_pid_info(plane.landing.get_pid_info(), PID_TUNING_LANDING, degrees(gyro.z));
     376             :     }
     377             : #if HAL_QUADPLANE_ENABLED
     378      466275 :     if (g.gcs_pid_mask & TUNING_BITS_ACCZ && plane.quadplane.in_vtol_mode()) {
     379           0 :         pid_info = &plane.quadplane.pos_control->D_get_accel_pid().get_pid_info();
     380           0 :         send_pid_info(pid_info, PID_TUNING_ACCZ, pid_info->actual);
     381             :     }
     382             : #endif
     383             : }
     384             : 
     385             : // try to send a message, return false if it won't fit in the serial tx buffer
     386    46343812 : bool GCS_MAVLINK_Plane::try_send_message(enum ap_message id)
     387             : {
     388    46343812 :     switch (id) {
     389             : 
     390      552407 :     case MSG_WIND:
     391      552407 :         CHECK_PAYLOAD_SIZE(WIND);
     392      552407 :         send_wind();
     393      552407 :         break;
     394             : 
     395      553322 :     case MSG_ADSB_VEHICLE:
     396             : #if HAL_ADSB_ENABLED
     397      553322 :         CHECK_PAYLOAD_SIZE(ADSB_VEHICLE);
     398      553322 :         plane.adsb.send_adsb_vehicle(chan);
     399             : #endif
     400      553322 :         break;
     401             : 
     402      552407 :     case MSG_AOA_SSA:
     403      552407 :         CHECK_PAYLOAD_SIZE(AOA_SSA);
     404      552407 :         send_aoa_ssa();
     405      552407 :         break;
     406             : 
     407             : #if HAL_LANDING_DEEPSTALL_ENABLED
     408      552407 :     case MSG_LANDING:
     409      552407 :         plane.landing.send_landing_message(chan);
     410      552407 :         break;
     411             : #endif  // HAL_LANDING_DEEPSTALL_ENABLED
     412             : 
     413      552407 :     case MSG_HYGROMETER:
     414             : #if AP_AIRSPEED_HYGROMETER_ENABLE
     415      552407 :         CHECK_PAYLOAD_SIZE(HYGROMETER_SENSOR);
     416      552407 :         send_hygrometer();
     417             : #endif
     418      552407 :         break;
     419             : 
     420    43580862 :     default:
     421    43580862 :         return GCS_MAVLINK::try_send_message(id);
     422             :     }
     423     2762950 :     return true;
     424             : }
     425             : 
     426             : #if AP_AIRSPEED_HYGROMETER_ENABLE
     427      552407 : void GCS_MAVLINK_Plane::send_hygrometer()
     428             : {
     429      552407 :     if (!HAVE_PAYLOAD_SPACE(chan, HYGROMETER_SENSOR)) {
     430           0 :         return;
     431             :     }
     432             : 
     433      552407 :     const auto *airspeed = AP::airspeed();
     434      552407 :     if (airspeed == nullptr) {
     435           0 :         return;
     436             :     } 
     437      552407 :     const uint32_t now = AP_HAL::millis();
     438             : 
     439     1657221 :     for (uint8_t i=0; i<AIRSPEED_MAX_SENSORS; i++) {
     440     1104814 :         uint8_t idx = (i+last_hygrometer_send_idx+1) % AIRSPEED_MAX_SENSORS;
     441             :         float temperature, humidity;
     442             :         uint32_t last_sample_ms;
     443     1104814 :         if (!airspeed->get_hygrometer(idx, last_sample_ms, temperature, humidity)) {
     444     1104814 :             continue;
     445             :         }
     446           0 :         if (now - last_sample_ms > 2000) {
     447             :             // not updating, stop sending
     448           0 :             continue;
     449             :         }
     450           0 :         if (!HAVE_PAYLOAD_SPACE(chan, HYGROMETER_SENSOR)) {
     451           0 :             return;
     452             :         }
     453             : 
     454           0 :         mavlink_msg_hygrometer_sensor_send(
     455             :             chan,
     456             :             idx,
     457           0 :             int16_t(temperature*100),
     458           0 :             uint16_t(humidity*100));
     459           0 :         last_hygrometer_send_idx = idx;
     460             :     }
     461             : }
     462             : #endif // AP_AIRSPEED_HYGROMETER_ENABLE
     463             : 
     464             : /*
     465             :   handle a request to switch to guided mode. This happens via a
     466             :   callback from handle_mission_item()
     467             :  */
     468           2 : bool GCS_MAVLINK_Plane::handle_guided_request(AP_Mission::Mission_Command &cmd)
     469             : {
     470           2 :     return plane.control_mode->handle_guided_request(cmd.content.location);
     471             : }
     472             : 
     473             : /*
     474             :   handle a request to change current WP altitude. This happens via a
     475             :   callback from handle_mission_item()
     476             :  */
     477           7 : void GCS_MAVLINK_Plane::handle_change_alt_request(Location &location)
     478             : {
     479           7 :     plane.fix_terrain_WP(location, __AP_LINE__);
     480             : 
     481           7 :     if (location.terrain_alt) {
     482           1 :         plane.next_WP_loc.copy_alt_from(location);
     483             :     } else {
     484             :         // convert to absolute alt
     485             :         float abs_alt_m;
     486           6 :         if (location.get_alt_m(Location::AltFrame::ABSOLUTE, abs_alt_m)) {
     487           6 :             plane.next_WP_loc.set_alt_m(abs_alt_m, Location::AltFrame::ABSOLUTE);
     488             :         }
     489             :     }
     490           7 :     plane.reset_offset_altitude();
     491           7 : }
     492             : 
     493             : 
     494             : /*
     495             :   handle a LANDING_TARGET command. The timestamp has been jitter corrected
     496             : */
     497           0 : void GCS_MAVLINK_Plane::handle_landing_target(const mavlink_landing_target_t &packet, uint32_t timestamp_ms)
     498             : {
     499             : #if AC_PRECLAND_ENABLED
     500           0 :     plane.g2.precland.handle_msg(packet, timestamp_ms);
     501             : #endif
     502           0 : }
     503             : 
     504          41 : MAV_RESULT GCS_MAVLINK_Plane::handle_command_preflight_calibration(const mavlink_command_int_t &packet, const mavlink_message_t &msg)
     505             : {
     506          41 :     plane.in_calibration = true;
     507          41 :     MAV_RESULT ret = GCS_MAVLINK::handle_command_preflight_calibration(packet, msg);
     508          41 :     plane.in_calibration = false;
     509             : 
     510          41 :     return ret;
     511             : }
     512             : 
     513     1145832 : void GCS_MAVLINK_Plane::packetReceived(const mavlink_status_t &status,
     514             :                                        const mavlink_message_t &msg)
     515             : {
     516             : #if AP_ADSB_AVOIDANCE_ENABLED
     517     1145832 :     plane.avoidance_adsb.handle_msg(msg);
     518             : #endif
     519     1145832 :     GCS_MAVLINK::packetReceived(status, msg);
     520     1145832 : }
     521             : 
     522             : 
     523           4 : bool Plane::set_home_to_current_location(bool _lock)
     524             : {
     525           4 :     if (!set_home_persistently(AP::gps().location())) {
     526           0 :         return false;
     527             :     }
     528           4 :     if (_lock) {
     529           4 :         AP::ahrs().lock_home();
     530             :     }
     531           4 :     if ((control_mode == &mode_rtl)
     532             : #if HAL_QUADPLANE_ENABLED
     533           4 :             || (control_mode == &mode_qrtl)
     534             : #endif
     535             :                                                         ) {
     536             :         // if in RTL head to the updated home location
     537           0 :         control_mode->enter();
     538             :     }
     539           4 :     return true;
     540             : }
     541          47 : bool Plane::set_home(const Location& loc, bool _lock)
     542             : {
     543          47 :     if (!AP::ahrs().set_home(loc)) {
     544           0 :         return false;
     545             :     }
     546          47 :     if (_lock) {
     547          47 :         AP::ahrs().lock_home();
     548             :     }
     549          47 :     if ((control_mode == &mode_rtl)
     550             : #if HAL_QUADPLANE_ENABLED
     551          47 :             || (control_mode == &mode_qrtl)
     552             : #endif
     553             :                                                         ) {
     554             :         // if in RTL head to the updated home location
     555           0 :         control_mode->enter();
     556             :     }
     557          47 :     return true;
     558             : }
     559             : 
     560          59 : MAV_RESULT GCS_MAVLINK_Plane::handle_command_int_do_reposition(const mavlink_command_int_t &packet)
     561             : {
     562          59 :     Location requested_position;
     563          59 :     if (!location_from_command_t(packet, requested_position)) {
     564           0 :         return MAV_RESULT_DENIED;
     565             :     }
     566          59 :     plane.fix_terrain_WP(requested_position, __AP_LINE__);
     567             : 
     568          59 :     if (isnan(packet.param4) || is_zero(packet.param4)) {
     569          54 :         requested_position.loiter_ccw = 0;
     570             :     } else {
     571           5 :         requested_position.loiter_ccw = 1;
     572             :     }
     573             : 
     574          59 :     if (requested_position.sanitize(plane.current_loc)) {
     575             :         // if the location wasn't already sane don't load it
     576           0 :         return MAV_RESULT_DENIED;
     577             :     }
     578             : 
     579             : #if AP_FENCE_ENABLED
     580             :     // reject destination if outside the fence
     581          59 :     if (!plane.fence.check_location_within_fence(requested_position)) {
     582           1 :         LOGGER_WRITE_ERROR(LogErrorSubsystem::NAVIGATION, LogErrorCode::DEST_OUTSIDE_FENCE);
     583           1 :         return MAV_RESULT_FAILED;
     584             :     }
     585             : #endif
     586             : 
     587             :     // location is valid load and set
     588          58 :     if (((int32_t)packet.param2 & MAV_DO_REPOSITION_FLAGS_CHANGE_MODE) ||
     589          49 :         (plane.control_mode == &plane.mode_guided)) {
     590          58 :         plane.set_mode(plane.mode_guided, ModeReason::GCS_COMMAND);
     591             : #if AP_PLANE_OFFBOARD_GUIDED_SLEW_ENABLED
     592          58 :         plane.guided_state.target_heading_type = GUIDED_HEADING_NONE;
     593             : #endif
     594             : 
     595             :         // convert to absolute alt
     596          58 :         if (!requested_position.terrain_alt) {
     597          32 :             requested_position.change_alt_frame(Location::AltFrame::ABSOLUTE);
     598             :         }
     599             : 
     600          58 :         plane.set_guided_WP(requested_position);
     601             : 
     602             :         // Loiter radius for planes. Positive radius in meters, direction is controlled by Yaw (param4) value, parsed above
     603          58 :         if (!isnan(packet.param3) && packet.param3 > 0) {
     604           6 :             plane.mode_guided.set_radius_and_direction(packet.param3, requested_position.loiter_ccw);
     605             :         }
     606             : 
     607          58 :         return MAV_RESULT_ACCEPTED;
     608             :     }
     609           0 :     return MAV_RESULT_FAILED;
     610             : }
     611             : 
     612           5 : MAV_RESULT GCS_MAVLINK_Plane::handle_command_int_DO_CHANGE_ALTITUDE(const mavlink_command_int_t &packet)
     613             : {
     614           5 :     const float alt = packet.param1;
     615           5 :     MAV_FRAME mav_frame = (MAV_FRAME)packet.param2;
     616             :     Location::AltFrame alt_frame;
     617           5 :     if (!mavlink_coordinate_frame_to_location_alt_frame(mav_frame, alt_frame)) {
     618           0 :         return MAV_RESULT_DENIED;
     619             :     }
     620             :     Location loc {
     621             :         0,
     622             :         0,
     623           5 :         int32_t(alt * 100),  // m -> cm
     624             :         alt_frame,
     625           5 :     };
     626           5 :     handle_change_alt_request(loc);
     627           5 :     return MAV_RESULT_ACCEPTED;
     628             : }
     629             : 
     630             : #if AP_PLANE_OFFBOARD_GUIDED_SLEW_ENABLED
     631             : // these are GUIDED mode commands that are RATE or slew enabled, so you can have more powerful control than default controls.
     632          15 : MAV_RESULT GCS_MAVLINK_Plane::handle_command_int_guided_slew_commands(const mavlink_command_int_t &packet)
     633             : {
     634          15 :   switch(packet.command) {
     635           4 :     case MAV_CMD_GUIDED_CHANGE_SPEED: {
     636             :         // command is only valid in guided mode
     637           4 :         if (plane.control_mode != &plane.mode_guided) {
     638           0 :             return MAV_RESULT_FAILED;
     639             :         }
     640             : 
     641             :         // only airspeed commands are supported right now...
     642           4 :         if (int(packet.param1) != SPEED_TYPE_AIRSPEED) {  // since SPEED_TYPE is int in range 0-1 and packet.param1 is a *float* this works.
     643           0 :             return MAV_RESULT_DENIED;
     644             :         }
     645             : 
     646           4 :         if (!plane.mode_guided.handle_change_airspeed(packet.param2, packet.param3)) {
     647           0 :             return MAV_RESULT_FAILED;
     648             :         }
     649           4 :         return MAV_RESULT_ACCEPTED;
     650             :     }
     651             : 
     652          11 :      case MAV_CMD_GUIDED_CHANGE_ALTITUDE: {
     653             :         // command is only valid in guided
     654          11 :         if (plane.control_mode != &plane.mode_guided) {
     655           0 :             return MAV_RESULT_FAILED;
     656             :         }
     657             : 
     658             :         // disallow default value of -1 and dangerous value of zero
     659          11 :         if (is_equal(packet.z, -1.0f) || is_equal(packet.z, 0.0f)){
     660           0 :             return MAV_RESULT_DENIED;
     661             :         }
     662             : 
     663             :         Location::AltFrame new_target_alt_frame;
     664          11 :         if (!mavlink_coordinate_frame_to_location_alt_frame((MAV_FRAME)packet.frame, new_target_alt_frame)) {
     665           0 :             return MAV_RESULT_DENIED;
     666             :         }
     667             :         // keep a copy of what came in via MAVLink - this is needed for logging, but not for anything else
     668          11 :         plane.guided_state.target_mav_frame = packet.frame;
     669             : 
     670          11 :         const int32_t new_target_alt_cm = packet.z * 100;
     671          11 :         plane.guided_state.target_location.set_alt_cm(new_target_alt_cm, new_target_alt_frame); 
     672          11 :         plane.guided_state.target_alt_time_ms = AP_HAL::millis();
     673             : 
     674             :         // param3 contains the desired vertical velocity (not acceleration)
     675          11 :         if (is_zero(packet.param3)) {
     676             :             // the user wanted /maximum altitude change rate, pick a large value as close enough
     677           5 :             plane.guided_state.target_alt_rate = 1000.0;
     678             :         } else {
     679           6 :             plane.guided_state.target_alt_rate = fabsf(packet.param3);
     680             :         }
     681             : 
     682          11 :         return MAV_RESULT_ACCEPTED;
     683             :     }
     684             : 
     685           0 :      case MAV_CMD_GUIDED_CHANGE_HEADING: {
     686             : 
     687             :         // command is only valid in guided mode
     688           0 :         if (plane.control_mode != &plane.mode_guided) {
     689           0 :             return MAV_RESULT_FAILED;
     690             :         }
     691             : 
     692             :          // don't accept packets outside of [0-360] degree range
     693           0 :         if (packet.param2 < 0.0f || packet.param2 >= 360.0f) {
     694           0 :             return MAV_RESULT_DENIED;
     695             :         }
     696             : 
     697           0 :         float new_target_heading = radians(wrap_180(packet.param2));
     698             : 
     699           0 :         switch(HEADING_TYPE(packet.param1)) {
     700           0 :         case HEADING_TYPE_COURSE_OVER_GROUND:
     701             :             // course over ground
     702           0 :             plane.guided_state.target_heading_type = GUIDED_HEADING_COG;
     703           0 :             plane.prev_WP_loc = plane.current_loc;
     704           0 :             break;
     705           0 :         case HEADING_TYPE_HEADING:
     706             :             // normal vehicle heading
     707           0 :             plane.guided_state.target_heading_type = GUIDED_HEADING_HEADING;
     708           0 :             break;
     709           0 :         case HEADING_TYPE_DEFAULT:
     710           0 :             plane.guided_state.target_heading_type = GUIDED_HEADING_NONE;
     711           0 :             return MAV_RESULT_ACCEPTED;
     712           0 :         default:
     713             :             //  MAV_RESULT_DENIED  means Command is invalid (is supported but has invalid parameters).
     714           0 :             return MAV_RESULT_DENIED;
     715             :         }
     716             : 
     717           0 :         plane.g2.guidedHeading.reset_I();
     718             : 
     719           0 :         plane.guided_state.target_heading = new_target_heading;
     720           0 :         plane.guided_state.target_heading_accel_limit = MAX(packet.param3, 0.05f);
     721           0 :         plane.guided_state.target_heading_time_ms = AP_HAL::millis();
     722           0 :         return MAV_RESULT_ACCEPTED;
     723             :     }
     724             :   }
     725             :   // anything else ...
     726           0 :   return MAV_RESULT_UNSUPPORTED;
     727             : }
     728             : #endif // AP_PLANE_OFFBOARD_GUIDED_SLEW_ENABLED
     729             : 
     730      214727 : MAV_RESULT GCS_MAVLINK_Plane::handle_command_int_packet(const mavlink_command_int_t &packet, const mavlink_message_t &msg)
     731             : {
     732      214727 :     switch(packet.command) {
     733             : 
     734           2 :     case MAV_CMD_DO_AUTOTUNE_ENABLE:
     735           2 :         return handle_MAV_CMD_DO_AUTOTUNE_ENABLE(packet);
     736             : 
     737          59 :     case MAV_CMD_DO_REPOSITION:
     738          59 :         return handle_command_int_do_reposition(packet);
     739             : 
     740           5 :     case MAV_CMD_DO_CHANGE_ALTITUDE:
     741           5 :         return handle_command_int_DO_CHANGE_ALTITUDE(packet);
     742             : 
     743             : #if AP_PLANE_OFFBOARD_GUIDED_SLEW_ENABLED
     744             :     // special 'slew-enabled' guided commands here... for speed,alt, and direction commands
     745          15 :     case MAV_CMD_GUIDED_CHANGE_SPEED:
     746             :     case MAV_CMD_GUIDED_CHANGE_ALTITUDE:
     747             :     case MAV_CMD_GUIDED_CHANGE_HEADING:
     748          15 :         return handle_command_int_guided_slew_commands(packet);
     749             : #endif
     750             : 
     751             : #if AP_ICENGINE_ENABLED
     752          15 :     case MAV_CMD_DO_ENGINE_CONTROL:
     753          15 :         if (!plane.g2.ice_control.engine_control(packet.param1, packet.param2, packet.param3, (uint32_t)packet.param4)) {
     754           4 :             return MAV_RESULT_FAILED;
     755             :         }
     756          11 :         return MAV_RESULT_ACCEPTED;
     757             : #endif
     758             : 
     759          28 :     case MAV_CMD_DO_CHANGE_SPEED:
     760          28 :         return handle_command_DO_CHANGE_SPEED(packet);
     761             : 
     762             : #if HAL_PARACHUTE_ENABLED
     763           2 :     case MAV_CMD_DO_PARACHUTE:
     764           2 :         return handle_MAV_CMD_DO_PARACHUTE(packet);
     765             : #endif
     766             : 
     767             : #if HAL_QUADPLANE_ENABLED
     768           4 :     case MAV_CMD_DO_MOTOR_TEST:
     769           4 :         return handle_MAV_CMD_DO_MOTOR_TEST(packet);
     770             : 
     771           4 :     case MAV_CMD_DO_VTOL_TRANSITION:
     772           4 :         return handle_command_DO_VTOL_TRANSITION(packet);
     773             : 
     774          12 :     case MAV_CMD_NAV_TAKEOFF:
     775          12 :         return handle_command_MAV_CMD_NAV_TAKEOFF(packet);
     776             : #endif
     777             : 
     778           2 :     case MAV_CMD_DO_GO_AROUND:
     779           2 :         return plane.trigger_land_abort(packet.param1) ? MAV_RESULT_ACCEPTED : MAV_RESULT_FAILED;
     780             : 
     781           0 :     case MAV_CMD_DO_RETURN_PATH_START:
     782             :         // attempt to rejoin after the next DO_RETURN_PATH_START command in the mission
     783           0 :         if (plane.have_position && plane.mission.jump_to_closest_mission_leg(plane.current_loc)) {
     784           0 :             plane.mission.set_force_resume(true);
     785           0 :             if (plane.set_mode(plane.mode_auto, ModeReason::GCS_COMMAND)) {
     786           0 :                 return MAV_RESULT_ACCEPTED;
     787             :             }
     788             :             // mode change failed, revert force resume flag
     789           0 :             plane.mission.set_force_resume(false);
     790             :         }
     791           0 :         return MAV_RESULT_FAILED;
     792             : 
     793           2 :     case MAV_CMD_DO_LAND_START:
     794             :         // attempt to switch to next DO_LAND_START command in the mission
     795           2 :         if (plane.have_position && plane.mission.jump_to_landing_sequence(plane.current_loc)) {
     796           2 :             plane.mission.set_force_resume(true);
     797           2 :             if (plane.set_mode(plane.mode_auto, ModeReason::GCS_COMMAND)) {
     798           2 :                 return MAV_RESULT_ACCEPTED;
     799             :             }
     800             :             // mode change failed, revert force resume flag
     801           0 :             plane.mission.set_force_resume(false);
     802             :         }
     803           0 :         return MAV_RESULT_FAILED;
     804             : 
     805           7 :     case MAV_CMD_MISSION_START:
     806           7 :         if (!is_zero(packet.param1) || !is_zero(packet.param2)) {
     807             :             // first-item/last item not supported
     808           0 :             return MAV_RESULT_DENIED;
     809             :         }
     810           7 :         plane.set_mode(plane.mode_auto, ModeReason::GCS_COMMAND);
     811           7 :         return MAV_RESULT_ACCEPTED;
     812             : 
     813           2 :     case MAV_CMD_NAV_LOITER_UNLIM:
     814           2 :         plane.set_mode(plane.mode_loiter, ModeReason::GCS_COMMAND);
     815           2 :         return MAV_RESULT_ACCEPTED;
     816             : 
     817           2 :     case MAV_CMD_NAV_RETURN_TO_LAUNCH:
     818           2 :         plane.set_mode(plane.mode_rtl, ModeReason::GCS_COMMAND);
     819           2 :         return MAV_RESULT_ACCEPTED;
     820             : 
     821             : #if AP_MAVLINK_MAV_CMD_SET_HAGL_ENABLED
     822           0 :     case MAV_CMD_SET_HAGL:
     823           0 :         plane.handle_external_hagl(packet);
     824           0 :         return MAV_RESULT_ACCEPTED;
     825             : #endif
     826             :         
     827      214566 :     default:
     828      214566 :         return GCS_MAVLINK::handle_command_int_packet(packet, msg);
     829             :     }
     830             : }
     831             : 
     832          28 : MAV_RESULT GCS_MAVLINK_Plane::handle_command_DO_CHANGE_SPEED(const mavlink_command_int_t &packet)
     833             : {
     834             :         // if we're in failsafe modes (e.g., RTL, LOITER) or in pilot
     835             :         // controlled modes (e.g., MANUAL, TRAINING)
     836             :         // this command should be ignored since it comes in from GCS
     837             :         // or a companion computer:
     838          29 :         if ((!plane.control_mode->is_guided_mode()) &&
     839           1 :             (plane.control_mode != &plane.mode_auto)) {
     840             :             // failed
     841           0 :             return MAV_RESULT_FAILED;
     842             :         }
     843             : 
     844          28 :         if (plane.do_change_speed((SPEED_TYPE)packet.param1, packet.param2, packet.param3)) {
     845          28 :             return MAV_RESULT_ACCEPTED;
     846             :         }
     847           0 :         return MAV_RESULT_FAILED;
     848             : }
     849             : 
     850             : #if HAL_QUADPLANE_ENABLED
     851             : #if AP_MAVLINK_COMMAND_LONG_ENABLED
     852          24 : void GCS_MAVLINK_Plane::convert_MAV_CMD_NAV_TAKEOFF_to_COMMAND_INT(const mavlink_command_long_t &in, mavlink_command_int_t &out)
     853             : {
     854             :     // convert to MAV_FRAME_LOCAL_OFFSET_NED, "NED local tangent frame
     855             :     // with origin that travels with the vehicle"
     856          24 :     out = {};
     857          24 :     out.target_system = in.target_system;
     858          24 :     out.target_component = in.target_component;
     859          24 :     out.frame = MAV_FRAME_LOCAL_OFFSET_NED;
     860          24 :     out.command = in.command;
     861             :     // out.current = 0;
     862             :     // out.autocontinue = 0;
     863             :     // out.param1 = in.param1;  // we only use the "z" parameter in this command:
     864             :     // out.param2 = in.param2;
     865             :     // out.param3 = in.param3;
     866             :     // out.param4 = in.param4;
     867             :     // out.x = 0;  // we don't handle positioning when doing takeoffs
     868             :     // out.y = 0;
     869          24 :     out.z = -in.param7;  // up -> down
     870          24 : }
     871             : 
     872      428567 : void GCS_MAVLINK_Plane::convert_COMMAND_LONG_to_COMMAND_INT(const mavlink_command_long_t &in, mavlink_command_int_t &out, MAV_FRAME frame)
     873             : {
     874      428567 :     switch (in.command) {
     875          24 :     case MAV_CMD_NAV_TAKEOFF:
     876          24 :         convert_MAV_CMD_NAV_TAKEOFF_to_COMMAND_INT(in, out);
     877          24 :         return;
     878             :     }
     879      428543 :     return GCS_MAVLINK::convert_COMMAND_LONG_to_COMMAND_INT(in, out, frame);
     880             : }
     881             : #endif  // AP_MAVLINK_COMMAND_LONG_ENABLED
     882             : 
     883          12 : MAV_RESULT GCS_MAVLINK_Plane::handle_command_MAV_CMD_NAV_TAKEOFF(const mavlink_command_int_t &packet)
     884             : {
     885          12 :     float takeoff_alt = packet.z;
     886          12 :     switch (packet.frame) {
     887          12 :     case MAV_FRAME_LOCAL_OFFSET_NED:  // "NED local tangent frame with origin that travels with the vehicle"
     888          12 :         takeoff_alt = -takeoff_alt;  // down -> up
     889          12 :         break;
     890           0 :     default:
     891           0 :         return MAV_RESULT_DENIED; // "is supported but has invalid parameters"
     892             :     }
     893          12 :     if (!plane.quadplane.available()) {
     894           0 :         return MAV_RESULT_FAILED;
     895             :     }
     896          12 :     if (!plane.quadplane.do_user_takeoff(takeoff_alt)) {
     897           0 :         return MAV_RESULT_FAILED;
     898             :     }
     899          12 :     return MAV_RESULT_ACCEPTED;
     900             : }
     901             : #endif
     902             : 
     903           2 : MAV_RESULT GCS_MAVLINK_Plane::handle_MAV_CMD_DO_AUTOTUNE_ENABLE(const mavlink_command_int_t &packet)
     904             : {
     905             :         // param1 : enable/disable
     906           2 :         plane.autotune_enable(!is_zero(packet.param1));
     907           2 :         return MAV_RESULT_ACCEPTED;
     908             : }
     909             : 
     910             : #if HAL_PARACHUTE_ENABLED
     911           2 : MAV_RESULT GCS_MAVLINK_Plane::handle_MAV_CMD_DO_PARACHUTE(const mavlink_command_int_t &packet)
     912             : {
     913             :         // configure or release parachute
     914           2 :         switch ((uint16_t)packet.param1) {
     915           0 :         case PARACHUTE_DISABLE:
     916           0 :             plane.parachute.enabled(false);
     917           0 :             return MAV_RESULT_ACCEPTED;
     918           0 :         case PARACHUTE_ENABLE:
     919           0 :             plane.parachute.enabled(true);
     920           0 :             return MAV_RESULT_ACCEPTED;
     921           2 :         case PARACHUTE_RELEASE:
     922             :             // treat as a manual release which performs some additional check of altitude
     923           2 :             if (plane.parachute.released()) {
     924           0 :                 gcs().send_text(MAV_SEVERITY_NOTICE, "Parachute already released");
     925           0 :                 return MAV_RESULT_FAILED;
     926             :             }
     927           2 :             if (!plane.parachute.enabled()) {
     928           0 :                 gcs().send_text(MAV_SEVERITY_NOTICE, "Parachute not enabled");
     929           0 :                 return MAV_RESULT_FAILED;
     930             :             }
     931           2 :             if (!plane.parachute_manual_release()) {
     932           0 :                 return MAV_RESULT_FAILED;
     933             :             }
     934           2 :             return MAV_RESULT_ACCEPTED;
     935           0 :         default:
     936           0 :             break;
     937             :         }
     938           0 :         return MAV_RESULT_FAILED;
     939             : }
     940             : #endif
     941             : 
     942             : 
     943             : #if HAL_QUADPLANE_ENABLED
     944           4 : MAV_RESULT GCS_MAVLINK_Plane::handle_MAV_CMD_DO_MOTOR_TEST(const mavlink_command_int_t &packet)
     945             : {
     946             :         // param1 : motor sequence number (a number from 1 to max number of motors on the vehicle)
     947             :         // param2 : throttle type (0=throttle percentage, 1=PWM, 2=pilot throttle channel pass-through. See MOTOR_TEST_THROTTLE_TYPE enum)
     948             :         // param3 : throttle (range depends upon param2)
     949             :         // param4 : timeout (in seconds)
     950             :         // param5 : motor count (number of motors to test in sequence)
     951           4 :         return plane.quadplane.mavlink_motor_test_start(chan,
     952           4 :                                                         (uint8_t)packet.param1,
     953           4 :                                                         (uint8_t)packet.param2,
     954           4 :                                                         (uint16_t)packet.param3,
     955           4 :                                                         packet.param4,
     956           4 :                                                         (uint8_t)packet.x);
     957             : }
     958             : 
     959           4 : MAV_RESULT GCS_MAVLINK_Plane::handle_command_DO_VTOL_TRANSITION(const mavlink_command_int_t &packet)
     960             : {
     961           4 :         if (!plane.quadplane.handle_do_vtol_transition((enum MAV_VTOL_STATE)packet.param1)) {
     962           0 :             return MAV_RESULT_FAILED;
     963             :         }
     964           4 :         return MAV_RESULT_ACCEPTED;
     965             : }
     966             : #endif
     967             : 
     968             : // this is called on receipt of a MANUAL_CONTROL packet and is
     969             : // expected to call manual_override to override RC input on desired
     970             : // axes.
     971           8 : void GCS_MAVLINK_Plane::handle_manual_control_axes(const mavlink_manual_control_t &packet, const uint32_t tnow)
     972             : {
     973           8 :     manual_override(plane.channel_roll, packet.y, 1000, 2000, tnow);
     974           8 :     manual_override(plane.channel_pitch, packet.x, 1000, 2000, tnow, true);
     975           8 :     manual_override(plane.channel_throttle, packet.z, 0, 1000, tnow);
     976           8 :     manual_override(plane.channel_rudder, packet.r, 1000, 2000, tnow);
     977           8 : }
     978             : 
     979     1145828 : void GCS_MAVLINK_Plane::handle_message(const mavlink_message_t &msg)
     980             : {
     981     1145828 :     switch (msg.msgid) {
     982             : 
     983        1041 :     case MAVLINK_MSG_ID_SET_ATTITUDE_TARGET:
     984        1041 :         handle_set_attitude_target(msg);
     985        1041 :         break;
     986             : 
     987           0 :     case MAVLINK_MSG_ID_SET_POSITION_TARGET_LOCAL_NED:
     988           0 :         handle_set_position_target_local_ned(msg);
     989           0 :         break;
     990             : 
     991           2 :     case MAVLINK_MSG_ID_SET_POSITION_TARGET_GLOBAL_INT:
     992           2 :         handle_set_position_target_global_int(msg);
     993           2 :         break;
     994             : 
     995     1144785 :     default:
     996     1144785 :         GCS_MAVLINK::handle_message(msg);
     997     1144286 :         break;
     998             :     } // end switch
     999     1145329 : } // end handle mavlink
    1000             : 
    1001        1041 : void GCS_MAVLINK_Plane::handle_set_attitude_target(const mavlink_message_t &msg)
    1002             :     {
    1003             :         // Only allow companion computer (or other external controller) to
    1004             :         // control attitude in GUIDED mode.  We DON'T want external control
    1005             :         // in e.g., RTL, CICLE. Specifying a single mode for companion
    1006             :         // computer control is more safe (even more so when using
    1007             :         // FENCE_ACTION = 4 for geofence failures).
    1008        1041 :         if (plane.control_mode != &plane.mode_guided) { // don't screw up failsafes
    1009           0 :             return;
    1010             :         }
    1011             : 
    1012             :         mavlink_set_attitude_target_t att_target;
    1013        1041 :         mavlink_msg_set_attitude_target_decode(&msg, &att_target);
    1014             : 
    1015             :         // Mappings: If any of these bits are set, the corresponding input should be ignored.
    1016             :         // NOTE, when parsing the bits we invert them for easier interpretation but transport has them inverted
    1017             :         // bit 1: body roll rate
    1018             :         // bit 2: body pitch rate
    1019             :         // bit 3: body yaw rate
    1020             :         // bit 4: unknown
    1021             :         // bit 5: unknown
    1022             :         // bit 6: reserved
    1023             :         // bit 7: throttle
    1024             :         // bit 8: attitude
    1025             : 
    1026             :         // if not setting all Quaternion values, use _rate flags to indicate which fields.
    1027             : 
    1028             :         // Extract the Euler roll angle from the Quaternion.
    1029             :         Quaternion q(att_target.q[0], att_target.q[1],
    1030        1041 :                 att_target.q[2], att_target.q[3]);
    1031             : 
    1032             :         // NOTE: att_target.type_mask is inverted for easier interpretation
    1033        1041 :         att_target.type_mask = att_target.type_mask ^ 0xFF;
    1034             : 
    1035        1041 :         uint8_t attitude_mask = att_target.type_mask & 0b10000111; // q plus rpy
    1036             : 
    1037        1041 :         uint32_t now = AP_HAL::millis();
    1038        1041 :         if ((attitude_mask & 0b10000001) ||    // partial, including roll
    1039             :                 (attitude_mask == 0b10000000)) { // all angles
    1040        1041 :             plane.guided_state.forced_rpy_cd.x = degrees(q.get_euler_roll()) * 100.0f;
    1041             : 
    1042             :             // Update timer for external roll to the nav control
    1043        1041 :             plane.guided_state.last_forced_rpy_ms.x = now;
    1044             :         }
    1045             : 
    1046        1041 :         if ((attitude_mask & 0b10000010) ||    // partial, including pitch
    1047             :                 (attitude_mask == 0b10000000)) { // all angles
    1048        1041 :             plane.guided_state.forced_rpy_cd.y = degrees(q.get_euler_pitch()) * 100.0f;
    1049             : 
    1050             :             // Update timer for external pitch to the nav control
    1051        1041 :             plane.guided_state.last_forced_rpy_ms.y = now;
    1052             :         }
    1053             : 
    1054        1041 :         if ((attitude_mask & 0b10000100) ||    // partial, including yaw
    1055             :                 (attitude_mask == 0b10000000)) { // all angles
    1056        1041 :             plane.guided_state.forced_rpy_cd.z = degrees(q.get_euler_yaw()) * 100.0f;
    1057             : 
    1058             :             // Update timer for external yaw to the nav control
    1059        1041 :             plane.guided_state.last_forced_rpy_ms.z = now;
    1060             :         }
    1061        1041 :         if (att_target.type_mask & 0b01000000) { // throttle
    1062         257 :             plane.guided_state.forced_throttle = att_target.thrust * 100.0f;
    1063             : 
    1064             :             // Update timer for external throttle
    1065         257 :             plane.guided_state.last_forced_throttle_ms = now;
    1066             :         }
    1067             :     }
    1068             : 
    1069           0 : void GCS_MAVLINK_Plane::handle_set_position_target_local_ned(const mavlink_message_t &msg)
    1070             :     {
    1071             :         // decode packet
    1072             :         mavlink_set_position_target_local_ned_t packet;
    1073           0 :         mavlink_msg_set_position_target_local_ned_decode(&msg, &packet);
    1074             : 
    1075             :         // exit if vehicle is not in Guided mode
    1076           0 :         if (plane.control_mode != &plane.mode_guided) {
    1077           0 :             return;
    1078             :         }
    1079             : 
    1080             :         // only local moves for now
    1081           0 :         if (packet.coordinate_frame != MAV_FRAME_LOCAL_OFFSET_NED) {
    1082           0 :             return;
    1083             :         }
    1084             : 
    1085             :         // just do altitude for now
    1086           0 :         plane.next_WP_loc.alt += -packet.z*100.0;
    1087           0 :         gcs().send_text(MAV_SEVERITY_INFO, "Change alt to %.1f",
    1088           0 :                         (double)((plane.next_WP_loc.alt - plane.home.alt)*0.01));
    1089             :     }
    1090             : 
    1091           2 : void GCS_MAVLINK_Plane::handle_set_position_target_global_int(const mavlink_message_t &msg)
    1092             :     {
    1093             :         // Only want to allow companion computer position control when
    1094             :         // in a certain mode to avoid inadvertently sending these
    1095             :         // kinds of commands when the autopilot is responding to problems
    1096             :         // in modes such as RTL, CIRCLE, etc.  Specifying ONLY one mode
    1097             :         // for companion computer control is more safe (provided
    1098             :         // one uses the FENCE_ACTION = 4 (RTL) for geofence failures).
    1099           2 :         if (plane.control_mode != &plane.mode_guided) {
    1100             :             //don't screw up failsafes
    1101           0 :             return;
    1102             :         }
    1103             : 
    1104             :         mavlink_set_position_target_global_int_t pos_target;
    1105           2 :         mavlink_msg_set_position_target_global_int_decode(&msg, &pos_target);
    1106             : 
    1107             :         Location::AltFrame frame;
    1108           2 :         if (!mavlink_coordinate_frame_to_location_alt_frame((MAV_FRAME)pos_target.coordinate_frame, frame)) {
    1109           0 :             gcs().send_text(MAV_SEVERITY_WARNING, "Invalid coord frame in SET_POSITION_TARGET_GLOBAL_INT");
    1110             :             // Even though other parts of the command may be valid, reject the whole thing.
    1111           0 :             return;
    1112             :         }
    1113             : 
    1114             :         // Unexpectedly, the mask is expecting "ones" for dimensions that should
    1115             :         // be IGNORED rather than INCLUDED.  See mavlink documentation of the
    1116             :         // SET_POSITION_TARGET_GLOBAL_INT message, type_mask field.
    1117           2 :         const bool alt_ignore = (pos_target.type_mask & POSITION_TARGET_TYPEMASK_Z_IGNORE);
    1118           2 :         if (!alt_ignore) {
    1119             :             Location loc {
    1120             :                 0,  // lat
    1121             :                 0,  // lng
    1122           1 :                 int32_t(pos_target.alt * 100),  // m -> cm
    1123             :                 frame,
    1124           1 :             };
    1125           1 :             handle_change_alt_request(loc);
    1126             :         }
    1127             :     }
    1128             : 
    1129           0 : MAV_RESULT GCS_MAVLINK_Plane::handle_command_do_set_mission_current(const mavlink_command_int_t &packet)
    1130             : {
    1131           0 :     const MAV_RESULT result = GCS_MAVLINK::handle_command_do_set_mission_current(packet);
    1132           0 :     if (result != MAV_RESULT_ACCEPTED) {
    1133           0 :         return result;
    1134             :     }
    1135             : 
    1136             :     // if you change this you must change handle_mission_set_current
    1137           0 :     plane.auto_state.next_wp_crosstrack = false;
    1138           0 :     if (plane.control_mode == &plane.mode_auto && plane.mission.state() == AP_Mission::MISSION_STOPPED) {
    1139           0 :         plane.mission.resume();
    1140             :     }
    1141             : 
    1142           0 :     return result;
    1143             : }
    1144             : 
    1145             : #if AP_MAVLINK_MISSION_SET_CURRENT_ENABLED
    1146         939 : void GCS_MAVLINK_Plane::handle_mission_set_current(AP_Mission &mission, const mavlink_message_t &msg)
    1147             : {
    1148             :     // if you change this you must change handle_command_do_set_mission_current
    1149         939 :     plane.auto_state.next_wp_crosstrack = false;
    1150         939 :     GCS_MAVLINK::handle_mission_set_current(mission, msg);
    1151         939 :     if (plane.control_mode == &plane.mode_auto && plane.mission.state() == AP_Mission::MISSION_STOPPED) {
    1152           0 :         plane.mission.resume();
    1153             :     }
    1154         939 : }
    1155             : #endif
    1156             : 
    1157           9 : uint64_t GCS_MAVLINK_Plane::capabilities() const
    1158             : {
    1159             :     return (MAV_PROTOCOL_CAPABILITY_MISSION_FLOAT |
    1160             :             MAV_PROTOCOL_CAPABILITY_COMMAND_INT |
    1161             :             MAV_PROTOCOL_CAPABILITY_MISSION_INT |
    1162             :             MAV_PROTOCOL_CAPABILITY_SET_POSITION_TARGET_GLOBAL_INT |
    1163           9 :             MAV_PROTOCOL_CAPABILITY_SET_ATTITUDE_TARGET |
    1164             : #if AP_TERRAIN_AVAILABLE
    1165           9 :             (plane.terrain.enabled() ? MAV_PROTOCOL_CAPABILITY_TERRAIN : 0) |
    1166             : #endif
    1167           9 :             GCS_MAVLINK::capabilities());
    1168             : }
    1169             : 
    1170             : #if HAL_HIGH_LATENCY2_ENABLED
    1171         729 : int16_t GCS_MAVLINK_Plane::high_latency_target_altitude() const
    1172             : {
    1173         729 :     AP_AHRS &ahrs = AP::ahrs();
    1174         729 :     Location global_position_current;
    1175         729 :     UNUSED_RESULT(ahrs.get_location(global_position_current));
    1176             : 
    1177             : #if HAL_QUADPLANE_ENABLED
    1178         729 :     const QuadPlane &quadplane = plane.quadplane;
    1179             :     //return units are m
    1180         729 :     if (quadplane.show_vtol_view()) {
    1181           0 :         return (plane.control_mode != &plane.mode_qstabilize) ? (global_position_current.alt * 0.01 - quadplane.pos_control->get_pos_error_D_m()) : 0;
    1182             :     }
    1183             : #endif
    1184         729 :     return 0.01 * (global_position_current.alt + plane.calc_altitude_error_cm());
    1185             : }
    1186             : 
    1187         729 : uint8_t GCS_MAVLINK_Plane::high_latency_tgt_heading() const
    1188             : {
    1189             :     // return units are deg/2
    1190             : #if HAL_QUADPLANE_ENABLED
    1191         729 :     const QuadPlane &quadplane = plane.quadplane;
    1192         729 :     if (quadplane.show_vtol_view()) {
    1193           0 :         const Vector3f &targets = quadplane.attitude_control->get_att_target_euler_cd();
    1194           0 :         return ((uint16_t)(targets.z * 0.01)) / 2;
    1195             :     }
    1196             : #endif
    1197         729 :         const AP_Navigation *nav_controller = plane.nav_controller;
    1198             :         // need to convert -18000->18000 to 0->360/2
    1199         729 :         return wrap_360_cd(nav_controller->target_bearing_cd() ) / 200;
    1200             : }
    1201             : 
    1202         729 : uint16_t GCS_MAVLINK_Plane::high_latency_tgt_dist_dam() const
    1203             : {
    1204             : #if HAL_QUADPLANE_ENABLED
    1205         729 :     const QuadPlane &quadplane = plane.quadplane;
    1206         729 :     if (quadplane.show_vtol_view()) {
    1207           0 :         bool wp_nav_valid = quadplane.using_wp_nav();
    1208           0 :         return (wp_nav_valid ? MIN(static_cast<uint16_t>(quadplane.wp_nav->get_wp_distance_to_destination_cm() * 1e-3), UINT16_MAX) : 0);
    1209             :     }
    1210             :     #endif
    1211             : 
    1212         729 :     return MIN(static_cast<uint16_t>(plane.auto_state.wp_distance * 0.1), UINT16_MAX);
    1213             : }
    1214             : 
    1215         729 : uint8_t GCS_MAVLINK_Plane::high_latency_tgt_airspeed() const
    1216             : {
    1217             :     // return units are m/s*5
    1218         729 :     return plane.target_airspeed_cm * 0.05;
    1219             : }
    1220             : 
    1221         729 : uint8_t GCS_MAVLINK_Plane::high_latency_wind_speed() const
    1222             : {
    1223         729 :     Vector3f wind;
    1224             :     // use the estimate even if it is not marked valid, to preserve
    1225             :     // existing behaviour
    1226         729 :     IGNORE_RETURN(AP::ahrs().get_wind(wind));
    1227             : 
    1228             :     // return units are m/s*5
    1229         729 :     return MIN(wind.xy().length() * 5, UINT8_MAX);
    1230             : }
    1231             : 
    1232         729 : uint8_t GCS_MAVLINK_Plane::high_latency_wind_direction() const
    1233             : {
    1234         729 :     Vector3f wind;
    1235             :     // use the estimate even if it is not marked valid, to preserve
    1236             :     // existing behaviour
    1237         729 :     IGNORE_RETURN(AP::ahrs().get_wind(wind));
    1238             : 
    1239             :     // return units are deg/2
    1240             :     // need to convert -180->180 to 0->360/2
    1241         729 :     return wrap_360(degrees(atan2f(-wind.y, -wind.x))) / 2;
    1242             : }
    1243             : #endif // HAL_HIGH_LATENCY2_ENABLED
    1244             : 
    1245        3428 : MAV_VTOL_STATE GCS_MAVLINK_Plane::vtol_state() const
    1246             : {
    1247             : #if !HAL_QUADPLANE_ENABLED
    1248             :     return MAV_VTOL_STATE_UNDEFINED;
    1249             : #else
    1250        3428 :     if (!plane.quadplane.available()) {
    1251           0 :         return MAV_VTOL_STATE_UNDEFINED;
    1252             :     }
    1253             : 
    1254        3428 :     return plane.quadplane.transition->get_mav_vtol_state();
    1255             : #endif
    1256             : };
    1257             : 
    1258        3432 : MAV_LANDED_STATE GCS_MAVLINK_Plane::landed_state() const
    1259             : {
    1260        3432 :     if (plane.is_flying()) {
    1261        2885 :         if (plane.is_taking_off()) {
    1262         413 :             return MAV_LANDED_STATE_TAKEOFF;
    1263             :         }
    1264        2472 :         if (plane.is_landing()) {
    1265         528 :             return MAV_LANDED_STATE_LANDING;
    1266             :         }
    1267             : 
    1268             :         // note that Q-modes almost always consider themselves as flying
    1269        1944 :         return MAV_LANDED_STATE_IN_AIR;
    1270             :     }
    1271             : 
    1272         547 :     return MAV_LANDED_STATE_ON_GROUND;
    1273             : }
    1274             : 
    1275             : // Send the mode with the given index (not mode number!) return the total number of modes
    1276             : // Index starts at 1
    1277           0 : uint8_t GCS_MAVLINK_Plane::send_available_mode(uint8_t index) const
    1278             : {
    1279             :     // Fixed wing modes
    1280           0 :     const Mode* fw_modes[] {
    1281             :         &plane.mode_manual,
    1282             :         &plane.mode_circle,
    1283             :         &plane.mode_stabilize,
    1284             :         &plane.mode_training,
    1285             :         &plane.mode_acro,
    1286             :         &plane.mode_fbwa,
    1287             :         &plane.mode_fbwb,
    1288             :         &plane.mode_cruise,
    1289             :         &plane.mode_autotune,
    1290             :         &plane.mode_auto,
    1291             :         &plane.mode_rtl,
    1292             :         &plane.mode_loiter,
    1293             : #if HAL_ADSB_ENABLED
    1294             :         &plane.mode_avoidADSB,
    1295             : #endif
    1296             :         &plane.mode_guided,
    1297             :         &plane.mode_initializing,
    1298             :         &plane.mode_takeoff,
    1299             : #if HAL_SOARING_ENABLED
    1300             :         &plane.mode_thermal,
    1301             : #endif
    1302             : #if MODE_AUTOLAND_ENABLED
    1303             :         &plane.mode_autoland,
    1304             : #endif
    1305             :     };
    1306             : 
    1307           0 :     const uint8_t fw_mode_count = ARRAY_SIZE(fw_modes);
    1308             : 
    1309             :     // Fixedwing modes are always present
    1310           0 :     uint8_t mode_count = fw_mode_count;
    1311             : 
    1312             : #if HAL_QUADPLANE_ENABLED
    1313             :     // Quadplane modes
    1314           0 :     const Mode* q_modes[] {
    1315             :         &plane.mode_qstabilize,
    1316             :         &plane.mode_qhover,
    1317             :         &plane.mode_qloiter,
    1318             :         &plane.mode_qland,
    1319             :         &plane.mode_qrtl,
    1320             :         &plane.mode_qacro,
    1321             :         &plane.mode_loiter_qland,
    1322             : #if QAUTOTUNE_ENABLED
    1323             :         &plane.mode_qautotune,
    1324             : #endif
    1325             :     };
    1326             : 
    1327             :     // Quadplane modes must be enabled
    1328           0 :     if (plane.quadplane.available()) {
    1329           0 :         mode_count += ARRAY_SIZE(q_modes);
    1330             :     }
    1331             : #endif // HAL_QUADPLANE_ENABLED
    1332             : 
    1333             : 
    1334             :     // Convert to zero indexed
    1335           0 :     const uint8_t index_zero = index - 1;
    1336           0 :     if (index_zero >= mode_count) {
    1337             :         // Mode does not exist!?
    1338           0 :         return mode_count;
    1339             :     }
    1340             : 
    1341             :     // Ask the mode for its name and number
    1342             :     const char* name;
    1343             :     Mode::Number mode_number;
    1344             : 
    1345           0 :     if (index_zero < fw_mode_count) {
    1346             :         // A fixedwing mode
    1347           0 :         name = fw_modes[index_zero]->name();
    1348           0 :         mode_number = fw_modes[index_zero]->mode_number();
    1349             : 
    1350             :     } else {
    1351             : #if HAL_QUADPLANE_ENABLED
    1352             :         // A Quadplane mode
    1353           0 :         const uint8_t q_index = index_zero - fw_mode_count;
    1354           0 :         name = q_modes[q_index]->name();
    1355           0 :         mode_number = q_modes[q_index]->mode_number();
    1356             : #else
    1357             :         // Should not endup here
    1358             :         return mode_count;
    1359             : #endif
    1360             :     }
    1361             : 
    1362             :     // All modes can be selected except Initialising and those disabled by FLTMODE_GCSBLOCK param
    1363           0 :     const bool user_selectable = (mode_number != Mode::Number::INITIALISING) && plane.gcs_mode_enabled(mode_number);
    1364             : 
    1365           0 :     mavlink_msg_available_modes_send(
    1366           0 :         chan,
    1367             :         mode_count,
    1368             :         index,
    1369             :         MAV_STANDARD_MODE::MAV_STANDARD_MODE_NON_STANDARD,
    1370             :         (uint8_t)mode_number,
    1371             :         // MAV_MODE_PROPERTY bitmask,
    1372             :         user_selectable ? 0 : MAV_MODE_PROPERTY_NOT_USER_SELECTABLE,
    1373             :         name
    1374             :     );
    1375             : 
    1376           0 :     return mode_count;
    1377             : }

Generated by: LCOV version 1.14