INSTINCT Code Coverage Report


Directory: src/
File: Navigation/Gravity/Gravity.hpp
Date: 2025-07-19 10:51:51
Exec Total Coverage
Lines: 54 74 73.0%
Functions: 3 7 42.9%
Branches: 39 98 39.8%

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 Gravity.hpp
10 /// @brief Different Gravity Models
11 /// @author T. Topp (topp@ins.uni-stuttgart.de)
12 /// @author M. Maier (marcel.maier@ins.uni-stuttgart.de)
13 /// @date 2020-09-15
14
15 #pragma once
16
17 #include <cstdint>
18 #include <Eigen/Dense>
19 #include <Eigen/Core>
20
21 #include "Navigation/Transformations/CoordinateFrames.hpp"
22 #include "Navigation/Constants.hpp"
23 #include "internal/AssociatedLegendre.hpp"
24 #include "internal/egm96Coeffs.hpp"
25
26 namespace NAV
27 {
28 /// Available Gravitation Models
29 enum class GravitationModel : uint8_t
30 {
31 None, ///< Gravity Model turned off
32 Const, ///< Constant gravitation
33 WGS84, ///< World Geodetic System 1984
34 WGS84_Skydel, ///< World Geodetic System 1984 implemented by the Skydel Simulator // FIXME: Remove after Skydel uses the same as Instinct
35 Somigliana, ///< Somigliana gravity model
36 EGM96, ///< Earth Gravitational Model 1996
37 COUNT, ///< Amount of items in the enum
38 };
39
40 /// @brief Converts the enum to a string
41 /// @param[in] gravitationModel Enum value to convert into text
42 /// @return String representation of the enum
43 const char* to_string(GravitationModel gravitationModel);
44
45 /// @brief Shows a ComboBox to select the gravitation model
46 /// @param[in] label Label to show beside the combo box. This has to be a unique id for ImGui.
47 /// @param[in] gravitationModel Reference to the gravitation model to select
48 bool ComboGravitationModel(const char* label, GravitationModel& gravitationModel);
49
50 /// @brief Returns a constant gravitation of 9.81 [m/s^2] in down direction
51 /// @return Gravitation vector in local-navigation frame coordinates in [m/s^2]
52 template<typename T>
53 [[nodiscard]] Eigen::Vector3<T> n_calcGravitation_Const()
54 {
55 return { T(0.0), T(0.0), T(9.81) };
56 }
57
58 /// @brief Calculates the gravitation (acceleration due to mass attraction of the Earth) at the WGS84 reference ellipsoid
59 /// using the Somigliana model and makes corrections for altitude
60 /// @param[in] latitude Latitude in [rad]
61 /// @param[in] altitude Altitude in [m]
62 /// @return Gravitation vector in local-navigation frame coordinates in [m/s^2]
63 ///
64 /// @note See S. Gleason (2009) - GNSS Applications and Methods (Chapter 6.2.3.2 - eq. 6.16)
65 template<typename T>
66 97530 [[nodiscard]] Eigen::Vector3<T> n_calcGravitation_SomiglianaAltitude(const T& latitude, const T& altitude)
67 {
68 // eq 6.16 has a fault in the denominator, it should be a sin^2(latitude)
69 97530 auto g_0 = 9.7803253359 * (1.0 + 1.931853e-3 * std::pow(std::sin(latitude), 2))
70 97530 / std::sqrt(1.0 - InsConst::WGS84::e_squared * std::pow(std::sin(latitude), 2));
71
72 // Altitude compensation (Matlab example from Chapter 6_GNSS_INS_1 - glocal.m)
73 97530 auto k = 1.0
74 195060 - (2.0 * altitude / InsConst::WGS84::a)
75 97530 * (1.0 + InsConst::WGS84::f
76 97530 + (std::pow(InsConst::omega_ie * InsConst::WGS84::a, 2))
77 97530 * (InsConst::WGS84::b / InsConst::WGS84::MU))
78 97530 + 3.0 * std::pow(altitude / InsConst::WGS84::a, 2.0);
79
80
1/2
✓ Branch 1 taken 97530 times.
✗ Branch 2 not taken.
195060 return { T(0.0), T(0.0), k * g_0 };
81 }
82
83 /// @brief Calculates the gravitation (acceleration due to mass attraction of the Earth) at the WGS84 reference ellipsoid
84 /// using gravity as derived from the gravity potential. However, the north component of the centrifugal acceleration is neglected in order to match the implementation of Skydel's 'ImuPlugin'
85 /// @param[in] latitude Latitude in [rad]
86 /// @param[in] altitude Altitude in [m]
87 /// @return Gravitation vector in local-navigation frame coordinates in [m/s^2]
88 ///
89 /// @note See Skydel API plug-in 'skydel_plugin/source/library/inertial_math/Sources/source/gravity.cpp'
90 template<typename T>
91 2890019 [[nodiscard]] Eigen::Vector3<T> n_calcGravitation_WGS84_Skydel(const T& latitude, const T& altitude)
92 {
93 // geocentric latitude determination from geographic latitude
94 2890019 auto latitudeGeocentric = std::atan((std::pow(InsConst::WGS84::b, 2.0) / std::pow(InsConst::WGS84::a, 2.0)) * std::tan(latitude));
95 // effective radius determination, i.e. earth radius on WGS84 spheroid plus local altitude --> possible error!! altitude in lla should be added rather than subtracted!
96 2890019 auto radiusSpheroid = InsConst::WGS84::a * (1.0 - InsConst::WGS84::f * std::pow(std::sin(latitudeGeocentric), 2.0)) - altitude;
97
98 // Derivation of gravity, i.e. gravitational potential derived after effective radius
99 2890019 auto gravitationMagnitude = InsConst::WGS84::MU * std::pow(radiusSpheroid, -2.0)
100 2890019 - 3.0 * InsConst::WGS84::MU * InsConst::WGS84::J2 * std::pow(InsConst::WGS84::a, 2.0) * 0.5 * std::pow(radiusSpheroid, -4.0) * (3.0 * std::pow(std::sin(latitudeGeocentric), 2.0) - 1.0)
101 2890019 - std::pow(InsConst::omega_ie_Skydel, 2.0) * radiusSpheroid * std::pow(std::cos(latitudeGeocentric), 2.0);
102
103
1/2
✓ Branch 1 taken 2881128 times.
✗ Branch 2 not taken.
5771147 return { T(0.0), T(0.0), gravitationMagnitude };
104 }
105
106 /// @brief Calculates the gravitation (acceleration due to mass attraction of the Earth) at the WGS84 reference ellipsoid
107 /// using gravity as derived from the gravity potential.
108 /// @param[in] latitude Latitude in [rad]
109 /// @param[in] altitude Altitude in [m]
110 /// @return Gravitation vector in local-navigation frame coordinates in [m/s^2]
111 ///
112 /// @note See 'INS-Projects/INSTINCT/SpecificLiterature/GravityPotentialWGS84' in NC folder (eq. (3) derived after 'r')
113 template<typename T>
114 [[nodiscard]] Eigen::Vector3<T> n_calcGravitation_WGS84(const T& latitude, const T& altitude)
115 {
116 // Geocentric latitude determination from geographic latitude
117 auto latitudeGeocentric = std::atan((std::pow(InsConst::WGS84::b, 2.0) / std::pow(InsConst::WGS84::a, 2.0)) * std::tan(latitude));
118 // Radius of spheroid determination
119 auto radiusSpheroid = InsConst::WGS84::a * (1.0 - InsConst::WGS84::f * std::pow(std::sin(latitudeGeocentric), 2.0)) + altitude;
120
121 // Magnitude of the gravity, i.e. without orientation
122 auto gravitationMagnitude = InsConst::WGS84::MU * std::pow(radiusSpheroid, -2.0)
123 - 3.0 * InsConst::WGS84::MU * InsConst::WGS84::J2 * std::pow(InsConst::WGS84::a, 2.0) * 0.5 * std::pow(radiusSpheroid, -4.0) * (3.0 * std::pow(std::sin(latitudeGeocentric), 2.0) - 1.0)
124 - std::pow(InsConst::omega_ie, 2.0) * radiusSpheroid * std::pow(std::cos(latitudeGeocentric), 2.0);
125
126 return { T(0.0), T(0.0), gravitationMagnitude };
127 }
128
129 /// @brief Calculates the gravitation (acceleration due to mass attraction of the Earth) at the WGS84 reference ellipsoid
130 /// using the EGM96 spherical harmonic model (up to order 10)
131 /// @param[in] lla_position [ϕ, λ, h] Latitude, Longitude, Altitude in [rad, rad, m]
132 /// @param[in] ndegree Degree of the EGM96 (1 <= ndegree <= 10)
133 /// @return Gravitation vector in local-navigation frame coordinates in [m/s^2]
134 ///
135 /// @note See Groves (2013) Chapter 2.4.3 and 'GUT User Guide' (2018) Chapter 7.4
136 template<typename Derived>
137 99994 [[nodiscard]] Eigen::Vector3<typename Derived::Scalar> n_calcGravitation_EGM96(const Eigen::MatrixBase<Derived>& lla_position, size_t ndegree = 10)
138 {
139 using internal::egm96Coeffs;
140 using internal::associatedLegendre;
141
142
1/2
✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
99994 auto e_position = trafo::lla2ecef_WGS84(lla_position);
143
144 // Geocentric latitude determination from Groves (2013) - eq. (2.114)
145
5/10
✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49997 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 49997 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 49997 times.
✗ Branch 14 not taken.
99994 auto latitudeGeocentric = std::atan(e_position(2) / std::sqrt(e_position(0) * e_position(0) + e_position(1) * e_position(1)));
146
147 // Spherical coordinates
148
6/12
✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49997 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 49997 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 49997 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 49997 times.
✗ Branch 17 not taken.
99994 auto radius = std::sqrt(e_position(0) * e_position(0) + e_position(1) * e_position(1) + e_position(2) * e_position(2));
149 99994 auto elevation = M_PI_2 - latitudeGeocentric; // [rad]
150
1/2
✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
99994 const auto& azimuth = lla_position(1); // [rad]
151
152 // Gravitation vector in local-navigation frame coordinates in [m/s^2]
153
2/4
✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
99994 Eigen::Vector3<typename Derived::Scalar> n_gravitation = Eigen::Vector3<typename Derived::Scalar>::Zero();
154
155 99994 typename Derived::Scalar Pnm(0.0);
156 99994 typename Derived::Scalar Pnmd(0.0);
157
158 99994 auto coeffsRows = egm96Coeffs.size();
159
160 // Associated Legendre Polynomial Coefficients 'P' and their derivatives 'Pd'
161
1/2
✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
99994 auto [P, Pd] = associatedLegendre(elevation, ndegree);
162
163
2/2
✓ Branch 0 taken 3199808 times.
✓ Branch 1 taken 49997 times.
6499610 for (size_t i = 0; i < coeffsRows; i++) // NOLINT(clang-analyzer-core.UndefinedBinaryOperatorResult) // FIXME: Wrong error message about Eigen (error: The left operand of '*' is a garbage value)
164 {
165 // Retrieving EGM96 coefficients
166
2/4
✓ Branch 1 taken 3199808 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3199808 times.
✗ Branch 5 not taken.
6399616 auto n = static_cast<int>(egm96Coeffs.at(i).at(0)); // Degree of the Associated Legendre Polynomial
167
2/4
✓ Branch 1 taken 3199808 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3199808 times.
✗ Branch 5 not taken.
6399616 auto m = static_cast<int>(egm96Coeffs.at(i).at(1)); // Order of the Associated Legendre Polynomial
168
2/4
✓ Branch 1 taken 3199808 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3199808 times.
✗ Branch 5 not taken.
6399616 auto C = egm96Coeffs.at(i).at(2);
169
2/4
✓ Branch 1 taken 3199808 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3199808 times.
✗ Branch 5 not taken.
6399616 auto S = egm96Coeffs.at(i).at(3);
170
171
2/2
✓ Branch 0 taken 49997 times.
✓ Branch 1 taken 3149811 times.
6399616 if (static_cast<size_t>(n) == ndegree + 1)
172 {
173 // Ending of the for-loop once the iterated 'n' becomes larger than the user-defined 'ndegree'
174 99994 i = coeffsRows;
175 }
176 else
177 {
178 // Retrieving the parameters of the associated Legendre Polynomials
179
1/2
✓ Branch 1 taken 3149811 times.
✗ Branch 2 not taken.
6299622 Pnm = P(n, m);
180
1/2
✓ Branch 1 taken 3149811 times.
✗ Branch 2 not taken.
6299622 Pnmd = Pd(n, m);
181
182 6299622 auto nd = static_cast<double>(n);
183 6299622 auto md = static_cast<double>(m);
184
185 // Gravity vector from differentiation of the gravity potential in spherical coordinates (see 'GUT User Guide' eq. 7.4.2)
186
1/2
✓ Branch 1 taken 3149811 times.
✗ Branch 2 not taken.
6299622 n_gravitation(0) += std::pow((InsConst::WGS84::a / radius), nd) * (C * std::cos(md * azimuth) + S * std::sin(md * azimuth)) * Pnmd;
187
1/2
✓ Branch 1 taken 3149811 times.
✗ Branch 2 not taken.
6299622 n_gravitation(1) += std::pow((InsConst::WGS84::a / radius), nd) * md * (C * std::sin(md * azimuth) - S * std::cos(md * azimuth)) * Pnm;
188
1/2
✓ Branch 1 taken 3149811 times.
✗ Branch 2 not taken.
6299622 n_gravitation(2) += (nd + 1.0) * std::pow((InsConst::WGS84::a / radius), nd) * (C * std::cos(md * azimuth) + S * std::sin(md * azimuth)) * Pnm;
189 }
190 }
191
192
1/2
✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
99994 return { -InsConst::WGS84::MU / (radius * radius) * n_gravitation(0),
193
1/2
✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
99994 (1.0 / std::sin(elevation)) * (-InsConst::WGS84::MU / (radius * radius)) * n_gravitation(1),
194
2/4
✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
199988 InsConst::WGS84::MU / (radius * radius) * (1.0 + n_gravitation(2)) };
195 99994 }
196
197 /// @brief Calculates the gravitation (acceleration due to mass attraction of the Earth)
198 /// @param[in] lla_position [ϕ, λ, h] Latitude, Longitude, Altitude in [rad, rad, m]
199 /// @param[in] gravitationModel Gravitation model to use
200 /// @return Gravitation vector in local-navigation frame coordinates in [m/s^2]
201 template<typename Derived>
202 2976029 [[nodiscard]] Eigen::Vector3<typename Derived::Scalar> n_calcGravitation(const Eigen::MatrixBase<Derived>& lla_position,
203 GravitationModel gravitationModel = GravitationModel::EGM96)
204 {
205 2976029 const typename Derived::Scalar& latitude = lla_position(0);
206 2975727 const typename Derived::Scalar& altitude = lla_position(2);
207
208
0/8
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2984939 switch (gravitationModel)
209 {
210 case NAV::GravitationModel::None:
211 return Eigen::Vector3<typename Derived::Scalar>::Zero();
212 case NAV::GravitationModel::Const:
213 return n_calcGravitation_Const<typename Derived::Scalar>();
214 case GravitationModel::WGS84:
215 return n_calcGravitation_WGS84(latitude, altitude);
216 2887409 case GravitationModel::WGS84_Skydel:
217 2887409 return n_calcGravitation_WGS84_Skydel(latitude, altitude);
218 97530 case GravitationModel::Somigliana:
219 97530 return n_calcGravitation_SomiglianaAltitude(latitude, altitude);
220 case GravitationModel::EGM96:
221 return n_calcGravitation_EGM96(lla_position);
222 case NAV::GravitationModel::COUNT:
223 return Eigen::Vector3<typename Derived::Scalar>::Zero();
224 }
225
226 return Eigen::Vector3<typename Derived::Scalar>::Zero();
227 }
228
229 } // namespace NAV
230