| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // This file is part of INSTINCT, the INS Toolkit for Integrated | ||
| 2 | // Navigation Concepts and Training by the Institute of Navigation of | ||
| 3 | // the University of Stuttgart, Germany. | ||
| 4 | // | ||
| 5 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 7 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 8 | |||
| 9 | /// @file Ellipsoid.hpp | ||
| 10 | /// @brief Functions concerning the ellipsoid model | ||
| 11 | /// @author T. Topp (topp@ins.uni-stuttgart.de) | ||
| 12 | /// @date 2021-11-28 | ||
| 13 | |||
| 14 | #pragma once | ||
| 15 | |||
| 16 | #include <concepts> | ||
| 17 | #include "Navigation/Constants.hpp" | ||
| 18 | |||
| 19 | namespace NAV | ||
| 20 | { | ||
| 21 | |||
| 22 | /// @brief r_eS^e The distance of a point on the Earth's surface from the center of the Earth | ||
| 23 | /// @param[in] latitude 𝜙 Latitude in [rad] | ||
| 24 | /// @param[in] R_E Prime vertical radius of curvature (East/West) in [m] | ||
| 25 | /// @param[in] e_squared Square of the first eccentricity of the ellipsoid | ||
| 26 | /// @return Geocentric Radius in [m] | ||
| 27 | /// @note \cite Groves2013 Groves, ch. 2.4.7, eq. 2.137, p. 71 | ||
| 28 | template<std::floating_point Scalar> | ||
| 29 | 606423 | [[nodiscard]] Scalar calcGeocentricRadius(const Scalar& latitude, const Scalar& R_E, const Scalar& e_squared = InsConst::WGS84::e_squared) | |
| 30 | { | ||
| 31 | 606423 | return R_E * std::sqrt(std::pow(std::cos(latitude), 2) + std::pow((1.0 - e_squared) * std::sin(latitude), 2)); | |
| 32 | } | ||
| 33 | |||
| 34 | /// @brief Calculates the North/South (meridian) earth radius | ||
| 35 | /// @param[in] latitude 𝜙 Latitude in [rad] | ||
| 36 | /// @param[in] a Semi-major axis | ||
| 37 | /// @param[in] e_squared Square of the first eccentricity of the ellipsoid | ||
| 38 | /// @return North/South (meridian) earth radius [m] | ||
| 39 | /// @note See \cite Groves2013 Groves, ch. 2.4.2, eq. 2.105, p. 59 | ||
| 40 | /// @note See \cite Titterton2004 Titterton, ch. 3.7.2, eq. 3.83, p. 49 | ||
| 41 | template<std::floating_point Scalar> | ||
| 42 | 5886869 | [[nodiscard]] Scalar calcEarthRadius_N(const Scalar& latitude, const Scalar& a = InsConst::WGS84::a, const Scalar& e_squared = InsConst::WGS84::e_squared) | |
| 43 | { | ||
| 44 | 5886869 | Scalar k = std::sqrt(1 - e_squared * std::pow(std::sin(latitude), 2)); | |
| 45 | |||
| 46 | // North/South (meridian) earth radius [m] | ||
| 47 | 5899001 | return a * (1 - e_squared) / std::pow(k, 3); | |
| 48 | } | ||
| 49 | |||
| 50 | /// @brief Calculates the East/West (prime vertical) earth radius | ||
| 51 | /// @param[in] latitude 𝜙 Latitude in [rad] | ||
| 52 | /// @param[in] a Semi-major axis | ||
| 53 | /// @param[in] e_squared Square of the first eccentricity of the ellipsoid | ||
| 54 | /// @return East/West (prime vertical) earth radius [m] | ||
| 55 | /// @note See \cite Groves2013 Groves, ch. 2.4.2, eq. 2.106, p. 59 | ||
| 56 | /// @note See \cite Titterton2004 Titterton, ch. 3.7.2, eq. 3.84, p. 49 | ||
| 57 | template<std::floating_point Scalar> | ||
| 58 | 25140556 | [[nodiscard]] Scalar calcEarthRadius_E(const Scalar& latitude, const Scalar& a = InsConst::WGS84::a, const Scalar& e_squared = InsConst::WGS84::e_squared) | |
| 59 | { | ||
| 60 | // East/West (prime vertical) earth radius [m] | ||
| 61 | 25140556 | return a / std::sqrt(1 - e_squared * std::pow(std::sin(latitude), 2)); | |
| 62 | } | ||
| 63 | |||
| 64 | /// @brief Conversion matrix between cartesian and curvilinear perturbations to the position | ||
| 65 | /// @param[in] lla_position Position as Lat Lon Alt in [rad rad m] | ||
| 66 | /// @param[in] R_N Meridian radius of curvature in [m] | ||
| 67 | /// @param[in] R_E Prime vertical radius of curvature (East/West) [m] | ||
| 68 | /// @return T_rn_p A 3x3 matrix | ||
| 69 | /// @note See \cite Groves2013 Groves, ch. 2.4.3, eq. 2.119, p. 63 | ||
| 70 | template<typename Derived> | ||
| 71 | 62626 | [[nodiscard]] Eigen::Matrix3<typename Derived::Scalar> conversionMatrixCartesianCurvilinear(const Eigen::MatrixBase<Derived>& lla_position, | |
| 72 | const typename Derived::Scalar& R_N, const typename Derived::Scalar& R_E) | ||
| 73 | { | ||
| 74 |
1/2✓ Branch 1 taken 62626 times.
✗ Branch 2 not taken.
|
62626 | return Eigen::DiagonalMatrix<typename Derived::Scalar, 3>{ 1.0 / (R_N + lla_position(2)), |
| 75 |
2/4✓ Branch 1 taken 62626 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 62626 times.
✗ Branch 5 not taken.
|
62626 | 1.0 / ((R_E + lla_position(2)) * std::cos(lla_position(0))), |
| 76 |
2/4✓ Branch 1 taken 62626 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 62626 times.
✗ Branch 5 not taken.
|
125252 | -1.0 }; |
| 77 | } | ||
| 78 | |||
| 79 | /// @brief Measure the distance between two points on a sphere | ||
| 80 | /// @param[in] lat1 Latitude of first point in [rad] | ||
| 81 | /// @param[in] lon1 Longitude of first point in [rad] | ||
| 82 | /// @param[in] lat2 Latitude of second point in [rad] | ||
| 83 | /// @param[in] lon2 Longitude of second point in [rad] | ||
| 84 | /// @return The distance in [m] | ||
| 85 | /// | ||
| 86 | /// @note See Haversine Formula (https://www.movable-type.co.uk/scripts/latlong.html) | ||
| 87 | template<std::floating_point Scalar> | ||
| 88 | 278216 | [[nodiscard]] Scalar calcGreatCircleDistance(Scalar lat1, Scalar lon1, Scalar lat2, Scalar lon2) | |
| 89 | { | ||
| 90 |
2/4✓ Branch 1 taken 278216 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 278216 times.
✗ Branch 5 not taken.
|
278216 | Scalar R = calcGeocentricRadius(lat1, calcEarthRadius_E(lat1)); |
| 91 | 278216 | Scalar dLat = lat2 - lat1; | |
| 92 | 278216 | Scalar dLon = lon2 - lon1; | |
| 93 | 278216 | Scalar a = std::pow(std::sin(dLat / 2.0), 2) + std::cos(lat1) * std::cos(lat2) * std::pow(std::sin(dLon / 2.0), 2); | |
| 94 | 278216 | Scalar c = 2.0 * std::atan2(std::sqrt(a), std::sqrt(1.0 - a)); | |
| 95 | 278216 | return R * c; // meters | |
| 96 | } | ||
| 97 | |||
| 98 | /// @brief Measure the distance between two points over an ellipsoidal-surface | ||
| 99 | /// @param[in] lat1 Latitude of first point in [rad] | ||
| 100 | /// @param[in] lon1 Longitude of first point in [rad] | ||
| 101 | /// @param[in] lat2 Latitude of second point in [rad] | ||
| 102 | /// @param[in] lon2 Longitude of second point in [rad] | ||
| 103 | /// @return The distance in [m] | ||
| 104 | /// | ||
| 105 | /// @note See Lambert's formula for long lines (https://en.wikipedia.org/wiki/Geographical_distance#Lambert's_formula_for_long_lines) | ||
| 106 | template<std::floating_point Scalar> | ||
| 107 | 278224 | [[nodiscard]] Scalar calcGeographicalDistance(Scalar lat1, Scalar lon1, Scalar lat2, Scalar lon2) | |
| 108 | { | ||
| 109 |
4/4✓ Branch 0 taken 139116 times.
✓ Branch 1 taken 139108 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 139108 times.
|
278224 | if (lat1 == lat2 && lon1 == lon2) |
| 110 | { | ||
| 111 | 8 | return 0; | |
| 112 | } | ||
| 113 | // First convert the latitudes 𝜙₁,𝜙₂ of the two points to reduced latitudes 𝛽₁,𝛽₂ | ||
| 114 | 278216 | Scalar beta1 = std::atan((1 - InsConst::WGS84::f) * std::tan(lat1)); | |
| 115 | 278216 | Scalar beta2 = std::atan((1 - InsConst::WGS84::f) * std::tan(lat2)); | |
| 116 | |||
| 117 | // Then calculate the central angle 𝜎 in radians between two points 𝛽₁,𝜆₁ and 𝛽₂,𝜆₂ on a sphere using the | ||
| 118 | // Great-circle distance method (law of cosines or haversine formula), with longitudes 𝜆₁ and 𝜆₂ being the same on the sphere as on the spheroid. | ||
| 119 | 278216 | Scalar sigma = calcGreatCircleDistance(beta1, lon1, beta2, lon2) | |
| 120 |
2/4✓ Branch 1 taken 278216 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 278216 times.
✗ Branch 5 not taken.
|
278216 | / calcGeocentricRadius(lat1, calcEarthRadius_E(lat1)); |
| 121 | |||
| 122 | 278216 | Scalar P = (beta1 + beta2) / 2; | |
| 123 | 278216 | Scalar Q = (beta2 - beta1) / 2; | |
| 124 | |||
| 125 | 278216 | Scalar X = (sigma - std::sin(sigma)) * std::pow((std::sin(P) * std::cos(Q)) / std::cos(sigma / 2), 2); | |
| 126 | 278216 | Scalar Y = (sigma + std::sin(sigma)) * std::pow((std::cos(P) * std::sin(Q)) / std::sin(sigma / 2), 2); | |
| 127 | |||
| 128 | 278216 | return InsConst::WGS84::a * (sigma - InsConst::WGS84::f / 2.0 * (X + Y)); | |
| 129 | } | ||
| 130 | |||
| 131 | } // namespace NAV | ||
| 132 |