# Source code for scenarioAttitudeFeedback2T_TH

#
#
#  Copyright (c) 2016, 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 example script demonstrates how to use thrusters to stabilize the tumble of a spacecraft orbiting the
This script sets up a 6-DOF spacecraft which is orbiting the Earth.  The goal is to
illustrate how a set of thrusters can be added to the rigid :ref:spacecraft hub, and what
FSW modules are needed to control these thrusters. The simulation setup is performed with two
processes, similarly to :ref:scenarioAttitudeFeedback2T,
in which the dynamics and the FSW algorithms are run at different time steps.  The control setup is the same
as in :ref:scenarioAttitudeFeedbackRW, but here the RW actuation is replaced with
thruster based control torque solution.

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

python3 scenarioAttitudeFeedback2T_TH.py

The simulation layout is shown in the following illustration. The two processes (SIM and FSW) are simulated
and run at different time rates. Interface messages are shared across SIM and
FSW message passing interfaces (MPIs).

.. image:: /_images/static/test_scenarioAttitudeFeedback2T_TH.svg
:align: center

When the simulation completes several plots are shown for the MRP attitude history, the rate
tracking errors, the requested torque, the requested forces for each thruster and the On-Time commands.

Setup Changes to Simulate Thrusters Dynamic Effectors
-----------------------------------------------------

At the beginning of the script all the plot functions are declared. Then the fundamental simulation setup is the same
as the one used in :ref:scenarioAttitudeFeedback2T.
The dynamics simulation is setup using a :ref:spacecraft module to which an Earth gravity
effector is attached.  The simple navigation module is still used to output the inertial attitude,
angular rate, as well as position and velocity messages.

The Thruster Dynamic Effector is added to the the rigid spacecraft hub, similarly to
:ref:scenarioAttitudeFeedbackRW.  The support macro simIncludeThruster.py
provides several convenient tools to facilitate the setup process.  This script allows the user to
readily create thrusters from a database of public specifications, customize them if needed, and add
them to the :ref:spacecraft module.

The first thing to do is to create the (empty) set of thrusters that will later contain all the devices. Then
a fresh instance of the thruster factory class thrusterFactory() is created.  This factory is able
to create a list of thruster devices, and return copies that can easily be manipulated and customized if needed.
The next step in this simulation setup is to use create() to include a particular thruster device.
The thrusterFactory() class contains several
public specifications of thruster devices which can be accessed by specifying their name. In our case we will consider
MOOG_Monarc_1 for the ACS Thrusters configuration, and MOOG_Monarc_22_6 for the DV Thrusters one.
The  2nd and 3rd required arguments are respectively the location of the
thruster :math:\hat{\mathbf r} and the direction of its force :math:\hat{\mathbf g}_t.  Both
vectors are expressed in the :math:\cal B-frame.  The remaining arguments are all optional.
The thrusters are generated by using the create() command inside a 'for' loop, which has the
job of assigning the respective location and direction arguments to each thruster, by cycling through the two
pre-defined arrays 'location' and 'direction'.

The following table provides a comprehensive list of all the optional arguments of the create()
command.  This table list the arguments, default values, as well as expected units.

+---------------------+-------+----------+----------------------------------------+--------------------+
|  Argument           | Units | Type     | Description                            | Default            |
+=====================+=======+==========+========================================+====================+
| useMinPulseTime     |       | Bool     | flag if the thruster model should      | False              |
|                     |       |          | use a minimum impulse time             |                    |
+---------------------+-------+----------+----------------------------------------+--------------------+
| areaNozzle          | m^2   | Float    | thruster nozzle exhaust cone exit area | 0.1                |
+---------------------+-------+----------+----------------------------------------+--------------------+
| steadyIsp           | s     | Float    | thruster fuel efficiency in Isp        | 100.0              |
+---------------------+-------+----------+----------------------------------------+--------------------+
| MaxThrust           | N     | Float    | maximum thruster force                 | 0.200              |
+---------------------+-------+----------+----------------------------------------+--------------------+
| thrusterMagDisp     | %     | Float    | thruster dispersion percentage         | 0.0                |
+---------------------+-------+----------+----------------------------------------+--------------------+
| MinOnTime           | s     | Float    | thruster minimum on time               | 0.020              |
+---------------------+-------+----------+----------------------------------------+--------------------+

The command addToSpacecraft() adds all the created thrusters to the :ref:spacecraft module.  The final step
is to add the :ref:thrusterDynamicEffector to the list of simulation tasks.

Flight Algorithm Changes to Control Thrusters
---------------------------------------------

The general flight algorithm setup is the same as in the earlier simulation scripts. Here we
use again the :ref:inertial3D guidance module, the :ref:attTrackingError module to evaluate the
tracking error states, and the :ref:mrpFeedback module to provide the desired :math:{\mathbf L}_r
control torque vector.  In addition, this time, we have to add two more modules: :ref:thrForceMapping
and :ref:thrFiringSchmitt.

The :ref:thrForceMapping module takes a commanded attitude control torque vector and determines a set of desired
thruster force values to implement this torque. It is assumed that the nominal thruster configuration is such that
pure torque solutions are possible. The module supports both on- and off-pulsing solutions, including
cases where the thruster solutions are saturated due to a large commanded attitude control torque.
The module set up is done in an analogous way as the previous ones. It can be noted that one of the inputs
corresponds to the output of the :ref:mrpFeedback, being the commanded control torque. The other ones are the
information on the thrusters configuration and spacecraft inertia, whose messages will be created later in the script.
In addition, the control axes are specified using the full identity matrix for ACS thrusters, and its first
two rows for the DV ones, since in the latter case we are not able to control one axis (z in our case),
due to the geometrical configuration. The last value to specify is the thrForceSign, which will have the
value of -1 if off-pulsing DV thrusters are employed, and +1 with the on-pulsing ACS configuration.

The last needed FSW module is :ref:thrFiringSchmitt. A Schmitt trigger logic is implemented to map a desired
thruster force value into a thruster on command time. The module reads in the attitude control thruster
force values for both on- and off-pulsing scenarios, and then maps this into a time which specifies how
long a thruster should be on. Four values are specified: thrMinFireTime (minimum thruster on-time in seconds),
level_on (Upper duty cycle percentage threshold relative to t min to turn on thrusters), level_off
(upper duty cycle percentage threshold relative to t min to turn on thrusters), and baseThrustState (0 by default
and set to 1 for DV thrusters). As expected, the thrusters force
input is directly the output of thrForceMapping, and also in this case we will need the thrusters configuration
message. It can be noted how the output of this module ends up to be the input commands for the
:ref:thrusterDynamicEffector.

The flight algorithm needs to know how many thruster devices are on the spacecraft and what their
location and direction are.  This is set through a flight software message that is read
in by flight algorithm modules that need this info.  To write the required flight thrusters configuration message
a separate support macros called fswSetupThrusters.py is used.

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

::

show_plots = True, useDVThrusters = False

The first scenario
has the 8 ACS Thrusters.  By looking at the plots we can see how every axis is controlled, and
the de-tumbling action is perfectly performed.
We can also see how the requested force for each thruster (third plot) never reaches the 1 N limit (apart from during
the initial transitory), which means that they are never saturated.

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

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

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

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

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

::

show_plots = True, useDVThrusters = True

In this setup we use the 6 DV Thrusters configuration. In this case, given the spacial configuration of the thrusters,
it is impossible to control the third axis, so the result is a spacecraft attitude in which the x, y axes are
controlled, but with a tumbling motion in the z axis.  In this setup it can be clearly seen
how the control action is performed just on the x and y axes, leaving the
spacecraft tumbling around the z one. Another important remark is that, since the default state of the DV thrusters
is 'on', the requested thruster force is always negative, as it can be seen in the plot.

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

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

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

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

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

"""

#
# Basilisk Scenario Script and Integrated Test
#
# Purpose:  Integrated test of the spacecraft(), extForceTorque, simpleNav(), thrusterDynamicEffector() and
#           MRP_Feedback() modules.  Illustrates a 6-DOV spacecraft detumbling in orbit, while using thrusters
#           to do the attitude control actuation.
# Author: Giulio Napolitano
# Creation Date:  June 26, 2019
#

import os
import numpy as np

# import general simulation support files
from Basilisk.utilities import SimulationBaseClass
from Basilisk.architecture import sim_model
from Basilisk.utilities import unitTestSupport  # general support file with common unit test functions
import matplotlib.pyplot as plt
from Basilisk.utilities import macros
from Basilisk.utilities import orbitalMotion
from Basilisk.utilities import fswSetupThrusters
from Basilisk.utilities import simIncludeThruster

# import simulation related support
from Basilisk.simulation import spacecraft
from Basilisk.simulation import extForceTorque
from Basilisk.utilities import simIncludeGravBody
from Basilisk.simulation import simpleNav
from Basilisk.simulation import thrusterDynamicEffector

# import FSW Algorithm related support
from Basilisk.fswAlgorithms import mrpFeedback
from Basilisk.fswAlgorithms import inertial3D
from Basilisk.fswAlgorithms import attTrackingError
from Basilisk.fswAlgorithms import thrForceMapping
from Basilisk.fswAlgorithms import thrFiringSchmitt

# import message declarations
from Basilisk.architecture import messaging

# attempt to import vizard
from Basilisk.utilities import vizSupport

# The path to the location of Basilisk
# Used to get the location of supporting data.
from Basilisk import __path__
bskPath = __path__[0]
fileName = os.path.basename(os.path.splitext(__file__)[0])

# Plotting functions
[docs]def plot_attitude_error(timeDataFSW, dataSigmaBR):
"""Plot the attitude errors."""
plt.figure(1)
for idx in range(3):
plt.plot(timeDataFSW, dataSigmaBR[:, idx],
color=unitTestSupport.getLineColor(idx, 3),
label=r'$\sigma_' + str(idx) + '$')
plt.legend(loc='lower right')
plt.xlabel('Time [min]')
plt.ylabel(r'Attitude Error $\sigma_{B/R}$')

[docs]def plot_rate_error(timeDataFSW, dataOmegaBR):
"""Plot the body angular velocity tracking errors."""
plt.figure(2)
for idx in range(3):
plt.plot(timeDataFSW, dataOmegaBR[:, idx],
color=unitTestSupport.getLineColor(idx, 3),
label=r'$\omega_{BR,' + str(idx) + '}$')
plt.legend(loc='lower right')
plt.xlabel('Time [min]')

[docs]def plot_requested_torque(timeDataFSW, dataLr):
"""Plot the commanded attitude control torque."""
plt.figure(3)
for idx in range(3):
plt.plot(timeDataFSW, dataLr[:, idx],
color=unitTestSupport.getLineColor(idx, 3),
label='$L_{r,' + str(idx) + '}$')
plt.legend(loc='lower right')
plt.xlabel('Time [min]')
plt.ylabel('Control Torque $L_r$ [Nm]')

[docs]def plot_thrForce(timeDataFSW, dataMap, numTh):
"""Plot the Thruster force values."""
plt.figure(4)
for idx in range(numTh):
plt.plot(timeDataFSW, dataMap[:, idx],
color=unitTestSupport.getLineColor(idx, numTh),
label='$thrForce,' + str(idx) + '}$')
plt.legend(loc='lower right')
plt.xlabel('Time [min]')
plt.ylabel('Force requested [N]')

[docs]def plot_OnTimeRequest(timeDataFSW, dataSchm, numTh):
"""Plot the thruster on time requests."""
plt.figure(5)
for idx in range(numTh):
plt.plot(timeDataFSW, dataSchm[:, idx],
color=unitTestSupport.getLineColor(idx, numTh),
label='$OnTimeRequest,' + str(idx) + '}$')
plt.legend(loc='lower right')
plt.xlabel('Time [min]')
plt.ylabel('OnTimeRequest [sec]')

[docs]def run(show_plots, useDVThrusters):
"""
The scenarios can be run with the followings setups parameters:

Args:
show_plots (bool): Determines if the script should display plots
useDVThrusters (bool): Use 6 DV thrusters instead of the default 8 ACS thrusters.

"""

# Create simulation variable names
dynProcessName = "dynProcess"

fswProcessName = "fswProcess"

#  Create a sim module as an empty container
scSim = SimulationBaseClass.SimBaseClass()

# set the simulation time variable used later on
simulationTime = macros.min2nano(10.)

#
#  create the simulation process
#
dynProcess = scSim.CreateNewProcess(dynProcessName)
fswProcess = scSim.CreateNewProcess(fswProcessName)

# create the dynamics task and specify the integration update time
simTimeStep = macros.sec2nano(0.1)
fswTimeStep = macros.sec2nano(0.5)

#
#

# initialize spacecraft object and set properties
scObject = spacecraft.Spacecraft()
scObject.ModelTag = "bsk-Sat"
# define the simulation 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)

# add spacecraft object to the simulation process

# clear prior gravitational body and SPICE setup definitions
gravFactory = simIncludeGravBody.gravBodyFactory()

# setup Earth Gravity Body
earth = gravFactory.createEarth()
earth.isCentralBody = True  # ensure this is the central gravitational body
mu = earth.mu

# attach gravity model to spacecraft
scObject.gravField.gravBodies = spacecraft.GravBodyVector(list(gravFactory.gravBodies.values()))

# setup extForceTorque module
# the control torque is read in through the messaging system
extFTObject = extForceTorque.ExtForceTorque()
extFTObject.ModelTag = "externalDisturbance"
extFTObject.extTorquePntB_B = [[0.25], [-0.25], [0.1]]

# add the simple Navigation sensor module.  This sets the SC attitude, rate, position
sNavObject = simpleNav.SimpleNav()

# create arrays for thrusters' locations and directions
if useDVThrusters:

location = [
[
0,
0.95,
-1.1
],
[
0.8227241335952166,
0.4750000000000003,
-1.1
],
[
0.8227241335952168,
-0.47499999999999976,
-1.1
],
[
0,
-0.95,
-1.1
],
[
-0.8227241335952165,
-0.4750000000000004,
-1.1
],
[
-0.822724133595217,
0.4749999999999993,
-1.1
]
]

direction = [[0.0, 0.0, 1.0],
[0.0, 0.0, 1.0],
[0.0, 0.0, 1.0],
[0.0, 0.0, 1.0],
[0.0, 0.0, 1.0],
[0.0, 0.0, 1.0]]
else:

location = [
[
3.874945160902288e-2,
-1.206182747348013,
0.85245
],
[
3.874945160902288e-2,
-1.206182747348013,
-0.85245
],
[
-3.8749451609022656e-2,
-1.206182747348013,
0.85245
],
[
-3.8749451609022656e-2,
-1.206182747348013,
-0.85245
],
[
-3.874945160902288e-2,
1.206182747348013,
0.85245
],
[
-3.874945160902288e-2,
1.206182747348013,
-0.85245
],
[
3.8749451609022656e-2,
1.206182747348013,
0.85245
],
[
3.8749451609022656e-2,
1.206182747348013,
-0.85245
]
]

direction = [
[
-0.7071067811865476,
0.7071067811865475,
0.0
],
[
-0.7071067811865476,
0.7071067811865475,
0.0
],
[
0.7071067811865475,
0.7071067811865476,
0.0
],
[
0.7071067811865475,
0.7071067811865476,
0.0
],
[
0.7071067811865476,
-0.7071067811865475,
0.0
],
[
0.7071067811865476,
-0.7071067811865475,
0.0
],
[
-0.7071067811865475,
-0.7071067811865476,
0.0
],
[
-0.7071067811865475,
-0.7071067811865476,
0.0
]
]

# create the set of thruster in the dynamics task
thrusterSet = thrusterDynamicEffector.ThrusterDynamicEffector()

# Make a fresh thruster factory instance, this is critical to run multiple times
thFactory = simIncludeThruster.thrusterFactory()

# create the thruster devices by specifying the thruster type and its location and direction
for pos_B, dir_B in zip(location, direction):

if useDVThrusters:
thFactory.create('MOOG_Monarc_22_6', pos_B, dir_B)
else:
thFactory.create('MOOG_Monarc_1', pos_B, dir_B)

# get number of thruster devices
numTh = thFactory.getNumOfDevices()

# create thruster object container and tie to spacecraft object
thrModelTag = "ACSThrusterDynamics"

#
#   setup the FSW algorithm tasks
#

# setup inertial3D guidance module
inertial3DConfig = inertial3D.inertial3DConfig()
inertial3DWrap = scSim.setModelDataWrap(inertial3DConfig)
inertial3DWrap.ModelTag = "inertial3D"
inertial3DConfig.sigma_R0N = [0., 0., 0.]  # set the desired inertial orientation

# setup the attitude tracking error evaluation module
attErrorConfig = attTrackingError.attTrackingErrorConfig()
attErrorWrap = scSim.setModelDataWrap(attErrorConfig)
attErrorWrap.ModelTag = "attErrorInertial3D"

# setup the MRP Feedback control module
mrpControlConfig = mrpFeedback.mrpFeedbackConfig()
mrpControlWrap = scSim.setModelDataWrap(mrpControlConfig)
mrpControlWrap.ModelTag = "MRP_Feedback"
mrpControlConfig.K = 3.5*10.0
mrpControlConfig.Ki = 0.0002  # make value negative to turn off integral feedback
mrpControlConfig.P = 30.0*10.0
mrpControlConfig.integralLimit = 2. / mrpControlConfig.Ki * 0.1

# setup the thruster force mapping module
thrForceMappingConfig = thrForceMapping.thrForceMappingConfig()
thrForceMappingWrap = scSim.setModelDataWrap(thrForceMappingConfig)
thrForceMappingWrap.ModelTag = "thrForceMapping"

if useDVThrusters:
controlAxes_B = [1, 0, 0,
0, 1, 0]
thrForceMappingConfig.thrForceSign = -1
else:
controlAxes_B = [1, 0, 0,
0, 1, 0,
0, 0, 1]
thrForceMappingConfig.thrForceSign = +1
thrForceMappingConfig.controlAxes_B = controlAxes_B

# setup the Schmitt trigger thruster firing logic module
thrFiringSchmittConfig = thrFiringSchmitt.thrFiringSchmittConfig()
thrFiringSchmittWrap = scSim.setModelDataWrap(thrFiringSchmittConfig)
thrFiringSchmittWrap.ModelTag = "thrFiringSchmitt"
thrFiringSchmittConfig.thrMinFireTime = 0.002
thrFiringSchmittConfig.level_on = .75
thrFiringSchmittConfig.level_off = .25
if useDVThrusters:
thrFiringSchmittConfig.baseThrustState = 1

#
#   Setup data logging before the simulation is initialized
#

numDataPoints = 100
samplingTime = unitTestSupport.samplingTime(simulationTime, fswTimeStep, numDataPoints)
mrpTorqueLog = mrpControlConfig.cmdTorqueOutMsg.recorder(samplingTime)
attErrorLog = attErrorConfig.attGuidOutMsg.recorder(samplingTime)
snTransLog = sNavObject.transOutMsg.recorder(samplingTime)
snAttLog = sNavObject.attOutMsg.recorder(samplingTime)
thrMapLog = thrForceMappingConfig.thrForceCmdOutMsg.recorder(samplingTime)
thrTrigLog = thrFiringSchmittConfig.onTimeOutMsg.recorder(samplingTime)

#
# create FSW simulation messages
#

# create the FSW vehicle configuration message

vehicleConfigOut.ISCPntB_B = I  # use the same inertia in the FSW algorithm as in the simulation
vcMsg = messaging.VehicleConfigMsg().write(vehicleConfigOut)

# create the FSW Thruster configuration message
if useDVThrusters:
maxThrust = 22
else:
maxThrust = 1

# A clearSetup() should be called first to clear out any pre-existing devices from an
# earlier simulation run.  Next, the maxThrust value should be specified and used in the macro create(),
# together with the locations and directions, and looped through a for cycle to consider all the thrusters.
# The support macro writeConfigMessage() creates the required thrusters flight configuration message.
fswSetupThrusters.clearSetup()
for pos_B, dir_B in zip(location, direction):
fswSetupThrusters.create(pos_B, dir_B, maxThrust)
fswThrConfigMsg = fswSetupThrusters.writeConfigMessage()
# an alternate method to pull un-modifed SIM Thruster configuration and create the corresponding FSW
# configuration message is:
fswThrConfigMsg = thFactory.getConfigMessage()

#   set initial Spacecraft States
#
# setup the orbit using classical orbit elements
oe = orbitalMotion.ClassicElements()
oe.a = 10000000.0  # meters
oe.e = 0.01
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)
scObject.hub.r_CN_NInit = rN  # m   - r_CN_N
scObject.hub.v_CN_NInit = vN  # m/s - v_CN_N
scObject.hub.sigma_BNInit = [[0.1], [0.2], [-0.3]]  # sigma_BN_B
scObject.hub.omega_BN_BInit = [[0.001], [-0.01], [0.03]]  # rad/s - omega_BN_B

# connect messages
sNavObject.scStateInMsg.subscribeTo(scObject.scStateOutMsg)
attErrorConfig.attNavInMsg.subscribeTo(sNavObject.attOutMsg)
attErrorConfig.attRefInMsg.subscribeTo(inertial3DConfig.attRefOutMsg)
mrpControlConfig.guidInMsg.subscribeTo(attErrorConfig.attGuidOutMsg)
mrpControlConfig.vehConfigInMsg.subscribeTo(vcMsg)
thrForceMappingConfig.cmdTorqueInMsg.subscribeTo(mrpControlConfig.cmdTorqueOutMsg)
thrForceMappingConfig.thrConfigInMsg.subscribeTo(fswThrConfigMsg)
thrForceMappingConfig.vehConfigInMsg.subscribeTo(vcMsg)
thrFiringSchmittConfig.thrConfInMsg.subscribeTo(fswThrConfigMsg)
thrFiringSchmittConfig.thrForceInMsg.subscribeTo(thrForceMappingConfig.thrForceCmdOutMsg)
thrusterSet.cmdsInMsg.subscribeTo(thrFiringSchmittConfig.onTimeOutMsg)

# if this scenario is to interface with the BSK Viz, uncomment the following lines
# , saveFile=fileName
, thrEffectorList=thrusterSet
, thrColors=vizSupport.toRGBA255("red")
)
vizSupport.setActuatorGuiSetting(viz, showThrusterLabels=True)

#
#   initialize Simulation
#
scSim.InitializeSimulation()

#
#   configure a simulation stop time time and execute the simulation run
#
scSim.ConfigureStopTime(simulationTime)
scSim.ExecuteSimulation()

#
#   retrieve the logged data
#
dataLr = mrpTorqueLog.torqueRequestBody
dataSigmaBR = attErrorLog.sigma_BR
dataOmegaBR = attErrorLog.omega_BR_B
dataMap = thrMapLog.thrForce
dataSchm = thrTrigLog.OnTimeRequest
np.set_printoptions(precision=16)

#
#   plot the results
#
timeDataFSW = attErrorLog.times() * macros.NANO2MIN
plt.close("all")  # clears out plots from earlier test runs

plot_requested_torque(timeDataFSW, dataLr)
figureList = {}
pltName = fileName + "1" + str(int(useDVThrusters))
figureList[pltName] = plt.figure(1)

plot_rate_error(timeDataFSW, dataOmegaBR)
pltName = fileName + "2" + str(int(useDVThrusters))
figureList[pltName] = plt.figure(2)

plot_attitude_error(timeDataFSW, dataSigmaBR)
pltName = fileName + "3" + str(int(useDVThrusters))
figureList[pltName] = plt.figure(3)

plot_thrForce(timeDataFSW, dataMap, numTh)
pltName = fileName + "4" + str(int(useDVThrusters))
figureList[pltName] = plt.figure(4)

plot_OnTimeRequest(timeDataFSW, dataSchm, numTh)
pltName = fileName + "5" + str(int(useDVThrusters))
figureList[pltName] = plt.figure(5)

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
False,  # useDVThrusters
)