/**************************************************************************** * * Copyright (c) 2023 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name PX4 nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ #include "autopilot_tester_rtl.h" #include "math_helpers.h" #include #include #include #include #include #include void AutopilotTesterRtl::connect(const std::string uri) { AutopilotTester::connect(uri); } void AutopilotTesterRtl::set_rtl_type(int rtl_type) { CHECK(getParams()->set_param_int("RTL_TYPE", rtl_type) == Param::Result::Success); } void AutopilotTesterRtl::set_rtl_appr_force(int rtl_appr_force) { CHECK(getParams()->set_param_int("RTL_APPR_FORCE", rtl_appr_force) == Param::Result::Success); } void AutopilotTesterRtl::set_takeoff_land_requirements(int req) { CHECK(getParams()->set_param_int("MIS_TKO_LAND_REQ", req) == Param::Result::Success); } void AutopilotTesterRtl::upload_custom_mission(std::chrono::seconds timeout) { std::promise prom; auto fut = prom.get_future(); uint8_t mission_type = _custom_mission[0].mission_type; // Register callback to mission item request. add_mavlink_message_callback(MAVLINK_MSG_ID_MISSION_REQUEST_INT, [this, mission_type, &prom](const mavlink_message_t &message) { mavlink_mission_request_int_t request_int_message; mavlink_msg_mission_request_int_decode(&message, &request_int_message); if (request_int_message.mission_type == mission_type) { // send requested element. mavlink_message_t mission_item_int_mavlink_message; mavlink_msg_mission_item_int_encode(getMavlinkPassthrough()->get_our_sysid(), getMavlinkPassthrough()->get_our_compid(), &mission_item_int_mavlink_message, &(_custom_mission[request_int_message.seq])); send_custom_mavlink_message(mission_item_int_mavlink_message); if (request_int_message.seq + 1U == _custom_mission.size()) { add_mavlink_message_callback(MAVLINK_MSG_ID_MISSION_REQUEST_INT, nullptr); prom.set_value(); } } }); // send mission count mavlink_mission_count_t mission_count_message; mission_count_message.count = _custom_mission.size(); mission_count_message.target_system = getMavlinkPassthrough()->get_target_sysid(); mission_count_message.target_component = getMavlinkPassthrough()->get_target_compid(); mission_count_message.mission_type = mission_type; mavlink_message_t mission_count_mavlink_message; mavlink_msg_mission_count_encode(getMavlinkPassthrough()->get_our_sysid(), getMavlinkPassthrough()->get_our_compid(), &mission_count_mavlink_message, &mission_count_message); send_custom_mavlink_message(mission_count_mavlink_message); REQUIRE(fut.wait_for(timeout) == std::future_status::ready); } void AutopilotTesterRtl::add_home_to_rally_point() { add_local_rally_point({0., 0.}); } void AutopilotTesterRtl::add_home_with_approaches_to_rally_point() { add_local_rally_point({0., 0.}); add_approaches_to_point({0., 0.}); } void AutopilotTesterRtl::add_local_rally_point(mavsdk::geometry::CoordinateTransformation::LocalCoordinate local_coordinate) { _rally_points.push_back(local_coordinate); mavsdk::geometry::CoordinateTransformation ct(get_coordinate_transformation()); mavsdk::geometry::CoordinateTransformation::GlobalCoordinate pos(ct.global_from_local(local_coordinate)); // Set rally point mavlink_mission_item_int_t tmp_mission_item; tmp_mission_item.param1 = 0.f; tmp_mission_item.param2 = 0.f; tmp_mission_item.param3 = 0.f; tmp_mission_item.param4 = 0.f; tmp_mission_item.x = static_cast(pos.latitude_deg * 1E7); tmp_mission_item.y = static_cast(pos.longitude_deg * 1E7); tmp_mission_item.z = 0.f; tmp_mission_item.seq = static_cast(_custom_mission.size()); tmp_mission_item.command = MAV_CMD_NAV_RALLY_POINT; tmp_mission_item.target_system = getMavlinkPassthrough()->get_target_sysid(); tmp_mission_item.target_component = getMavlinkPassthrough()->get_target_compid(); tmp_mission_item.frame = MAV_FRAME_GLOBAL_RELATIVE_ALT; tmp_mission_item.current = 0; tmp_mission_item.autocontinue = 0; tmp_mission_item.mission_type = MAV_MISSION_TYPE_RALLY; _custom_mission.push_back(tmp_mission_item); } void AutopilotTesterRtl::add_local_rally_with_approaches_point( mavsdk::geometry::CoordinateTransformation::LocalCoordinate local_coordinate) { add_local_rally_point(local_coordinate); add_approaches_to_point(local_coordinate); } void AutopilotTesterRtl::add_approaches_to_point(mavsdk::geometry::CoordinateTransformation::LocalCoordinate local_coordinate) { mavsdk::geometry::CoordinateTransformation ct(get_coordinate_transformation()); // Set north loiter to alt mavsdk::geometry::CoordinateTransformation::LocalCoordinate tmp_coordinate{local_coordinate}; tmp_coordinate.north_m += 200.; mavsdk::geometry::CoordinateTransformation::GlobalCoordinate pos(ct.global_from_local(tmp_coordinate)); mavlink_mission_item_int_t tmp_mission_item; tmp_mission_item.param1 = 0.f; tmp_mission_item.param2 = 80.f; tmp_mission_item.param3 = 0.f; tmp_mission_item.param4 = 0.f; tmp_mission_item.x = static_cast(pos.latitude_deg * 1E7); tmp_mission_item.y = static_cast(pos.longitude_deg * 1E7); tmp_mission_item.z = 15.f; tmp_mission_item.seq = static_cast(_custom_mission.size()); tmp_mission_item.command = MAV_CMD_NAV_LOITER_TO_ALT; tmp_mission_item.target_system = getMavlinkPassthrough()->get_target_sysid(); tmp_mission_item.target_component = getMavlinkPassthrough()->get_target_compid(); tmp_mission_item.frame = MAV_FRAME_GLOBAL_RELATIVE_ALT; tmp_mission_item.current = 0; tmp_mission_item.autocontinue = 0; tmp_mission_item.mission_type = MAV_MISSION_TYPE_RALLY; _custom_mission.push_back(tmp_mission_item); // Set east loiter to alt tmp_coordinate = local_coordinate; tmp_coordinate.east_m += 200.; pos = ct.global_from_local(tmp_coordinate); tmp_mission_item.x = static_cast(pos.latitude_deg * 1E7); tmp_mission_item.y = static_cast(pos.longitude_deg * 1E7); tmp_mission_item.seq = static_cast(_custom_mission.size()); _custom_mission.push_back(tmp_mission_item); } void AutopilotTesterRtl::check_rally_point_within(float acceptance_radius_m) { auto old_home(getHome()); mavsdk::geometry::CoordinateTransformation ct({old_home.latitude_deg, old_home.longitude_deg}); Telemetry::GroundTruth land_coord{}; mavsdk::geometry::CoordinateTransformation::GlobalCoordinate pos; bool within_rally_point{false}; for (const auto &rally_point : _rally_points) { pos = ct.global_from_local(rally_point); land_coord.latitude_deg = pos.latitude_deg; land_coord.longitude_deg = pos.longitude_deg; within_rally_point |= ground_truth_horizontal_position_close_to(land_coord, acceptance_radius_m); } CHECK(within_rally_point); } void AutopilotTesterRtl::check_rtl_approaches(float acceptance_radius_m, std::chrono::seconds timeout) { auto prom = std::promise {}; auto fut = prom.get_future(); auto ct = get_coordinate_transformation(); auto return_rtl_alt = getParams()->get_param_float("RTL_RETURN_ALT"); auto descend_rtl_alt = getParams()->get_param_float("RTL_DESCEND_ALT"); REQUIRE(return_rtl_alt.first == Param::Result::Success); REQUIRE(descend_rtl_alt.first == Param::Result::Success); getTelemetry()->subscribe_position_velocity_ned([&prom, acceptance_radius_m, return_rtl_alt, descend_rtl_alt, ct, this](Telemetry::PositionVelocityNed position_velocity_ned) { if ((-position_velocity_ned.position.down_m < return_rtl_alt.second - 3.) && (position_velocity_ned.velocity.down_m_s > 0.05)) { // We started to loiter down so we should be on the approach loiter bool on_approach_loiter(false); for (const auto mission_item : _custom_mission) { if (mission_item.command == MAV_CMD_NAV_LOITER_TO_ALT) { mavsdk::geometry::CoordinateTransformation::LocalCoordinate pos(ct.local_from_global({static_cast(mission_item.x) / 1E7, static_cast(mission_item.y) / 1E7})); double rel_distance_to_center = sqrt(sq(position_velocity_ned.position.north_m - pos.north_m) + sq( position_velocity_ned.position.east_m - pos.east_m)); if ((rel_distance_to_center > (mission_item.param2 - acceptance_radius_m)) && (rel_distance_to_center > (mission_item.param2 + acceptance_radius_m))) { on_approach_loiter |= true; if (-position_velocity_ned.position.down_m < descend_rtl_alt.second + 3.) { // We reached the altitude getTelemetry()->subscribe_position_velocity_ned(nullptr); prom.set_value(true); return; } } } } if (!on_approach_loiter) { getTelemetry()->subscribe_position_velocity_ned(nullptr); prom.set_value(false); } } }); REQUIRE(fut.wait_for(timeout) == std::future_status::ready); REQUIRE(fut.get()); }