Nadir Tracking (With Yaw Compensation)¶
This tutorial demonstrates how to generate a nadir tracking profile, and generate Yaw-Pitch-Roll (YPR) angles.
Setup¶
import math
import pandas as pd
import plotly.express as px
from ostk.mathematics.geometry.d3.transformation.rotation import EulerAngle
from ostk.physics import Environment
from ostk.physics.unit import Length
from ostk.physics.unit import Angle
from ostk.physics.time import Scale
from ostk.physics.time import Instant
from ostk.physics.time import Duration
from ostk.physics.time import Interval
from ostk.physics.time import DateTime
from ostk.physics.time import Time
from ostk.physics.coordinate import Frame
from ostk.physics.coordinate.spherical import LLA
from ostk.astrodynamics import Trajectory
from ostk.astrodynamics.trajectory import State
from ostk.astrodynamics.trajectory import Orbit
from ostk.astrodynamics.trajectory.model import Nadir
from ostk.astrodynamics.flight import Profile
Computation¶
Initialize an environment:
environment = Environment.default(True)
earth = environment.access_celestial_object_with_name("Earth")
Construct an orbit:
orbit = Orbit.sun_synchronous(
epoch=Instant.date_time(DateTime(2024, 1, 1, 0, 0, 0), Scale.UTC),
altitude=Length.kilometers(550.0),
local_time_at_descending_node=Time(14, 30, 0),
celestial_object=earth,
)
Define an interval of interest:
interval = Interval.closed(
start_instant=Instant.date_time(DateTime(2024, 1, 1, 0, 0, 0), Scale.UTC),
end_instant=Instant.date_time(DateTime(2024, 1, 1, 2, 0, 0), Scale.UTC),
)
Construct a Vehicle Velocity, Local Horizontal (VVLH) frame from the orbit:
vvlh_frame = orbit.get_orbital_frame(Orbit.FrameType.VVLH)
Generate a flight profile:
instants = interval.generate_grid(Duration.seconds(20.0))
nadir_trajectory = Trajectory(
model=Nadir(
orbit=orbit,
)
)
uncompensated_profile = Profile.local_orbital_frame_pointing(
orbit=orbit,
orbital_frame_type=Orbit.FrameType.VVLH,
)
yaw_compensated_profile = Profile.custom_pointing(
orbit=orbit,
alignment_target=Profile.TrajectoryTarget.target_position(
trajectory=nadir_trajectory,
axis=Profile.Axis.Z,
),
clocking_target=Profile.TrajectoryTarget.target_velocity(
trajectory=nadir_trajectory,
axis=Profile.Axis.X,
),
)
Calculate and plot the Yaw-Pitch-Roll (YPR) angles in the VVLH frame during the access:
def reduce_angle(angle: Angle):
# Reduce angle to be within the range 0 to 2*pi
reduced_angle = float(angle.in_radians()) % (2 * math.pi)
# If angle is negative, add 2*pi to make it positive
if reduced_angle < 0:
reduced_angle += 2 * math.pi
return Angle.radians(reduced_angle)
def calculate_ypr(state: State):
state_vvlh = state.in_frame(vvlh_frame)
q_B_VVLH = state_vvlh.get_attitude()
ypr = EulerAngle.quaternion(q_B_VVLH, EulerAngle.AxisSequence.ZYX)
phi = ypr.phi
theta = ypr.theta
psi = ypr.psi
if phi.in_radians() > Angle.half_pi().in_radians():
phi = reduce_angle(phi + Angle.pi())
theta = reduce_angle(Angle.pi() - theta)
psi = reduce_angle(psi + Angle.pi())
if theta.in_radians() > Angle.pi().in_radians():
theta = theta - Angle.two_pi()
satellite_lla = LLA.cartesian(
cartesian_coordinates=state.in_frame(Frame.ITRF())
.get_position()
.get_coordinates(),
ellipsoid_equatorial_radius=earth.get_equatorial_radius(),
ellipsoid_flattening=earth.get_flattening(),
)
ascending = state.get_velocity().get_coordinates()[2] > 0
return (
state.get_instant().get_date_time(Scale.UTC),
float(satellite_lla.get_longitude().in_degrees()),
float(satellite_lla.get_latitude().in_degrees()),
float(satellite_lla.get_altitude().in_meters()),
float(phi.in_degrees()) if not ascending else float(phi.in_degrees() - 360.0),
float(theta.in_degrees()),
float(psi.in_degrees()),
ascending
)
yprs = pd.DataFrame(
data=[
calculate_ypr(state)
for state in yaw_compensated_profile.get_states_at(
instants=instants,
)
],
columns=[
"Timestamp",
"Longitude [deg]",
"Latitude [deg]",
"Altitude [m]",
"Yaw [deg]",
"Pitch [deg]",
"Roll [deg]",
"Ascending"
],
)
px.scatter(
yprs,
x="Latitude [deg]",
y="Yaw [deg]",
title="Yaw Evolution by Latitude",
color="Ascending",
)