Vulcan
Aerospace Engineering Utilities Built on Janus
Loading...
Searching...
No Matches
SphericalHarmonics.hpp
Go to the documentation of this file.
1// Vulcan Spherical Harmonics Gravity Model
2// General spherical harmonic expansion for high-fidelity applications
3#pragma once
4
5#include <janus/janus.hpp>
9
10#include <vector>
11
13
21 int n_max;
22 std::vector<std::vector<double>> C;
23 std::vector<std::vector<double>> S;
24 double mu;
25 double R_eq;
26
28 explicit GravityCoefficients(int n_max_val = 20,
29 double mu_val = constants::earth::mu,
30 double R_eq_val = constants::earth::R_eq)
31 : n_max(n_max_val), mu(mu_val), R_eq(R_eq_val) {
32 C.resize(static_cast<size_t>(n_max + 1));
33 S.resize(static_cast<size_t>(n_max + 1));
34 for (int n = 0; n <= n_max; ++n) {
35 C[static_cast<size_t>(n)].resize(static_cast<size_t>(n + 1), 0.0);
36 S[static_cast<size_t>(n)].resize(static_cast<size_t>(n + 1), 0.0);
37 }
38 // Monopole (point mass) term
39 C[0][0] = 1.0;
40
41 // J2, J3, J4 as zonal harmonics (m=0)
42 if (n_max >= 2)
43 C[2][0] = -constants::earth::J2;
44 if (n_max >= 3)
45 C[3][0] = -constants::earth::J3;
46 if (n_max >= 4)
47 C[4][0] = -constants::earth::J4;
48 }
49};
50
53 static GravityCoefficients coeffs(4);
54 return coeffs;
55}
56
69template <typename Scalar> Scalar legendre_Pnm(int n, int m, const Scalar &x) {
70 // Handle m > n case
71 if (m > n)
72 return Scalar(0.0);
73
74 // P_mm: diagonal recurrence
75 // P_mm = (-1)^m (2m-1)!! (1-x²)^(m/2)
76 Scalar pmm = Scalar(1.0);
77 if (m > 0) {
78 Scalar somx2 = janus::sqrt((1.0 - x) * (1.0 + x));
79 double fact = 1.0;
80 for (int i = 1; i <= m; ++i) {
81 pmm = -pmm * fact * somx2;
82 fact += 2.0;
83 }
84 }
85
86 if (n == m)
87 return pmm;
88
89 // P_{m+1,m} = x(2m+1)P_mm
90 Scalar pmm1 = x * (2.0 * m + 1.0) * pmm;
91 if (n == m + 1)
92 return pmm1;
93
94 // General recurrence for n > m+1
95 // P_nm = [(2n-1)x P_{n-1,m} - (n+m-1)P_{n-2,m}] / (n-m)
96 Scalar pnm = Scalar(0.0);
97 for (int nn = m + 2; nn <= n; ++nn) {
98 pnm = ((2.0 * nn - 1.0) * x * pmm1 - (nn + m - 1.0) * pmm) / (nn - m);
99 pmm = pmm1;
100 pmm1 = pnm;
101 }
102
103 return pnm;
104}
105
119template <typename Scalar>
120Vec3<Scalar>
121acceleration(const Vec3<Scalar> &r_ecef,
122 const GravityCoefficients &coeffs = default_coefficients()) {
123 // Convert to spherical coordinates
124 const Spherical<Scalar> sph = ecef_to_spherical(r_ecef);
125 const Scalar r = sph.radius;
126 const Scalar lon = sph.lon;
127 const Scalar lat_gc = sph.lat_gc;
128
129 const Scalar sin_lat = janus::sin(lat_gc);
130 const Scalar cos_lat = janus::cos(lat_gc);
131
132 const double mu = coeffs.mu;
133 const double R_eq = coeffs.R_eq;
134 const int n_max = coeffs.n_max;
135
136 // Initialize partial derivatives of potential
137 Scalar dU_dr = Scalar(0.0); // ∂U/∂r
138 Scalar dU_dlat = Scalar(0.0); // ∂U/∂φ
139 Scalar dU_dlon = Scalar(0.0); // ∂U/∂λ
140
141 // Summation over degrees and orders
142 // Loop bounds are structural (n_max is int, not Scalar)
143 for (int n = 0; n <= n_max; ++n) {
144 const Scalar Re_r_n = janus::pow(R_eq / r, static_cast<double>(n));
145
146 for (int m = 0; m <= n; ++m) {
147 const double C_nm =
148 coeffs.C[static_cast<size_t>(n)][static_cast<size_t>(m)];
149 const double S_nm =
150 coeffs.S[static_cast<size_t>(n)][static_cast<size_t>(m)];
151
152 // Skip zero coefficients for efficiency
153 if (C_nm == 0.0 && S_nm == 0.0)
154 continue;
155
156 const Scalar cos_m_lon = janus::cos(static_cast<double>(m) * lon);
157 const Scalar sin_m_lon = janus::sin(static_cast<double>(m) * lon);
158
159 const Scalar P_nm = legendre_Pnm(n, m, sin_lat);
160
161 // Derivative of Legendre polynomial using recurrence
162 const Scalar P_nm1 =
163 (n > 0) ? legendre_Pnm(n - 1, m, sin_lat) : Scalar(0.0);
164 const Scalar tan_lat = janus::tan(lat_gc);
165 const Scalar dP_dlat = static_cast<double>(n) * tan_lat * P_nm -
166 static_cast<double>(n + m) / cos_lat * P_nm1;
167
168 const Scalar trig_term = C_nm * cos_m_lon + S_nm * sin_m_lon;
169 const Scalar dtrig_dlon =
170 static_cast<double>(m) * (-C_nm * sin_m_lon + S_nm * cos_m_lon);
171
172 // Accumulate partial derivatives
173 dU_dr += static_cast<double>(n + 1) * Re_r_n / r * trig_term * P_nm;
174 dU_dlat += Re_r_n * trig_term * dP_dlat;
175 dU_dlon += Re_r_n * dtrig_dlon * P_nm;
176 }
177 }
178
179 // Scale by μ/r
180 const Scalar scale = mu / r;
181 dU_dr = dU_dr * scale;
182 dU_dlat = dU_dlat * scale;
183 dU_dlon = dU_dlon * scale;
184
185 // Convert spherical gradient to ECEF acceleration
186 // g = -∇U
187 const Scalar sin_lon = janus::sin(lon);
188 const Scalar cos_lon = janus::cos(lon);
189
190 // Spherical to Cartesian transformation
191 const Scalar g_r = -dU_dr;
192 const Scalar g_lat = -dU_dlat / r;
193 const Scalar g_lon = -dU_dlon / (r * cos_lat);
194
195 // Transform to ECEF
196 Vec3<Scalar> g_ecef;
197 g_ecef(0) =
198 cos_lat * cos_lon * g_r - sin_lat * cos_lon * g_lat - sin_lon * g_lon;
199 g_ecef(1) =
200 cos_lat * sin_lon * g_r - sin_lat * sin_lon * g_lat + cos_lon * g_lon;
201 g_ecef(2) = sin_lat * g_r + cos_lat * g_lat;
202
203 return g_ecef;
204}
205
216template <typename Scalar>
217Scalar potential(const Vec3<Scalar> &r_ecef,
218 const GravityCoefficients &coeffs = default_coefficients()) {
219 const Spherical<Scalar> sph = ecef_to_spherical(r_ecef);
220 const Scalar r = sph.radius;
221 const Scalar lon = sph.lon;
222 const Scalar lat_gc = sph.lat_gc;
223
224 const Scalar sin_lat = janus::sin(lat_gc);
225
226 const double mu = coeffs.mu;
227 const double R_eq = coeffs.R_eq;
228 const int n_max = coeffs.n_max;
229
230 Scalar U = -mu / r; // Point mass term
231
232 for (int n = 2; n <= n_max; ++n) {
233 const Scalar Re_r_n = janus::pow(R_eq / r, static_cast<double>(n));
234
235 for (int m = 0; m <= n; ++m) {
236 const double C_nm =
237 coeffs.C[static_cast<size_t>(n)][static_cast<size_t>(m)];
238 const double S_nm =
239 coeffs.S[static_cast<size_t>(n)][static_cast<size_t>(m)];
240
241 if (C_nm == 0.0 && S_nm == 0.0)
242 continue;
243
244 const Scalar cos_m_lon = janus::cos(static_cast<double>(m) * lon);
245 const Scalar sin_m_lon = janus::sin(static_cast<double>(m) * lon);
246 const Scalar P_nm = legendre_Pnm(n, m, sin_lat);
247
248 U = U -
249 mu / r * Re_r_n * (C_nm * cos_m_lon + S_nm * sin_m_lon) * P_nm;
250 }
251 }
252
253 return U;
254}
255
256} // namespace vulcan::gravity::spherical_harmonics
constexpr double R_eq
Equatorial radius [m] (WGS84).
Definition Constants.hpp:16
constexpr double J4
J4 zonal harmonic coefficient.
Definition Constants.hpp:34
constexpr double J3
J3 zonal harmonic coefficient.
Definition Constants.hpp:31
constexpr double J2
J2 zonal harmonic coefficient.
Definition Constants.hpp:28
constexpr double mu
Gravitational parameter (GM) [m^3/s^2].
Definition Constants.hpp:13
Definition SphericalHarmonics.hpp:12
Scalar potential(const Vec3< Scalar > &r_ecef, const GravityCoefficients &coeffs=default_coefficients())
Spherical harmonic gravitational potential.
Definition SphericalHarmonics.hpp:217
const GravityCoefficients & default_coefficients()
Default Earth model with zonal harmonics only.
Definition SphericalHarmonics.hpp:52
Vec3< Scalar > acceleration(const Vec3< Scalar > &r_ecef, const GravityCoefficients &coeffs=default_coefficients())
Spherical harmonic gravitational acceleration.
Definition SphericalHarmonics.hpp:121
Scalar legendre_Pnm(int n, int m, const Scalar &x)
Compute associated Legendre polynomial P_nm(x).
Definition SphericalHarmonics.hpp:69
Spherical< Scalar > ecef_to_spherical(const Vec3< Scalar > &r)
Definition Geodetic.hpp:190
Definition Geodetic.hpp:49
Scalar lon
Longitude [rad], positive East, range [-π, π].
Definition Geodetic.hpp:50
Scalar radius
Distance from Earth center [m].
Definition Geodetic.hpp:52
Scalar lat_gc
Geocentric latitude [rad], range [-π/2, π/2].
Definition Geodetic.hpp:51
Gravity model coefficients container.
Definition SphericalHarmonics.hpp:20
GravityCoefficients(int n_max_val=20, double mu_val=constants::earth::mu, double R_eq_val=constants::earth::R_eq)
Initialize with given maximum degree.
Definition SphericalHarmonics.hpp:28
double mu
Gravitational parameter [m³/s²].
Definition SphericalHarmonics.hpp:24
std::vector< std::vector< double > > S
Sine coefficients S[n][m].
Definition SphericalHarmonics.hpp:23
std::vector< std::vector< double > > C
Cosine coefficients C[n][m].
Definition SphericalHarmonics.hpp:22
double R_eq
Reference radius [m].
Definition SphericalHarmonics.hpp:25
int n_max
Maximum degree.
Definition SphericalHarmonics.hpp:21