Source code for scenarioAsteroidArrival

#
#  ISC License
#
#  Copyright (c) 2022, 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"""
Overview
--------

This simulation demonstrates how to put a spacecraft in orbit about a custom gravitational body while conducting several
attitude changes. Several attitude pointing modes are implemented, along with other visual tools including antenna
transmission and thruster visualization.

The spacecraft starts on a elliptical orbit towards the asteroid Bennu. The spacecraft conducts a
burn at periapsis of the elliptical orbit, transferring to a circular orbit about Bennu with a radius of 800
meters. The spacecraft then completes a series of Hohmann transfers while also conducting several attitude changes
until reaching a final elliptical orbit about the asteroid.

The script is found in the folder ``basilisk/examples`` and executed by using::

      python3 scenarioAsteroidArrival.py

.. attention::

    To see the asteroid Bennu in Vizard the asteroid asset bundle must be installed.  See
    the Vizard `Download <http://hanspeterschaub.info/basilisk/Vizard/VizardDownload.html>`__ web page.

Setting Up The Custom Gravitational Body
----------------------------------------

Because Spice will not be used to generate the ephemeris information for Bennu, an instance of the module
``planetEphemeris`` is created to generate Bennu's ephemeris::

    gravBodyEphem = planetEphemeris.PlanetEphemeris()
    gravBodyEphem.ModelTag = 'planetEphemeris'
    scSim.AddModelToTask(simTaskName, gravBodyEphem)
    gravBodyEphem.setPlanetNames(planetEphemeris.StringVector(["Bennu"]))

Next, the module is configured by specifying the orbital parameters of Bennu::

    timeInitString = "2011 January 1 0:00:00.0"
    diam = 2 * 245.03  # m
    G = 6.67408 * (10 ** -11)  # m^3 / kg*s^2
    massBennu = 7.329 * (10 ** 10)  # kg
    mu = G * massBennu  # Bennu grav. parameter, m^3/s^2
    oeAsteroid = planetEphemeris.ClassicElementsMsgPayload()
    oeAsteroid.a = 1.1264 * orbitalMotion.AU * 1000  # m
    oeAsteroid.e = 0.20375
    oeAsteroid.i = 6.0349 * macros.D2R
    oeAsteroid.Omega = 2.0609 * macros.D2R
    oeAsteroid.omega = 66.2231 * macros.D2R
    oeAsteroid.f = 0.0 * macros.D2R
    gravBodyEphem.planetElements = planetEphemeris.classicElementVector([oeAsteroid])

    gravBodyEphem.rightAscension = planetEphemeris.DoubleVector([85.65 * macros.D2R])
    gravBodyEphem.declination = planetEphemeris.DoubleVector([-60.17 * macros.D2R])
    gravBodyEphem.lst0 = planetEphemeris.DoubleVector([0.0 * macros.D2R])
    gravBodyEphem.rotRate = planetEphemeris.DoubleVector([360 * macros.D2R / (4.296057 * 3600.)])  # rad/sec

Next, Bennu can be created as a gravitational body using the ``createCustomGravObject()`` method::

    asteroid = gravFactory.createCustomGravObject("Bennu", mu)
    asteroid.isCentralBody = True  # ensure this is the central gravitational body

Finally, subscribe the custom gravitational body ``planetBodyInMsg`` to the planetEphemeris output message
``planetOutMsgs``::

    asteroid.planetBodyInMsg.subscribeTo(gravBodyEphem.planetOutMsgs[0])

The spacecraft object is then created and all gravitational bodies are connected to the spacecraft.

Recall that when configuring the ephemeris converter module, Bennu was not created with Spice. Therefore its input
message is of type ``planetEphemeris``::

    ephemObject.addSpiceInputMsg(gravBodyEphem.planetOutMsgs[0])

Implementing Attitude Pointing Modes
------------------------------------

After the spacecraft's initial orbital elements about Bennu are set using the ``orbitalMotion`` module, the attitude
modules and modes are created and configured. The four attitude pointing modes incorporated into this script include
Earth-pointing using the spacecraft's antenna with transmission visualization, Sun-pointing with the spacecraft's
solar panel normal axis, orbital velocity pointing while conducting thruster burn visualizations, and science-pointing
towards the asteroid using a sensor created on the spacecraft.

.. important:: Refer to the integrated example script :ref:`scenarioFlybySpice` for a more detailed discussion on
   configuring attitude modules and modes for a mission scenario.

To execute the desired attitude-pointing mode, the run flight mode function must be called
with the desired simulation time::

    runAntennaEarthPointing(desiredSimTimeSec)

Additional Visualization Features
---------------------------------

To add a visualization of antenna transmission back to Earth during the Earth-pointing mode we
can't use the typical way of adding these generic sensors, thrusters, etc.  The reason is that we want to illustrate a
thruster, but we are not using a thruster effector.  Thus, to add a thruster to the Vizard binary
we need to manually add these to the ``vizInterface`` spacecraft data structure.

First, as is typical, a transceiver is created through the ``vizInterface``::

    transceiverHUD = vizInterface.Transceiver()
    transceiverHUD.r_SB_B = [0., 0., 1.38]
    transceiverHUD.fieldOfView = 40.0 * macros.D2R
    transceiverHUD.normalVector = [0., 0., 1.]
    transceiverHUD.color = vizInterface.IntVector(vizSupport.toRGBA255("cyan"))
    transceiverHUD.label = "antenna"
    transceiverHUD.animationSpeed = 1

To add a sensor visualization for the science-pointing mode, a sensor is created using the ``vizInterface``::

    genericSensor = vizInterface.GenericSensor()
    genericSensor.r_SB_B = cameraLocation
    genericSensor.fieldOfView.push_back(10.0 * macros.D2R)
    genericSensor.fieldOfView.push_back(10.0 * macros.D2R)
    genericSensor.normalVector = cameraLocation
    genericSensor.size = 10
    genericSensor.color = vizInterface.IntVector(vizSupport.toRGBA255("white", alpha=0.1))
    genericSensor.label = "scienceCamera"
    genericSensor.genericSensorCmd = 1

To add a camera to the science-pointing mode, the ``createStandardCamera`` method is used::

    vizSupport.createStandardCamera(viz, setMode=1, spacecraftName=scObject.ModelTag,
                                    fieldOfView=10 * macros.D2R,
                                    pointingVector_B=[0,1,0], position_B=cameraLocation)

Finally, to add a thruster visualization for the thruster burn mode, the ``vizInterface`` is again invoked.
Here we manually add the Vizard interface elements back in to redo what the ``enableUnityVisualization()``
normally does for us.  The main difference is that we are manually setting the thruster information as
the spacecraft dynamics does not contain a thruster effector::

    scData = vizInterface.VizSpacecraftData()
    scData.spacecraftName = scObject.ModelTag
    scData.scStateInMsg.subscribeTo(scObject.scStateOutMsg)
    scData.transceiverList = vizInterface.TransceiverVector([transceiverHUD])
    scData.genericSensorList = vizInterface.GenericSensorVector([genericSensor])

    thrusterMsgInfo = messaging.THROutputMsgPayload()
    thrusterMsgInfo.maxThrust = 1  # Newtons
    thrusterMsgInfo.thrustForce = 0  # Newtons
    thrusterMsgInfo.thrusterLocation = [0, 0, -1.5]
    thrusterMsgInfo.thrusterDirection = [0, 0, 1]
    thrMsg = messaging.THROutputMsg().write(thrusterMsgInfo)
    scData.thrInMsgs = messaging.THROutputInMsgsVector([thrMsg.addSubscriber()])

After running the ``enableUnityVisualization()`` method, we need to clear the ``vizInterface`` spacecraft
data container ``scData`` and push our custom copy to it::

    viz.scData.clear()
    viz.scData.push_back(scData)


Illustration of Simulation Results
----------------------------------

The following image illustrates the expected simulation run return for the case when plots are requested.

.. image:: /_images/Scenarios/scenarioAsteroidArrival1.svg
   :align: center

Visualization In Vizard
----------------------------------

The following image illustrates the expected visualization of this simulation script.

.. image:: /_images/static/scenarioAsteroidArrival2.jpg
   :align: center

"""

#
# Basilisk Scenario Script and Integrated Test
#
# Purpose:  Basic simulation showing how to setup a flyby capture orbit about a custom gravity body.
# Author:  Leah Kiner
# Creation Date:  Feb. 6, 2022
#

import os

import matplotlib.pyplot as plt
import numpy as np
from Basilisk import __path__

bskPath = __path__[0]
fileName = os.path.basename(os.path.splitext(__file__)[0])


from Basilisk.utilities import (SimulationBaseClass, macros, simIncludeGravBody, vizSupport, unitTestSupport, orbitalMotion)
from Basilisk.simulation import spacecraft, extForceTorque, simpleNav, ephemerisConverter, planetEphemeris
from Basilisk.fswAlgorithms import mrpFeedback, attTrackingError, velocityPoint, locationPointing
from Basilisk.architecture import messaging

try:
    from Basilisk.simulation import vizInterface
except ImportError:
    pass

# 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 """ # Create simulation variable names simTaskName = "simTask" simProcessName = "simProcess" # Configure the simulation scSim = SimulationBaseClass.SimBaseClass() # Shows the simulation progress bar in the terminal scSim.SetProgressBar(True) # Create the simulation process dynProcess = scSim.CreateNewProcess(simProcessName) # Create the dynamics task and specify the simulation time step information simulationTimeStep = macros.sec2nano(20.0) # Add dynamics task to the simulation process dynProcess.addTask(scSim.CreateNewTask(simTaskName, simulationTimeStep)) # Setup celestial object ephemeris module for the asteroid gravBodyEphem = planetEphemeris.PlanetEphemeris() gravBodyEphem.ModelTag = 'planetEphemeris' scSim.AddModelToTask(simTaskName, gravBodyEphem) gravBodyEphem.setPlanetNames(planetEphemeris.StringVector(["bennu"])) # Specify orbital parameters of the asteroid timeInitString = "2011 January 1 0:00:00.0" diam = 2 * 245.03 # m G = 6.67408 * (10 ** -11) # m^3 / kg*s^2 massBennu = 7.329 * (10 ** 10) # kg mu = G * massBennu # Bennu grav. parameter, m^3/s^2 oeAsteroid = planetEphemeris.ClassicElementsMsgPayload() oeAsteroid.a = 1.1264 * orbitalMotion.AU * 1000 # m oeAsteroid.e = 0.20375 oeAsteroid.i = 6.0349 * macros.D2R oeAsteroid.Omega = 2.0609 * macros.D2R oeAsteroid.omega = 66.2231 * macros.D2R oeAsteroid.f = 0.0 * macros.D2R gravBodyEphem.planetElements = planetEphemeris.classicElementVector([oeAsteroid]) # Specify orientation parameters of the asteroid gravBodyEphem.rightAscension = planetEphemeris.DoubleVector([85.65 * macros.D2R]) gravBodyEphem.declination = planetEphemeris.DoubleVector([-60.17 * macros.D2R]) gravBodyEphem.lst0 = planetEphemeris.DoubleVector([0.0 * macros.D2R]) gravBodyEphem.rotRate = planetEphemeris.DoubleVector([360 * macros.D2R / (4.296057 * 3600.)]) # rad/sec # Set orbital radii about asteroid r0 = diam/2.0 + 800 # capture orbit, meters r1 = diam/2.0 + 600 # intermediate orbit, meters r2 = diam/2.0 + 400 # final science orbit, meters r3 = diam/2.0 + 200 # meters, very close fly-by, elliptic orbit rP = r0 rA = 3*rP # Set orbital periods P0 = np.pi*2/np.sqrt(mu/(r0**3)) P01 = np.pi*2/np.sqrt(mu/(((r0+r1)/2)**3)) P1 = np.pi*2/np.sqrt(mu/(r1**3)) P12 = np.pi*2/np.sqrt(mu/(((r1+r2)/2)**3)) P2 = np.pi*2/np.sqrt(mu/(r2**3)) P23 = np.pi*2/np.sqrt(mu/(((r2+r3)/2)**3)) # Create additional gravitational bodies gravFactory = simIncludeGravBody.gravBodyFactory() gravFactory.createBodies("earth", "sun") # Set gravity body index values earthIdx = 0 sunIdx = 1 asteroidIdx = 2 # Create and configure the default SPICE support module. The first step is to store # the date and time of the start of the simulation. spiceObject = gravFactory.createSpiceInterface(time=timeInitString, epochInMsg=True) # Add the SPICE object to the simulation task list scSim.AddModelToTask(simTaskName, spiceObject) # Create the asteroid custom gravitational body asteroid = gravFactory.createCustomGravObject("bennu", mu , modelDictionaryKey="Bennu" , radEquator=565. / 2.0 ) asteroid.isCentralBody = True # ensures the asteroid is the central gravitational body asteroid.planetBodyInMsg.subscribeTo(gravBodyEphem.planetOutMsgs[0]) # connect asteroid ephem. to custom grav body # Create the spacecraft object scObject = spacecraft.Spacecraft() scObject.ModelTag = "bskSat" # Connect all gravitational bodies to the spacecraft gravFactory.addBodiesTo(scObject) scSim.AddModelToTask(simTaskName, scObject) # Create an ephemeris converter to convert messages of type # 'SpicePlanetStateMsgPayload' to 'EphemerisMsgPayload' ephemObject = ephemerisConverter.EphemerisConverter() ephemObject.ModelTag = 'EphemData' ephemObject.addSpiceInputMsg(spiceObject.planetStateOutMsgs[earthIdx]) ephemObject.addSpiceInputMsg(spiceObject.planetStateOutMsgs[sunIdx]) # Recall the asteroid was not created with Spice. ephemObject.addSpiceInputMsg(gravBodyEphem.planetOutMsgs[0]) scSim.AddModelToTask(simTaskName, ephemObject) # Define the spacecraft inertia I = [900., 0., 0., 0., 800., 0., 0., 0., 600.] scObject.hub.mHub = 750.0 # kg - spacecraft mass scObject.hub.r_BcB_B = [[0.0], [0.0], [0.0]] # m - position vector of body-fixed point B relative to CM scObject.hub.IHubPntBc_B = unitTestSupport.np2EigenMatrix3d(I) # Define the initial spacecraft orbit about the asteroid oe = orbitalMotion.ClassicElements() oe.a = (rP + rA)/2.0 oe.e = 1 - (rP / oe.a) oe.i = 90.0 * macros.D2R oe.Omega = 180.0 * macros.D2R oe.omega = 347.8 * macros.D2R oe.f = -45.0 * macros.D2R Ecc = np.arctan(np.tan(-oe.f/2)*np.sqrt((1-oe.e)/(1+oe.e)))*2 # eccentric anomaly M = Ecc - oe.e*np.sin(Ecc) # mean anomaly n = np.sqrt(mu/(oe.a**3)) h = np.sqrt(mu*oe.a*(1-oe.e**2)) # specific angular momentum vP = h/rP V_SC_C_B = np.sqrt(mu / rP) # [m/s] (2) spacecraft circular parking speed relative to bennu. Delta_V_Parking_Orbit = V_SC_C_B - vP # Setting initial position and velocity vectors using orbital elements r_N, v_N = orbitalMotion.elem2rv(mu, oe) T1 = M/n # time until spacecraft reaches periapsis of arrival trajectory # Initialize spacecraft states with the initialization variables scObject.hub.r_CN_NInit = r_N # [m] = r_BN_N scObject.hub.v_CN_NInit = v_N # [m/s] = v_BN_N scObject.hub.sigma_BNInit = [[0.1], [0.2], [-0.3]] # sigma_BN_B scObject.hub.omega_BN_BInit = [[0.000], [-0.00], [0.00]] # rad/s - omega_BN_B # Set up the extForceTorque module extFTObject = extForceTorque.ExtForceTorque() extFTObject.ModelTag = "externalDisturbance" scObject.addDynamicEffector(extFTObject) scSim.AddModelToTask(simTaskName, extFTObject) # Add the simple Navigation sensor module. This sets the SC attitude, rate, position # velocity navigation message sNavObject = simpleNav.SimpleNav() sNavObject.ModelTag = "SimpleNavigation" scSim.AddModelToTask(simTaskName, sNavObject) sNavObject.scStateInMsg.subscribeTo(scObject.scStateOutMsg) # # Set up the FSW algorithm tasks # # Set up solar panel Sun-pointing guidance module sunPointGuidance = locationPointing.locationPointing() sunPointGuidance.ModelTag = "panelSunPoint" sunPointGuidance.celBodyInMsg.subscribeTo(ephemObject.ephemOutMsgs[sunIdx]) sunPointGuidance.scTransInMsg.subscribeTo(sNavObject.transOutMsg) sunPointGuidance.scAttInMsg.subscribeTo(sNavObject.attOutMsg) sunPointGuidance.pHat_B = [0.0, 0.0, 1.0] sunPointGuidance.useBoresightRateDamping = 1 scSim.AddModelToTask(simTaskName, sunPointGuidance) # Set up asteroid-relative velocityPoint guidance module velAsteroidGuidance = velocityPoint.velocityPoint() velAsteroidGuidance.ModelTag = "velocityPointAsteroid" velAsteroidGuidance.transNavInMsg.subscribeTo(sNavObject.transOutMsg) velAsteroidGuidance.celBodyInMsg.subscribeTo(ephemObject.ephemOutMsgs[asteroidIdx]) velAsteroidGuidance.mu = mu scSim.AddModelToTask(simTaskName, velAsteroidGuidance) # Set up sensor science-pointing guidance module cameraLocation = [0.0, 1.5, 0.0] sciencePointGuidance = locationPointing.locationPointing() sciencePointGuidance.ModelTag = "sciencePointAsteroid" sciencePointGuidance.celBodyInMsg.subscribeTo(ephemObject.ephemOutMsgs[asteroidIdx]) sciencePointGuidance.scTransInMsg.subscribeTo(sNavObject.transOutMsg) sciencePointGuidance.scAttInMsg.subscribeTo(sNavObject.attOutMsg) sciencePointGuidance.pHat_B = cameraLocation # y-axis set for science-pointing sensor sciencePointGuidance.useBoresightRateDamping = 1 scSim.AddModelToTask(simTaskName, sciencePointGuidance) # Set up an antenna pointing to Earth guidance module earthPointGuidance = locationPointing.locationPointing() earthPointGuidance.ModelTag = "antennaEarthPoint" earthPointGuidance.celBodyInMsg.subscribeTo(ephemObject.ephemOutMsgs[earthIdx]) earthPointGuidance.scTransInMsg.subscribeTo(sNavObject.transOutMsg) earthPointGuidance.scAttInMsg.subscribeTo(sNavObject.attOutMsg) earthPointGuidance.pHat_B = [0.0, 0.0, 1.0] earthPointGuidance.useBoresightRateDamping = 1 scSim.AddModelToTask(simTaskName, earthPointGuidance) # Set up the attitude tracking error evaluation module attError = attTrackingError.attTrackingError() attError.ModelTag = "attErrorInertial3D" scSim.AddModelToTask(simTaskName, attError) attError.attRefInMsg.subscribeTo(sunPointGuidance.attRefOutMsg) # initial flight mode attError.attNavInMsg.subscribeTo(sNavObject.attOutMsg) # Create the FSW vehicle configuration message vehicleConfigOut = messaging.VehicleConfigMsgPayload() vehicleConfigOut.ISCPntB_B = I # use the same inertia in the FSW algorithm as in the simulation vcMsg = messaging.VehicleConfigMsg().write(vehicleConfigOut) # Set up the MRP Feedback control module mrpControl = mrpFeedback.mrpFeedback() mrpControl.ModelTag = "mrpFeedback" scSim.AddModelToTask(simTaskName, mrpControl) mrpControl.guidInMsg.subscribeTo(attError.attGuidOutMsg) mrpControl.vehConfigInMsg.subscribeTo(vcMsg) mrpControl.Ki = -1.0 # make value negative to turn off integral feedback II = 900. mrpControl.P = 2*II/(20*60) mrpControl.K = mrpControl.P*mrpControl.P/II mrpControl.integralLimit = 2. / mrpControl.Ki * 0.1 # Connect the torque command to external torque effector extFTObject.cmdTorqueInMsg.subscribeTo(mrpControl.cmdTorqueOutMsg) # Set the simulation time # Set up data logging before the simulation is initialized scRec = scObject.scStateOutMsg.recorder() astRec = gravBodyEphem.planetOutMsgs[0].recorder() scSim.AddModelToTask(simTaskName, scRec) scSim.AddModelToTask(simTaskName, astRec) if vizSupport.vizFound: # Set up the sensor for the science-pointing mode genericSensor = vizInterface.GenericSensor() genericSensor.r_SB_B = cameraLocation genericSensor.fieldOfView.push_back(10.0 * macros.D2R) genericSensor.fieldOfView.push_back(10.0 * macros.D2R) genericSensor.normalVector = cameraLocation genericSensor.size = 10 genericSensor.color = vizInterface.IntVector(vizSupport.toRGBA255("white", alpha=0.1)) genericSensor.label = "scienceCamera" genericSensor.genericSensorCmd = 1 # Set up the antenna visualization for transmission to Earth transceiverHUD = vizInterface.Transceiver() transceiverHUD.r_SB_B = [0., 0., 1.38] transceiverHUD.fieldOfView = 40.0 * macros.D2R transceiverHUD.normalVector = [0., 0., 1.] transceiverHUD.color = vizInterface.IntVector(vizSupport.toRGBA255("cyan")) transceiverHUD.label = "antenna" transceiverHUD.animationSpeed = 1 # Set up the thruster visualization info # Note: This process is different from the usual procedure of creating a thruster effector. # The following code creates a thruster visualization only. # before adding the thruster scData = vizInterface.VizSpacecraftData() scData.spacecraftName = scObject.ModelTag scData.scStateInMsg.subscribeTo(scObject.scStateOutMsg) scData.transceiverList = vizInterface.TransceiverVector([transceiverHUD]) scData.genericSensorList = vizInterface.GenericSensorVector([genericSensor]) thrusterMsgInfo = messaging.THROutputMsgPayload() thrusterMsgInfo.maxThrust = 1 # Newtons thrusterMsgInfo.thrustForce = 0 # Newtons thrusterMsgInfo.thrusterLocation = [0, 0, -1.5] thrusterMsgInfo.thrusterDirection = [0, 0, 1] thrMsg = messaging.THROutputMsg().write(thrusterMsgInfo) scData.thrInMsgs = messaging.THROutputMsgInMsgsVector([thrMsg.addSubscriber()]) thrInfo = vizInterface.ThrClusterMap() thrInfo.thrTag = "DV" scData.thrInfo = vizInterface.ThrClusterVector([thrInfo]) # Create the Vizard visualization file and set parameters viz = vizSupport.enableUnityVisualization(scSim, simTaskName, scObject # , saveFile=fileName ) viz.epochInMsg.subscribeTo(gravFactory.epochMsg) viz.settings.showCelestialBodyLabels = 1 viz.settings.scViewToPlanetViewBoundaryMultiplier = 100 viz.settings.planetViewToHelioViewBoundaryMultiplier = 100 viz.settings.orbitLinesOn = -1 viz.settings.keyboardAngularRate = np.deg2rad(0.5) # Create the science mode camera vizSupport.createStandardCamera(viz, setMode=1, spacecraftName=scObject.ModelTag, fieldOfView=10 * macros.D2R, displayName="10˚ FOV Camera", pointingVector_B=[0, 1, 0], position_B=cameraLocation) # Note: After running the enableUnityVisualization() method, we need to clear the # vizInterface spacecraft data container, scData, and push our custom copy to it. viz.scData.clear() viz.scData.push_back(scData) # Initialize and execute the simulation for the first section scSim.InitializeSimulation() # Set up flight modes def runPanelSunPointing(simTime): nonlocal simulationTime attError.attRefInMsg.subscribeTo(sunPointGuidance.attRefOutMsg) if vizSupport.vizFound: transceiverHUD.transceiverState = 0 # antenna off genericSensor.isHidden = 1 thrusterMsgInfo.thrustForce = 0 thrMsg.write(thrusterMsgInfo, simulationTime) attError.sigma_R0R = [0, 0, 0] simulationTime += macros.sec2nano(simTime) scSim.ConfigureStopTime(simulationTime) scSim.ExecuteSimulation() def runSensorSciencePointing(simTime): nonlocal simulationTime attError.attRefInMsg.subscribeTo(sciencePointGuidance.attRefOutMsg) if vizSupport.vizFound: transceiverHUD.transceiverState = 0 # antenna off genericSensor.isHidden = 0 thrusterMsgInfo.thrustForce = 0 thrMsg.write(thrusterMsgInfo, simulationTime) attError.sigma_R0R = [0, 0, 0] simulationTime += macros.sec2nano(simTime) scSim.ConfigureStopTime(simulationTime) scSim.ExecuteSimulation() def runAntennaEarthPointing(simTime): nonlocal simulationTime attError.attRefInMsg.subscribeTo(earthPointGuidance.attRefOutMsg) if vizSupport.vizFound: transceiverHUD.transceiverState = 3 # antenna in send and receive mode genericSensor.isHidden = 1 thrusterMsgInfo.thrustForce = 0 thrMsg.write(thrusterMsgInfo, simulationTime) attError.sigma_R0R = [0, 0, 0] simulationTime += macros.sec2nano(simTime) scSim.ConfigureStopTime(simulationTime) scSim.ExecuteSimulation() def runDvBurn(simTime, burnSign, planetMsg): nonlocal simulationTime attError.attRefInMsg.subscribeTo(planetMsg) if vizSupport.vizFound: transceiverHUD.transceiverState = 0 # antenna off genericSensor.isHidden = 1 if burnSign > 0: attError.sigma_R0R = [np.tan((np.pi/2)/4), 0, 0] else: attError.sigma_R0R = [-np.tan((np.pi / 2) / 4), 0, 0] minTime = 40 * 60 if simTime < minTime: print("ERROR: runPosDvBurn must have simTime larger than " + str(minTime) + " min") exit(1) else: simulationTime += macros.sec2nano(minTime) scSim.ConfigureStopTime(simulationTime) scSim.ExecuteSimulation() if vizSupport.vizFound: thrusterMsgInfo.thrustForce = thrusterMsgInfo.maxThrust thrMsg.write(thrusterMsgInfo, simulationTime) simulationTime += macros.sec2nano(simTime - minTime) scSim.ConfigureStopTime(simulationTime) scSim.ExecuteSimulation() simulationTime = 0 np.set_printoptions(precision=16) burnTime = 200*60 # Run thruster burn for arrival to the capture orbit with thrusters on runDvBurn(T1, -1, velAsteroidGuidance.attRefOutMsg) # Get current spacecraft states velRef = scObject.dynManager.getStateObject(scObject.hub.nameOfHubVelocity) vN = scRec.v_BN_N[-1] - astRec.VelocityVector[-1] # Apply a delta V and set the new velocity state in the circular capture orbit vHat = vN / np.linalg.norm(vN) vN = vN + Delta_V_Parking_Orbit * vHat velRef.setState(vN) # Travel in a circular orbit at r0, incorporating several attitude pointing modes runDvBurn(burnTime, -1, velAsteroidGuidance.attRefOutMsg) runSensorSciencePointing(P0/3.-burnTime) runPanelSunPointing(P0/3.) runAntennaEarthPointing(P0/3. - burnTime) runDvBurn(burnTime, -1, velAsteroidGuidance.attRefOutMsg) # Get access to dynManager translational states for future access to the states velRef = scObject.dynManager.getStateObject(scObject.hub.nameOfHubVelocity) # Retrieve the latest relative position and velocity components rN = scRec.r_BN_N[-1] - astRec.PositionVector[-1] vN = scRec.v_BN_N[-1] - astRec.VelocityVector[-1] # Conduct the first burn of a Hohmann transfer from r0 to r1 rData = np.linalg.norm(rN) vData = np.linalg.norm(vN) at = (rData + r1) * .5 v0p = np.sqrt((2 * mu / rData) - (mu / at)) vHat = vN / vData vVt = vN + vHat * (v0p - vData) # Update state manager's velocity velRef.setState(vVt) # Run thruster burn mode along with sun-pointing during the transfer orbit runDvBurn(burnTime, -1, velAsteroidGuidance.attRefOutMsg) runPanelSunPointing(P01/2. - burnTime*2) runDvBurn(burnTime, -1, velAsteroidGuidance.attRefOutMsg) # Retrieve the latest relative position and velocity components rN = scRec.r_BN_N[-1] - astRec.PositionVector[-1] vN = scRec.v_BN_N[-1] - astRec.VelocityVector[-1] # Conduct the second burn of the Hohmann transfer to arrive in a circular orbit at r1 rData = np.linalg.norm(rN) vData = np.linalg.norm(vN) v1p = np.sqrt(mu / rData) vHat = vN / vData vVt2 = vN + vHat * (v1p - vData) # Update state manager's velocity velRef.setState(vVt2) # Run thruster burn visualization along with attitude pointing modes runDvBurn(burnTime, -1, velAsteroidGuidance.attRefOutMsg) runSensorSciencePointing(P1/4-burnTime) runAntennaEarthPointing(P1/4-burnTime) runDvBurn(burnTime, -1, velAsteroidGuidance.attRefOutMsg) # Retrieve the latest relative position and velocity components rN = scRec.r_BN_N[-1] - astRec.PositionVector[-1] vN = scRec.v_BN_N[-1] - astRec.VelocityVector[-1] # Conduct a second Hohmann transfer from r1 to r2, initial burn rData = np.linalg.norm(rN) vData = np.linalg.norm(vN) at = (rData + r2) * .5 v2p = np.sqrt((2 * mu / rData) - (mu / at)) vHat = vN / vData vVt = vN + vHat * (v2p - vData) # Update state manager's velocity velRef.setState(vVt) # Run thruster burn section with science pointing mode runDvBurn(burnTime, -1, velAsteroidGuidance.attRefOutMsg) runSensorSciencePointing(P12/2-burnTime*2) runDvBurn(burnTime, -1, velAsteroidGuidance.attRefOutMsg) # Retrieve the latest relative position and velocity components rN = scRec.r_BN_N[-1] - astRec.PositionVector[-1] vN = scRec.v_BN_N[-1] - astRec.VelocityVector[-1] # Conduct the second burn of the second transfer to a cicular orbit at r2 rData = np.linalg.norm(rN) vData = np.linalg.norm(vN) v3p = np.sqrt(mu / rData) vHat = vN / vData vVt = vN + vHat * (v3p - vData) # Update state manager's velocity velRef.setState(vVt) # Run thruster visualization with science pointing mode runDvBurn(burnTime, -1, velAsteroidGuidance.attRefOutMsg) runSensorSciencePointing(P2-burnTime) # Retrieve the latest relative position and velocity components rN = scRec.r_BN_N[-1] - astRec.PositionVector[-1] vN = scRec.v_BN_N[-1] - astRec.VelocityVector[-1] # Conduct a third Hohmann transfer from r2 to r3, initial burn rData = np.linalg.norm(rN) vData = np.linalg.norm(vN) at = (rData + r3) * .5 v3p = np.sqrt((2 * mu / rData) - (mu / at)) vHat = vN / vData vVt = vN + vHat * (v3p - vData) # Update state manager's velocity velRef.setState(vVt) # Run thruster visualization with science-pointing mode runDvBurn(burnTime, -1, velAsteroidGuidance.attRefOutMsg) runSensorSciencePointing(3*P23-burnTime) # Retrieve logged spacecraft position relative to asteroid posData1 = scRec.r_BN_N # inertial pos. wrt. Sun posData2 = scRec.r_BN_N - astRec.PositionVector # relative pos. wrt. Asteroid # Call plotting function: plotOrbits figureList = plotOrbits(scRec.times(), posData1, posData2, rP, diam) if show_plots: plt.show() # Close the plots being saved off to avoid over-writing old and new figures plt.close("all") # Unload Spice kernels gravFactory.unloadSpiceKernels() return figureList
def plotOrbits(timeAxis, posData1, posData2, rP, diam): fileName = os.path.basename(os.path.splitext(__file__)[0]) plt.close("all") # Clears out plots from earlier test runs figureList = {} # Plot arrival to Asteroid plt.figure(1, figsize=(5, 5)) # Draw the planet fig = plt.gcf() ax = fig.gca() ax.set_aspect('equal') ax.ticklabel_format(useOffset=False, style='sci') ax.get_yaxis().set_major_formatter(plt.FuncFormatter(lambda x, loc: "{:,}".format(int(x)))) ax.get_xaxis().set_major_formatter(plt.FuncFormatter(lambda x, loc: "{:,}".format(int(x)))) planetColor = '#008800' planetRadius = .5*(diam) # m ax.add_artist(plt.Circle((0, 0), planetRadius, color=planetColor)) # Draw the actual orbit from pulled data (DataRec) plt.plot(posData2[:, 0], posData2[:, 2], color='orangered', label='Simulated Flight') plt.xlabel('X Distance, Inertial [m]') plt.ylabel('Z Distance, Inertial [m]') # Draw desired parking orbit fData = np.linspace(0, 2 * np.pi, 100) rData = [] for indx in range(0, len(fData)): rData.append(rP) plt.plot(rData* np.cos(fData), rData * np.sin(fData), '--', color='#555555', label='Desired Circ.Capture Orbit') plt.legend(loc='upper right') plt.grid() pltName = fileName + "1" figureList[pltName] = plt.figure(1) return figureList if __name__ == "__main__": run( True # show_plots )