Custom Event Condition¶
In this notebook, we will:
Create two custom event conditions
Beta angle condition (real value based condition)
Eclipse condition (boolean value based condition)
import numpy as np
from ostk.physics import Environment
from ostk.physics.environment.object.celestial import Earth
from ostk.physics.environment.object.celestial import Sun
from ostk.physics.environment.gravitational import Earth as EarthGravitationalModel
from ostk.physics.environment.atmospheric import Earth as EarthAtmosphericModel
from ostk.physics.environment.magnetic import Earth as EarthMagneticModel
from ostk.physics.time import Instant
from ostk.physics.time import Duration
from ostk.physics.coordinate import Frame
from ostk.physics.coordinate import Position
from ostk.physics.coordinate import Velocity
from ostk.astrodynamics.trajectory import State
from ostk.astrodynamics.trajectory import Propagator
from ostk.astrodynamics.trajectory.state import NumericalSolver
from ostk.astrodynamics.event_condition import BooleanCondition
from ostk.astrodynamics.event_condition import RealCondition
Setup the initial state and propagation interval
start_instant = Instant.J2000()
initial_state = State(
instant=start_instant,
position=Position.meters([7000000.0, 0.0, 0.0], Frame.GCRF()),
velocity=Velocity.meters_per_second(
[0.0, 5335.865450622126, 5335.865450622126], Frame.GCRF()
),
)
earth = Earth.from_models(
EarthGravitationalModel(EarthGravitationalModel.Type.Spherical),
EarthMagneticModel(EarthMagneticModel.Type.Undefined),
EarthAtmosphericModel(EarthAtmosphericModel.Type.Undefined),
)
environment = Environment(Instant.J2000(), [earth, Sun.default()])
Eclipse condition¶
A boolean condition is defined by:
A name
A criteria that defines what kind of root crossing is desired
The evaluation function which accepts 2 arguments
a state_vector
a time
If the condition is to be inverted
def in_eclipse(state):
# global environment
environment.set_instant(state.get_instant())
return environment.is_position_in_eclipse(state.get_position())
eclipse_condition = BooleanCondition(
"Eclipse Condition", BooleanCondition.Criterion.AnyCrossing, in_eclipse, False
)
Beta angle condition¶
A real condition is similarly defined by:
A name
A criteria that defines what kind of root crossing is desired
The evaluation function
The target value
sun = Sun.default()
def get_beta_angle(state):
instant = state.get_instant()
sun_vector = sun.get_position_in(Frame.GCRF(), instant).get_coordinates()
orbit_plane_normal = np.cross(
state.get_position().get_coordinates(), state.get_velocity().get_coordinates()
)
return 90 - np.rad2deg(
np.arccos(
np.clip(
np.dot(
sun_vector / np.linalg.norm(sun_vector),
orbit_plane_normal / np.linalg.norm(orbit_plane_normal),
),
-1.0,
1.0,
)
)
)
beta_angle_condition = RealCondition(
"Beta Angle Condition", RealCondition.Criterion.AnyCrossing, get_beta_angle, 21.17
)
propagator = Propagator.from_environment(
NumericalSolver.default_conditional(), environment
)
Let's find the first time the satellite is in eclipse
solution = propagator.calculate_state_to_condition(
initial_state, initial_state.get_instant() + Duration.hours(24.0), eclipse_condition
)
print(
f"Satellite eclipse state [{in_eclipse(solution.state)}] at {solution.state.get_instant().to_string()}"
)
Satellite eclipse state [True] at 2000-01-01 12:09:00.876.134.713 [UTC]
Let's now find when the beta angle reaches a target value
solution = propagator.calculate_state_to_condition(
initial_state,
initial_state.get_instant() + Duration.hours(24.0),
beta_angle_condition,
)
print(
f"Satellite beta angle [{get_beta_angle(solution.state)}] at {solution.state.get_instant().to_string()}"
)
Satellite beta angle [21.16999999999848] at 2000-01-01 18:52:09.813.823.449 [UTC]