INSTINCT Code Coverage Report


Directory: src/
File: Navigation/Gravity/Gravity.hpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 48 65 73.8%
Functions: 3 6 50.0%
Branches: 38 88 43.2%

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 "util/Logger.hpp"
23 #include "Navigation/Constants.hpp"
24 #include "internal/AssociatedLegendre.hpp"
25 #include "internal/egm96Coeffs.hpp"
26
27 namespace NAV
28 {
29 /// Available Gravitation Models
30 enum class GravitationModel : uint8_t
31 {
32 None, ///< Gravity Model turned off
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 Calculates the gravitation (acceleration due to mass attraction of the Earth) at the WGS84 reference ellipsoid
51 /// using the Somigliana model and makes corrections for altitude
52 /// @param[in] latitude Latitude in [rad]
53 /// @param[in] altitude Altitude in [m]
54 /// @return Gravitation vector in local-navigation frame coordinates in [m/s^2]
55 ///
56 /// @note See S. Gleason (2009) - GNSS Applications and Methods (Chapter 6.2.3.2 - eq. 6.16)
57 template<std::floating_point Scalar>
58 2885972 [[nodiscard]] Eigen::Vector3<Scalar> n_calcGravitation_SomiglianaAltitude(const Scalar& latitude, const Scalar& altitude)
59 {
60 // eq 6.16 has a fault in the denominator, it should be a sin^2(latitude)
61 2885972 auto g_0 = 9.7803253359 * (1.0 + 1.931853e-3 * std::pow(std::sin(latitude), 2))
62 2893868 / std::sqrt(1.0 - InsConst::WGS84::e_squared * std::pow(std::sin(latitude), 2));
63
64 // Altitude compensation (Matlab example from Chapter 6_GNSS_INS_1 - glocal.m)
65 2881708 auto k = 1
66 5763971 - (2 * altitude / InsConst::WGS84::a)
67 2874460 * (1 + InsConst::WGS84::f
68 2889511 + (std::pow(InsConst::omega_ie * InsConst::WGS84::a, 2))
69 2874460 * (InsConst::WGS84::b / InsConst::WGS84::MU))
70 2874460 + 3 * std::pow(altitude / InsConst::WGS84::a, 2);
71
72
1/2
✓ Branch 1 taken 2883168 times.
✗ Branch 2 not taken.
5764876 return { 0.0, 0.0, k * g_0 };
73 }
74
75 /// @brief Calculates the gravitation (acceleration due to mass attraction of the Earth) at the WGS84 reference ellipsoid
76 /// 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'
77 /// @param[in] latitude Latitude in [rad]
78 /// @param[in] altitude Altitude in [m]
79 /// @return Gravitation vector in local-navigation frame coordinates in [m/s^2]
80 ///
81 /// @note See Skydel API plug-in 'skydel_plugin/source/library/inertial_math/Sources/source/gravity.cpp'
82 template<std::floating_point Scalar>
83 [[nodiscard]] Eigen::Vector3<Scalar> n_calcGravitation_WGS84_Skydel(const Scalar& latitude, const Scalar& altitude)
84 {
85 // geocentric latitude determination from geographic latitude
86 auto latitudeGeocentric = std::atan((std::pow(InsConst::WGS84::b, 2.0) / std::pow(InsConst::WGS84::a, 2.0)) * std::tan(latitude));
87 // effective radius determination, i.e. earth radius on WGS84 spheroid plus local altitude --> possible error!! altitude in lla should be added rather than subtracted!
88 auto radiusSpheroid = InsConst::WGS84::a * (1.0 - InsConst::WGS84::f * std::pow(std::sin(latitudeGeocentric), 2.0)) - altitude;
89
90 // Derivation of gravity, i.e. gravitational potential derived after effective radius
91 auto gravitationMagnitude = InsConst::WGS84::MU * std::pow(radiusSpheroid, -2.0)
92 - 3 * InsConst::WGS84::MU * InsConst::WGS84::J2 * std::pow(InsConst::WGS84::a, 2.0) * 0.5 * std::pow(radiusSpheroid, -4.0) * (3 * std::pow(std::sin(latitudeGeocentric), 2.0) - 1)
93 - std::pow(InsConst::omega_ie_Skydel, 2.0) * radiusSpheroid * std::pow(std::cos(latitudeGeocentric), 2.0);
94
95 return { 0, 0, gravitationMagnitude };
96 }
97
98 /// @brief Calculates the gravitation (acceleration due to mass attraction of the Earth) at the WGS84 reference ellipsoid
99 /// using gravity as derived from the gravity potential.
100 /// @param[in] latitude Latitude in [rad]
101 /// @param[in] altitude Altitude in [m]
102 /// @return Gravitation vector in local-navigation frame coordinates in [m/s^2]
103 ///
104 /// @note See 'INS-Projects/INSTINCT/SpecificLiterature/GravityPotentialWGS84' in NC folder (eq. (3) derived after 'r')
105 template<std::floating_point Scalar>
106 [[nodiscard]] Eigen::Vector3<Scalar> n_calcGravitation_WGS84(const Scalar& latitude, const Scalar& altitude)
107 {
108 // Geocentric latitude determination from geographic latitude
109 auto latitudeGeocentric = std::atan((std::pow(InsConst::WGS84::b, 2.0) / std::pow(InsConst::WGS84::a, 2.0)) * std::tan(latitude));
110 // Radius of spheroid determination
111 auto radiusSpheroid = InsConst::WGS84::a * (1.0 - InsConst::WGS84::f * std::pow(std::sin(latitudeGeocentric), 2.0)) + altitude;
112
113 // Magnitude of the gravity, i.e. without orientation
114 auto gravitationMagnitude = InsConst::WGS84::MU * std::pow(radiusSpheroid, -2.0)
115 - 3 * InsConst::WGS84::MU * InsConst::WGS84::J2 * std::pow(InsConst::WGS84::a, 2.0) * 0.5 * std::pow(radiusSpheroid, -4.0) * (3 * std::pow(std::sin(latitudeGeocentric), 2.0) - 1)
116 - std::pow(InsConst::omega_ie, 2.0) * radiusSpheroid * std::pow(std::cos(latitudeGeocentric), 2.0);
117
118 return { 0, 0, gravitationMagnitude };
119 }
120
121 /// @brief Calculates the gravitation (acceleration due to mass attraction of the Earth) at the WGS84 reference ellipsoid
122 /// using the EGM96 spherical harmonic model (up to order 10)
123 /// @param[in] lla_position [ϕ, λ, h] Latitude, Longitude, Altitude in [rad, rad, m]
124 /// @param[in] ndegree Degree of the EGM96 (1 <= ndegree <= 10)
125 /// @return Gravitation vector in local-navigation frame coordinates in [m/s^2]
126 ///
127 /// @note See Groves (2013) Chapter 2.4.3 and 'GUT User Guide' (2018) Chapter 7.4
128 template<typename Derived>
129 362646 [[nodiscard]] Eigen::Vector3<typename Derived::Scalar> n_calcGravitation_EGM96(const Eigen::MatrixBase<Derived>& lla_position, size_t ndegree = 10)
130 {
131 using internal::egm96Coeffs;
132 using internal::associatedLegendre;
133
134
1/2
✓ Branch 1 taken 181323 times.
✗ Branch 2 not taken.
362646 auto e_position = trafo::lla2ecef_WGS84(lla_position);
135
136 // Geocentric latitude determination from Groves (2013) - eq. (2.114)
137
5/10
✓ Branch 1 taken 181323 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 181323 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 181323 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 181323 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 181323 times.
✗ Branch 14 not taken.
362646 auto latitudeGeocentric = std::atan(e_position(2) / std::sqrt(e_position(0) * e_position(0) + e_position(1) * e_position(1)));
138
139 // Spherical coordinates
140
6/12
✓ Branch 1 taken 181323 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 181323 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 181323 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 181323 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 181323 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 181323 times.
✗ Branch 17 not taken.
362646 auto radius = std::sqrt(e_position(0) * e_position(0) + e_position(1) * e_position(1) + e_position(2) * e_position(2));
141 362646 auto elevation = M_PI_2 - latitudeGeocentric; // [rad]
142
1/2
✓ Branch 1 taken 181323 times.
✗ Branch 2 not taken.
362646 auto azimuth = lla_position(1); // [rad]
143
144 // Gravitation vector in local-navigation frame coordinates in [m/s^2]
145
2/4
✓ Branch 1 taken 181323 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 181323 times.
✗ Branch 5 not taken.
362646 Eigen::Vector3<typename Derived::Scalar> n_gravitation = Eigen::Vector3<typename Derived::Scalar>::Zero();
146
147 362646 typename Derived::Scalar Pnm = 0;
148 362646 typename Derived::Scalar Pnmd = 0;
149
150 362646 auto coeffsRows = egm96Coeffs.size();
151
152 // Associated Legendre Polynomial Coefficients 'P' and their derivatives 'Pd'
153
1/2
✓ Branch 1 taken 181323 times.
✗ Branch 2 not taken.
362646 auto [P, Pd] = associatedLegendre(static_cast<double>(elevation), ndegree);
154
155
2/2
✓ Branch 0 taken 11604672 times.
✓ Branch 1 taken 181323 times.
23571990 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)
156 {
157 // Retrieving EGM96 coefficients
158
2/4
✓ Branch 1 taken 11604672 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11604672 times.
✗ Branch 5 not taken.
23209344 auto n = static_cast<int>(egm96Coeffs.at(i).at(0)); // Degree of the Associated Legendre Polynomial
159
2/4
✓ Branch 1 taken 11604672 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11604672 times.
✗ Branch 5 not taken.
23209344 auto m = static_cast<int>(egm96Coeffs.at(i).at(1)); // Order of the Associated Legendre Polynomial
160
2/4
✓ Branch 1 taken 11604672 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11604672 times.
✗ Branch 5 not taken.
23209344 auto C = egm96Coeffs.at(i).at(2);
161
2/4
✓ Branch 1 taken 11604672 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11604672 times.
✗ Branch 5 not taken.
23209344 auto S = egm96Coeffs.at(i).at(3);
162
163
2/2
✓ Branch 0 taken 181323 times.
✓ Branch 1 taken 11423349 times.
23209344 if (static_cast<size_t>(n) == ndegree + 1)
164 {
165 // Ending of the for-loop once the iterated 'n' becomes larger than the user-defined 'ndegree'
166 362646 i = coeffsRows;
167 }
168 else
169 {
170 // Retrieving the parameters of the associated Legendre Polynomials
171
1/2
✓ Branch 1 taken 11423349 times.
✗ Branch 2 not taken.
22846698 Pnm = P(n, m);
172
1/2
✓ Branch 1 taken 11423349 times.
✗ Branch 2 not taken.
22846698 Pnmd = Pd(n, m);
173
174 22846698 auto nd = static_cast<double>(n);
175 22846698 auto md = static_cast<double>(m);
176
177 // Gravity vector from differentiation of the gravity potential in spherical coordinates (see 'GUT User Guide' eq. 7.4.2)
178
1/2
✓ Branch 1 taken 11423349 times.
✗ Branch 2 not taken.
22846698 n_gravitation(0) += std::pow((InsConst::WGS84::a / radius), nd) * (C * std::cos(md * azimuth) + S * std::sin(md * azimuth)) * Pnmd;
179
1/2
✓ Branch 1 taken 11423349 times.
✗ Branch 2 not taken.
22846698 n_gravitation(1) += std::pow((InsConst::WGS84::a / radius), nd) * md * (C * std::sin(md * azimuth) - S * std::cos(md * azimuth)) * Pnm;
180
1/2
✓ Branch 1 taken 11423349 times.
✗ Branch 2 not taken.
22846698 n_gravitation(2) += (nd + 1.0) * std::pow((InsConst::WGS84::a / radius), nd) * (C * std::cos(md * azimuth) + S * std::sin(md * azimuth)) * Pnm;
181 }
182 }
183
184
1/2
✓ Branch 1 taken 181323 times.
✗ Branch 2 not taken.
362646 return { -InsConst::WGS84::MU / (radius * radius) * n_gravitation(0),
185
1/2
✓ Branch 1 taken 181323 times.
✗ Branch 2 not taken.
362646 (1.0 / std::sin(elevation)) * (-InsConst::WGS84::MU / (radius * radius)) * n_gravitation(1),
186
2/4
✓ Branch 1 taken 181323 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 181323 times.
✗ Branch 5 not taken.
725292 InsConst::WGS84::MU / (radius * radius) * (1.0 + n_gravitation(2)) };
187 362646 }
188
189 /// @brief Calculates the gravitation (acceleration due to mass attraction of the Earth)
190 /// @param[in] lla_position [ϕ, λ, h] Latitude, Longitude, Altitude in [rad, rad, m]
191 /// @param[in] gravitationModel Gravitation model to use
192 /// @return Gravitation vector in local-navigation frame coordinates in [m/s^2]
193 template<typename Derived>
194 3007960 [[nodiscard]] Eigen::Vector3<typename Derived::Scalar> n_calcGravitation(const Eigen::MatrixBase<Derived>& lla_position,
195 GravitationModel gravitationModel = GravitationModel::EGM96)
196 {
197 3007960 const typename Derived::Scalar& latitude = lla_position(0);
198 3010981 const typename Derived::Scalar& altitude = lla_position(2);
199
200
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
3015559 if (gravitationModel == GravitationModel::WGS84)
201 {
202 return n_calcGravitation_WGS84(latitude, altitude);
203 }
204
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
3015559 if (gravitationModel == GravitationModel::WGS84_Skydel) // TODO: This function becomes obsolete, once the ImuStream is deactivated due to the 'InstinctDataStream'
205 {
206 return n_calcGravitation_WGS84_Skydel(latitude, altitude);
207 }
208
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
3015559 if (gravitationModel == GravitationModel::Somigliana)
209 {
210 2884285 return n_calcGravitation_SomiglianaAltitude(latitude, altitude);
211 }
212
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
131274 if (gravitationModel == GravitationModel::EGM96)
213 {
214 131326 return n_calcGravitation_EGM96(lla_position);
215 }
216 return Eigen::Vector3<typename Derived::Scalar>::Zero();
217 }
218
219 } // namespace NAV
220