# ISC License
#
# Copyright (c) 2025, 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 scenario demonstrates the branching capability of the prescribed motion module using the
spinningBodyOneDOFStateEffector module. The prescribed motion module can be configured to have state effectors
attached to it rather than the spacecraft hub. To do so, the state effectors are set up normally, except their
parameters are set with respect to the prescribed motion effector rather than the hub. The parameters that were
previously required to be provided relative to the hub frame must instead be provided relative to the prescribed
motion frame.
This example configures an ISS-scale spacecraft containing a cylindrical rigid hub,
two large prescribed trusses which rotate about their longitudinal axes, and eight single-axis solar panels
which rotate about their transverse (bending) axes. The spacecraft configuration is shown below.
.. image:: /_images/static/scenarioPrescribedMotionWithRotationBranching.svg
:align: center
The spacecraft is at rest at the start of the simulation.
To demonstrate the impact of the prescribed truss motion on the solar panel dynamics, all solar panels are
given zero initial deflections. The trusses are profiled to symmetrically rotate 45 degrees from their
initial configurations using a smoothed bang-coast-bang acceleration profile.
The script is found in the folder ``basilisk/examples`` and executed by using::
python3 scenarioPrescribedMotionWithRotationBranching.py
The prescribed truss motion, solar panel deflections and their rates, and the hub response to the panel deflections
is plotted in this scenario.
Illustration of Simulation Results
----------------------------------
.. image:: /_images/Scenarios/scenarioPrescribedMotionWithRotationBranching_1.svg
:align: center
.. image:: /_images/Scenarios/scenarioPrescribedMotionWithRotationBranching_2.svg
:align: center
.. image:: /_images/Scenarios/scenarioPrescribedMotionWithRotationBranching_3.svg
:align: center
.. image:: /_images/Scenarios/scenarioPrescribedMotionWithRotationBranching_4.svg
:align: center
.. image:: /_images/Scenarios/scenarioPrescribedMotionWithRotationBranching_5.svg
:align: center
.. image:: /_images/Scenarios/scenarioPrescribedMotionWithRotationBranching_6.svg
:align: center
.. image:: /_images/Scenarios/scenarioPrescribedMotionWithRotationBranching_7.svg
:align: center
.. image:: /_images/Scenarios/scenarioPrescribedMotionWithRotationBranching_8.svg
:align: center
"""
#
# Prescribed Motion with 1-DOF Rotation Branching Scenario
# Author: Leah Kiner
# Creation Date: October 1, 2025
#
import inspect
import matplotlib.pyplot as plt
import numpy as np
import os
from Basilisk.utilities import SimulationBaseClass, unitTestSupport, macros
from Basilisk.simulation import prescribedMotionStateEffector, spacecraft, spinningBodyOneDOFStateEffector
from Basilisk.simulation import prescribedLinearTranslation
from Basilisk.simulation import prescribedRotation1DOF
from Basilisk.architecture import messaging
from Basilisk.utilities import RigidBodyKinematics as rbk
from Basilisk.utilities import vizSupport
filename = os.path.basename(os.path.splitext(__file__)[0])
path = os.path.dirname(os.path.abspath(filename))
[docs]
def run(show_plots):
"""
The scenario can be run with the followings set up parameter:
Args:
show_plots (bool): Determines if the script should display plots
"""
# Set up the simulation
sc_sim = SimulationBaseClass.SimBaseClass()
sim_process_name = "simProcess"
sim_process = sc_sim.CreateNewProcess(sim_process_name)
dyn_time_step_sec = 0.01 # [s]
fsw_time_step_sec = 0.1 # [s]
data_rec_time_step_sec = 0.1 # [s]
dyn_task_name = "dynTask"
data_rec_task_name = "dataRecTask"
sim_process.addTask(sc_sim.CreateNewTask(dyn_task_name, macros.sec2nano(dyn_time_step_sec)))
sim_process.addTask(sc_sim.CreateNewTask("fswTask", macros.sec2nano(fsw_time_step_sec)))
sim_process.addTask(sc_sim.CreateNewTask(data_rec_task_name, macros.sec2nano(data_rec_time_step_sec)))
# Create the spacecraft hub
mass_hub = 15000 # [kg]
length_hub = 8 # [m]
width_hub = 8 # [m]
depth_hub = 20 # [m]
I_hub_11 = (1 / 12) * mass_hub * (length_hub * length_hub + depth_hub * depth_hub) # [kg m^2]
I_hub_22 = (1 / 12) * mass_hub * (length_hub * length_hub + width_hub * width_hub) # [kg m^2]
I_hub_33 = (1 / 12) * mass_hub * (width_hub * width_hub + depth_hub * depth_hub) # [kg m^2]
sc_object = spacecraft.Spacecraft()
sc_object.ModelTag = "scObject"
sc_object.hub.mHub = mass_hub # kg
sc_object.hub.r_BcB_B = [0.0, 0.0, 0.0] # [m]
sc_object.hub.IHubPntBc_B = [[I_hub_11, 0.0, 0.0], [0.0, I_hub_22, 0.0], [0.0, 0.0, I_hub_33]] # [kg m^2]
sc_object.hub.r_CN_NInit = [[0.0], [0.0], [0.0]]
sc_object.hub.v_CN_NInit = [[0.0], [0.0], [0.0]]
sc_object.hub.omega_BN_BInit = [[0.0], [0.0], [0.0]]
sc_object.hub.sigma_BNInit = [[0.0], [0.0], [0.0]]
sc_sim.AddModelToTask(dyn_task_name, sc_object)
# Shared prescribed array boom parameters
prescribed_pos_init = 0.0
prescribed_theta_init = 0.0
prescribed_trans_axis_M = np.array([1.0, 0.0, 0.0])
prescribed_rot_axis_M = np.array([1.0, 0.0, 0.0])
prv_P0M = prescribed_theta_init * prescribed_rot_axis_M
sigma_P0M = rbk.PRV2MRP(prv_P0M)
mass_prescribed = 6000.0 # [kg]
length_prescribed = 50.0 # [m]
width_prescribed = 4.0 # [m]
depth_prescribed = 4.0 # [m]
I_prescribed_11 = (1 / 12) * mass_prescribed * (length_prescribed * length_prescribed + depth_prescribed * depth_prescribed) # [kg m^2]
I_prescribed_22 = (1 / 12) * mass_prescribed * (length_prescribed * length_prescribed + width_prescribed * width_prescribed) # [kg m^2]
I_prescribed_33 = (1 / 12) * mass_prescribed * (width_prescribed * width_prescribed + depth_prescribed * depth_prescribed) # [kg m^2]
I_prescribed_Pc_P = [[I_prescribed_11, 0.0, 0.0], [0.0, I_prescribed_22, 0.0], [0.0, 0.0,I_prescribed_33]] # [kg m^2]
# Create prescribed array boom 1
prescribed_truss_1 = prescribedMotionStateEffector.PrescribedMotionStateEffector()
prescribed_truss_1.ModelTag = "prescribedTruss1"
prescribed_truss_1.setMass(mass_prescribed)
prescribed_truss_1.setIPntPc_P(I_prescribed_Pc_P)
prescribed_truss_1.setR_MB_B([4.0, 0.0, 0.0])
prescribed_truss_1.setR_PcP_P([25.0, 0.0, 0.0])
prescribed_truss_1.setR_PM_M([0.0, 0.0, 0.0])
prescribed_truss_1.setRPrime_PM_M(np.array([0.0, 0.0, 0.0]))
prescribed_truss_1.setRPrimePrime_PM_M(np.array([0.0, 0.0, 0.0]))
prescribed_truss_1.setOmega_PM_P(np.array([0.0, 0.0, 0.0]))
prescribed_truss_1.setOmegaPrime_PM_P(np.array([0.0, 0.0, 0.0]))
prescribed_truss_1.setSigma_PM(sigma_P0M)
prescribed_truss_1.setSigma_MB([0.0, 0.0, 0.0])
sc_sim.AddModelToTask(dyn_task_name, prescribed_truss_1)
sc_object.addStateEffector(prescribed_truss_1)
# Create prescribed array boom 2
prescribed_truss_2 = prescribedMotionStateEffector.PrescribedMotionStateEffector()
prescribed_truss_2.ModelTag = "prescribedTruss2"
prescribed_truss_2.setMass(mass_prescribed)
prescribed_truss_2.setIPntPc_P(I_prescribed_Pc_P)
prescribed_truss_2.setR_MB_B([-4.0, 0.0, 0.0])
prescribed_truss_2.setR_PcP_P([25.0, 0.0, 0.0])
prescribed_truss_2.setR_PM_M([0.0, 0.0, 0.0])
prescribed_truss_2.setRPrime_PM_M(np.array([0.0, 0.0, 0.0]))
prescribed_truss_2.setRPrimePrime_PM_M(np.array([0.0, 0.0, 0.0]))
prescribed_truss_2.setOmega_PM_P(np.array([0.0, 0.0, 0.0]))
prescribed_truss_2.setOmegaPrime_PM_P(np.array([0.0, 0.0, 0.0]))
prescribed_truss_2.setSigma_PM(sigma_P0M)
dcm_MB = np.array([[-1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, -1.0]])
prescribed_truss_2.setSigma_MB(rbk.C2MRP(dcm_MB))
sc_sim.AddModelToTask(dyn_task_name, prescribed_truss_2)
sc_object.addStateEffector(prescribed_truss_2)
# Shared prescribed motion profiler parameters
prescribed_ang_accel_max = 0.5 * macros.D2R # [rad/s^2]
prescribed_trans_accel_max = 0.005 # [m/s^2]
prescribed_theta_ref = 45.0 * macros.D2R # [rad]
prescribed_pos_ref = 0.0 # [m]
# Create prescribed array boom 1 rotational motion profiler
one_dof_rotation_profiler_1 = prescribedRotation1DOF.PrescribedRotation1DOF()
one_dof_rotation_profiler_1.ModelTag = "prescribedRotation1DOF_1"
one_dof_rotation_profiler_1.setRotHat_M(prescribed_rot_axis_M)
one_dof_rotation_profiler_1.setThetaDDotMax(prescribed_ang_accel_max)
one_dof_rotation_profiler_1.setThetaInit(prescribed_theta_init)
one_dof_rotation_profiler_1.setCoastOptionBangDuration(1.0)
one_dof_rotation_profiler_1.setSmoothingDuration(1.0)
sc_sim.AddModelToTask(dyn_task_name, one_dof_rotation_profiler_1)
# Create prescribed array boom 2 rotational motion profiler
one_dof_rotation_profiler_2 = prescribedRotation1DOF.PrescribedRotation1DOF()
one_dof_rotation_profiler_2.ModelTag = "prescribedRotation1DOF_2"
one_dof_rotation_profiler_2.setRotHat_M(prescribed_rot_axis_M)
one_dof_rotation_profiler_2.setThetaDDotMax(prescribed_ang_accel_max)
one_dof_rotation_profiler_2.setThetaInit(prescribed_theta_init)
one_dof_rotation_profiler_2.setCoastOptionBangDuration(1.0)
one_dof_rotation_profiler_2.setSmoothingDuration(1.0)
sc_sim.AddModelToTask(dyn_task_name, one_dof_rotation_profiler_2)
# Create prescribed array boom 1 rotational motion reference message
prescribed_rotation_1_msg_data = messaging.HingedRigidBodyMsgPayload()
prescribed_rotation_1_msg_data.theta = prescribed_theta_ref
prescribed_rotation_1_msg_data.thetaDot = 0.0
prescribed_rotation_1_msg = messaging.HingedRigidBodyMsg().write(prescribed_rotation_1_msg_data)
one_dof_rotation_profiler_1.spinningBodyInMsg.subscribeTo(prescribed_rotation_1_msg)
prescribed_truss_1.prescribedRotationInMsg.subscribeTo(one_dof_rotation_profiler_1.prescribedRotationOutMsg)
# Create prescribed array boom 2 rotational motion reference message
prescribed_rotation_2_msg_data = messaging.HingedRigidBodyMsgPayload()
prescribed_rotation_2_msg_data.theta = - prescribed_theta_ref
prescribed_rotation_2_msg_data.thetaDot = 0.0
prescribed_rotation_2_msg = messaging.HingedRigidBodyMsg().write(prescribed_rotation_2_msg_data)
one_dof_rotation_profiler_2.spinningBodyInMsg.subscribeTo(prescribed_rotation_2_msg)
prescribed_truss_2.prescribedRotationInMsg.subscribeTo(one_dof_rotation_profiler_2.prescribedRotationOutMsg)
# Create prescribed array boom 1 translational motion profiler
one_dof_translation_profiler_1 = prescribedLinearTranslation.PrescribedLinearTranslation()
one_dof_translation_profiler_1.ModelTag = "prescribedLinearTranslation_1"
one_dof_translation_profiler_1.setTransHat_M(prescribed_trans_axis_M)
one_dof_translation_profiler_1.setTransAccelMax(prescribed_trans_accel_max)
one_dof_translation_profiler_1.setTransPosInit(prescribed_pos_init)
one_dof_translation_profiler_1.setCoastOptionBangDuration(1.0)
one_dof_translation_profiler_1.setSmoothingDuration(1.0)
sc_sim.AddModelToTask(dyn_task_name, one_dof_translation_profiler_1)
# Create prescribed array boom 2 translational motion profiler
one_dof_translation_profiler_2 = prescribedLinearTranslation.PrescribedLinearTranslation()
one_dof_translation_profiler_2.ModelTag = "prescribedLinearTranslation_2"
one_dof_translation_profiler_2.setTransHat_M(prescribed_trans_axis_M)
one_dof_translation_profiler_2.setTransAccelMax(prescribed_trans_accel_max)
one_dof_translation_profiler_2.setTransPosInit(prescribed_pos_init)
one_dof_translation_profiler_2.setCoastOptionBangDuration(1.0)
one_dof_translation_profiler_2.setSmoothingDuration(1.0)
sc_sim.AddModelToTask(dyn_task_name, one_dof_translation_profiler_2)
# Create prescribed array boom translational motion reference message
prescribed_translation_msg_data = messaging.LinearTranslationRigidBodyMsgPayload()
prescribed_translation_msg_data.rho = prescribed_pos_ref
prescribed_translation_msg_data.rhoDot = 0.0
prescribed_translation_msg = messaging.LinearTranslationRigidBodyMsg().write(prescribed_translation_msg_data)
one_dof_translation_profiler_1.linearTranslationRigidBodyInMsg.subscribeTo(prescribed_translation_msg)
one_dof_translation_profiler_2.linearTranslationRigidBodyInMsg.subscribeTo(prescribed_translation_msg)
prescribed_truss_1.prescribedTranslationInMsg.subscribeTo(one_dof_translation_profiler_1.prescribedTranslationOutMsg)
prescribed_truss_2.prescribedTranslationInMsg.subscribeTo(one_dof_translation_profiler_2.prescribedTranslationOutMsg)
# Shared spinning body parameters
mass_panel = 1000.0 # [kg]
length_panel = 10.0 # [m]
width_panel = 0.3 # [m]
depth_panel = 30.0 # [m]
I_panel_11 = (1 / 12) * mass_panel * (length_panel * length_panel + depth_panel * depth_panel) # [kg m^2]
I_panel_22 = (1 / 12) * mass_panel * (length_panel * length_panel + width_panel * width_panel) # [kg m^2]
I_panel_33 = (1 / 12) * mass_panel * (width_prescribed * width_prescribed + depth_panel * depth_panel) # [kg m^2]
I_panel_ScS = [[I_panel_11, 0.0, 0.0], [0.0, I_panel_22, 0.0], [0.0, 0.0, I_panel_33]] # [kg m^2]
r_ScS_S = [[0.0], [0.0], [15.0]]
panel_s_hat_S = [[1], [0], [0]]
k = 700000.0
c = 50000.0
r_S1B_B = [[50.0], [0.0], [2.0]] # [m] r_S1P_P
r_S2B_B = [[35.0], [0.0], [2.0]] # [m] r_S2P_P
r_S3B_B = [[50.0], [0.0], [-2.0]] # [m] r_S3P_P
r_S4B_B = [[35.0], [0.0], [-2.0]] # [m] r_S4P_P
r_S5B_B = [[35.0], [0.0], [-2.0]] # [m] r_S5P_P
r_S6B_B = [[50.0], [0.0], [-2.0]] # [m] r_S6P_P
r_S7B_B = [[35.0], [0.0], [2.0]] # [m] r_S7P_P
r_S8B_B = [[50.0], [0.0], [2.0]] # [m] r_S8P_P
# Create the spinning bodies
num_panels = 8
spinning_panel_list = list()
for idx in range(num_panels):
panel_num = idx + 1
spinning_panel_list.append(spinningBodyOneDOFStateEffector.SpinningBodyOneDOFStateEffector())
spinning_panel_list[idx].ModelTag = "spinningBody" + str(panel_num)
spinning_panel_list[idx].mass = mass_panel
spinning_panel_list[idx].IPntSc_S = I_panel_ScS
spinning_panel_list[idx].r_ScS_S = r_ScS_S
spinning_panel_list[idx].sHat_S = panel_s_hat_S
spinning_panel_list[idx].k = k
spinning_panel_list[idx].c = c
if panel_num == 1 or panel_num == 2 or panel_num == 7 or panel_num == 8:
spinning_panel_list[idx].dcm_S0B = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
else:
spinning_panel_list[idx].dcm_S0B = [[-1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, -1.0]]
sc_sim.AddModelToTask(dyn_task_name, spinning_panel_list[idx])
spinning_panel_list[0].r_SB_B = r_S1B_B
spinning_panel_list[1].r_SB_B = r_S2B_B
spinning_panel_list[2].r_SB_B = r_S3B_B
spinning_panel_list[3].r_SB_B = r_S4B_B
spinning_panel_list[4].r_SB_B = r_S5B_B
spinning_panel_list[5].r_SB_B = r_S6B_B
spinning_panel_list[6].r_SB_B = r_S7B_B
spinning_panel_list[7].r_SB_B = r_S8B_B
# Connect the solar panels to the correct prescribed truss
prescribed_truss_1.addStateEffector(spinning_panel_list[0])
prescribed_truss_1.addStateEffector(spinning_panel_list[1])
prescribed_truss_1.addStateEffector(spinning_panel_list[2])
prescribed_truss_1.addStateEffector(spinning_panel_list[3])
prescribed_truss_2.addStateEffector(spinning_panel_list[4])
prescribed_truss_2.addStateEffector(spinning_panel_list[5])
prescribed_truss_2.addStateEffector(spinning_panel_list[6])
prescribed_truss_2.addStateEffector(spinning_panel_list[7])
# Set up data logging
sc_state_data_log = sc_object.scStateOutMsg.recorder()
one_dof_rotation_profiler_1_data_log = one_dof_rotation_profiler_1.spinningBodyOutMsg.recorder()
one_dof_rotation_profiler_2_data_log = one_dof_rotation_profiler_2.spinningBodyOutMsg.recorder()
prescribed_rotation_1_data_log = prescribed_truss_1.prescribedRotationOutMsg.recorder()
prescribed_rotation_2_data_log = prescribed_truss_2.prescribedRotationOutMsg.recorder()
spinning_panel_1_data_log = spinning_panel_list[0].spinningBodyOutMsg.recorder()
spinning_panel_3_data_log = spinning_panel_list[2].spinningBodyOutMsg.recorder()
sc_sim.AddModelToTask(data_rec_task_name, sc_state_data_log)
sc_sim.AddModelToTask(data_rec_task_name, one_dof_rotation_profiler_1_data_log)
sc_sim.AddModelToTask(data_rec_task_name, one_dof_rotation_profiler_2_data_log)
sc_sim.AddModelToTask(data_rec_task_name, prescribed_rotation_1_data_log)
sc_sim.AddModelToTask(data_rec_task_name, prescribed_rotation_2_data_log)
sc_sim.AddModelToTask(data_rec_task_name, spinning_panel_1_data_log)
sc_sim.AddModelToTask(data_rec_task_name, spinning_panel_3_data_log)
# Add Vizard
sc_body_list = [sc_object]
sc_body_list.append(["prescribedTruss1", prescribed_truss_1.prescribedMotionConfigLogOutMsg])
sc_body_list.append(["prescribedTruss2", prescribed_truss_2.prescribedMotionConfigLogOutMsg])
for idx in range(num_panels):
sc_body_list.append(["spinningBody" + str(idx + 1), spinning_panel_list[idx].spinningBodyConfigLogOutMsg])
if vizSupport.vizFound:
viz = vizSupport.enableUnityVisualization(sc_sim, data_rec_task_name, sc_body_list,
saveFile=filename
)
vizSupport.createCustomModel(viz
, simBodiesToModify=[sc_object.ModelTag]
, modelPath="CUBE"
, scale=[width_hub, length_hub, depth_hub]
, color=vizSupport.toRGBA255("gray"))
vizSupport.createCustomModel(viz
, simBodiesToModify=["prescribedTruss1"]
, modelPath="CUBE"
, scale=[length_prescribed, width_prescribed, depth_prescribed]
, color=vizSupport.toRGBA255("green"))
vizSupport.createCustomModel(viz
, simBodiesToModify=["prescribedTruss2"]
, modelPath="CUBE"
, scale=[length_prescribed, width_prescribed, depth_prescribed]
, color=vizSupport.toRGBA255("green"))
for idx in range(num_panels):
vizSupport.createCustomModel(viz
, simBodiesToModify=["spinningBody" + str(idx + 1)]
, modelPath="CUBE"
, scale=[length_panel, width_panel, depth_panel]
, color=vizSupport.toRGBA255("blue"))
viz.settings.orbitLinesOn = -1
# Run the simulation
sc_sim.InitializeSimulation()
sim_time = 90.0 # [s]
sc_sim.ConfigureStopTime(macros.sec2nano(sim_time))
sc_sim.ExecuteSimulation()
# Extract the logged variables
timespan = sc_state_data_log.times() * macros.NANO2SEC # [s]
r_BN_N = sc_state_data_log.r_BN_N # [m]
sigma_BN = sc_state_data_log.sigma_BN
omega_BN_B = sc_state_data_log.omega_BN_B * macros.R2D # [deg/s]
prescribed_theta_1 = one_dof_rotation_profiler_1_data_log.theta * macros.R2D # [deg]
prescribed_theta_2 = one_dof_rotation_profiler_2_data_log.theta * macros.R2D # [deg]
omega_P1M_P1 = prescribed_rotation_1_data_log.omega_PM_P * macros.R2D # [deg/s]
omega_P2M_P2 = prescribed_rotation_2_data_log.omega_PM_P * macros.R2D # [deg/s]
omega_prime_P1M_P1 = prescribed_rotation_1_data_log.omegaPrime_PM_P * macros.R2D # [deg/s^2]
omega_prime_P2M_P2 = prescribed_rotation_2_data_log.omegaPrime_PM_P * macros.R2D # [deg/s^2]
spinning_panel_1_theta = spinning_panel_1_data_log.theta * macros.R2D # [deg]
spinning_panel_3_theta = spinning_panel_3_data_log.theta * macros.R2D # [deg]
spinning_panel_1_theta_dot = spinning_panel_1_data_log.thetaDot * macros.R2D # [deg]
spinning_panel_3_theta_dot = spinning_panel_3_data_log.thetaDot * macros.R2D # [deg]
figure_list = {}
plt.close("all")
# Plot prescribed truss angles
plt.figure(1)
plt.clf()
plt.plot(timespan, prescribed_theta_1, label=r'$\theta_{P_1}$', color="teal")
plt.plot(timespan, prescribed_theta_2, label=r'$\theta_{P_2}$', color="darkviolet")
plt.title(r'Prescribed Truss Angles', fontsize=16)
plt.ylabel('(deg)', fontsize=16)
plt.xlabel('Time (s)', fontsize=16)
plt.legend(loc="center right", prop={"size": 16})
plt.grid(True)
plt_name = filename + "_1"
figure_list[plt_name] = plt.figure(1)
# Plot prescribed truss angle rates
prescribed_theta_dot_1 = np.dot(omega_P1M_P1, prescribed_rot_axis_M) # [deg/s]
prescribed_theta_dot_2 = np.dot(omega_P2M_P2, prescribed_rot_axis_M) # [deg/s]
plt.figure(2)
plt.clf()
plt.plot(timespan, prescribed_theta_dot_1, label=r'$\dot{\theta}_{P_1}$', color="teal")
plt.plot(timespan, prescribed_theta_dot_2, label=r'$\dot{\theta}_{P_2}$', color="darkviolet")
plt.title(r'Prescribed Truss Angle Rates', fontsize=16)
plt.ylabel('(deg/s)', fontsize=16)
plt.xlabel('Time (s)', fontsize=16)
plt.legend(loc="upper right", prop={"size": 16})
plt.grid(True)
plt_name = filename + "_2"
figure_list[plt_name] = plt.figure(2)
# Plot prescribed truss accelerations
prescribed_theta_ddot_1 = np.dot(omega_prime_P1M_P1, prescribed_rot_axis_M) # [deg/s^2]
prescribed_theta_ddot_2 = np.dot(omega_prime_P2M_P2, prescribed_rot_axis_M) # [deg/s^2]
plt.figure(3)
plt.clf()
plt.plot(timespan, prescribed_theta_ddot_1, label=r'$\ddot{\theta}_{P_1}$', color="teal")
plt.plot(timespan, prescribed_theta_ddot_2, label=r'$\ddot{\theta}_{P_2}$', color="darkviolet")
plt.title(r'Prescribed Truss Accelerations', fontsize=16)
plt.ylabel('(deg/s$^2$)', fontsize=16)
plt.xlabel('Time (s)', fontsize=16)
plt.legend(loc="upper right", prop={"size": 16})
plt.grid(True)
plt_name = filename + "_3"
figure_list[plt_name] = plt.figure(3)
# Plot spinning body solar panel angles
plt.figure(4)
plt.clf()
plt.plot(timespan, spinning_panel_1_theta, label=r'$\theta_{S_{1,2,5,6}}$', color="teal")
plt.plot(timespan, spinning_panel_3_theta, label=r'$\theta_{S_{3,4,7,8}}$', color="darkviolet")
plt.title(r'Solar Array Angles', fontsize=16)
plt.ylabel('(deg)', fontsize=16)
plt.xlabel('Time (s)', fontsize=16)
plt.legend(loc="upper right", prop={"size": 16})
plt.grid(True)
plt_name = filename + "_4"
figure_list[plt_name] = plt.figure(4)
# Plot spinning body solar panel angle rates
plt.figure(5)
plt.clf()
plt.plot(timespan, spinning_panel_1_theta_dot, label=r'$\dot{\theta}_{S_{1,2,5,6}}$', color="teal")
plt.plot(timespan, spinning_panel_3_theta_dot, label=r'$\dot{\theta}_{S_{3,4,7,8}}$', color="darkviolet")
plt.title(r'Solar Array Angle Rates', fontsize=16)
plt.ylabel('(deg/s)', fontsize=16)
plt.xlabel('Time (s)', fontsize=16)
plt.legend(loc="upper right", prop={"size": 16})
plt.grid(True)
plt_name = filename + "_5"
figure_list[plt_name] = plt.figure(5)
# Plot hub inertial position
plt.figure(6)
plt.clf()
plt.plot(timespan, r_BN_N[:, 0], label=r'$r_1$', color="teal")
plt.plot(timespan, r_BN_N[:, 1], label=r'$r_2$', color="darkviolet")
plt.plot(timespan, r_BN_N[:, 2], label=r'$r_3$', color="blue")
plt.title(r'Hub Inertial Position ${}^\mathcal{N} r_{B/N}$', fontsize=16)
plt.ylabel('(m)', fontsize=16)
plt.xlabel('Time (s)', fontsize=16)
plt.legend(loc="center left", prop={"size": 16})
plt.grid(True)
plt_name = filename + "_6"
figure_list[plt_name] = plt.figure(6)
# Plot hub inertial attitude
plt.figure(7)
plt.clf()
plt.plot(timespan, sigma_BN[:, 0], label=r'$\sigma_1$', color="teal")
plt.plot(timespan, sigma_BN[:, 1], label=r'$\sigma_2$', color="darkviolet")
plt.plot(timespan, sigma_BN[:, 2], label=r'$\sigma_3$', color="blue")
plt.title(r'Hub Inertial Attitude $\sigma_{\mathcal{B}/\mathcal{N}}$', fontsize=16)
plt.ylabel('', fontsize=16)
plt.xlabel('Time (s)', fontsize=16)
plt.legend(loc="center right", prop={"size": 16})
plt.grid(True)
plt_name = filename + "_7"
figure_list[plt_name] = plt.figure(7)
# Plot hub inertial angular velocity
plt.figure(8)
plt.clf()
plt.plot(timespan, omega_BN_B[:, 0], label=r'$\omega_1$', color="teal")
plt.plot(timespan, omega_BN_B[:, 1], label=r'$\omega_2$', color="darkviolet")
plt.plot(timespan, omega_BN_B[:, 2], label=r'$\omega_3$', color="blue")
plt.title(r'Hub Inertial Angular Velocity ${}^\mathcal{B} \omega_{\mathcal{B}/\mathcal{N}}$', fontsize=16)
plt.ylabel('(deg/s)', fontsize=16)
plt.xlabel('Time (s)', fontsize=16)
plt.legend(loc="center right", prop={"size": 16})
plt.grid(True)
plt_name = filename + "_8"
figure_list[plt_name] = plt.figure(8)
if show_plots:
plt.show()
plt.close("all")
return figure_list
if __name__ == "__main__":
run(True)