open-space-collective / open-space-toolkit-astrodynamics

Flight profile, orbit, attitude, access.
https://open-space-collective.github.io/open-space-toolkit-astrodynamics/
Apache License 2.0
58 stars 14 forks source link

[fix] Access generation fails when using a Tabulated Orbit model #322

Open kyle-cochran opened 10 months ago

kyle-cochran commented 10 months ago

Describe the bug There seems to be a bug in Access generation where we request the state at an instant that is beyond the end of the search interval. This is not a problem in Orbit models such as SGP4 where states can be requested at arbitrary instants.

However, this causes issues when using a Tabulated Orbit model with a fixed validity window. Requesting an Instant outside of the valid range results in an error like: Provided instant [2024-02-01 19:30:59.620.066 [UTC]] is outside of interpolation range [2024-01-18 19:40:59.620.066 [UTC], 2024-02-01 19:30:49.620.066 [UTC].

Steps to reproduce Steps to reproduce the behavior (add code snippets as needed):

  1. Run access generation with a Tabulated Orbit model.

Expected behavior Access generation works for Tabulated orbit models.

Additionally, if access generation is performed on a tabulated model without sufficient data for the search interval, it would be nice to have a useful error that says there is not sufficient data.

Additional context Fuller error log that makes me suspect the source of the issue:

Provided instant [2024-02-01 19:30:59.620.066 [UTC]] is outside of interpolation range [2024-01-18 19:40:59.620.066 [UTC], 2024-02-01 19:30:49.620.066 [UTC].

[ERROR] 2024-01-18 19:44:24.542 [P:1] [T:140272016541440] 0# ostk::core::error::RuntimeError::RuntimeError(ostk::core::types::String const&) at /app/src/OpenSpaceToolkit/Core/Error/RuntimeError.cpp:14
[ERROR] 2024-01-18 19:44:24.542 [P:1] [T:140272016541440] 1# ostk::astro::trajectory::models::Tabulated::calculateStateAt(ostk::physics::time::Instant const&) const [clone .cold] at /app/src/OpenSpaceToolkit/Astrodynamics/Trajectory/Models/Tabulated.cpp:161
[ERROR] 2024-01-18 19:44:24.542 [P:1] [T:140272016541440] 2# virtual thunk to ostk::astro::trajectory::orbit::models::Tabulated::calculateStateAt(ostk::physics::time::Instant const&) const at /app/src/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Models/Tabulated.cpp:53
[ERROR] 2024-01-18 19:44:24.542 [P:1] [T:140272016541440] 3# ostk::astro::Trajectory::getStateAt(ostk::physics::time::Instant const&) const at /app/src/OpenSpaceToolkit/Astrodynamics/Trajectory.cpp:92
[ERROR] 2024-01-18 19:44:24.542 [P:1] [T:140272016541440] 4# ostk::astro::access::GeneratorContext::GetStatesAt(ostk::physics::time::Instant const&, ostk::astro::Trajectory const&, ostk::astro::Trajectory const&) at /app/src/OpenSpaceToolkit/Astrodynamics/Access/Generator.cpp:519
[ERROR] 2024-01-18 19:44:24.542 [P:1] [T:140272016541440] 5# ostk::astro::access::GeneratorContext::isAccessActive(ostk::physics::time::Instant const&) at /app/src/OpenSpaceToolkit/Astrodynamics/Access/Generator.cpp:469
[ERROR] 2024-01-18 19:44:24.542 [P:1] [T:140272016541440] 6# ostk::astro::solvers::TemporalConditionSolver::EvaluateConditionAt(ostk::physics::time::Instant const&, ostk::core::ctnr::Array<std::function<bool (ostk::physics::time::Instant const&)> > const&) at /app/src/OpenSpaceToolkit/Astrodynamics/Solvers/TemporalConditionSolver.cpp:144
[ERROR] 2024-01-18 19:44:24.542 [P:1] [T:140272016541440] 7# ostk::astro::solvers::TemporalConditionSolver::solve(ostk::core::ctnr::Array<std::function<bool (ostk::physics::time::Instant const&)> > const&, ostk::physics::time::Interval const&) const at /app/src/OpenSpaceToolkit/Astrodynamics/Solvers/TemporalConditionSolver.cpp:73
[ERROR] 2024-01-18 19:44:24.542 [P:1] [T:140272016541440] 8# ostk::astro::solvers::TemporalConditionSolver::solve(std::function<bool (ostk::physics::time::Instant const&)> const&, ostk::physics::time::Interval const&) const at /app/src/OpenSpaceToolkit/Astrodynamics/Solvers/TemporalConditionSolver.cpp:46
[ERROR] 2024-01-18 19:44:24.542 [P:1] [T:140272016541440] 9# ostk::astro::access::Generator::computeAccesses(ostk::physics::time::Interval const&, ostk::astro::Trajectory const&, ostk::astro::Trajectory const&) const at /usr/include/c++/9/bits/std_function.h:369
kyle-cochran commented 10 months ago

Have not been able to replicate this with a smaller example. It's happening in the context of some Loft code.

Here is the current working notebook to try and replicate the issue. This computes accesses successfully. Not sure what is being done differently in the other code.

from datetime import datetime, timedelta

from ostk.mathematics.objects import RealInterval

from ostk.physics import Environment as OSTkEnvironment
from ostk.physics.time import Instant, Scale, Interval, Duration
from ostk.physics.coordinate import Position, Frame
from ostk.physics.coordinate.spherical import LLA, AER

from ostk.astrodynamics.access import Generator
from ostk.astrodynamics import Trajectory
from ostk.astrodynamics.trajectory import Orbit as OSTkOrbit
from ostk.astrodynamics.trajectory import State as OSTkState
from ostk.astrodynamics.trajectory.orbit.models import SGP4 as OSTkSGP4
from ostk.astrodynamics.trajectory.orbit.models.sgp4 import TLE as OSTkTLE
from ostk.astrodynamics.trajectory.orbit.models import Tabulated as OSTkTabulated

# make some fake data from a TLE orbit
time_step = timedelta(seconds=5)

time_span_lower = datetime(2019, 1, 10)
time_span_upper = datetime(2019, 1, 15)

tle_line1 = "1 39419U 13066D   19010.71180397 -.00000353  00000-0 -27933-4 0  9990"
tle_line2 = "2 39419  97.6151  78.1610 0014346 151.8501 208.3493 14.93932599279976"

ostk_environment: OSTkEnvironment = OSTkEnvironment.default()

orbit: OSTkOrbit = OSTkOrbit(
    model=OSTkSGP4(OSTkTLE(first_line=tle_line1, second_line=tle_line2)),
    celestial_object=ostk_environment.access_celestial_object_with_name("Earth"),
)

start_instant = Instant.date_time(time_span_lower, Scale.UTC)
end_instant = Instant.date_time(time_span_upper, Scale.UTC)

interval: Interval = Interval.closed(start_instant, end_instant)

instants: list[Instant] = interval.generate_grid(
    Duration.seconds(time_step.total_seconds())
)

states = orbit.get_states_at(instants)

# create tabulated orbit
tabulated_model = OSTkTabulated(
    states=states,
    initial_revolution_number=0,
    interpolation_type=OSTkTabulated.InterpolationType.BarycentricRational,
)

tab_orbit: OSTkOrbit = OSTkOrbit(
    model=tabulated_model,
    celestial_object=ostk_environment.access_celestial_object_with_name("Earth"),
)

# create access generator with spatial constraints

az_range = RealInterval.closed(
    10.0,
    80.0,
)

access_generator = Generator.aer_ranges(
    az_range, az_range, RealInterval.closed(0.0, 1000e3), ostk_environment
)

# access_generator = Generator(ostk_environment)

earth = ostk_environment.access_celestial_object_with_name("Earth")

targetPosition = Position.meters(
    LLA.vector([70, 0, 0]).to_cartesian(
        earth.get_equatorial_radius(), earth.get_flattening()
    ),
    Frame.ITRF(),
)

targetTrajectory = Trajectory.position(targetPosition);

# Compute accesses
access_generator.compute_accesses(interval, targetTrajectory, tab_orbit);