Skip to content

File StateHistory.cpp

File List > astrea > astro > astro > state > StateHistory.cpp

Go to the documentation of this file

/*
 * The GNU Lesser General Public License (LGPL)
 *
 * Copyright (c) 2025 Jay Iuliano
 *
 * This file is part of Astrea.
 * Astrea is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * Astrea is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should
 * have received a copy of the GNU General Public License along with Astrea. If not, see <https://www.gnu.org/licenses/>.
 */

#include <astro/state/StateHistory.hpp>

#include <iostream>

#include <mp-units/math.h>

using namespace mp_units;

namespace astrea {
namespace astro {


State& StateHistory::operator[](const Date& date) { return _states[date]; }
const State& StateHistory::at(const Date& date) const { return _states.at(date); }

void StateHistory::insert(const State& state) { _states.insert({ state.get_epoch(), state }); }
std::size_t StateHistory::size() const { return _states.size(); }
void StateHistory::clear() { _states.clear(); }

const State& StateHistory::get_closest_state(const Date& date) const
{
    // If exact, return
    if (_states.contains(date)) { return _states.at(date); }

    // Check if input date is out of bounds
    auto iter = _states.lower_bound(date);
    if (iter == _states.begin()) { return first(); }
    else if (iter == _states.end()) {
        return last();
    }

    // Compare date before and after index
    const Time upperDiff = abs((iter)->first - date);
    const Time lowerDiff = abs(std::prev(iter)->first - date);

    // Return closest
    if (lowerDiff < upperDiff) { return std::prev(iter)->second; }
    else {
        return (iter)->second;
    }
}

State StateHistory::get_state_at(const Date& date) const
{
    if (_states.size() == 0) { throw std::runtime_error("No states stored in StateHistory to extrapolate from."); }

    // If exact, return
    if (_states.contains(date)) { return _states.at(date); }

    // Check if input date is out of bounds
    auto iter = _states.lower_bound(date);
    if (iter == _states.begin()) {
        std::ostringstream oss;
        oss << "Cannot extrapolate to date (" << date << ") before first state (" << _states.begin()->first
            << "). Try repropagating to include all desired dates.";
        throw std::runtime_error(oss.str());
    }
    else if (iter == _states.end()) {
        std::ostringstream oss;
        oss << "Cannot extrapolate to date (" << date << ") after last state (" << _states.rbegin()->first
            << "). Try repropagating to include all desired dates.";
        throw std::runtime_error(oss.str());
    }

    // Interpolate
    const Date& postDate                = iter->first;
    const OrbitalElements& postElements = (iter->second).get_elements();

    const Date& preDate                = std::prev(iter)->first;
    const State& preState              = std::prev(iter)->second;
    const OrbitalElements& preElements = preState.get_elements();

    const AstrodynamicsSystem& system = preState.get_system();
    const auto& mu                    = system.get_mu();

    // Normalize to initial date for simplicity
    const Time time0 = 0.0 * astrea::detail::time_unit;
    const Time timef = postDate - preDate;
    const Time time  = date - preDate;

    const OrbitalElements interpolatedElements = preElements.interpolate(time0, timef, postElements, mu, time);
    return State({ interpolatedElements, date, system });

    // // Insert if we want this to store
    // _states[date] = interpolatedState;

    // return _states.at(date);
}

} // namespace astro
} // namespace astrea