Astro  0.0.0
A C++ library for space dynamics
Loading...
Searching...
No Matches
Body.hh
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * Copyright (c) 2025, Davide Stocco and Enrico Bertolazzi. *
3 * *
4 * The Astro project is distributed under the GNU GPLv3. *
5 * *
6 * Davide Stocco Enrico Bertolazzi *
7 * University of Trento University of Trento *
8 * e-mail: davide.stocco@unitn.it e-mail: enrico.bertolazzi@unitn.it *
9\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10
11#pragma once
12
13#ifndef ASTRO_BODY_HH
14#define ASTRO_BODY_HH
15
16#include "Astro/Orbit.hh"
17
18// Sandals library
19#include <Sandals/RungeKutta.hh>
20#include <Sandals/Solution.hh>
21
22namespace Astro
23{
24
26 using Coordinates = enum class Coordinates : Integer {CARTESIAN = 0, KEPLERIAN = 1, EQUINOCTIAL = 2, QUATERNIONIC = 3};
27
28 /*\
29 | ____ _
30 | | __ ) ___ __| |_ _
31 | | _ \ / _ \ / _` | | | |
32 | | |_) | (_) | (_| | |_| |
33 | |____/ \___/ \__,_|\__, |
34 | |___/
35 \*/
36
44 class Body
45 {
46 public:
48
49 private:
50 std::string m_name{"(undefined)"};
56
57 public:
61 Body() {}
62
67 Body(std::string const & t_name, Real const t_mass, Real const t_radius = 0.0)
68 {
69 this->name(t_name);
70 this->mass(t_mass);
71 this->radius(t_radius);
72 }
73
77 Body(Body const &) = default;
78
82 Body(Body &&) = default;
83
87 Body & operator=(const Body &) = default;
88
92 Body & operator=(Body &&) = default;
93
98 std::string name() const {return this->m_name;}
99
104 void name(std::string const & t_name)
105 {
106 #define CMD "Astro::Body::name(...): "
107
108 ASTRO_ASSERT(!t_name.empty(), CMD "name cannot be empty.");
109 this->m_name = t_name;
110
111 #undef CMD
112 }
113
118 Orbit const & orbit() const {return this->m_orbit;}
119
124 Orbit & set_orbit() {return this->m_orbit;}
125
130 Anomaly const & epoch_anomaly() const {return this->m_epoch_anom;}
131
136 Anomaly & set_epoch_anomaly() {return this->m_epoch_anom;}
137
142 void set_epoch_anomaly(Anomaly const & t_epoch_anomaly)
143 {
144 ASTRO_ASSERT(t_epoch_anomaly.sanity_check(),
145 "Astro::Orbit::epoch_anomaly(...): invalid orbital anomalies detected.");
146 this->m_epoch_anom = t_epoch_anomaly;
147 }
148
154 Anomaly anomaly(Real const t) const
155 {
156 Anomaly anom;
157 anom.set_lambda(
158 this->m_orbit.compute_lambda(this->m_epoch+t, this->m_epoch_anom), this->m_orbit.keplerian(), this->m_orbit.factor()
159 );
160 return anom;
161 }
162
167 Real epoch() const {return this->m_epoch;}
168
173 void set_epoch(Real const t_epoch) {this->m_epoch = t_epoch;}
174
179 Real mass() const {return this->m_mass;}
180
185 void mass(Real const t_mass) {
186 #define CMD "Astro::Body::mass(...): "
187
188 ASTRO_ASSERT(t_mass > 0.0, CMD "mass must be positive.");
189 this->m_mass = t_mass;
190
191 #undef CMD
192 }
193
198 Real radius() const {return this->m_radius;}
199
204 void radius(Real const t_radius) {
205 #define CMD "Astro::Body::radius(...): "
206
207 ASTRO_ASSERT(t_radius >= 0.0, CMD "radius must be non-negative.");
208 this->m_radius = t_radius;
209
210 #undef CMD
211 }
212
218 {
219 Vector6 cart(this->m_orbit.cartesian().vector());
220 ASTRO_ASSERT(cart.allFinite(),
221 "Astro::Body::cartesian(...): invalid cartesian state vector detected.");
222 return cart;
223 }
224
231 {
232 // Compute the anomaly at time t
233 Anomaly anom;
234 anom.set_lambda(
235 this->m_orbit.compute_lambda(this->m_epoch+t, this->m_epoch_anom), this->m_orbit.keplerian(), this->m_orbit.factor()
236 );
237
238 // Compute the cartesian state vector
240 OrbitalElements::equinoctial_to_cartesian(this->m_orbit.equinoctial(), anom.L, this->m_orbit.mu(), cart);
241 return cart.vector();
242 }
243
249 Vector3 cartesian_position(Real const t) const {return this->cartesian_state(t).head<3>();}
250
256 Vector3 cartesian_velocity(Real const t) const {return this->cartesian_state(t).tail<3>();}
257
262 void set_cartesian_state(Vector6 const & cart)
263 {
264 this->set_orbit().set_cartesian(cart);
265 }
266
273 {
274 Anomaly anom{this->anomaly(t)};
275 Vector6 kepl;
276 kepl <<
277 this->orbit().keplerian().a,
278 this->orbit().keplerian().e,
279 this->orbit().keplerian().i,
280 this->orbit().keplerian().Omega,
281 this->orbit().keplerian().omega,
282 anom.M;
283 ASTRO_ASSERT(kepl.allFinite(),
284 "Astro::Body::keplerian(...): invalid keplerian state vector detected.");
285 return kepl;
286 }
287
292 void set_keplerian_state(Vector6 const & state)
293 {
294 Real E{OrbitalElements::M_to_E(state(5), state(1))};
295 Real nu{OrbitalElements::E_to_nu(E, state(1))};
296 this->set_orbit().set_keplerian(state.head<5>(), nu);
297 }
298
305 {
306 Anomaly anom{this->anomaly(t)};
307 Vector6 equi;
308 equi <<
309 this->orbit().equinoctial().p,
310 this->orbit().equinoctial().f,
311 this->orbit().equinoctial().g,
312 this->orbit().equinoctial().h,
313 this->orbit().equinoctial().k,
314 anom.L;
315 ASTRO_ASSERT(equi.allFinite(),
316 "Astro::Body::equinoctial(...): invalid equinoctial state vector detected.");
317 return equi;
318 }
319
324 void set_equinoctial_state(Vector6 const & state)
325 {
326 this->set_orbit().set_equinoctial(state.head<5>(), state(5));
327 }
328
336 template<typename T>
337 Eigen::Matrix<T, 6, 1> cartesian_eom(Eigen::Matrix<T, 6, 1> const & x, Eigen::Matrix<T, 3, 1> const & thrust_rtn) const
338 {
339 // Compute the cartesian perturbation
340 Eigen::Matrix<T, 3, 1> thrust_xyz(this->orbit().cartesian_rtn_to_xyz(thrust_rtn));
341 thrust_xyz /= this->m_mass;
342
343 // Compute the equations of motion
344 T mur3{this->orbit().mu()/Power3(x.template head<3>().norm())};
345 return Eigen::Matrix<T, 6, 1>(
346 /* dx/dt */ x[3],
347 /* dy/dt */ x[4],
348 /* dz/dt */ x[5],
349 /* dv_x/dt */ thrust_xyz[0] - mur3*x[0],
350 /* dv_y/dt */ thrust_xyz[1] - mur3*x[1],
351 /* dv_z/dt */ thrust_xyz[2] - mur3*x[2]
352 );
353 }
354
362 Vector6 keplerian_eom(Vector6 const & x, Vector3 const & thrust) const
363 {
364 // https://farside.ph.utexas.edu/teaching/celestial/Celestial/node164.html
365
366 Real const & a{x[0]};
367 Real const & e{x[1]};
368 Real const & i{x[2]};
369 // Real const & Omega{x[3]};
370 Real const & omega{x[4]};
371 Real const & M{x[5]};
372 Real const & mu{this->orbit().mu()};
373
374 Real const C_rad{thrust[0]/this->m_mass};
375 Real const C_tan{thrust[1]/this->m_mass};
376 Real const C_nor{thrust[2]/this->m_mass};
377
378 // Mean motion
379 Real const n{std::sqrt(mu / std::pow(a, 3))};
380 Real const e2{e*e};
381 Real const ecc{std::sqrt(1.0 - e2)};
382
383 // Anomalies calculations
384 Real const E{OrbitalElements::M_to_E(M, e)};
385 Real const cos_E{std::cos(E)};
386 Real const nu{OrbitalElements::E_to_nu(E, e)};
387 Real const cos_nu{std::cos(nu)};
388 Real const sin_nu{std::sin(nu)};
389
390 // Other parameters
391 Real const p{a*(1.0 - e2)};
392 Real const r{p / (1.0 + e*cos_nu)};
393 Real const u{omega + nu};
394 Real const h{ecc * std::sqrt(mu*a)}; // Specific angular momentum
395
396 Real const sin_i{std::sin(i)};
397 Real const cos_i{std::cos(i)};
398 Real const sin_u{std::sin(u)};
399 Real const cos_u{std::cos(u)};
400
401 Vector6 res;
402 res <<
403 /* da/dt */ 2.0*a*h/(mu*(1.0 - e2))*(e*sin_nu*C_rad + (1.0 + e*cos_nu)*C_tan),
404 /* de/dt */ h/mu*(sin_nu*C_rad + (cos_nu + cos_E)*C_tan),
405 /* di/dt */ cos_u*r*C_nor/h,
406 /* dO/dt */ sin_u*r*C_nor/(h*sin_i),
407 /* do/dt */ -h/(mu*e)*(cos_nu*C_rad - (2.0 + e*cos_nu)/(1.0 + e*cos_nu)*sin_nu*C_tan) - cos_i*sin_u*r*C_nor/(h*sin_i),
408 /* dM/dt */ n + h*ecc/(mu*e) * ((cos_nu - 2.0*e*r/p)*C_rad - (1.0 + r/p)*sin_nu*C_tan);
409
410 return res;
411 }
412
420 Vector6 equinoctial_eom(Vector6 const & x, Vector3 const & thrust) const
421 {
422 // Source: https://spsweb.fltops.jpl.nasa.gov/portaldataops/mpg/MPG_Docs/Source%20Docs/EquinoctalElements-modified.pdf
423
424 Real const & p{x[0]};
425 Real const & f{x[1]};
426 Real const & g{x[2]};
427 Real const & h{x[3]};
428 Real const & k{x[4]};
429 Real const & L{x[5]};
430 Real const & mu{this->orbit().mu()};
431
432 Real sin_L{std::sin(L)};
433 Real cos_L{std::cos(L)};
434
435 Real w{1.0 + (f*cos_L + g*sin_L)};
436 Real s2{1.0 + (h*h + k*k)};
437 Real sqrt_p_mu{std::sqrt(p/mu)};
438
439 Real C_rad{thrust[0]/this->m_mass};
440 Real C_tan{thrust[1]/this->m_mass};
441 Real C_nor{thrust[2]/this->m_mass};
442
443 Vector6 res;
444 res <<
445 /* dp/dt */ 2.0*p*sqrt_p_mu/w*C_tan,
446 /* df/dt */ sqrt_p_mu*(+C_rad*sin_L + ((w + 1.0)*cos_L + f)*C_tan/w - (h*sin_L - k*cos_L)*g/w*C_nor),
447 /* dg/dt */ sqrt_p_mu*(-C_rad*cos_L + ((w + 1.0)*sin_L + g)*C_tan/w + (h*sin_L - k*cos_L)*g/w*C_nor),
448 /* dh/dt */ sqrt_p_mu*s2*C_nor*cos_L/(2.0*w),
449 /* dk/dt */ sqrt_p_mu*s2*C_nor*sin_L/(2.0*w),
450 /* dL/dt */ std::sqrt(mu*p) * Power2(w/p) + sqrt_p_mu/w*(h*sin_L - k*cos_L)*C_nor;
451
452 return res;
453 }
454
464 Vector6 equinoctial_eom(Vector6 const & x, Real thrust_rad, Real thrust_tan, Real thrust_nor) const
465 {
466 return this->equinoctial_eom(x, Vector3(thrust_rad, thrust_tan, thrust_nor));
467 }
468
484 template <Coordinates IntCoords, Coordinates OutCoords = Coordinates::CARTESIAN, Integer S>
485 bool integrate(Sandals::RungeKutta<Real, S, 6, 0> & rk, VectorX const &t_mesh, Vector6 const &ics,
486 Sandals::Solution<Real, 6, 0> & sol, bool const adaptive = false)
487 {
488 #define CMD "Astro::Body::integrate(...): "
489
490 // Create the solution object according to the integration coordinates
491 if constexpr (IntCoords == Coordinates::CARTESIAN) {
492 rk.explicit_system(
493 [this](Vector6 const & x, Real) -> Vector6 {return this->cartesian_eom<Real>(x, 50.0*Vector3::UnitZ());}, // f(x, t)
494 [](Vector6 const &, Real) -> Matrix6 {return ZEROS_MAT6;} // Jf_x(x, t)
495 );
496 } else if constexpr (IntCoords == Coordinates::KEPLERIAN) {
497 rk.explicit_system(
498 [this](Vector6 const & x, Real) -> Vector6 {return this->keplerian_eom(x, 50.0*Vector3::UnitZ());}, // f(x, t)
499 [](Vector6 const &, Real) -> Matrix6 {return ZEROS_MAT6;} // Jf_x(x, t)
500 );
501 } else if constexpr (IntCoords == Coordinates::EQUINOCTIAL) {
502 rk.explicit_system(
503 [this](Vector6 const & x, Real) -> Vector6 {return this->equinoctial_eom(x, 50.0*Vector3::UnitZ());}, // f(x, t)
504 [](Vector6 const &, Real) -> Matrix6 {return ZEROS_MAT6;} // Jf_x(x, t)
505 );
506 } else if constexpr (IntCoords == Coordinates::QUATERNIONIC) {
507 ASTRO_ERROR(CMD "quaternionic integration not implemented yet.");
508 } else {
509 ASTRO_ERROR(CMD "unknown coordinate system for the integration.");
510 }
511
512 // Transform the internal state to the output coordinate system
513 rk.step_callback([this, &sol](Integer const i, Vector6 const & x, Real const t) {
514
515 (void)sol; // Avoid unused variable warning
516
517 // Set the internal state according to the output coordinate system
518 if constexpr (IntCoords == Coordinates::CARTESIAN) {
519 this->set_cartesian_state(x);
520 } else if constexpr (IntCoords == Coordinates::KEPLERIAN) {
521 this->set_keplerian_state(x);
522 } else if constexpr (IntCoords == Coordinates::EQUINOCTIAL) {
523 this->set_equinoctial_state(x);
524 } else if constexpr (IntCoords == Coordinates::QUATERNIONIC) {
525 ASTRO_ERROR(CMD "quaternionic output not implemented yet.");
526 } else {
527 ASTRO_ERROR(CMD "unknown coordinate system for the output.");
528 }
529
530 // Set the output state in the solution object
531 if constexpr (IntCoords != OutCoords) {
532 if constexpr (OutCoords == Coordinates::CARTESIAN) {
533 sol.x.col(i) << this->cartesian_state(t);
534 } else if constexpr (OutCoords == Coordinates::KEPLERIAN) {
535 sol.x.col(i) << this->keplerian_state(t);
536 } else if constexpr (OutCoords == Coordinates::EQUINOCTIAL) {
537 sol.x.col(i) << this->equinoctial_state(t);
538 } else if constexpr (OutCoords == Coordinates::QUATERNIONIC) {
539 ASTRO_ERROR(CMD "quaternionic output not implemented yet.");
540 } else {
541 ASTRO_ERROR(CMD "unknown coordinate system for the output.");
542 }
543 }
544
545 });
546
547 // Solve the equations of motion
548 if (adaptive) {
549 return rk.adaptive_solve(t_mesh, ics, sol);
550 } else {
551 return rk.solve(t_mesh, ics, sol);
552 }
553
554 #undef CMD
555 }
556
557 }; // class Body
558
559} // namespace Astro
560
561#endif // ASTRO_BODY_HH
#define ASTRO_ASSERT(COND, MSG)
Definition Astro.hh:43
#define ASTRO_ERROR(MSG)
Definition Astro.hh:32
#define CMD
Real m_mass
Definition Body.hh:55
Anomaly const & epoch_anomaly() const
Definition Body.hh:130
void name(std::string const &t_name)
Definition Body.hh:104
Vector6 keplerian_state(Real const t) const
Definition Body.hh:272
Real m_radius
Definition Body.hh:54
Real m_epoch
Definition Body.hh:51
Body & operator=(Body &&)=default
void set_epoch_anomaly(Anomaly const &t_epoch_anomaly)
Definition Body.hh:142
Orbit const & orbit() const
Definition Body.hh:118
Vector6 cartesian_state() const
Definition Body.hh:217
void radius(Real const t_radius)
Definition Body.hh:204
Real epoch() const
Definition Body.hh:167
std::string name() const
Definition Body.hh:98
void set_cartesian_state(Vector6 const &cart)
Definition Body.hh:262
Body()
Definition Body.hh:61
Vector3 cartesian_position(Real const t) const
Definition Body.hh:249
Real mass() const
Definition Body.hh:179
void set_epoch(Real const t_epoch)
Definition Body.hh:173
Vector6 equinoctial_eom(Vector6 const &x, Vector3 const &thrust) const
Definition Body.hh:420
Real radius() const
Definition Body.hh:198
Orbit & set_orbit()
Definition Body.hh:124
Vector6 equinoctial_state(Real const t) const
Definition Body.hh:304
void set_equinoctial_state(Vector6 const &state)
Definition Body.hh:324
Body(Body const &)=default
OrbitalElements::Anomaly Anomaly
Definition Body.hh:47
Anomaly m_epoch_anom
Definition Body.hh:52
Vector6 equinoctial_eom(Vector6 const &x, Real thrust_rad, Real thrust_tan, Real thrust_nor) const
Definition Body.hh:464
bool integrate(Sandals::RungeKutta< Real, S, 6, 0 > &rk, VectorX const &t_mesh, Vector6 const &ics, Sandals::Solution< Real, 6, 0 > &sol, bool const adaptive=false)
Definition Body.hh:485
Anomaly & set_epoch_anomaly()
Definition Body.hh:136
Eigen::Matrix< T, 6, 1 > cartesian_eom(Eigen::Matrix< T, 6, 1 > const &x, Eigen::Matrix< T, 3, 1 > const &thrust_rtn) const
Definition Body.hh:337
Body(std::string const &t_name, Real const t_mass, Real const t_radius=0.0)
Definition Body.hh:67
void mass(Real const t_mass)
Definition Body.hh:185
Vector6 keplerian_eom(Vector6 const &x, Vector3 const &thrust) const
Definition Body.hh:362
std::string m_name
Definition Body.hh:50
Body(Body &&)=default
Anomaly anomaly(Real const t) const
Definition Body.hh:154
Vector3 cartesian_velocity(Real const t) const
Definition Body.hh:256
Orbit m_orbit
Definition Body.hh:53
Vector6 cartesian_state(Real const t) const
Definition Body.hh:230
Body & operator=(const Body &)=default
void set_keplerian_state(Vector6 const &state)
Definition Body.hh:292
Orbit class container.
Definition Orbit.hh:39
Real mu() const
Definition Orbit.hh:316
Keplerian const & keplerian() const
Definition Orbit.hh:140
void set_equinoctial(Real const t_p, Real const t_f, Real const t_g, Real const t_h, Real const t_k, Real const L)
Definition Orbit.hh:203
void set_cartesian(Real r_x, Real r_y, Real r_z, Real v_x, Real v_y, Real v_z)
Definition Orbit.hh:96
void set_keplerian(Real const t_a, Real const t_e, Real const t_i, Real const t_Omega, Real const t_omega, Real const nu)
Definition Orbit.hh:151
Equinoctial const & equinoctial() const
Definition Orbit.hh:192
Cartesian const & cartesian() const
Definition Orbit.hh:85
Real E_to_nu(Real const E, Real const e)
Definition OrbitalElements.hh:963
Real M_to_E(Real M, Real const e)
Definition OrbitalElements.hh:865
void equinoctial_to_cartesian(Equinoctial const &equi, Real const L, Real const mu, Cartesian &cart)
Definition OrbitalElements.hh:1487
The namespace for the Astro library.
Definition Astro.hh:73
enum class Coordinates :Integer {CARTESIAN=0, KEPLERIAN=1, EQUINOCTIAL=2, QUATERNIONIC=3} Coordinates
Definition Body.hh:26
int Integer
Definition Astro.hh:85
Real Power2(Real x)
Definition Utilities.hh:34
static Real const QUIET_NAN
Definition Astro.hh:134
Real Power3(Real x)
Definition Utilities.hh:42
Eigen::Vector< Real, 6 > Vector6
Definition Astro.hh:99
double Real
Definition Astro.hh:84
Eigen::Matrix< Real, 6, 6 > Matrix6
Definition Astro.hh:100
Eigen::Vector< Real, 3 > Vector3
Definition Astro.hh:93
Eigen::Vector< Real, Eigen::Dynamic > VectorX
Definition Astro.hh:108
static Matrix6 const ZEROS_MAT6
Definition Astro.hh:179
Structure container for the orbital anomalies.
Definition OrbitalElements.hh:1110
bool sanity_check() const
Definition OrbitalElements.hh:1323
void set_lambda(Real t_lambda, Keplerian const &kepl, Factor const I)
Set the mean longitude .
Definition OrbitalElements.hh:1248
Real L
Definition OrbitalElements.hh:1115
Real M
Definition OrbitalElements.hh:1112
Structure container for the cartesian orbital elements.
Definition OrbitalElements.hh:59
Vector6 vector() const
Definition OrbitalElements.hh:118
Real h
Definition OrbitalElements.hh:443
Real g
Definition OrbitalElements.hh:442
Real p
Definition OrbitalElements.hh:440
Real f
Definition OrbitalElements.hh:441
Real k
Definition OrbitalElements.hh:444
Real i
Definition OrbitalElements.hh:233
Real omega
Definition OrbitalElements.hh:235
Real e
Definition OrbitalElements.hh:232
Real Omega
Definition OrbitalElements.hh:234
Real a
Definition OrbitalElements.hh:231