Source code for scenario_StationKeepingMultiSat

#
#  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"""
Overview
--------

This script sets up three 6-DOF spacecraft orbiting the Earth in formation. The goal of this scenario is to

#. introduce formation flying control with desired orbital element differences,
#. evidence how the module can conciliate attitude requests with thruster requirements, and
#. show how one can choose whether the chief of the formation is a spacecraft or the formation's barycenter.

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

      python3 scenario_StationKeepingMultiSat.py

This simulation is based on the :ref:`scenario_AttGuidMultiSat` with the addition of station keeping control. Attitude
mode requests are processed in the same way as before, but now there is the added complexity of introducing formation
control, which can influence attitude requests.

The user can choose whether to use the zeroth index spacecraft or the formation's barycenter as the formation's chief.
On that note, some geometries are not possible when using the barycenter as a reference point for the formation. This is
because the barycenter is influenced by the spacecraft reorienting themselves, and so only some geometries are feasible.
Failure to take this into account results in the spacecraft continuously correcting their orbits without ever
converging.

For simplicity, the script plots only the information related to one of the spacecraft, despite logging the necessary
information for all spacecraft in the simulation.

Custom Dynamics Configurations Instructions
-------------------------------------------

The dynamics modules required for this scenario are the same used in :ref:`scenario_BasicOrbitMultiSat` and
:ref:`scenario_AttGuidMultiSat`. However, this example takes full advantage of all the features of the dynamics class,
which includes thrusters for orbit corrections.

Custom FSW Configurations Instructions
--------------------------------------

As stated in the previous section, the :ref:`BSK_MultiSatFsw` class used in this example is the same as the one used in
:ref:`scenario_AttGuidMultiSat`. The main difference is that the station keeping module is now used, which allows for
relative orbit geometry control.

If no station keeping is desired, then the FSW stack works exactly as in :ref:`scenario_AttGuidMultiSat`. However, if
station keeping is set properly, the FSW events work as follows. First, the attitude reference is set given the pointing
requirements. Then, the station keeping module computes the information regarding the necessary corrective burns, such
as point in orbit, duration, thrust, attitude requirements, etc. With this information, the module then chooses whether
the spacecraft is in a point in orbit where a burn is required. If it is, the attitude reference from the pointing
requirement is overruled in favor of the necessary attitude to complete the current burn. If it is not, the reference
attitude passes through unchanged.

The control law used to drive the formation to its intended orbital element differences is guaranteed to converge if the
chief has Keplerian motion. This might not be the case when the chief is the formation's barycenter, as its orbital
elements change in accordance to how each spacecraft is maneuvering. One way to help with convergence is to make sure
that the barycenter has invariant orbital elements. This can be achieved by guaranteeing that the following equation
holds:

.. math::
    \sum_i m_i\Delta oe_i = 0

Activation of the station keeping mode is done through the ``stationKeeping`` flag. If set to ``True``, formation
control will be activated.

Due to the fact that the ``spacecraftReconfig`` module only accepts messages of the type :ref:`attRefMsgPayload`, the
``locationPointing`` module always outputs a reference message and the ``attTrackingError`` module is always called,
unlike how it happens in :ref:`scenario_AttGuidMultiSat`.

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

Since three spacecraft are simulated, and to prevent excessively busy plots, only the information pertaining to one
spacecraft is plotted per simulation.

::

    show_plots = True, numberSpacecraft=3, relativeNavigation=False

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

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

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

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

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

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

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

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

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

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

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

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

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

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

"""

import copy
# Get current file path
import inspect
import math
import os
import sys

import numpy as np
from Basilisk.architecture import messaging
# Import utilities
from Basilisk.utilities import orbitalMotion, macros, vizSupport, RigidBodyKinematics as rbk

filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))

# Import master classes: simulation base class and scenario base class
sys.path.append(path + '/../')
sys.path.append(path + '/../modelsMultiSat')
sys.path.append(path + '/../plottingMultiSat')
from BSK_MultiSatMasters import BSKSim, BSKScenario
import BSK_EnvironmentEarth
import BSK_MultiSatDynamics
import BSK_MultiSatFsw

# Import plotting files for your scenario
import BSK_MultiSatPlotting as plt

# Create your own scenario child class
[docs]class scenario_StationKeepingFormationFlying(BSKSim, BSKScenario): def __init__(self, numberSpacecraft, relativeNavigation): super(scenario_StationKeepingFormationFlying, self).__init__( numberSpacecraft, relativeNavigation=relativeNavigation, fswRate=1, dynRate=1, envRate=1, relNavRate=1) self.name = 'scenario_StationKeepingFormationFlying' # Connect the environment, dynamics and FSW classes. It is crucial that these are set in the order specified, as # some connects made imply that some modules already exist self.set_EnvModel(BSK_EnvironmentEarth) self.set_DynModel([BSK_MultiSatDynamics] * numberSpacecraft) self.set_FswModel([BSK_MultiSatFsw] * numberSpacecraft) # declare empty class variables self.samplingTime = [] self.snTransLog = [] self.snAttLog = [] self.attErrorLog = [] self.attRefLog = [] self.rwMotorLog = [] self.rwSpeedLog = [] self.spLog = [] self.psLog = [] self.pmLog = [] self.rwLogs = [[] for _ in range(self.numberSpacecraft)] self.rwPowerLogs = [[] for _ in range(self.numberSpacecraft)] self.fuelLog = [] self.thrLogs = [[] for _ in range(self.numberSpacecraft)] self.chiefTransLog = None # declare empty containers for orbital elements self.oe = [] # Set initial conditions and record the relevant messages self.configure_initial_conditions() self.log_outputs(relativeNavigation) if vizSupport.vizFound: # if this scenario is to interface with the BSK Viz, uncomment the following line DynModelsList = [] rwStateEffectorList = [] thDynamicEffectorList = [] for i in range(self.numberSpacecraft): DynModelsList.append(self.DynModels[i].scObject) rwStateEffectorList.append(self.DynModels[i].rwStateEffector) thDynamicEffectorList.append([self.DynModels[i].thrusterDynamicEffector]) gsList = [] for i in range(self.numberSpacecraft): batteryPanel = vizSupport.vizInterface.GenericStorage() batteryPanel.label = "Battery" batteryPanel.units = "Ws" batteryPanel.color = vizSupport.vizInterface.IntVector(vizSupport.toRGBA255("red") + vizSupport.toRGBA255("lightgreen")) batteryPanel.thresholds = vizSupport.vizInterface.IntVector([20]) batteryInMsg = messaging.PowerStorageStatusMsgReader() batteryInMsg.subscribeTo(self.DynModels[i].powerMonitor.batPowerOutMsg) batteryPanel.batteryStateInMsg = batteryInMsg batteryPanel.this.disown() tankPanel = vizSupport.vizInterface.GenericStorage() tankPanel.label = "Tank" tankPanel.units = "kg" tankPanel.color = vizSupport.vizInterface.IntVector(vizSupport.toRGBA255("cyan")) tankInMsg = messaging.FuelTankMsgReader() tankInMsg.subscribeTo(self.DynModels[i].fuelTankStateEffector.fuelTankOutMsg) tankPanel.fuelTankStateInMsg = tankInMsg tankPanel.this.disown() gsList.append([batteryPanel, tankPanel]) viz = vizSupport.enableUnityVisualization(self, self.DynModels[0].taskName, DynModelsList # , saveFile=__file__ , rwEffectorList=rwStateEffectorList , thrEffectorList=thDynamicEffectorList , genericStorageList=gsList ) viz.settings.showSpacecraftLabels = True viz.settings.orbitLinesOn = 2 # show osculating relative orbit trajectories viz.settings.mainCameraTarget = "sat-1" viz.liveSettings.relativeOrbitChief = "sat-0" # set the chief for relative orbit trajectory for i in range(self.numberSpacecraft): vizSupport.setInstrumentGuiSetting(viz, spacecraftName=self.DynModels[i].scObject.ModelTag, showGenericStoragePanel=True)
[docs] def configure_initial_conditions(self): EnvModel = self.get_EnvModel() DynModels = self.get_DynModel() # Configure initial conditions for spacecraft 0 self.oe.append(orbitalMotion.ClassicElements()) self.oe[0].a = 1.4*EnvModel.planetRadius # meters self.oe[0].e = 0.2 self.oe[0].i = 45.0 * macros.D2R self.oe[0].Omega = 48.2 * macros.D2R self.oe[0].omega = 347.8 * macros.D2R self.oe[0].f = 85.3 * macros.D2R rN, vN = orbitalMotion.elem2rv(EnvModel.mu, self.oe[0]) orbitalMotion.rv2elem(EnvModel.mu, rN, vN) DynModels[0].scObject.hub.r_CN_NInit = rN # m - r_CN_N DynModels[0].scObject.hub.v_CN_NInit = vN # m/s - v_CN_N DynModels[0].scObject.hub.sigma_BNInit = [[0.1], [0.6], [-0.8]] # sigma_BN_B DynModels[0].scObject.hub.omega_BN_BInit = [[0.0], [0.0], [0.0]] # rad/s - omega_BN_B # Configure initial conditions for spacecraft 1 self.oe.append(copy.deepcopy(self.oe[0])) self.oe[1].f *= 1.001 rN2, vN2 = orbitalMotion.elem2rv(EnvModel.mu, self.oe[1]) orbitalMotion.rv2elem(EnvModel.mu, rN2, vN2) DynModels[1].scObject.hub.r_CN_NInit = rN2 # m - r_CN_N DynModels[1].scObject.hub.v_CN_NInit = vN2 # m/s - v_CN_N DynModels[1].scObject.hub.sigma_BNInit = [[0.1], [0.6], [-0.8]] # sigma_BN_B DynModels[1].scObject.hub.omega_BN_BInit = [[0.0], [0.0], [0.0]] # rad/s - omega_BN_B # Configure initial conditions for spacecraft 2 self.oe.append(copy.deepcopy(self.oe[0])) self.oe[2].f *= 0.999 rN3, vN3 = orbitalMotion.elem2rv(EnvModel.mu, self.oe[2]) orbitalMotion.rv2elem(EnvModel.mu, rN3, vN3) DynModels[2].scObject.hub.r_CN_NInit = rN3 # m - r_CN_N DynModels[2].scObject.hub.v_CN_NInit = vN3 # m/s - v_CN_N DynModels[2].scObject.hub.sigma_BNInit = [[0.1], [0.6], [-0.8]] # sigma_BN_B DynModels[2].scObject.hub.omega_BN_BInit = [[0.0], [0.0], [0.0]] # rad/s - omega_BN_B
[docs] def log_outputs(self, relativeNavigation): # Process outputs DynModels = self.get_DynModel() FswModels = self.get_FswModel() # Set the sampling time self.samplingTime = macros.sec2nano(10) # Log the barycentre's position and velocity if relativeNavigation: self.chiefTransLog = self.relativeNavigationModule.transOutMsg.recorder(self.samplingTime) self.AddModelToTask(self.relativeNavigationTaskName, self.chiefTransLog) # Loop through every spacecraft for spacecraft in range(self.numberSpacecraft): # log the navigation messages self.snTransLog.append(DynModels[spacecraft].simpleNavObject.transOutMsg.recorder(self.samplingTime)) self.snAttLog.append(DynModels[spacecraft].simpleNavObject.attOutMsg.recorder(self.samplingTime)) self.AddModelToTask(DynModels[spacecraft].taskName, self.snTransLog[spacecraft]) self.AddModelToTask(DynModels[spacecraft].taskName, self.snAttLog[spacecraft]) # log the reference messages self.attRefLog.append(FswModels[spacecraft].attRefMsg.recorder(self.samplingTime)) self.AddModelToTask(DynModels[spacecraft].taskName, self.attRefLog[spacecraft]) # log the attitude error messages self.attErrorLog.append(FswModels[spacecraft].attGuidMsg.recorder(self.samplingTime)) self.AddModelToTask(DynModels[spacecraft].taskName, self.attErrorLog[spacecraft]) # log the RW torque messages self.rwMotorLog.append( FswModels[spacecraft].rwMotorTorque.rwMotorTorqueOutMsg.recorder(self.samplingTime)) self.AddModelToTask(DynModels[spacecraft].taskName, self.rwMotorLog[spacecraft]) # log the RW wheel speed information self.rwSpeedLog.append(DynModels[spacecraft].rwStateEffector.rwSpeedOutMsg.recorder(self.samplingTime)) self.AddModelToTask(DynModels[spacecraft].taskName, self.rwSpeedLog[spacecraft]) # log addition RW information (power, etc) for item in range(DynModels[spacecraft].numRW): self.rwLogs[spacecraft].append(DynModels[spacecraft].rwStateEffector.rwOutMsgs[item].recorder(self.samplingTime)) self.rwPowerLogs[spacecraft].append(DynModels[spacecraft].rwPowerList[item].nodePowerOutMsg.recorder(self.samplingTime)) self.AddModelToTask(DynModels[spacecraft].taskName, self.rwLogs[spacecraft][item]) self.AddModelToTask(DynModels[spacecraft].taskName, self.rwPowerLogs[spacecraft][item]) # log the remaining power modules self.spLog.append(DynModels[spacecraft].solarPanel.nodePowerOutMsg.recorder(self.samplingTime)) self.psLog.append(DynModels[spacecraft].powerSink.nodePowerOutMsg.recorder(self.samplingTime)) self.pmLog.append(DynModels[spacecraft].powerMonitor.batPowerOutMsg.recorder(self.samplingTime)) self.AddModelToTask(DynModels[spacecraft].taskName, self.spLog[spacecraft]) self.AddModelToTask(DynModels[spacecraft].taskName, self.psLog[spacecraft]) self.AddModelToTask(DynModels[spacecraft].taskName, self.pmLog[spacecraft]) # log fuel information self.fuelLog.append(DynModels[spacecraft].fuelTankStateEffector.fuelTankOutMsg.recorder(self.samplingTime)) self.AddModelToTask(DynModels[spacecraft].taskName, self.fuelLog[spacecraft]) # log thruster information for item in range(DynModels[spacecraft].numThr): self.thrLogs[spacecraft].append(DynModels[spacecraft].thrusterDynamicEffector.thrusterOutMsgs[item].recorder(self.samplingTime)) self.AddModelToTask(DynModels[spacecraft].taskName, self.thrLogs[spacecraft][item])
[docs] def pull_outputs(self, showPlots, relativeNavigation, spacecraftIndex): # Process outputs DynModels = self.get_DynModel() EnvModel = self.get_EnvModel() # # Retrieve the logged data # dataUsReq = self.rwMotorLog[spacecraftIndex].motorTorque dataSigmaBR = self.attErrorLog[spacecraftIndex].sigma_BR dataOmegaBR = self.attErrorLog[spacecraftIndex].omega_BR_B dataSigmaBN = self.snAttLog[spacecraftIndex].sigma_BN dataOmegaBN_B = self.snAttLog[spacecraftIndex].omega_BN_B dataOmegaRW = self.rwSpeedLog[spacecraftIndex].wheelSpeeds dataSigmaRN = self.attRefLog[spacecraftIndex].sigma_RN dataOmegaRN_N = self.attRefLog[spacecraftIndex].omega_RN_N dataFuelMass = self.fuelLog[spacecraftIndex].fuelMass # Save RW information dataRW = [] dataRWPower = [] for item in range(DynModels[spacecraftIndex].numRW): dataRW.append(self.rwLogs[spacecraftIndex][item].u_current) dataRWPower.append(self.rwPowerLogs[spacecraftIndex][item].netPower) # Save thrusters information dataThrust = [] dataThrustPercentage = [] for item in range(DynModels[spacecraftIndex].numThr): dataThrust.append(self.thrLogs[spacecraftIndex][item].thrustForce_B) dataThrustPercentage.append(self.thrLogs[spacecraftIndex][item].thrustFactor) # Save power info supplyData = self.spLog[spacecraftIndex].netPower sinkData = self.psLog[spacecraftIndex].netPower storageData = self.pmLog[spacecraftIndex].storageLevel netData = self.pmLog[spacecraftIndex].currentNetPower # Retrieve the time info timeLineSetMin = self.snTransLog[spacecraftIndex].times() * macros.NANO2MIN timeLineSetSec = self.snTransLog[spacecraftIndex].times() * macros.NANO2SEC # Compute the number of time steps of the simulation simLength = len(timeLineSetMin) # Convert the reference attitude rate into body frame components dataOmegaRN_B = [] for i in range(simLength): dcmBN = rbk.MRP2C(dataSigmaBN[i, :]) dataOmegaRN_B.append(dcmBN.dot(dataOmegaRN_N[i, :])) dataOmegaRN_B = np.array(dataOmegaRN_B) # Extract position and velocity information for all spacecraft r_BN_N = [] v_BN_N = [] for i in range(self.numberSpacecraft): r_BN_N.append(self.snTransLog[i].r_BN_N) v_BN_N.append(self.snTransLog[i].v_BN_N) # Extract position and velocity information of the chief if relativeNavigation: dataChiefPosition = self.chiefTransLog.r_BN_N dataChiefVelocity = self.chiefTransLog.v_BN_N else: dataChiefPosition = r_BN_N[0] dataChiefVelocity = v_BN_N[0] # Compute the relative position in the Hill frame dr = [] if relativeNavigation: for i in range(self.numberSpacecraft): rd = np.array([orbitalMotion.rv2hill(dataChiefPosition[item], dataChiefVelocity[item], r_BN_N[i][item], v_BN_N[i][item])[0] for item in range(simLength)]) dr.append(rd) else: for i in range(1, self.numberSpacecraft): rd = np.array([orbitalMotion.rv2hill(dataChiefPosition[item], dataChiefVelocity[item], r_BN_N[i][item], v_BN_N[i][item])[0] for item in range(simLength)]) dr.append(rd) # Compute the orbital element differences between the spacecraft and the chief oed = np.empty((simLength, 6)) for i in range(simLength): oe_tmp = orbitalMotion.rv2elem(EnvModel.mu, dataChiefPosition[i], dataChiefVelocity[i]) oe2_tmp = orbitalMotion.rv2elem(EnvModel.mu, r_BN_N[spacecraftIndex][i], v_BN_N[spacecraftIndex][i]) oed[i, 0] = (oe2_tmp.a - oe_tmp.a) / oe_tmp.a oed[i, 1] = oe2_tmp.e - oe_tmp.e oed[i, 2] = oe2_tmp.i - oe_tmp.i oed[i, 3] = oe2_tmp.Omega - oe_tmp.Omega oed[i, 4] = oe2_tmp.omega - oe_tmp.omega E_tmp = orbitalMotion.f2E(oe_tmp.f, oe_tmp.e) E2_tmp = orbitalMotion.f2E(oe2_tmp.f, oe2_tmp.e) oed[i, 5] = orbitalMotion.E2M(E2_tmp, oe2_tmp.e) - orbitalMotion.E2M(E_tmp, oe_tmp.e) for j in range(3, 6): if oed[i, j] > math.pi: oed[i, j] = oed[i, j] - 2 * math.pi if oed[i, j] < -math.pi: oed[i, j] = oed[i, j] + 2 * math.pi # Compute the orbit period T = 2*math.pi*math.sqrt(self.oe[spacecraftIndex].a ** 3 / EnvModel.mu) # # Plot results # plt.clear_all_plots() plt.plot_attitude(timeLineSetMin, dataSigmaBN, 1) plt.plot_rate(timeLineSetMin, dataOmegaBN_B, 2) plt.plot_attitude_error(timeLineSetMin, dataSigmaBR, 3) plt.plot_rate_error(timeLineSetMin, dataOmegaBR, 4) plt.plot_attitude_reference(timeLineSetMin, dataSigmaRN, 5) plt.plot_rate_reference(timeLineSetMin, dataOmegaRN_B, 6) plt.plot_rw_motor_torque(timeLineSetMin, dataUsReq, dataRW, DynModels[spacecraftIndex].numRW, 7) plt.plot_rw_speeds(timeLineSetMin, dataOmegaRW, DynModels[spacecraftIndex].numRW, 8) plt.plot_orbits(r_BN_N, self.numberSpacecraft, 9) plt.plot_relative_orbits(dr, len(dr), 10) plt.plot_orbital_element_differences(timeLineSetSec / T, oed, 11) plt.plot_power(timeLineSetMin, netData, supplyData, sinkData, 12) plt.plot_fuel(timeLineSetMin, dataFuelMass, 13) plt.plot_thrust_percentage(timeLineSetMin, dataThrustPercentage, DynModels[spacecraftIndex].numThr, 14) figureList = {} if showPlots: plt.show_all_plots() else: fileName = os.path.basename(os.path.splitext(__file__)[0]) figureNames = ["attitude", "rate", "attitudeTrackingError", "trackingErrorRate", "attitudeReference", "rateReference", "rwMotorTorque", "rwSpeeds", "orbits", "relativeOrbits", "oeDifferences", "power", "fuel", "thrustPercentage"] figureList = plt.save_all_plots(fileName, figureNames) # close the plots being saved off to avoid over-writing old and new figures plt.clear_all_plots() return figureList
def runScenario(scenario, relativeNavigation): # Get the environment model EnvModel = scenario.get_EnvModel() # Configure initial FSW attitude modes scenario.FSWModels[0].modeRequest = "sunPointing" scenario.FSWModels[1].modeRequest = "inertialPointing" scenario.FSWModels[2].modeRequest = "locationPointing" # Configure station keeping module for spacecraft in range(scenario.numberSpacecraft): if relativeNavigation: scenario.relativeNavigationModule.addSpacecraftToModel( scenario.DynModels[spacecraft].simpleNavObject.transOutMsg, scenario.DynModels[spacecraft].simpleMassPropsObject.vehicleConfigOutMsg) scenario.FSWModels[spacecraft].spacecraftReconfig.chiefTransInMsg.subscribeTo( scenario.relativeNavigationModule.transOutMsg) else: scenario.FSWModels[spacecraft].spacecraftReconfig.chiefTransInMsg.subscribeTo( scenario.DynModels[0].simpleNavObject.transOutMsg) # Configure the relative navigation module if relativeNavigation: scenario.relativeNavigationModule.useOrbitalElements = False scenario.relativeNavigationModule.mu = EnvModel.mu # Set up the station keeping requirements if relativeNavigation: scenario.FSWModels[0].stationKeeping = "ON" scenario.FSWModels[0].spacecraftReconfig.targetClassicOED = [0.0000, -0.005, -0.001, 0.0000, 0.0000, 0.000] scenario.FSWModels[1].stationKeeping = "ON" scenario.FSWModels[1].spacecraftReconfig.targetClassicOED = [0.0000, 0.005, 0.0000, 0.0000, 0.0000, -0.003] scenario.FSWModels[2].stationKeeping = "ON" scenario.FSWModels[2].spacecraftReconfig.targetClassicOED = [0.0000, 0.000, 0.001, 0.0000, 0.0000, 0.003] # Initialize simulation scenario.InitializeSimulation() # Configure run time and execute simulation simulationTime = macros.hour2nano(2.) scenario.ConfigureStopTime(simulationTime) scenario.ExecuteSimulation() # Reconfigure FSW attitude modes scenario.FSWModels[0].modeRequest = "locationPointing" scenario.FSWModels[1].modeRequest = "sunPointing" scenario.FSWModels[2].modeRequest = "inertialPointing" # Execute the simulation scenario.ConfigureStopTime(2 * simulationTime) scenario.ExecuteSimulation()
[docs]def run(showPlots, numberSpacecraft, relativeNavigation): """ The scenarios can be run with the followings setups parameters: Args: showPlots (bool): Determines if the script should display plots numberSpacecraft (int): Number of spacecraft in the simulation relativeNavigation (bool): Determines if the formation's chief is the barycenter or the zeroth index spacecraft """ # Configure a scenario in the base simulation TheScenario = scenario_StationKeepingFormationFlying(numberSpacecraft, relativeNavigation) runScenario(TheScenario, relativeNavigation) figureList = TheScenario.pull_outputs(showPlots, relativeNavigation, 1) return figureList
if __name__ == "__main__": run(showPlots=True, numberSpacecraft=3, relativeNavigation=False )