File AstrodynamicsSystem.hpp¶
File List > astrea > astro > astro > systems > AstrodynamicsSystem.hpp
Go to the documentation of this file
#pragma once
#include <memory>
#include <unordered_set>
#include <vector>
#include <astro/systems/CelestialBody.hpp>
#include <astro/systems/planetary_bodies/planetary_bodies.hpp>
#include <astro/time/Date.hpp>
#include <astro/types/enums.hpp>
namespace astrea {
namespace astro {
class AstrodynamicsSystem {
public:
constexpr AstrodynamicsSystem(
const CelestialBodyId& centralBody = CelestialBodyId::EARTH,
const std::unordered_set<CelestialBodyId>& secondaryBodies = {}
) :
_centerType(SystemCenter::CENTRAL_BODY),
_centralBody(centralBody)
{
add_body(centralBody);
for (const auto& body : secondaryBodies) {
add_body(body);
}
}
constexpr AstrodynamicsSystem(const CelestialBody& centralBody, const std::unordered_set<CelestialBodyId>& secondaryBodies = {}) :
_centerType(SystemCenter::CENTRAL_BODY),
_centralBody(centralBody.get_id())
{
add_body(centralBody);
for (const auto& body : secondaryBodies) {
add_body(body);
}
}
constexpr AstrodynamicsSystem(const CelestialBody& centralBody, const std::unordered_set<CelestialBody>& secondaryBodies = {}) :
_centerType(SystemCenter::CENTRAL_BODY),
_centralBody(centralBody.get_id())
{
add_body(centralBody);
for (const auto& body : secondaryBodies) {
add_body(body);
}
}
~AstrodynamicsSystem() = default;
AstrodynamicsSystem(const AstrodynamicsSystem&) = delete;
AstrodynamicsSystem& operator=(const AstrodynamicsSystem&) = delete;
AstrodynamicsSystem(AstrodynamicsSystem&&) = default;
AstrodynamicsSystem& operator=(AstrodynamicsSystem&&) = default;
static constexpr AstrodynamicsSystem EarthMoon()
{
return AstrodynamicsSystem(CelestialBodyId::EARTH, { CelestialBodyId::MOON });
}
constexpr const SystemCenter& get_center_type() const { return _centerType; }
constexpr const CelestialBodyId& get_central_body_id() const { return _centralBody; }
constexpr const CelestialBodyUniquePtr& get_central_body() const
{
switch (_centerType) {
case SystemCenter::CENTRAL_BODY: return get_body(_centralBody);
case SystemCenter::BARYCENTER:
default: throw std::runtime_error("Barycenteric systems have no central body.");
}
}
constexpr const CelestialBodyUniquePtr& get_body(const CelestialBodyId& id) const
{
if (_bodies.count(id) > 0) { return _bodies.at(id); }
throw std::out_of_range("Input gravitational body not found.");
}
template <typename T, typename... Args>
requires(std::is_base_of<CelestialBody, T>::value)
constexpr const CelestialBodyUniquePtr& add_body(Args&&... args)
{
const CelestialBodyId id = T::get_id();
if (_bodies.count(id) == 0) {
CelestialBodyUniquePtr body = std::make_unique<T>(std::forward<Args>(args)...);
_bodies.emplace(id, std::move(body));
_activeBodies.insert(id);
_root = find_common_root(_activeBodies);
}
return get_body(id);
}
constexpr const CelestialBodyUniquePtr& add_body(const CelestialBodyId& id)
{
if (_bodies.count(id) == 0) {
_bodies.emplace(id, create_impl(id));
_activeBodies.insert(id);
_root = find_common_root(_activeBodies);
}
return get_body(id);
}
constexpr const CelestialBodyUniquePtr& add_body(const CelestialBody& body)
{
const CelestialBodyId id = body.get_id();
if (_bodies.count(id) == 0) {
_bodies.emplace(id, std::make_unique<CelestialBody>(body));
_activeBodies.insert(id);
_root = find_common_root(_activeBodies);
}
return get_body(id);
}
constexpr CelestialBodyUniquePtr add_body(const CelestialBodyId& id) const { return create_impl(id); }
constexpr const auto& get_all_bodies() const { return _bodies; }
constexpr const CelestialBodyId& get_system_root() const { return _root; }
constexpr GravParam get_mu() const
{
switch (_centerType) {
case SystemCenter::CENTRAL_BODY: return get_central_body()->get_mu();
case SystemCenter::BARYCENTER:
throw std::runtime_error("Barycenteric systems have not been implemented yet.");
default: throw std::runtime_error("AstrodynamicsSystem::get_mu: Unknown system center type.");
}
}
constexpr const std::size_t size() const { return _bodies.size(); }
constexpr void clear() { return _bodies.clear(); }
CartesianVector<Distance, frames::solar_system_barycenter::icrf>
get_relative_position(const Date& date, const CelestialBodyId id1, const CelestialBodyId id2) const;
using iterator = std::unordered_map<CelestialBodyId, CelestialBodyUniquePtr>::iterator;
using const_iterator = std::unordered_map<CelestialBodyId, CelestialBodyUniquePtr>::const_iterator;
constexpr auto begin() const { return _bodies.begin(); }
constexpr auto end() const { return _bodies.end(); }
private:
SystemCenter _centerType;
CelestialBodyId _centralBody;
std::unordered_set<CelestialBodyId> _activeBodies;
CelestialBodyId _root;
std::unordered_map<CelestialBodyId, CelestialBodyUniquePtr> _bodies;
constexpr CelestialBodyId find_common_root(const std::unordered_set<CelestialBodyId>& bodies)
{
// If there's only one body, it is the root
if (bodies.size() == 1) { return *(bodies.begin()); }
// Count total planets
CelestialBodyId root;
std::size_t planetCount = 0;
for (const auto& id : bodies) {
const auto& body = get_body(id);
if (body->get_type() == CelestialBodyType::PLANET) {
planetCount++;
root = id;
}
}
// Check if other bodies are children of only planet -
// assumes the common root cannot be a satellite
if (planetCount == 1) {
for (const auto& id : bodies) {
CelestialBodyId parentId = id;
while (parentId != CelestialBodyId::SUN && parentId != _root) {
// Don't add parent to active bodies if it's not already there
parentId = add_body(parentId)->get_parent();
}
// If any object not in same planetary system, the common root
// must be the Sun
if (parentId == CelestialBodyId::SUN) {
root = CelestialBodyId::SUN;
break;
}
}
}
else {
// The only common root for multiple planets is the Sun
root = CelestialBodyId::SUN;
}
return root;
}
constexpr CelestialBodyUniquePtr create_impl(const CelestialBodyId& id) const
{
using namespace planetary_bodies;
switch (id) {
case (CelestialBodyId::SUN): {
return std::make_unique<Sun>();
}
case (CelestialBodyId::MERCURY): {
return std::make_unique<Mercury>();
}
case (CelestialBodyId::VENUS): {
return std::make_unique<Venus>();
}
case (CelestialBodyId::EARTH): {
return std::make_unique<Earth>();
}
case (CelestialBodyId::MOON): {
return std::make_unique<Moon>();
}
case (CelestialBodyId::MARS): {
return std::make_unique<Mars>();
}
case (CelestialBodyId::PHOBOS): {
return std::make_unique<Phobos>();
}
case (CelestialBodyId::DEIMOS): {
return std::make_unique<Deimos>();
}
case (CelestialBodyId::JUPITER): {
return std::make_unique<Jupiter>();
}
case (CelestialBodyId::GANYMEDE): {
return std::make_unique<Ganymede>();
}
case (CelestialBodyId::CALLISTO): {
return std::make_unique<Callisto>();
}
case (CelestialBodyId::IO): {
return std::make_unique<Io>();
}
case (CelestialBodyId::EUROPA): {
return std::make_unique<Europa>();
}
case (CelestialBodyId::SATURN): {
return std::make_unique<Saturn>();
}
case (CelestialBodyId::TITAN): {
return std::make_unique<Titan>();
}
case (CelestialBodyId::RHEA): {
return std::make_unique<Rhea>();
}
case (CelestialBodyId::IAPETUS): {
return std::make_unique<Iapetus>();
}
case (CelestialBodyId::URANUS): {
return std::make_unique<Uranus>();
}
case (CelestialBodyId::TITANIA): {
return std::make_unique<Titania>();
}
case (CelestialBodyId::OBERON): {
return std::make_unique<Oberon>();
}
case (CelestialBodyId::NEPTUNE): {
return std::make_unique<Neptune>();
}
case (CelestialBodyId::TRITON): {
return std::make_unique<Triton>();
}
default: throw std::runtime_error("Error: Celestial body not implemented in factory.");
}
}
};
} // namespace astro
} // namespace astrea