diff --git a/docs/examples/glide_computer.rst b/docs/examples/glide_computer.rst index a8f31d04..5595bb37 100644 --- a/docs/examples/glide_computer.rst +++ b/docs/examples/glide_computer.rst @@ -13,7 +13,7 @@ This example presents the usage of: - quantities text output formatting, - cooperation with `std::chrono`. -.. literalinclude:: ../../example/references/glide_computer.h +.. literalinclude:: ../../example/glide_computer/include/glide_computer.h :caption: glide_computer.h :start-at: #include :end-before: using namespace units; @@ -35,7 +35,7 @@ For example we have 3 for a quantity of length: - ``height`` - a relative altitude difference between 2 points in the air - ``altitude`` - an absolute altitude value measured form the mean sea level (AMSL). -.. literalinclude:: ../../example/references/glide_computer.h +.. literalinclude:: ../../example/glide_computer/include/glide_computer.h :caption: glide_computer.h :start-at: using namespace units; :end-before: // text output @@ -44,7 +44,7 @@ For example we have 3 for a quantity of length: Next a custom text output is provided both for C++ output streams and the text formatting facility. -.. literalinclude:: ../../example/references/glide_computer.h +.. literalinclude:: ../../example/glide_computer/include/glide_computer.h :caption: glide_computer.h :start-at: // text output :end-before: // definition of glide computer databases and utilities @@ -58,7 +58,7 @@ convert it to the one required by the engine interface. The glide calculator takes task created as a list of waypoints, glider performance data, weather conditions, safety constraints, and a towing height. -.. literalinclude:: ../../example/references/glide_computer.h +.. literalinclude:: ../../example/glide_computer/include/glide_computer.h :caption: glide_computer.h :start-at: // definition of glide computer databases and utilities :linenos: @@ -74,7 +74,7 @@ Having all of that it estimates the number of flight phases (towing, circling, g needed to finish a task. As an output it provides the duration needed to finish the task while flying a selected glider in the specific weather conditions. -.. literalinclude:: ../../example/references/glide_computer.cpp +.. literalinclude:: ../../example/glide_computer/glide_computer.cpp :caption: glide_computer.cpp :start-at: #include :linenos: diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index b2f477b9..2a789851 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -28,6 +28,10 @@ target_link_libraries(hello_units PRIVATE mp-units::core-fmt mp-units::core-io m add_executable(custom_systems custom_systems.cpp) target_link_libraries(custom_systems PRIVATE mp-units::core-io mp-units::si) +if(NOT UNITS_LIBCXX) + add_subdirectory(glide_computer) +endif() + add_subdirectory(alternative_namespaces) add_subdirectory(literals) add_subdirectory(references) diff --git a/example/glide_computer/CMakeLists.txt b/example/glide_computer/CMakeLists.txt new file mode 100644 index 00000000..c47eafab --- /dev/null +++ b/example/glide_computer/CMakeLists.txt @@ -0,0 +1,33 @@ +# The MIT License (MIT) +# +# Copyright (c) 2018 Mateusz Pusz +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.2) + +add_library(glide_computer STATIC + geographic.cpp include/geographic.h + glide_computer.cpp include/glide_computer.h +) +target_link_libraries(glide_computer + PRIVATE mp-units::core-fmt + PUBLIC mp-units::si +) +target_include_directories(glide_computer PUBLIC include) diff --git a/example/literals/geographic.cpp b/example/glide_computer/geographic.cpp similarity index 96% rename from example/literals/geographic.cpp rename to example/glide_computer/geographic.cpp index 2e8b7576..8f39cd8d 100644 --- a/example/literals/geographic.cpp +++ b/example/glide_computer/geographic.cpp @@ -27,8 +27,8 @@ namespace { -using namespace units::isq::si::literals; -inline constexpr auto earth_radius = 6371._q_km; +using namespace units::isq::si; +inline constexpr length earth_radius(6371); } // namespace diff --git a/example/literals/glide_computer.cpp b/example/glide_computer/glide_computer.cpp similarity index 100% rename from example/literals/glide_computer.cpp rename to example/glide_computer/glide_computer.cpp diff --git a/example/references/geographic.h b/example/glide_computer/include/geographic.h similarity index 100% rename from example/references/geographic.h rename to example/glide_computer/include/geographic.h diff --git a/example/literals/glide_computer.h b/example/glide_computer/include/glide_computer.h similarity index 100% rename from example/literals/glide_computer.h rename to example/glide_computer/include/glide_computer.h diff --git a/example/literals/CMakeLists.txt b/example/literals/CMakeLists.txt index 5f364b00..765ec342 100644 --- a/example/literals/CMakeLists.txt +++ b/example/literals/CMakeLists.txt @@ -44,14 +44,8 @@ add_example(total_energy mp-units::core-io mp-units::si mp-units::isq-natural) add_example(unknown_dimension mp-units::core-io mp-units::si) if(NOT UNITS_LIBCXX) - add_executable(glide_computer-literals - geographic.cpp geographic.h - glide_computer.cpp glide_computer.h - glide_computer_example.cpp - ) - target_link_libraries(glide_computer-literals PRIVATE mp-units::core-fmt mp-units::si mp-units::si-international) - target_include_directories(glide_computer-literals PRIVATE ${CMAKE_CURRENT_LIST_DIR}) - target_compile_definitions(glide_computer-literals PRIVATE UNITS_LITERALS) + add_example(glide_computer_example mp-units::core-fmt mp-units::si-international glide_computer) + target_compile_definitions(glide_computer_example-literals PRIVATE UNITS_LITERALS) find_package(linear_algebra CONFIG REQUIRED) add_example(linear_algebra mp-units::core-fmt mp-units::core-io mp-units::si) diff --git a/example/literals/geographic.h b/example/literals/geographic.h deleted file mode 100644 index f5ee6bd1..00000000 --- a/example/literals/geographic.h +++ /dev/null @@ -1,140 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include // IWYU pragma: keep -#include -#include - -UNITS_DIAGNOSTIC_PUSH -UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE -#include -UNITS_DIAGNOSTIC_POP - -#include -#include - -// IWYU pragma: begin_exports -#include -// IWYU pragma: end_exports - -namespace geographic { - -template -struct coordinate { - using value_type = Rep; - constexpr explicit coordinate(value_type v) : value_(v) {} - value_type value() const { return value_; } - auto operator<=>(const coordinate&) const = default; -private: - value_type value_; -}; - -struct latitude : coordinate { - using coordinate::coordinate; -}; - -struct longitude : coordinate { - using coordinate::coordinate; -}; - -template -std::basic_ostream& operator<<(std::basic_ostream& os, const latitude& lat) -{ - if (lat.value() > 0) - return os << "N" << lat.value(); - else - return os << "S" << -lat.value(); -} - -template -std::basic_ostream& operator<<(std::basic_ostream& os, const longitude& lon) -{ - if (lon.value() > 0) - return os << "E" << lon.value(); - else - return os << "W" << -lon.value(); -} - -inline namespace literals { - -constexpr auto operator"" _N(unsigned long long v) { return latitude(static_cast(v)); } -constexpr auto operator"" _N(long double v) { return latitude(static_cast(v)); } -constexpr auto operator"" _S(unsigned long long v) { return latitude(-static_cast(v)); } -constexpr auto operator"" _S(long double v) { return latitude(-static_cast(v)); } -constexpr auto operator"" _E(unsigned long long v) { return longitude(static_cast(v)); } -constexpr auto operator"" _E(long double v) { return longitude(static_cast(v)); } -constexpr auto operator"" _W(unsigned long long v) { return longitude(-static_cast(v)); } -constexpr auto operator"" _W(long double v) { return longitude(-static_cast(v)); } - -} // namespace literals - -} // namespace geographic - -template<> -class std::numeric_limits : public numeric_limits { - static constexpr auto min() noexcept { return geographic::latitude(-90); } - static constexpr auto lowest() noexcept { return geographic::latitude(-90); } - static constexpr auto max() noexcept { return geographic::latitude(90); } -}; - -template<> -class std::numeric_limits : public numeric_limits { - static constexpr auto min() noexcept { return geographic::longitude(-180); } - static constexpr auto lowest() noexcept { return geographic::longitude(-180); } - static constexpr auto max() noexcept { return geographic::longitude(180); } -}; - -template<> -struct fmt::formatter : formatter { - template - auto format(geographic::latitude lat, FormatContext& ctx) - { - format_to(ctx.out(), lat.value() > 0 ? "N" : "S"); - return formatter::format(lat.value() > 0 ? lat.value() : -lat.value(), ctx); - } -}; - -template<> -struct fmt::formatter : formatter { - template - auto format(geographic::longitude lon, FormatContext& ctx) - { - format_to(ctx.out(), lon.value() > 0 ? "E" : "W"); - return formatter::format(lon.value() > 0 ? lon.value() : -lon.value(), ctx); - } -}; - -namespace geographic { - -struct horizontal_kind : units::kind {}; -using distance = units::quantity_kind; - -struct position { - latitude lat; - longitude lon; -}; - -distance spherical_distance(position from, position to); - -} // namespace geographic diff --git a/example/references/CMakeLists.txt b/example/references/CMakeLists.txt index a0117447..47b583fe 100644 --- a/example/references/CMakeLists.txt +++ b/example/references/CMakeLists.txt @@ -44,14 +44,8 @@ add_example(total_energy mp-units::core-io mp-units::si mp-units::isq-natural) add_example(unknown_dimension mp-units::core-io mp-units::si) if(NOT UNITS_LIBCXX) - add_executable(glide_computer-references - geographic.cpp geographic.h - glide_computer.cpp glide_computer.h - glide_computer_example.cpp - ) - target_link_libraries(glide_computer-references PRIVATE mp-units::core-fmt mp-units::si mp-units::si-international) - target_include_directories(glide_computer-references PRIVATE ${CMAKE_CURRENT_LIST_DIR}) - target_compile_definitions(glide_computer-references PRIVATE UNITS_REFERENCES) + add_example(glide_computer_example mp-units::core-fmt mp-units::si-international glide_computer) + target_compile_definitions(glide_computer_example-references PRIVATE UNITS_REFERENCES) find_package(linear_algebra CONFIG REQUIRED) add_example(linear_algebra mp-units::core-fmt mp-units::core-io mp-units::si) diff --git a/example/references/geographic.cpp b/example/references/geographic.cpp deleted file mode 100644 index 316563d9..00000000 --- a/example/references/geographic.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "geographic.h" -#include -#include -#include - -namespace { - -using namespace units::isq::si::references; -inline constexpr auto earth_radius = 6371 * km; - -} // namespace - -namespace geographic { - -distance spherical_distance(position from, position to) -{ - using rep = std::common_type_t; - constexpr auto p = std::numbers::pi_v / 180; - const auto lat1 = from.lat.value() * p; - const auto lon1 = from.lon.value() * p; - const auto lat2 = to.lat.value() * p; - const auto lon2 = to.lon.value() * p; - - using std::sin, std::cos, std::asin, std::sqrt; - - // https://en.wikipedia.org/wiki/Great-circle_distance#Formulae - if constexpr (sizeof(rep) >= 8) { - // spherical law of cosines - const auto central_angle = acos(sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(lon2 - lon1)); - // const auto central_angle = 2 * asin(sqrt(0.5 - cos(lat2 - lat1) / 2 + cos(lat1) * cos(lat2) * (1 - cos(lon2 - lon1)) / 2)); - return distance(earth_radius * central_angle); - } else { - // the haversine formula - const auto sin_lat = sin(lat2 - lat1) / 2; - const auto sin_lon = sin(lon2 - lon1) / 2; - const auto central_angle = 2 * asin(sqrt(sin_lat * sin_lat + cos(lat1) * cos(lat2) * sin_lon * sin_lon)); - return distance(earth_radius * central_angle); - } -} - -} // namespace geographic diff --git a/example/references/glide_computer.cpp b/example/references/glide_computer.cpp deleted file mode 100644 index 6325ca46..00000000 --- a/example/references/glide_computer.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "glide_computer.h" -#include -#include - -namespace glide_computer { - -using namespace units::isq; - -task::legs task::make_legs(const waypoints& wpts) -{ - task::legs res; - res.reserve(wpts.size() - 1); - auto to_leg = [](const waypoint& w1, const waypoint& w2) { return task::leg(w1, w2); }; - std::ranges::transform(wpts.cbegin(), prev(wpts.cend()), next(wpts.cbegin()), wpts.cend(), std::back_inserter(res), to_leg); - return res; -} - -std::vector task::make_leg_total_distances(const legs& legs) -{ - std::vector res; - res.reserve(legs.size()); - auto to_length = [](const leg& l) { return l.get_length(); }; - std::transform_inclusive_scan(legs.cbegin(), legs.cend(), std::back_inserter(res), std::plus(), to_length); - return res; -} - -altitude terrain_level_alt(const task& t, const flight_point& pos) -{ - const task::leg& l = t.get_legs()[pos.leg_idx]; - const height alt_diff = l.end().alt - l.begin().alt; - return l.begin().alt + alt_diff * ((pos.dist - t.get_leg_dist_offset(pos.leg_idx)) / l.get_length()).common(); -} - -// Returns `x` of the intersection of a glide line and a terrain line. -// y = -x / glide_ratio + pos.alt; -// y = (finish_alt - ground_alt) / dist_to_finish * x + ground_alt + min_agl_height; -distance glide_distance(const flight_point& pos, const glider& g, const task& t, const safety& s, altitude ground_alt) -{ - const auto dist_to_finish = t.get_length() - pos.dist; - return distance((ground_alt + s.min_agl_height - pos.alt).common() / - ((ground_alt - t.get_finish().alt) / dist_to_finish - 1 / glide_ratio(g.polar[0]))); -} - -} - -namespace { - -using namespace glide_computer; - -void print(std::string_view phase_name, timestamp start_ts, const glide_computer::flight_point& point, const glide_computer::flight_point& new_point) -{ - fmt::print( - "| {:<12} | {:>9%.1Q %q} (Total: {:>9%.1Q %q}) | {:>8%.1Q %q} (Total: {:>8%.1Q %q}) | {:>7%.0Q %q} ({:>6%.0Q %q}) |\n", - phase_name, quantity_cast(new_point.ts - point.ts), quantity_cast(new_point.ts - start_ts), - new_point.dist - point.dist, new_point.dist, new_point.alt - point.alt, new_point.alt); -} - -flight_point takeoff(timestamp start_ts, const task& t) -{ - return {start_ts, t.get_start().alt}; -} - -flight_point tow(timestamp start_ts, const flight_point& pos, const aircraft_tow& at) -{ - const duration d = (at.height_agl / at.performance).common(); - const flight_point new_pos{pos.ts + d, pos.alt + at.height_agl, pos.leg_idx, pos.dist}; - - print("Tow", start_ts, pos, new_pos); - return new_pos; -} - -flight_point circle(timestamp start_ts, const flight_point& pos, const glider& g, const weather& w, const task& t, height& height_to_gain) -{ - const height h_agl = agl(pos.alt, terrain_level_alt(t, pos)); - const height circling_height = std::min(w.cloud_base - h_agl, height_to_gain); - const rate_of_climb circling_rate = w.thermal_strength + g.polar[0].climb; - const duration d = (circling_height / circling_rate).common(); - const flight_point new_pos{pos.ts + d, pos.alt + circling_height, pos.leg_idx, pos.dist}; - - height_to_gain -= circling_height; - - print("Circle", start_ts, pos, new_pos); - return new_pos; -} - -flight_point glide(timestamp start_ts, const flight_point& pos, const glider& g, const task& t, const safety& s) -{ - const auto ground_alt = terrain_level_alt(t, pos); - const auto dist = glide_distance(pos, g, t, s, ground_alt); - const auto new_distance = pos.dist + dist; - const auto alt = ground_alt + s.min_agl_height; - const auto l3d = length_3d(dist, pos.alt - alt); - const duration d = l3d / g.polar[0].v.common(); - const flight_point new_pos{pos.ts + d, terrain_level_alt(t, pos) + s.min_agl_height, t.get_leg_index(new_distance), new_distance}; - - print("Glide", start_ts, pos, new_pos); - return new_pos; -} - -flight_point final_glide(timestamp start_ts, const flight_point& pos, const glider& g, const task& t) -{ - const auto dist = t.get_length() - pos.dist; - const auto l3d = length_3d(dist, pos.alt - t.get_finish().alt); - const duration d = l3d / g.polar[0].v.common(); - const flight_point new_pos{pos.ts + d, t.get_finish().alt, t.get_legs().size() - 1, pos.dist + dist}; - - print("Final Glide", start_ts, pos, new_pos); - return new_pos; -} - -} // namespace - -namespace glide_computer { - -void estimate(timestamp start_ts, const glider& g, const weather& w, const task& t, const safety& s, const aircraft_tow& at) -{ - fmt::print("| {:<12} | {:^28} | {:^26} | {:^21} |\n", "Flight phase", "Duration", "Distance", "Height"); - fmt::print("|{0:-^14}|{0:-^30}|{0:-^28}|{0:-^23}|\n", ""); - - // ready to takeoff - flight_point pos = takeoff(start_ts, t); - - // estimate aircraft towing - pos = tow(start_ts, pos, at); - - // estimate the altitude needed to reach the finish line from this place - const altitude final_glide_alt = t.get_finish().alt + height(t.get_length().common() / glide_ratio(g.polar[0])); - - // how much height we still need to gain in the thermalls to reach the destination? - height height_to_gain = final_glide_alt - pos.alt; - - do { - // glide to the next thermall - pos = glide(start_ts, pos, g, t, s); - - // circle in a thermall to gain height - pos = circle(start_ts, pos, g, w, t, height_to_gain); - } while (height_to_gain > height{}); - - // final glide - pos = final_glide(start_ts, pos, g, t); -} - -} // namespace glide_computer diff --git a/example/references/glide_computer.h b/example/references/glide_computer.h deleted file mode 100644 index 40341237..00000000 --- a/example/references/glide_computer.h +++ /dev/null @@ -1,218 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -// IWYU pragma: begin_exports -#include "geographic.h" -#include -#include -#include -#include -// IWYU pragma: end_exports - -#include -#include // IWYU pragma: keep -#include -#include -#include -#include -#include -#include -#include // IWYU pragma: keep -#include - -// An example of a really simplified tactical glide computer -// Simplifications: -// - glider 100% clean and with full factory performance (brand new painting) -// - no influence of the ballast (pilot weight, water, etc) to glider performance -// - only one point on a glider polar curve -// - no influence of bank angle (during circling) on a glider performance -// - no wind -// - constant thermals strength -// - thermals exactly where and when we need them ;-) -// - no airspaces -// - ground level changes linearly between waypoints -// - no ground obstacles (i.e. mountains) to pass -// - flight path exactly on a shortest possible line to destination - -template -struct fmt::formatter : formatter { - template - auto format(const QK& v, FormatContext& ctx) - { - return formatter::format(v.common(), ctx); - } -}; - -namespace glide_computer { - -template -constexpr units::Dimensionless auto operator/(const QK1& lhs, const QK2& rhs) - requires(!units::QuantityKindRelatedTo) && requires { lhs.common() / rhs.common();} -{ - return lhs.common() / rhs.common(); -} - -// kinds -using horizontal_kind = geographic::horizontal_kind; -struct vertical_kind : units::kind {}; -struct vertical_point_kind : units::point_kind {}; -struct velocity_kind : units::derived_kind {}; -struct rate_of_climb_kind : units::derived_kind {}; - -// https://en.wikipedia.org/wiki/Flight_planning#Units_of_measurement -// length -using distance = units::quantity_kind; -using height = units::quantity_kind; -using altitude = units::quantity_point_kind; - -// time -using duration = units::isq::si::time; -using timestamp = units::quantity_point; - -// speed -using velocity = units::quantity_kind; -using rate_of_climb = units::quantity_kind; - -// text output -template -std::basic_ostream& operator<<(std::basic_ostream& os, const altitude& a) -{ - return os << a.relative().common() << " AMSL"; -} - -} // namespace glide_computer - -template<> -struct fmt::formatter : formatter> { - template - auto format(glide_computer::altitude a, FormatContext& ctx) - { - formatter>::format(a.relative().common(), ctx); - return format_to(ctx.out(), " AMSL"); - } -}; - -// definition of glide computer databases and utilities -namespace glide_computer { - -struct glider { - struct polar_point { - velocity v; - rate_of_climb climb; - }; - - std::string name; - std::array polar; -}; - -constexpr units::Dimensionless auto glide_ratio(const glider::polar_point& polar) { return polar.v / -polar.climb; } - -struct weather { - height cloud_base; - rate_of_climb thermal_strength; -}; - -struct waypoint { - std::string name; - geographic::position pos; - altitude alt; -}; - -class task { -public: - using waypoints = std::vector; - - class leg { - const waypoint* begin_; - const waypoint* end_; - distance length_ = geographic::spherical_distance(begin().pos, end().pos); - public: - leg(const waypoint& b, const waypoint& e) noexcept: begin_(&b), end_(&e) {} - constexpr const waypoint& begin() const { return *begin_; }; - constexpr const waypoint& end() const { return *end_; } - constexpr const distance get_length() const { return length_; } - }; - using legs = std::vector; - - template - requires std::same_as, waypoint> - explicit task(const R& r) : waypoints_(std::ranges::begin(r), std::ranges::end(r)) {} - - task(std::initializer_list wpts) : waypoints_(wpts) {} - - const waypoints& get_waypoints() const { return waypoints_; } - const legs& get_legs() const { return legs_; } - - const waypoint& get_start() const { return waypoints_.front(); } - const waypoint& get_finish() const { return waypoints_.back(); } - - distance get_length() const { return length_; } - - distance get_leg_dist_offset(std::size_t leg_index) const { return leg_index == 0 ? distance{} : leg_total_distances_[leg_index - 1]; } - std::size_t get_leg_index(distance dist) const - { - return static_cast(std::ranges::distance(leg_total_distances_.cbegin(), std::ranges::lower_bound(leg_total_distances_, dist))); - } - -private: - waypoints waypoints_; - legs legs_ = make_legs(waypoints_); - std::vector leg_total_distances_ = make_leg_total_distances(legs_); - distance length_ = leg_total_distances_.back(); - - static legs make_legs(const task::waypoints& wpts); - static std::vector make_leg_total_distances(const legs& legs); -}; - -struct safety { - height min_agl_height; -}; - -struct aircraft_tow { - height height_agl; - rate_of_climb performance; -}; - -struct flight_point { - timestamp ts; - altitude alt; - std::size_t leg_idx = 0; - distance dist{}; -}; - -altitude terrain_level_alt(const task& t, const flight_point& pos); - -constexpr height agl(altitude glider_alt, altitude terrain_level) { return glider_alt - terrain_level; } - -inline units::isq::si::length length_3d(distance dist, height h) -{ - // TODO support for hypot(q, q) - return sqrt(pow<2>(dist.common()) + pow<2>(h.common())); -} - -distance glide_distance(const flight_point& pos, const glider& g, const task& t, const safety& s, altitude ground_alt); - -void estimate(timestamp start_ts, const glider& g, const weather& w, const task& t, const safety& s, const aircraft_tow& at); - -} // namespace glide_computer