This example shows how to create a sun-synchronous repeat ground track trajectory using the GODOT libraries.
The objective of the optimisation problem is to generate an orbit with a ground track repeat pattern. We need to specify the longitude and local time of the first ascending node. Besides, at the end of the 175-orbits cycle, the longitude and local time have to be the same as in the first node.
Import all the necessary libraries
import numpy as np
from godot.core import tempo, astro
from godot.model import common
from godot import cosmos
from godot.model import interface
from godot.core.autodif import bridge as br
# optionally avoid verbose logging messages
import godot.core.util as util
util.suppressLogger()
Input files:
uni_file = "universe.yml"
tra_file = "trajectory.yml"
pro_file = "problem.yml"
Load the universe and trajectory configuration, and instantiate them.
uni_config = cosmos.util.load_yaml(uni_file)
uni = cosmos.Universe(uni_config)
tra_config = cosmos.util.load_yaml(tra_file)
tra = cosmos.Trajectory(uni, tra_config)
This class provides the longitude at a specific point defined in the trajectory input file.
class LonPnt(interface.ScalarTimeEvaluable):
def __init__(self, uni, tra, point):
interface.ScalarTimeEvaluable.__init__(self)
self.__uni = uni
self.__tra = tra
self.__point = point
def eval(self, epoch):
t1 = self.__tra.point(self.__point) if type(epoch) is tempo.Epoch else self.__tra.xpoint(self.__point)
x1 = self.__uni.frames.vector3('Earth', 'SCREF_center', 'ITRF', t1)
lon1 = astro.sphericalFromCart(x1)[1]
return lon1
This class provides the local time at a specific point defined in the trajectory input file.
class LtPnt(interface.ScalarTimeEvaluable):
def __init__(self, uni, tra, point):
interface.ScalarTimeEvaluable.__init__(self)
self.__uni = uni
self.__tra = tra
self.__point = point
self.__erpFile = "data/orient/erp2000A.ipf"
self.__nutFile = "data/orient/nutation2000A.ipf"
self.__erot = astro.EarthRot(1, self.__erpFile, self.__nutFile)
def eval(self, epoch):
t1 = self.__tra.point(self.__point) if type(epoch) is tempo.Epoch else self.__tra.xpoint(self.__point)
x1 = self.__uni.frames.vector6('Earth', 'SCREF_center', 'ICRF', t1)
kep1 = astro.convert("Cart", "Kep", x1, self.__uni.constants.getMu("Earth"))
raan = kep1[3]
ut1 = self.__erot.ut1(br.Epoch(t1,False))
gst = self.__erot.gmst(br.Epoch(t1,False))
ut1 = (np.mod(ut1, 1) - 0.5)*2*np.pi
lt = raan - (gst - ut1) + np.pi + 10.0*2.0*np.pi
while (lt >= 2*np.pi):
lt = lt - 2*np.pi
while (lt < 0.0):
lt = lt + 2*np.pi
lt = 24.0*lt/2.0/np.pi
return lt
This class allows us computing differences of objects from the previous two classes.
class TEDiff(interface.ScalarTimeEvaluable):
def __init__(self, te1, te2):
interface.ScalarTimeEvaluable.__init__(self)
self.__te1 = te1
self.__te2 = te2
def eval(self, epoch):
return self.__te1.eval(epoch) - self.__te2.eval(epoch)
Load the problem configuration and create the problem object, compute the trajectory.
pro_config = cosmos.util.load_yaml(pro_file)
pro = cosmos.Problem(uni, [tra], pro_config)
tra.compute(True)
Now, we add instances of the previously defined classes to the universe as time evaluables.
if uni.evaluables.contains( "firstLongitude" ):
uni.evaluables.remove( "firstLongitude" )
uni.evaluables.add( "firstLongitude", LonPnt(uni, tra, "refASC00001"))
if uni.evaluables.contains( "firstLTAN" ):
uni.evaluables.remove( "firstLTAN" )
uni.evaluables.add( "firstLTAN", LtPnt(uni, tra, "refASC00001"))
if uni.evaluables.contains( "diffLongitude" ):
uni.evaluables.remove( "diffLongitude" )
uni.evaluables.add( "diffLongitude", TEDiff(LonPnt(uni, tra, "refASC00176"), LonPnt(uni, tra, "refASC00001")))
if uni.evaluables.contains( "diffLTAN" ):
uni.evaluables.remove( "diffLTAN" )
uni.evaluables.add( "diffLTAN", TEDiff(LtPnt(uni, tra, "refASC00176"), LtPnt(uni, tra, "refASC00001")))
The following function allows us computing a variation in a specified universe's parameter in order to change a universe's time evaluable to meet a given target. The variation applied is linear, making use of the partial of the time evaluable with respect to the parameter.
def control_constraint(constraint_name, parameter, target):
# Evaluate the constraint at any point
constraint = common.resolve( uni.evaluables.get(constraint_name).eval(tempo.XEpoch("0.0 TT") ) )
# Extract the partial you want
dg_dp = constraint.at(parameter)
# Get the initial value of the contraint
g0 = constraint.value()
# Set the target value
g1 = target
# update the value of the parameter in the parameter book
p = uni.parameters.get(parameter).getPhysicalValue()
uni.parameters.get(parameter).setPhysicalValue( p + ( g1 - g0 ) / dg_dp )
Iterate solve the problem
for it in [1, 2]:
print("Iteration: ", it)
control_constraint("firstLTAN", "start_SCREF_center_ran", 18.0)
tra.compute(True)
control_constraint("firstLongitude", "start_dt", np.deg2rad(74.20205))
tra.compute(True)
control_constraint("diffLTAN", "start_SCREF_center_inc", 0.0)
tra.compute(True)
control_constraint("diffLongitude", "start_SCREF_center_sma", 0.0)
tra.compute(True)
Check the final solution. Print the output trajectory in yml.
lon1 = uni.evaluables.get("firstLongitude")
ltan1 = uni.evaluables.get("firstLTAN")
lonDiff = uni.evaluables.get("diffLongitude")
ltanDiff = uni.evaluables.get("diffLTAN")
print("Longitude of first node: ", np.rad2deg(lon1.eval(tempo.Epoch())))
print("Local time of first node: ", ltan1.eval(tempo.Epoch()))
print("Longitude difference: ", lonDiff.eval(tempo.Epoch()))
print("Local time difference: ", ltanDiff.eval(tempo.Epoch()))
up = tra.applyParameterChanges()
tra_config = cosmos.util.deep_update(tra_config, up)
cosmos.util.save_yaml(tra_config, "trajectory_reforb.yml")