/* handle an incoming mission item */ void GCS_MAVLINK::handle_mission_item(mavlink_message_t *msg, AP_Mission &mission) { mavlink_mission_item_t packet; uint8_t result = MAV_MISSION_ACCEPTED; struct AP_Mission::Mission_Command cmd = {}; mavlink_msg_mission_item_decode(msg, &packet); if (mavlink_check_target(packet.target_system,packet.target_component)) { return; } // convert mavlink packet to mission command if (!AP_Mission::mavlink_to_mission_cmd(packet, cmd)) { result = MAV_MISSION_INVALID; goto mission_ack; } if (packet.current == 2) { // current = 2 is a flag to tell us this is a "guided mode" // waypoint and not for the mission handle_guided_request(cmd); // verify we received the command result = 0; goto mission_ack; } if (packet.current == 3) { //current = 3 is a flag to tell us this is a alt change only // add home alt if needed handle_change_alt_request(cmd); // verify we recevied the command result = 0; goto mission_ack; } // Check if receiving waypoints (mission upload expected) if (!waypoint_receiving) { result = MAV_MISSION_ERROR; goto mission_ack; } // check if this is the requested waypoint if (packet.seq != waypoint_request_i) { result = MAV_MISSION_INVALID_SEQUENCE; goto mission_ack; } // if command index is within the existing list, replace the command if (packet.seq < mission.num_commands()) { if (mission.replace_cmd(packet.seq,cmd)) { result = MAV_MISSION_ACCEPTED; }else{ result = MAV_MISSION_ERROR; goto mission_ack; } // if command is at the end of command list, add the command } else if (packet.seq == mission.num_commands()) { if (mission.add_cmd(cmd)) { result = MAV_MISSION_ACCEPTED; }else{ result = MAV_MISSION_ERROR; goto mission_ack; } // if beyond the end of the command list, return an error } else { result = MAV_MISSION_ERROR; goto mission_ack; } // update waypoint receiving state machine waypoint_timelast_receive = hal.scheduler->millis(); waypoint_request_i++; if (waypoint_request_i >= waypoint_request_last) { mavlink_msg_mission_ack_send_buf( msg, chan, msg->sysid, msg->compid, MAV_MISSION_ACCEPTED); send_text_P(SEVERITY_LOW,PSTR("flight plan received")); waypoint_receiving = false; // XXX ignores waypoint radius for individual waypoints, can // only set WP_RADIUS parameter } else { waypoint_timelast_request = hal.scheduler->millis(); // if we have enough space, then send the next WP immediately if (comm_get_txspace(chan) - MAVLINK_NUM_NON_PAYLOAD_BYTES >= MAVLINK_MSG_ID_MISSION_ITEM_LEN) { queued_waypoint_send(); } else { send_message(MSG_NEXT_WAYPOINT); } } return; mission_ack: // we are rejecting the mission/waypoint mavlink_msg_mission_ack_send_buf( msg, chan, msg->sysid, msg->compid, result); }
void GCS_MAVLINK_Plane::handleMessage(mavlink_message_t* msg) { switch (msg->msgid) { #if GEOFENCE_ENABLED == ENABLED // receive a fence point from GCS and store in EEPROM case MAVLINK_MSG_ID_FENCE_POINT: { mavlink_fence_point_t packet; mavlink_msg_fence_point_decode(msg, &packet); if (plane.g.fence_action != FENCE_ACTION_NONE) { send_text(MAV_SEVERITY_WARNING,"Fencing must be disabled"); } else if (packet.count != plane.g.fence_total) { send_text(MAV_SEVERITY_WARNING,"Bad fence point"); } else if (!check_latlng(packet.lat,packet.lng)) { send_text(MAV_SEVERITY_WARNING,"Invalid fence point, lat or lng too large"); } else { plane.set_fence_point_with_index(Vector2l(packet.lat*1.0e7f, packet.lng*1.0e7f), packet.idx); } break; } // send a fence point to GCS case MAVLINK_MSG_ID_FENCE_FETCH_POINT: { mavlink_fence_fetch_point_t packet; mavlink_msg_fence_fetch_point_decode(msg, &packet); if (packet.idx >= plane.g.fence_total) { send_text(MAV_SEVERITY_WARNING,"Bad fence point"); } else { Vector2l point = plane.get_fence_point_with_index(packet.idx); mavlink_msg_fence_point_send_buf(msg, chan, msg->sysid, msg->compid, packet.idx, plane.g.fence_total, point.x*1.0e-7f, point.y*1.0e-7f); } break; } #endif // GEOFENCE_ENABLED case MAVLINK_MSG_ID_MANUAL_CONTROL: { if (msg->sysid != plane.g.sysid_my_gcs) { break; // only accept control from our gcs } mavlink_manual_control_t packet; mavlink_msg_manual_control_decode(msg, &packet); if (packet.target != plane.g.sysid_this_mav) { break; // only accept messages aimed at us } uint32_t tnow = AP_HAL::millis(); manual_override(plane.channel_roll, packet.y, 1000, 2000, tnow); manual_override(plane.channel_pitch, packet.x, 1000, 2000, tnow, true); manual_override(plane.channel_throttle, packet.z, 0, 1000, tnow); manual_override(plane.channel_rudder, packet.r, 1000, 2000, tnow); // a manual control message is considered to be a 'heartbeat' from the ground station for failsafe purposes plane.failsafe.last_heartbeat_ms = tnow; break; } case MAVLINK_MSG_ID_HEARTBEAT: { // We keep track of the last time we received a heartbeat from // our GCS for failsafe purposes if (msg->sysid != plane.g.sysid_my_gcs) break; plane.failsafe.last_heartbeat_ms = AP_HAL::millis(); break; } case MAVLINK_MSG_ID_HIL_STATE: { #if HIL_SUPPORT if (plane.g.hil_mode != 1) { break; } mavlink_hil_state_t packet; mavlink_msg_hil_state_decode(msg, &packet); // sanity check location if (!check_latlng(packet.lat, packet.lon)) { break; } last_hil_state = packet; // set gps hil sensor const Location loc{packet.lat, packet.lon, packet.alt/10, Location::AltFrame::ABSOLUTE}; Vector3f vel(packet.vx, packet.vy, packet.vz); vel *= 0.01f; // setup airspeed pressure based on 3D speed, no wind plane.airspeed.setHIL(sq(vel.length()) / 2.0f + 2013); plane.gps.setHIL(0, AP_GPS::GPS_OK_FIX_3D, packet.time_usec/1000, loc, vel, 10, 0); // rad/sec Vector3f gyros; gyros.x = packet.rollspeed; gyros.y = packet.pitchspeed; gyros.z = packet.yawspeed; // m/s/s Vector3f accels; accels.x = packet.xacc * GRAVITY_MSS*0.001f; accels.y = packet.yacc * GRAVITY_MSS*0.001f; accels.z = packet.zacc * GRAVITY_MSS*0.001f; plane.ins.set_gyro(0, gyros); plane.ins.set_accel(0, accels); plane.barometer.setHIL(packet.alt*0.001f); plane.compass.setHIL(0, packet.roll, packet.pitch, packet.yaw); plane.compass.setHIL(1, packet.roll, packet.pitch, packet.yaw); // cope with DCM getting badly off due to HIL lag if (plane.g.hil_err_limit > 0 && (fabsf(packet.roll - plane.ahrs.roll) > ToRad(plane.g.hil_err_limit) || fabsf(packet.pitch - plane.ahrs.pitch) > ToRad(plane.g.hil_err_limit) || wrap_PI(fabsf(packet.yaw - plane.ahrs.yaw)) > ToRad(plane.g.hil_err_limit))) { plane.ahrs.reset_attitude(packet.roll, packet.pitch, packet.yaw); } #endif break; } case MAVLINK_MSG_ID_RADIO: case MAVLINK_MSG_ID_RADIO_STATUS: { handle_radio_status(msg, plane.should_log(MASK_LOG_PM)); break; } case MAVLINK_MSG_ID_DISTANCE_SENSOR: plane.rangefinder.handle_msg(msg); break; case MAVLINK_MSG_ID_TERRAIN_DATA: case MAVLINK_MSG_ID_TERRAIN_CHECK: #if AP_TERRAIN_AVAILABLE plane.terrain.handle_data(chan, msg); #endif break; case MAVLINK_MSG_ID_SET_ATTITUDE_TARGET: { // Only allow companion computer (or other external controller) to // control attitude in GUIDED mode. We DON'T want external control // in e.g., RTL, CICLE. Specifying a single mode for companion // computer control is more safe (even more so when using // FENCE_ACTION = 4 for geofence failures). if ((plane.control_mode != &plane.mode_guided) && (plane.control_mode != &plane.mode_avoidADSB)) { // don't screw up failsafes break; } mavlink_set_attitude_target_t att_target; mavlink_msg_set_attitude_target_decode(msg, &att_target); // Mappings: If any of these bits are set, the corresponding input should be ignored. // NOTE, when parsing the bits we invert them for easier interpretation but transport has them inverted // bit 1: body roll rate // bit 2: body pitch rate // bit 3: body yaw rate // bit 4: unknown // bit 5: unknown // bit 6: reserved // bit 7: throttle // bit 8: attitude // if not setting all Quaternion values, use _rate flags to indicate which fields. // Extract the Euler roll angle from the Quaternion. Quaternion q(att_target.q[0], att_target.q[1], att_target.q[2], att_target.q[3]); // NOTE: att_target.type_mask is inverted for easier interpretation att_target.type_mask = att_target.type_mask ^ 0xFF; uint8_t attitude_mask = att_target.type_mask & 0b10000111; // q plus rpy uint32_t now = AP_HAL::millis(); if ((attitude_mask & 0b10000001) || // partial, including roll (attitude_mask == 0b10000000)) { // all angles plane.guided_state.forced_rpy_cd.x = degrees(q.get_euler_roll()) * 100.0f; // Update timer for external roll to the nav control plane.guided_state.last_forced_rpy_ms.x = now; } if ((attitude_mask & 0b10000010) || // partial, including pitch (attitude_mask == 0b10000000)) { // all angles plane.guided_state.forced_rpy_cd.y = degrees(q.get_euler_pitch()) * 100.0f; // Update timer for external pitch to the nav control plane.guided_state.last_forced_rpy_ms.y = now; } if ((attitude_mask & 0b10000100) || // partial, including yaw (attitude_mask == 0b10000000)) { // all angles plane.guided_state.forced_rpy_cd.z = degrees(q.get_euler_yaw()) * 100.0f; // Update timer for external yaw to the nav control plane.guided_state.last_forced_rpy_ms.z = now; } if (att_target.type_mask & 0b01000000) { // throttle plane.guided_state.forced_throttle = att_target.thrust * 100.0f; // Update timer for external throttle plane.guided_state.last_forced_throttle_ms = now; } break; } case MAVLINK_MSG_ID_SET_HOME_POSITION: { mavlink_set_home_position_t packet; mavlink_msg_set_home_position_decode(msg, &packet); Location new_home_loc {}; new_home_loc.lat = packet.latitude; new_home_loc.lng = packet.longitude; new_home_loc.alt = packet.altitude / 10; if (!set_home(new_home_loc, false)) { // silently fails... break; } break; } case MAVLINK_MSG_ID_SET_POSITION_TARGET_LOCAL_NED: { // decode packet mavlink_set_position_target_local_ned_t packet; mavlink_msg_set_position_target_local_ned_decode(msg, &packet); // exit if vehicle is not in Guided mode if (plane.control_mode != &plane.mode_guided) { break; } // only local moves for now if (packet.coordinate_frame != MAV_FRAME_LOCAL_OFFSET_NED) { break; } // just do altitude for now plane.next_WP_loc.alt += -packet.z*100.0; gcs().send_text(MAV_SEVERITY_INFO, "Change alt to %.1f", (double)((plane.next_WP_loc.alt - plane.home.alt)*0.01)); break; } case MAVLINK_MSG_ID_SET_POSITION_TARGET_GLOBAL_INT: { // Only want to allow companion computer position control when // in a certain mode to avoid inadvertently sending these // kinds of commands when the autopilot is responding to problems // in modes such as RTL, CIRCLE, etc. Specifying ONLY one mode // for companion computer control is more safe (provided // one uses the FENCE_ACTION = 4 (RTL) for geofence failures). if (plane.control_mode != &plane.mode_guided && plane.control_mode != &plane.mode_avoidADSB) { //don't screw up failsafes break; } mavlink_set_position_target_global_int_t pos_target; mavlink_msg_set_position_target_global_int_decode(msg, &pos_target); // Unexpectedly, the mask is expecting "ones" for dimensions that should // be IGNORNED rather than INCLUDED. See mavlink documentation of the // SET_POSITION_TARGET_GLOBAL_INT message, type_mask field. const uint16_t alt_mask = 0b1111111111111011; // (z mask at bit 3) bool msg_valid = true; AP_Mission::Mission_Command cmd = {0}; if (pos_target.type_mask & alt_mask) { cmd.content.location.alt = pos_target.alt * 100; cmd.content.location.relative_alt = false; cmd.content.location.terrain_alt = false; switch (pos_target.coordinate_frame) { case MAV_FRAME_GLOBAL_INT: break; //default to MSL altitude case MAV_FRAME_GLOBAL_RELATIVE_ALT_INT: cmd.content.location.relative_alt = true; break; case MAV_FRAME_GLOBAL_TERRAIN_ALT_INT: cmd.content.location.relative_alt = true; cmd.content.location.terrain_alt = true; break; default: gcs().send_text(MAV_SEVERITY_WARNING, "Invalid coord frame in SET_POSTION_TARGET_GLOBAL_INT"); msg_valid = false; break; } if (msg_valid) { handle_change_alt_request(cmd); } } // end if alt_mask break; } case MAVLINK_MSG_ID_ADSB_VEHICLE: case MAVLINK_MSG_ID_UAVIONIX_ADSB_OUT_CFG: case MAVLINK_MSG_ID_UAVIONIX_ADSB_OUT_DYNAMIC: case MAVLINK_MSG_ID_UAVIONIX_ADSB_TRANSCEIVER_HEALTH_REPORT: plane.adsb.handle_message(chan, msg); break; default: handle_common_message(msg); break; } // end switch } // end handle mavlink