#
# ISC License
#
# Copyright (c) 2021, Autonomous Vehicle Systems Lab, University of Colorado at Boulder
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
r"""
.. youtube:: uUomHSGQW3c
:width: 560
:height: 315
Overview
--------
Demonstrates how to setup a custom gravity object in Basilisk that is not directly supported by
the ``simIncludeGravBody.py`` file. In this simulation the sun is created using standard values, the Earth
is created using custom values, and the asteroid Itokawa is created with custom values.
.. image:: /_images/static/scenarioCustomGravObject.jpg
:align: center
Further, the Vizard binary file is setup to load up a custom CAD model for the asteroid. The spacecraft
orbit is defined relative to the asteroid. Note, this feature requires :ref:`Vizard <vizard>` version 1.8 or higher.
The script is found in the folder ``basilisk/examples`` and executed by using::
python3 scenarioCustomGravBody.py
The simulation layout is shown in the following illustration. A single simulation process is created
which contains both modules.
.. image:: /_images/static/test_scenarioCustomGravBody.svg
:align: center
:ref:`planetEphemeris` is used to create the planet ephemeris states. The sun is assumed to be stationary,
while Earth is on a circular orbit and Itokawa is on its elliptical heliocentric orbit.
The method ``createCustomGravObject()`` is used to create the BSK grav bodies for both earth and Itokawa.
The earth body is already supported in :ref:`simIncludeGravBody`, but in this script we show how this could
be customized. The gravity body ephemeris states are connected to the :ref:`planetEphemeris` planet
state output messages.
The ephemeris outputs remain heliocentric because the Sun gravity body is not
connected to an ephemeris message. Its default zero state therefore represents
the Sun at the heliocentric origin. Setting ``planetEphemeris.zeroBase`` to
Itokawa would incorrectly place both the Sun and Itokawa at the origin. To plot
the spacecraft motion relative to the asteroid, the script instead records the
Itokawa state and subtracts it from the spacecraft state during post-processing.
The simulation executes and shows a plot of the spacecraft motion relative to the asteroid.
Illustration of Simulation Results
----------------------------------
::
show_plots = True
.. image:: /_images/Scenarios/scenarioCustomGravBody1.svg
:align: center
"""
#
# Basilisk Scenario Script and Integrated Test
#
# Purpose: Basic simulation showing how to setup a custom gravity object
# Author: Hanspeter Schaub
# Creation Date: Feb. 23, 2021
#
import os
import matplotlib.pyplot as plt
from Basilisk.simulation import planetEphemeris
from Basilisk.simulation import spacecraft
from Basilisk.utilities import (SimulationBaseClass, macros, simIncludeGravBody, vizSupport)
from Basilisk.utilities import orbitalMotion
from Basilisk.utilities import simHelpers
# The path to the location of Basilisk
# Used to get the location of supporting data.
fileName = os.path.basename(os.path.splitext(__file__)[0])
[docs]
def run(show_plots):
"""
The scenarios can be run with the followings setups parameters:
Args:
show_plots (bool): Determines if the script should display plots
"""
path = os.path.dirname(os.path.abspath(__file__))
# Create simulation variable names
simTaskName = "simTask"
simProcessName = "simProcess"
# Create a sim module as an empty container
scSim = SimulationBaseClass.SimBaseClass()
#
# create the simulation process
#
dynProcess = scSim.CreateNewProcess(simProcessName)
# create the dynamics task and specify the simulation time step information
simulationTimeStep = macros.sec2nano(10.0)
simulationTime = macros.min2nano(1120.0)
dynProcess.addTask(scSim.CreateNewTask(simTaskName, simulationTimeStep))
# setup celestial object ephemeris module
gravBodyEphem = planetEphemeris.PlanetEphemeris()
gravBodyEphem.ModelTag = 'planetEphemeris'
scSim.AddModelToTask(simTaskName, gravBodyEphem)
gravBodyEphem.setPlanetNames(planetEphemeris.StringVector(["Itokawa", "earth"]))
# specify orbits of gravitational bodies
oeAsteroid = planetEphemeris.ClassicElements()
oeAsteroid.a = 1.3241 * orbitalMotion.AU * 1000 # meters
oeAsteroid.e = 0.2801
oeAsteroid.i = 1.6214*macros.D2R
oeAsteroid.Omega = 69.081*macros.D2R
oeAsteroid.omega = 162.82*macros.D2R
oeAsteroid.f = 90.0*macros.D2R
oeEarth = planetEphemeris.ClassicElements()
oeEarth.a = orbitalMotion.AU * 1000 # meters
oeEarth.e = 0.0167086
oeEarth.i = 7.155 * macros.D2R
oeEarth.Omega = 174.9 * macros.D2R
oeEarth.omega = 288.1 * macros.D2R
oeEarth.f = 270.0 * macros.D2R
# specify celestial object orbit
gravBodyEphem.planetElements = planetEphemeris.classicElementVector([oeAsteroid, oeEarth])
# Keep these outputs heliocentric. The unlinked Sun gravity body below uses
# its default zero state to represent the Sun at the heliocentric origin.
# The asteroid state is subtracted from the spacecraft state when plotting.
# specify celestial object orientation
gravBodyEphem.rightAscension = planetEphemeris.DoubleVector([0.0 * macros.D2R, 0.0 * macros.D2R])
gravBodyEphem.declination = planetEphemeris.DoubleVector([0.0 * macros.D2R, 0.0 * macros.D2R])
gravBodyEphem.lst0 = planetEphemeris.DoubleVector([0.0 * macros.D2R, 0.0 * macros.D2R])
gravBodyEphem.rotRate = planetEphemeris.DoubleVector(
[360 * macros.D2R / (12.132 * 3600.), 360 * macros.D2R / (24. * 3600.)])
# setup Sun gravity body
gravFactory = simIncludeGravBody.gravBodyFactory()
gravFactory.createSun()
# setup asteroid gravity body
mu = 2.34268 # meters^3/s^2
asteroid = gravFactory.createCustomGravObject("Itokawa", mu, radEquator=200)
asteroid.isCentralBody = True # ensure this is the central gravitational body
asteroid.planetBodyInMsg.subscribeTo(gravBodyEphem.planetOutMsgs[0])
# setup Earth gravity Body
earth = gravFactory.createCustomGravObject("earth", 0.3986004415E+15, radEquator=6378136.6)
earth.planetBodyInMsg.subscribeTo(gravBodyEphem.planetOutMsgs[1])
# create SC object
scObject = spacecraft.Spacecraft()
scObject.ModelTag = "bskSat"
gravFactory.addBodiesTo(scObject)
scSim.AddModelToTask(simTaskName, scObject)
# setup orbit initial conditions about the asteroid
oe = orbitalMotion.ClassicElements()
oe.a = 500.0 # meters
oe.e = 0.0001
oe.i = 33.3 * macros.D2R
oe.Omega = 48.2 * macros.D2R
oe.omega = 347.8 * macros.D2R
oe.f = 85.3 * macros.D2R
rN, vN = orbitalMotion.elem2rv(mu, oe)
# To set the spacecraft initial conditions, the following initial position and velocity variables are set:
scObject.hub.r_CN_NInit = rN # m - r_BN_N
scObject.hub.v_CN_NInit = vN # m/s - v_BN_N
#
# Setup data logging before the simulation is initialized
#
numDataPoints = 100
samplingTime = simHelpers.samplingTime(simulationTime, simulationTimeStep, numDataPoints)
scRec = scObject.scStateOutMsg.recorder(samplingTime)
astRec = gravBodyEphem.planetOutMsgs[0].recorder(samplingTime)
scSim.AddModelToTask(simTaskName, scRec)
scSim.AddModelToTask(simTaskName, astRec)
# if this scenario is to interface with the BSK Viz, uncomment the following lines
# to save the BSK data to a file, uncomment the saveFile line below
# Note that the gravitational body information is pulled automatically from the spacecraft object(s)
# Even if custom gravitational bodies are added, this information is pulled by the method below
if vizSupport.vizFound:
viz = vizSupport.enableUnityVisualization(scSim, simTaskName, scObject
# , saveFile=fileName
)
viz.settings.showSpacecraftLabels = 1
# load CAD for custom gravity model
vizSupport.createCustomModel(viz,
# Specifying relative model path is useful for sharing scenarios and resources:
modelPath=os.path.join("..", "dataForExamples", "Itokawa", "ItokawaHayabusa.obj"),
# Specifying absolute model path is preferable for live-streaming:
# modelPath=os.path.join(path, "dataForExamples", "Itokawa", "ItokawaHayabusa.obj"),
shader=1,
simBodiesToModify=['Itokawa'],
scale=[962, 962, 962])
# initialize Simulation
scSim.InitializeSimulation()
# configure a simulation stop time and execute the simulation run
scSim.ConfigureStopTime(simulationTime)
scSim.ExecuteSimulation()
# retrieve logged spacecraft position relative to asteroid
posData = scRec.r_BN_N - astRec.PositionVector
#
# plot the results
#
timeAxis = scRec.times() * macros.NANO2HOUR
plt.close("all") # clears out plots from earlier test runs
plt.figure(1)
fig = plt.gcf()
ax = fig.gca()
ax.ticklabel_format(useOffset=False, style='plain')
for idx in range(3):
plt.plot(timeAxis, posData[:, idx] ,
color=simHelpers.getLineColor(idx, 3),
label='$r_{BI,' + str(idx) + '}$')
plt.legend(loc='lower right')
plt.xlabel('Time [h]')
plt.ylabel('Itokawa Relative Position [m]')
figureList = {}
pltName = fileName + "1"
figureList[pltName] = plt.figure(1)
if show_plots:
plt.show()
# close the plots being saved off to avoid over-writing old and new figures
plt.close("all")
return figureList
#
# This statement below ensures that the unit test scrip can be run as a
# stand-along python script
#
if __name__ == "__main__":
run(
True # show_plots
)