INSTINCT Code Coverage Report


Directory: src/
File: Navigation/Ellipsoid/Ellipsoid.hpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 30 30 100.0%
Functions: 6 6 100.0%
Branches: 13 22 59.1%

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 447973 [[nodiscard]] Scalar calcGeocentricRadius(const Scalar& latitude, const Scalar& R_E, const Scalar& e_squared = InsConst::WGS84::e_squared)
30 {
31 447973 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 5904376 [[nodiscard]] Scalar calcEarthRadius_N(const Scalar& latitude, const Scalar& a = InsConst::WGS84::a, const Scalar& e_squared = InsConst::WGS84::e_squared)
43 {
44 5904376 Scalar k = std::sqrt(1 - e_squared * std::pow(std::sin(latitude), 2));
45
46 // North/South (meridian) earth radius [m]
47 5916912 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 25220124 [[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 25220124 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 198988 [[nodiscard]] Scalar calcGreatCircleDistance(Scalar lat1, Scalar lon1, Scalar lat2, Scalar lon2)
89 {
90
2/4
✓ Branch 1 taken 198988 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198988 times.
✗ Branch 5 not taken.
198988 Scalar R = calcGeocentricRadius(lat1, calcEarthRadius_E(lat1));
91 198988 Scalar dLat = lat2 - lat1;
92 198988 Scalar dLon = lon2 - lon1;
93 198988 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 198988 Scalar c = 2.0 * std::atan2(std::sqrt(a), std::sqrt(1.0 - a));
95 198988 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 198992 [[nodiscard]] Scalar calcGeographicalDistance(Scalar lat1, Scalar lon1, Scalar lat2, Scalar lon2)
108 {
109
4/4
✓ Branch 0 taken 99498 times.
✓ Branch 1 taken 99494 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 99494 times.
198992 if (lat1 == lat2 && lon1 == lon2)
110 {
111 4 return 0;
112 }
113 // First convert the latitudes 𝜙₁,𝜙₂ of the two points to reduced latitudes 𝛽₁,𝛽₂
114 198988 Scalar beta1 = std::atan((1 - InsConst::WGS84::f) * std::tan(lat1));
115 198988 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 198988 Scalar sigma = calcGreatCircleDistance(beta1, lon1, beta2, lon2)
120
2/4
✓ Branch 1 taken 198988 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198988 times.
✗ Branch 5 not taken.
198988 / calcGeocentricRadius(lat1, calcEarthRadius_E(lat1));
121
122 198988 Scalar P = (beta1 + beta2) / 2;
123 198988 Scalar Q = (beta2 - beta1) / 2;
124
125 198988 Scalar X = (sigma - std::sin(sigma)) * std::pow((std::sin(P) * std::cos(Q)) / std::cos(sigma / 2), 2);
126 198988 Scalar Y = (sigma + std::sin(sigma)) * std::pow((std::cos(P) * std::sin(Q)) / std::sin(sigma / 2), 2);
127
128 198988 return InsConst::WGS84::a * (sigma - InsConst::WGS84::f / 2.0 * (X + Y));
129 }
130
131 } // namespace NAV
132