#
# ISC License
#
# 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 script duplicates the ``bskSim`` basic orbit simulation in the scenario
:ref:`scenario_BasicOrbit`.
The difference is that instead of plotting the results after the simulation has stopped in this script a separate
thread is created to update the plots live during the simulation run itself. For more information on doing live
plotting see help file :ref:`usingLivePlotting`.
The script is found in the folder ``src/examples/BskSim/scenarios`` and executed by using::
python3 scenario_BasicOrbit_LivePlot.py
To enable live plotting with a ``bskSim`` Basilisk simulation additional
python packages ``Pipe``, ``Process``, ``Lock`` must be imported.
Next, the ``bskSim`` plotting routines must be called with a unique ID number. This is done in this example
with::
plotID = min([num for num in range(1,10) if not plt.fignum_exists(num)])
BSK_plt.plot_orbit(r_BN_N, plotID)
plotID = min([num for num in range(1,10) if not plt.fignum_exists(num)])
BSK_plt.plot_orientation(timeLineSet, r_BN_N, v_BN_N, sigma_BN, plotID)
The next step is to setup the ``live_outputs()`` method which details what to plot in a live manner.
The ``setup_live_outputs()`` method returns the required ``dataRequests`` structure as
discussed in the :ref:`usingLivePlotting` documentation page.
"""
# Import utilities
from Basilisk.utilities import orbitalMotion, macros, unitTestSupport
from multiprocessing import Pipe, Process, Lock
from time import sleep
import matplotlib.pyplot as plt
import numpy as np
# Get current file path
import sys, os, inspect
filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
from Basilisk.simulation import clock_synch
# Import master classes: simulation base class and scenario base class
sys.path.append(path + '/..')
from BSK_masters import BSKSim, BSKScenario
import BSK_Dynamics, BSK_Fsw
# Import plotting files for your scenario
sys.path.append(path + '/../plotting')
import BSK_Plotting as BSK_plt
sys.path.append(path + '/../../scenarios')
# Create your own scenario child class
[docs]class scenario_BasicOrbitLive(BSKSim, BSKScenario):
def __init__(self):
super(scenario_BasicOrbitLive, self).__init__()
self.name = 'scenario_BasicOrbitLive'
self.set_DynModel(BSK_Dynamics)
self.set_FswModel(BSK_Fsw)
self.initInterfaces()
self.configure_initial_conditions()
self.log_outputs()
[docs] def log_outputs(self):
print('%s: log_outputs' % self.name)
# Dynamics process outputs
samplingTime = self.get_DynModel().processTasksTimeStep
self.TotalSim.logThisMessage(self.get_DynModel().simpleNavObject.outputAttName, samplingTime)
self.TotalSim.logThisMessage(self.get_DynModel().simpleNavObject.outputTransName, samplingTime)
[docs] def pull_outputs(self, showPlots):
print('%s: pull_outputs' % self.name)
# Dynamics process outputs
sigma_BN = self.pullMessageLogData(self.get_DynModel().simpleNavObject.outputAttName + ".sigma_BN", range(3))
r_BN_N = self.pullMessageLogData(self.get_DynModel().simpleNavObject.outputTransName + ".r_BN_N", range(3))
v_BN_N = self.pullMessageLogData(self.get_DynModel().simpleNavObject.outputTransName + ".v_BN_N", range(3))
# Plot results
BSK_plt.clear_all_plots()
timeLineSet = r_BN_N[:, 0] * macros.NANO2MIN
plotID = min([num for num in range(1,10) if not plt.fignum_exists(num)])
BSK_plt.plot_orbit(r_BN_N, plotID)
plotID = min([num for num in range(1,10) if not plt.fignum_exists(num)])
BSK_plt.plot_orientation(timeLineSet, r_BN_N, v_BN_N, sigma_BN, plotID)
figureList = {}
if showPlots:
BSK_plt.show_all_plots()
return figureList
def live_outputs(self, plotComm, rate):
dataRequests = self.setup_live_outputs()
lock = Lock()
while True:
for request in dataRequests:
#send request for data
plotComm.send(request)
response = plotComm.recv()
if response == "TERM":
plt.close("all")
return
pltArgs = []
if response["plotFun"] == "plot_orbit":
lock.acquire()
for resp in response["dataResp"]:
pltArgs.append(np.array(resp))
pltArgs.append(response["plotID"])
getattr(BSK_plt, response["plotFun"])(*pltArgs)
lock.release()
plt.pause(.01)
elif response["plotFun"] == "plot_orientation":
lock.acquire()
pltArgs.append(response["dataResp"][0][:, 0] * macros.NANO2MIN)
for resp in response["dataResp"]:
pltArgs.append(np.array(resp))
pltArgs.extend((response["plotID"], True))
getattr(BSK_plt, response["plotFun"])(*pltArgs)
lock.release()
plt.pause(.01)
sleep(rate/1000.)
def setup_live_outputs(self):
#define plots of interest here
dataRequests = [{"plotID" : 1,
"plotFun" : "plot_orbit",
"dataReq" : [self.get_DynModel().simpleNavObject.outputTransName + ".r_BN_N"]},
{"plotID" : 2,
"plotFun" : "plot_orientation",
"dataReq" : [self.get_DynModel().simpleNavObject.outputTransName + ".r_BN_N",
self.get_DynModel().simpleNavObject.outputTransName + ".v_BN_N",
self.get_DynModel().simpleNavObject.outputAttName + ".sigma_BN"]}]
return dataRequests
def runScenario(scenario, livePlots=False, showPlots=False):
if livePlots:
clockSync = clock_synch.ClockSynch()
clockSync.accelFactor = 50.0
scenario.AddModelToTask(scenario.DynModels.taskName, clockSync)
# Initialize simulation
scenario.InitializeSimulationAndDiscover()
# Configure run time
simulationTime = macros.min2nano(10.)
scenario.ConfigureStopTime(simulationTime)
# Run simulation
figureList = {}
if livePlots:
# plotting refresh rate in ms
refreshRate = 1000
plotComm, simComm = Pipe()
plotArgs = [showPlots]
simProc = Process(target=scenario.ExecuteSimulation,
args=(showPlots, livePlots, simComm, scenario.pull_outputs, plotArgs))
plotProc = Process(target=scenario.live_outputs, args=(plotComm, refreshRate))
# Execute simulation and live plotting
simProc.start(), plotProc.start()
simProc.join(), plotProc.join()
else:
scenario.ExecuteSimulation()
[docs]def run(showPlots, livePlots=False):
"""
The scenarios can be run with the followings setups parameters:
Args:
showPlots (bool): Determines if the script should display plots
"""
# Configure a scenario in the base simulation
TheScenario = scenario_BasicOrbitLive()
runScenario(TheScenario, livePlots=livePlots, showPlots=showPlots)
figureList = TheScenario.pull_outputs(showPlots)
return figureList
if __name__ == "__main__":
run(True, True)