Package: paseos
PASEOS - PAseos Simulates the Environment for Operating multiple SpacecraftExplore the docs »·Report Bug·Request Feature·Read the PaperDisclaimer: This project is currently under development. Use at your own risk.Table of ContentsAbout the ProjectPASEOS space environment simulationInstallationExamplesActorsCreate a PASEOS actorLocal and Known ActorsPhysical ModelsSet an orbit for a PASEOS SpacecraftActorHow to add a communication deviceHow to add a power deviceThermal ModellingRadiation ModellingCustom ModellingCustom Central BodiesSimulation SettingsInitializing PASEOSUsing the cfgFaster than real-time executionEvent-based modeActivitiesSimple ActivityActivities with Inputs and OutputsConstraint FunctionOn-termination FunctionUtilitiesVisualizationMonitoring Simulation StatusWriting Simulation Results to a FileWrapping Other Software and ToolsVia ActivitiesVia Constraint FunctionsVia Custom PropertiesGlossaryPhysical Model ParametersContributingLicenseContactAbout the projectPASEOS is aPythonmodule that simulates the environment to operate multiple spacecraft. In particular, PASEOS offers the user some utilities to run their ownactivitiesby taking into account both operational and onboard (e.g. limited-power-budget, radiation, and thermal effects) constraints.PASEOS is designed to be:open-source: the source code of PASEOS is available under a GPL license.fully decentralised: one instance of PASEOS shall be executed in every node, i.e. individual spacecraft (actor), of the emulated spacecraft. Each instance of PASEOS is responsible for handling the useractivitiesexecuted on that node (the local actor) while keeping track of the status of the other nodes. In this way, the design of PASEOS is completely decentralised and independent of the number of nodes of the constellation. Because of that, both single-node and multi-node scenarios are possible.application-agnostic: each user operation that has to be executed on a node is modelled as anactivity. The user is only required to provide the code to run and some parameters (e.g., power consumption) for eachactivity. Thus, activities can be any code the user wants to simulate running on a spacecraft and thereby PASEOS is completely application-agnostic. Conceivable applications range from modelling constellations to training machine learning methods.The project is being developed by $\Phi$-lab@Swedenin the frame of a collaboration betweenAI Swedenand theEuropean Space Agencyto explore distributed edge learning for space applications. For more information on PASEOS and $\Phi$-lab@Sweden, please take a look at the recording of the $\Phi$-lab@Swedenkick-off event.PASEOS space environment simulationPASEOS allows simulating the effect of onboard and operational constraints on user-registeredactivities. The image above showcases the different phenomena considered (or to be implemented) in PASEOS.Installationpip / condaThe recommended way to install PASEOS is viaconda/mambausingconda install paseos -c conda-forgeAlternatively, on Linux you can install viapipusingpip install paseosThe pip version requiresPython 3.8.16due topykep's limited support of pip.Building from sourceTo build from source, first of all clone theGitHubrepository as follows (Gitrequired):git clone https://github.com/aidotse/PASEOS.gitTo install PASEOS you can usecondaas follows:cd PASEOS
conda env create -f environment.ymlThis will create a new conda environment calledPASEOSand install the required software packages.
To activate the new environment, you can use:conda activate paseosAlternatively, you can install PASEOS by usingpipas follows:cd PASEOS
pip install -e .Using DockerTwoDockerimages are available:paseos: corresponding to the latest release.paseos-nightly: based on the latest commit on the branchmain.If you want to install PASEOS using Docker, access the desired repository and follow the provided instructions.ExamplesThe next examples will introduce you to the use of PASEOS.Comprehensive, self-contained examples can also be found in theexamplesfolder where you can find an example on:Modelling and analysing a large constellation with PASEOSModelling distributed learning on heterogeneous data in a constellationUsing PASEOS with MPI to run PASEOS on supercomputersUsing PASEOS to model the task of onboard satellite volcanic eruptions detectionAn example showing how total ionizing dose could be considered using a PASEOScustom propertyThe following are small snippets on specific topics.ActorsCreate a PASEOS actorThe code snippet below shows how to create a PASEOSactornamedmySatof typeSpacecraftActor.pykepis used to define the satelliteepochin formatmjd2000format.actorsare created by using anActorBuilder. The latter is used to define theactorscaffoldthat includes theactorminimal properties. In this way,actorsare built in a modular fashion that enables their use also for non-space applications.importpykepaspkfrompaseosimportActorBuilder,SpacecraftActor# Define an actor of type SpacecraftActor of name mySatsat_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))Local and Known ActorsOnce you have instantiated aPASEOS simulationyou can add other PASEOSactors(Known actors) to the simulation. You can use this, e.g., to study communications between actors and to automatically monitor communication windows.The next code snippet will add both aSpacecraftActorand aGroundstationActor(other_sat). An orbit is set forother_sat, which is placed around Earth at position(x,y,z)=(-10000,0,0)and velocity(vx,vy,vz)=(0,-8000,0)at epochepoch=pk.epoch(0).
The latter (grndStation) will be placed at coordinates(lat,lon)=(79.002723, 14.642972)and elevation of 0 m.You cannot add a power device and an orbit to aGroundstationActor.importpykepaspkimportpaseosfrompaseosimportActorBuilder,SpacecraftActor,GroundstationActor# Define the local actor as a SpacecraftActor of name mySat and its orbitlocal_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))ActorBuilder.set_orbit(actor=local_actor,position=[10000000,0,0],velocity=[0,8000.0,0],epoch=pk.epoch(0),central_body=pk.planet.jpl_lp("earth"),# use Earth from pykep)# Initialize PASEOS simulationsim=paseos.init_sim(local_actor)# Create another SpacecraftActorother_spacraft_actor=ActorBuilder.get_actor_scaffold(name="other_sat",actor_type=SpacecraftActor,epoch=pk.epoch(0))# Let's set the orbit of other_spacraft_actor.ActorBuilder.set_orbit(actor=other_spacraft_actor,position=[-10000000,0,0],velocity=[0,-8000.0,0],epoch=pk.epoch(0),central_body=earth)#Create GroundstationActorgrndStation=GroundstationActor(name="grndStation",epoch=pk.epoch(0))#Set the ground station at lat lon 79.002723 / 14.642972# and its elevation 0mActorBuilder.set_ground_station_location(grndStation,latitude=79.002723,longitude=14.642972,elevation=0)# Adding other_spacraft_actor to PASEOS.sim.add_known_actor(other_spacraft_actor)# Adding grndStation to PASEOS.sim.add_known_actor(grndStation)Physical ModelsSet an orbit for a PASEOS SpacecraftActorOnce you have defined aSpacecraftActor, you can assign aKeplerian orbitor useSGP4 (Earth orbit only).Keplerian OrbitTo this aim, you need to define the central body theSpacecraftActoris orbiting around and specify its position and velocity (in the central body'sinertial frame) and an epoch. In this case, we will useEarthas a central body.importpykepaspkfrompaseosimportActorBuilder,SpacecraftActor# Define an actor of type SpacecraftActor of name mySatsat_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))# Define the central body as Earth by using pykep APIs.earth=pk.planet.jpl_lp("earth")# Let's set the orbit of sat_actor.ActorBuilder.set_orbit(actor=sat_actor,position=[10000000,0,0],velocity=[0,8000.0,0],epoch=pk.epoch(0),central_body=earth)SGP4 / Two-line element (TLE)For using SGP4 /Two-line element (TLE)you need to specify the TLE of theSpacecraftActor. In this case, we will use the TLE of theSentinel-2Asatellite fromcelestrak.frompaseosimportActorBuilder,SpacecraftActor# Define an actor of type SpacecraftActorsat_actor=ActorBuilder.get_actor_scaffold(name="Sentinel-2A",actor_type=SpacecraftActor,epoch=pk.epoch(0))# Specify your TLEline1="1 40697U 15028A   23188.15862373  .00000171  00000+0  81941-4 0  9994"line2="2 40697  98.5695 262.3977 0001349  91.8221 268.3116 14.30817084419867"# Set the orbit of the actorActorBuilder.set_TLE(sat_actor,line1,line2)Custom PropagatorsYou can define any kind of function you would like to determine actor positions and velocities. This allows integrating more sophisticated propagators such asorekit. A dedicated example on this topic can be found in theexamplesfolder.In short, you need to define a propagator function that returns the position and velocity of the actor at a given time. The function shall take the current epoch as arguments. You can then set the propagator function withimportpykepaspkfrompaseosimportActorBuilder,SpacecraftActor# Create a SpacecraftActorstarting_epoch=pk.epoch(42)my_sat=ActorBuilder.get_actor_scaffold(name="my_sat",actor_type=SpacecraftActor,epoch=starting_epoch)# Define a custom propagator function that just returns a sinus positiondefmy_propagator(epoch:pk.epoch):position,velocity=your_external_propagator(epoch)returnposition,velocity# Set the custom propagatorActorBuilder.set_custom_orbit(my_sat,my_propagator,starting_epoch)Accessing the orbitYou can access the orbit of aSpacecraftActorwith# Position, velocity and altitude can be accessed like thist0=pk.epoch("2022-06-16 00:00:00.000")# Define the time (epoch)print(sat_actor.get_position(t0))print(sat_actor.get_position_velocity(t0))print(sat_actor.get_altitude(t0))How to add a communication deviceThe following code snippet shows how to add a communication device to a [SpacecraftActors] (#spacecraftactor). A communication device is needed to model the communication between [SpacecraftActors] (#spacecraftactor) or aSpacecraftActorandGroundstationActor. Currently, given the maximum transmission data rate of a communication device, PASEOS calculates the maximum data that can be transmitted by multiplying the transmission data rate by the length of the communication window. The latter is calculated by taking the period for which two actors are in line-of-sight into account.importpykepaspkfrompaseosimportActorBuilder,SpacecraftActor# Define an actor of type SpacecraftActor of name mySatsat_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))# Add a communication deviceActorBuilder.add_comm_device(actor=sat_actor,# Communication device namedevice_name="my_communication_device",# Bandwidth in kbps.bandwidth_in_kbps=100000)How to add a power deviceThe following code snippet shows how to add a power device to aSpacecraftActor.
Moreover, PASEOS assumes that the battery will be charged by solar panels, which will provide energy thanks to the incoming solar radiation when the spacecraft is not eclipsed. Charging and discharging happens automatically duringactivities.importpykepaspkimportpaseosfrompaseosimportActorBuilder,SpacecraftActor# Define an actor of type SpacecraftActor of name mySatsat_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))# Add a power deviceActorBuilder.set_power_devices(actor=sat_actor,battery_level_in_Ws=100,# current levelmax_battery_level_in_Ws=2000,charging_rate_in_W=10,power_device_type=paseos.PowerDeviceType.SolarPanel)Alternatively to the defaultpaseos.PowerDeviceType.SolarPanelyou can also usepaseos.PowerDeviceType.RTG. The only difference at the moment is thatRTGsalso charge in eclipse.Note that at the moment only one power device is supported. Adding another will override the existing one.You can check the battery's state of charge and level in Ws with:print(my_actor.state_of_charge)print(my_actor.battery_level_in_Ws)Thermal ModellingTo model thermal constraints on spacecraft we utilize a model inspired by the one-node model described inMartínez - Spacecraft Thermal Modelling and Test. Thus, we model the change in temperature as$$mc , \frac{dT}{dt} = \dot{Q}{solar} + \dot{Q}{albedo} + \dot{Q}{central_body_IR} - \dot{Q}{dissipated} + \dot{Q}_{activity}.$$This means your spacecraft will heat up due to being in sunlight, albedo reflections, infrared radiation emitted by the central body as well as due to power consumption of activities. It will cool down due to heat dissipation.The model is only available for aSpacecraftActorand (like all the physical models) only evaluated for thelocal actor.The following parameters have to be specified for this:Spacecraft mass [kg], initial temperature [K], emissive area (for heat dissipation) and thermal capacity [J / (kg * K)]Spacecraft absorptance of Sun light, infrared light. [0 to 1]Spacecraft area [m^2] facing Sun and central body, respectivelySolar irradiance in this orbit [W] (defaults to 1360W)Central body surface temperature [k] (defaults to 288K)Central body emissivity and reflectance [0 to 1] (defaults to 0.6 and 0.3)Ratio of power converted to heat (defaults to 0.5)To use it, simply equip yourSpacecraftActorwith a thermal model with:frompaseosimportSpacecraftActor,ActorBuildermy_actor=ActorBuilder.get_actor_scaffold("my_actor",SpacecraftActor,pk.epoch(0))ActorBuilder.set_thermal_model(actor=my_actor,actor_mass=50.0,# Setting mass to 50kgactor_initial_temperature_in_K=273.15,# Setting initial temperature to 0°Cactor_sun_absorptance=1.0,# Depending on material, define absorptanceactor_infrared_absorptance=1.0,# Depending on material, define absorptanceactor_sun_facing_area=1.0,# Area in m2actor_central_body_facing_area=1.0,# Area in m2actor_emissive_area=1.0,# Area in m2actor_thermal_capacity=1000,# Capacity in J / (kg * K)# ... leaving out default valued parameters, see docs for details)The model is evaluated automatically duringactivities. You can check the spacecraft temperature with:print(my_actor.temperature_in_K)At the moment, only one thermal model per actor is supported. Setting a second will override the old one.Radiation ModellingPASEOS models three types of radiation effects.Data corruption due to single event upsets which a event rate $r_d$.Unexpected software faults leading to a random interruption ofactivitieswith a Poisson-distributed event rate $r_i$ per secondDevice failures with a Poisson-distributed event rate $r_f$ per second, which can be imputed mostly to single event latch-upsYou can add a radiation model affecting the operations of the devices you are interested in withfrompaseosimportSpacecraftActor,ActorBuildermy_actor=ActorBuilder.get_actor_scaffold("my_actor",SpacecraftActor,pk.epoch(0))ActorBuilder.set_radiation_model(actor=my_actor,data_corruption_events_per_s=r_d,restart_events_per_s=r_i,failure_events_per_s=r_f,)You can set any of the event rates to 0 to disable that part. OnlySpacecraftActorssupport radiation models. You can find out if your actor has failed withmy_actor.is_deadInterruptedactivitieswill return as if aconstraint functionwas no longer satisfied.To get a binary mask to model data corruption on thelocal actoryou can callmask=paseos_instance.model_data_corruption(data_shape=your_data_shape,exposure_time_in_s=your_time)Custom ModellingBeyond the default supported physical quantities (power, thermal, etc.) it possible to model any type of parameter by using custom properties. These are defined by a name, an update function and an initial value. The initial value is used to initialize the property. As for the other physical models, you can specify an update rate via thecfg.sim.dtcfg parameter.Custom properties are automatically logged in theoperations monitor.
Below is a simple example tracking actor altitude.importpykepaspkfrompaseosimportActorBuilder,SpacecraftActor# Define the local actor as a SpacecraftActor of name mySat and some orbitlocal_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))ActorBuilder.set_orbit(actor=local_actor,position=[10000000,0,0],velocity=[0,8000.0,0],epoch=pk.epoch(0),central_body=pk.planet.jpl_lp("earth"),# use Earth from pykep)# Define the update function for the custom property# PASEOS will always pass you the actor, the time step and the current power consumption# The function shall return the new value of the custom propertydefupdate_function(actor,dt,power_consumption):returnactor.get_altitude()# get current altitude# Add the custom property to the actor, defining name, update fn and initial valueActorBuilder.add_custom_property(actor=local_actor,property_name="altitude",update_function=update_function,initial_value=local_actor.get_altitude(),)# One can easily access the property at any point withprint(local_actor.get_custom_property("altitude"))Custom Central BodiesIn most examples here you will see Earth via the pykep API being used as a spherical, central body for Keplerian orbits. For Keplerian orbits around spherical bodies, you can simply use pykep with an type ofpykep planetjust as the above examples used Earth. E.g.importpykepaspkfrompaseosimportActorBuilder,SpacecraftActor# Define an actor of type SpacecraftActor of name mySatsat_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))# Define the central body as Mars by using pykep APIs.mars=pk.planet.jpl_lp("mars")# Let's set the orbit of sat_actor.ActorBuilder.set_orbit(actor=sat_actor,position=[10000000,1,1],velocity=[1,1000.0,1],epoch=pk.epoch(0),central_body=mars)However, you can also use any other central body defined via a mesh. This is especially useful in conjunction withcustom propagators. To use a custom central body, you need to define a mesh and add it to the simulation configuration. The following example shows how to do this for the comet 67P/Churyumov–Gerasimenko.We assumepolyhedral_propagatorto be a custom propagator as explained inCustom Propagators.To correctly compute eclipses, we also need to know the orbit of the custom central body around the Sun. In this case we use theorbital elementsonecan find online for 67P/Churyumov–Gerasimenko.importpykepaspkfrompaseosimportActorBuilder,SpacecraftActor# Define the epoch and orbital elementsepoch=pk.epoch(2460000.5,"jd")elements=(3.457*pk.AU,0.64989,3.8719*pk.DEG2RAD,36.33*pk.DEG2RAD,22.15*pk.DEG2RAD,73.57*pk.DEG2RAD)# Create a planet object from pykep for 67Pcomet=pk.planet.keplerian(epoch,elements,pk.MU_SUN,666.19868,2000,2000,"67P")# Load the 67P mesh with picklewithopen(mesh_path,"rb")asf:mesh_points,mesh_triangles=pickle.load(f)mesh_points=np.array(mesh_points)mesh_triangles=np.array(mesh_triangles)# Define local actormy_sat=ActorBuilder.get_actor_scaffold("my_sat",SpacecraftActor,epoch=epoch)# Set the custom propagatorActorBuilder.set_custom_orbit(my_sat,polyhedral_propagator,epoch)# Set the meshActorBuilder.set_central_body(my_sat,comet,(mesh_points,mesh_triangles))# Below computations will now use the mesh instead spherical approximationsprint(my_sat.is_in_eclipse())print(my_sat.is_in_line_of_sight(some_other_actor))# You could even specify a rotation of the central body.# Set a rotation period of 1 second around the z axisActorBuilder.set_central_body(my_sat,comet,(mesh_points,mesh_triangles),rotation_declination=90,rotation_right_ascension=0,rotation_period=1,)This is particularly useful if you want to use a central body that is not included in pykep or if you want to use a central body that is not a planet (e.g. an asteroid).N.B.get_altitudecomputes the altitude above [0,0,0] in the central body's frame, thus is not affected by the central body's rotation or mesh.
N.B. #2 Any custom central body still has to orbit the Sun for PASEOS to function correctly.Simulation SettingsInitializing PASEOSWe will now show how to create an instance of PASEOS. An instance of PASEOS shall be bounded to one PASEOSactorthat we calllocal actor. Please, notice that an orbit shall be placed for aSpacecraftActorbefore being added to a PASEOS instance.How to instantiate PASEOSimportpykepaspkimportpaseosfrompaseosimportActorBuilder,SpacecraftActor# Define the local actor as a SpacecraftActor of name mySat and its orbitlocal_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))ActorBuilder.set_orbit(actor=local_actor,position=[10000000,0,0],velocity=[0,8000.0,0],epoch=pk.epoch(0),central_body=pk.planet.jpl_lp("earth"),# use Earth from pykep)# initialize PASEOS simulationsim=paseos.init_sim(local_actor)For each actor you wish to model, you can create a PASEOS instance. Running multiple instances on the same machine / thread is supported.Using the cfgWhen you instantiate PASEOS as shown inInitializing PASEOS, a PASEOS instance is created by using the default configuration. However, sometimes it is useful to use a custom configuration.The next code snippet will show how to start the PASEOS simulation with a time different frompk.epoch(0)(MJD2000) by loading a custom configuration.importpykepaspkimportpaseosfrompaseosimportActorBuilder,SpacecraftActor#Define today as pykep epoch (16-06-22)#please, refer to https://esa.github.io/pykep/documentation/core.html#pykep.epochtoday=pk.epoch_from_string('2022-06-16 00:00:00.000')# Define the local actor as a SpacecraftActor of name mySat# pk.epoch is set to todaylocal_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=today)# Let's set the orbit of local_actor.# pk.epoch is set to todayActorBuilder.set_orbit(actor=local_actor,position=[10000000,0,0],velocity=[0,8000.0,0],epoch=pk.epoch(0),central_body=pk.planet.jpl_lp("earth"),# use Earth from pykep)# Loading cfg to modify defaultscfg=load_default_cfg()# Set simulation starting time by converting epoch to secondscfg.sim.start_time=today.mjd2000*pk.DAY2SEC# initialize PASEOS simulationsim=paseos.init_sim(local_actor)You can access the current simulation time (seconds since the start) and the current epoch like this:time_since_start_in_s=sim.simulation_timecurrent_epoch=sim.local_timeFaster than real-time executionIn some cases, you may be interested to simulate your spacecraft operating for an extended period. By default, PASEOS operates in real-time, thus this would take a lot of time. However, you can increase the rate of time passing (i.e. the spacecraft moving, power being charged / consumed etc.) using thetime_multiplierparameter. Set it as follows when initializing PASEOS.cfg=load_default_cfg()# loading cfg to modify defaultscfg.sim.time_multiplier=10# setting the parameter so that in 1s real time, paseos models 10s having passedpaseos_instance=paseos.init_sim(my_local_actor,cfg)# initialize paseos instanceEvent-based modeAlternatively, you can rely on an event-based mode where PASEOS will simulate the physical constraints for an amount of time. The below code shows how to run PASEOS for a fixed amount of time or until an event interrupts it.importpykepaspkimportpaseosfrompaseosimportActorBuilder,SpacecraftActor# Define the central body as Earth by using pykep APIs.earth=pk.planet.jpl_lp("earth")# Define a satellite with some orbit and simple power modellocal_actor=ActorBuilder.get_actor_scaffold("MySat",SpacecraftActor,pk.epoch(0))ActorBuilder.set_orbit(local_actor,[10000000,0,0],[0,8000.0,0],pk.epoch(0),earth)ActorBuilder.set_power_devices(local_actor,500,1000,1)# Abort when sat is at 10% batterydefconstraint_func():returnlocal_actor.state_of_charge>0.1# Set some settings to control evaluation of the constraintcfg=load_default_cfg()# loading cfg to modify defaultscfg.sim.dt=0.1# setting timestep of physical models (power, thermal, ...)cfg.sim.activity_timestep=1.0# how often constraint func is evaluatedsim=paseos.init_sim(local_actor,cfg)# Init simulation# Advance for a long time, will interrupt much sooner due to constraint functionsim.advance_time(3600,10,constraint_function=constraint_func)ActivitiesSimple activityPASEOS enables the user to register theiractivitiesthat will be executed on thelocal actor. This is an alternative to theevent-based modeTo register an activity, it is first necessary to define an asynchronousactivity function. The following code snippet shows how to create a simpleactivity functionactivity_function_Athat prints "Hello Universe!". Then, it waits for 0.1 s before concluding the activity.When you register anactivity, you need to specify the power consumption associated to the activity.#Activity functionasyncdefactivity_function_A(args):print("Hello Universe!")awaitasyncio.sleep(0.1)#Await is needed inside an async function.Once an activity is registered, the user shall callperform_activity(...)to run the registered activity.
The next snippet will showcase how to register and perform the activityactivity_A.importpykepaspkimportpaseosfrompaseosimportActorBuilder,SpacecraftActorimportasyncio# Define the local actor as a SpacecraftActor of name mySat and its orbitlocal_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))ActorBuilder.set_orbit(actor=local_actor,position=[10000000,0,0],velocity=[0,8000.0,0],epoch=pk.epoch(0),central_body=pk.planet.jpl_lp("earth"),# use Earth from pykep)# Add a power deviceActorBuilder.set_power_devices(actor=local_actor,# Battery level at the start of the simulation in Wsbattery_level_in_Ws=100,# Max battery level in Wsmax_battery_level_in_Ws=2000,# Charging rate in Wcharging_rate_in_W=10)# initialize PASEOS simulationsim=paseos.init_sim(local_actor)#Activity functionasyncdefactivity_function_A(args):print("Hello Universe!")awaitasyncio.sleep(0.1)#Await is needed inside an async function.# Register an activity that emulate event detectionsim.register_activity("activity_A",activity_function=activity_function_A,power_consumption_in_watt=10)#Run the activitysim.perform_activity("activity_A")Waiting for Activities to FinishAt the moment, parallel running of multiple activities is not supported.
However, if you want to run multiple activities in a row or just wait for the existing one to finish, you can useawaitsim.wait_for_activity()to wait until the running activity has finished.Activities with Inputs and OutputsThe next code snippet will show how to register and perform activities with inputs and outputs. In particular, we will register anactivity functionactivity_function_with_in_and_outsthat takes an input argument and returns its value multiplied by two. Then, it waits for 0.1 s before concluding the activity.Please, notice that the output value is placed inargs[1][0], which is returned as reference.importpykepaspkimportpaseosfrompaseosimportActorBuilder,SpacecraftActorimportasyncio# Define the local actor as a SpacecraftActor of name mySat and its orbitlocal_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))ActorBuilder.set_orbit(actor=local_actor,position=[10000000,0,0],velocity=[0,8000.0,0],epoch=pk.epoch(0),central_body=pk.planet.jpl_lp("earth"),# use Earth from pykep)# Add a power deviceActorBuilder.set_power_devices(actor=local_actor,# Battery level at the start of the simulation in Wsbattery_level_in_Ws=100,# Max battery level in Wsmax_battery_level_in_Ws=2000,# Charging rate in Wcharging_rate_in_W=10)# initialize PASEOS simulationsim=paseos.init_sim(local_actor)#Activity functionasyncdefactivity_function_with_in_and_outs(args):activity_in=args[0]activity_out=activity_in*2args[1][0]=activity_outawaitasyncio.sleep(0.1)#Await is needed inside an async function.# Register an activity that emulate event detectionsim.register_activity("my_activity",activity_function=activity_function_with_in_and_outs,power_consumption_in_watt=10,)#Creatie an input variable for activityactivity_in=1#Create a placeholder variable to contain the output of the activity function.#It is created as a list so its first value is edited# as reference by the activity function.activity_out=[None]#Run the activitysim.perform_activity("my_activity",activity_func_args=[activity_in,activity_out],)#Print return valueprint("The output of the activity function is: ",activity_out[0])Constraint FunctionIt is possible to associate aconstraint functionwith eachactivityto ensure that some particular constraints are met during theactivityexecution. When constraints are not met, the activity is interrupted. Constraints can be used, e.g., to impose power requirements, communication windows or maximum operational temperatures.The next code snippet shows how to:create aconstraint function(constraint_function_A) which returnsTruewhen the local actor's temperature is below ~86°C andFalseotherwise (this requires a thermal model on the actor)how useconstraint_function_Ato constraint ourSimple Activity.importpykepaspkimportpaseosfrompaseosimportActorBuilder,SpacecraftActorimportasyncio# Define the local actor as a SpacecraftActor of name mySat and its orbitlocal_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))ActorBuilder.set_orbit(actor=local_actor,position=[10000000,0,0],velocity=[0,8000.0,0],epoch=pk.epoch(0),central_body=pk.planet.jpl_lp("earth"),# use Earth from pykep)# Add a power deviceActorBuilder.set_power_devices(actor=local_actor,# Battery level at the start of the simulation in Wsbattery_level_in_Ws=100,# Max battery level in Wsmax_battery_level_in_Ws=2000,# Charging rate in Wcharging_rate_in_W=10)# initialize PASEOS simulationsim=paseos.init_sim(local_actor)#Activity functionasyncdefactivity_function_A(args):print("Hello Universe!")awaitasyncio.sleep(0.1)#Await is needed inside an async function.#Constraint functionasyncdefconstraint_function_A(args):local_actor_temperature=args[0]return(local_actor_temperature<350)# Register an activity that emulate event detectionsim.register_activity("activity_A_with_constraint",activity_function=activity_function_A,power_consumption_in_watt=10,constraint_function=constraint_function_A)#The constraint function is related to the operational temperature of the local actor.operational_temperature_in_K=local_actor.temperature_in_K#Run the activitysim.perform_activity("activity_A_with_constraint",constraint_func_args=[operational_temperature_in_K],)On-termination FunctionIt is also possible to define anon-termination functionto perform some specific operations when on termination of theactivity. The next code snippet shows:how to create anon-termination functionthat prints "activity (activity_A_with_termination_function) ended.".How to associate ouron-termination functionto ourSimple Activity.The name of theactivityis passed as input to theon-termination functionto showcase to handleon-termination functioninputs.importpykepaspkimportpaseosfrompaseosimportActorBuilder,SpacecraftActorimportasyncio# Define the local actor as a SpacecraftActor of name mySat and its orbitlocal_actor=ActorBuilder.get_actor_scaffold(name="mySat",actor_type=SpacecraftActor,epoch=pk.epoch(0))ActorBuilder.set_orbit(actor=local_actor,position=[10000000,0,0],velocity=[0,8000.0,0],epoch=pk.epoch(0),central_body=pk.planet.jpl_lp("earth"),# use Earth from pykep)# Add a power deviceActorBuilder.set_power_devices(actor=local_actor,# Battery level at the start of the simulation in Wsbattery_level_in_Ws=100,# Max battery level in Wsmax_battery_level_in_Ws=2000,# Charging rate in Wcharging_rate_in_W=10)# initialize PASEOS simulationsim=paseos.init_sim(local_actor)#Activity functionasyncdefactivity_function_A(args):print("Hello Universe!")awaitasyncio.sleep(0.1)#Await is needed inside an async function.#On-termination functionasyncdefon_termination_function_A(args):#Fetching inputactivity_name=args[0]print("Activity ("+str(activity_name)+") ended.")# Register an activity that emulate event detectionsim.register_activity("activity_A_with_termination_function",activity_function=activity_function_A,power_consumption_in_watt=10,on_termination_function=on_termination_function_A)#The termination function input is the activity nameactivity_name="activity_A_with_termination_function"#Run the activitysim.perform_activity("activity_A_with_termination_function",termination_func_args=[activity_name],)UtilitiesVisualizationNavigate to paseos/visualization to find a jupyter notebook containing examples of how to visualize PASEOS.
Visualization can be done in interactive mode or as an animation that is saved to your disc.
In the figure below, Earth is visualized in the centre as a blue sphere with different spacecraft in orbit.
Each spacecraft has a name and if provided, a battery level and a communications device.
The local device is illustrated with white text.
In the upper-right corner, the status of the communication link between each spacecraft is shown.
Finally, the time in the lower left and lower right corners corresponds to the epoch and the PASEOS local simulation time.Monitoring Simulation StatusYou can easily track the status of a PASEOS simulation via themonitorwhich keeps track of actor status.It allows access like this(...)# actor definition etc., see aboveinstance=paseos.init_sim(local_actor=my_local_actor)(...)# running the simulation# access tracked parameterstimesteps=instance.monitor["timesteps"]state_of_charge=instance.monitor["state_of_charge"]Writing Simulation Results to a FileTo evaluate your results, you will likely want to track the operational parameters, such as actor battery status, currently running activity etc. of actors over the course of your simulation. By default, PASEOS will log the current actor status every 10 seconds, however you can change that rate by editing the default configuration, as explained inHow to use the cfg. You can save the current log to a *.csv file at any point.cfg=load_default_cfg()# loading cfg to modify defaultscfg.io.logging_interval=0.25# log every 0.25 secondspaseos_instance=paseos.init_sim(my_local_actor,cfg)# initialize paseos instance# Performing activities, running the simulation (...)paseos_instance.save_status_log_csv("output.csv")Wrapping Other Software and ToolsPASEOS is designed to allow easily wrapping other software and tools to, e.g., use more sophisticated models for specific aspects of interest to the user. There are three ways to do this:Via Activities- Anactivityusing an external software is registered and executed as any otheractivity, e.g. to perform some computations while tracking runtime of that operation.Via Constraint Functions- Aconstraint functionusing an external software. This is useful to use a more sophisticated model to check whether, e.g., a physical constraint modelled outside of PASEOS is met.Via Custom Properties- Acustom propertyusing an external software. This is useful to, e.g., use a more sophisticated model for a physical quantity such as total ionization dose or current channel bandwidth.Via ActivitiesThe wrapping via activities is quite straight forward. Follow theinstructions on registering and performing activitiesand make use of your external software inside the activity function.importmy_external_software#Activity functionasyncdefactivity_function_A(args):my_external_software.complex_task_to_model()awaitasyncio.sleep(0.01)Via Constraint FunctionsInside constraint functions, external software can be used to check whether a constraint is met or not. This works both foractivity constraintsand forconstraints in event-based mode.The constraint function should returnTrueif the constraint is met andFalseotherwise.importpykepaspkfrompaseosimportActorBuilder,SpacecraftActorimportmy_complex_radiation_model# Defining a local actorlocal_actor=ActorBuilder.get_actor_scaffold("MySat",SpacecraftActor,pk.epoch(0))defconstraint_func():t=local_actor.local_timedevice_has_failed=my_complex_radiation_model.check_for_device_failure(t)returnnotdevice_has_failed# Can be passed either with event-based mode, will run until constraint is not metsim.advance_time(3600,10,constraint_function=constraint_func)# (...)# or via activity constraints, will run until constraint is not met# N.B: this is an excerpt follow the #constraint-function link for more detailssim.register_activity("activity_A_with_constraint_function",activity_function=activity_function_A,power_consumption_in_watt=10,constraint_function=constraint_func)Via Custom PropertiesFinally,custom propertiescan be used to wrap external software. This is useful to use a more sophisticated model for a physical quantity, e.g. one could use a simulator likens-3to model the current channel bandwidth.For more details seecustom properties.importmy_channel_model# Will be automatically called during PASEOS simulationdefupdate_function(actor,dt,power_consumption):# Get the current channel bandwidth from the external modelchannel_bandwidth=my_channel_model.get_channel_bandwidth(actor)returnchannel_bandwidth# Add the custom property to the actor, defining name, update fn and initial valueActorBuilder.add_custom_property(actor=local_actor,property_name="channel_bandwidth",update_function=update_function,initial_value=1000,)# (... run simulation)# One can easily access the property at any point withprint(local_actor.get_custom_property("channel_bandwidth"))GlossaryActivityActivity is the abstraction that PASEOS uses to keep track of specific actions performed by anactorupon a request from the user. >PASEOS is responsible for the execution of the activity and for updating the system status depending on the effects of the activity (e.g., by discharging the satellite battery).When registering an activity, the user can specify aconstraint functionto specify constraints to be met during the execution of the activity and anon-terminationfunction to specify additional operations to be performed by PASEOS on termination of the activity function.Activity functionUser-defined function emulating any operation to be executed in a PASEOS by anactor. Activity functions are necessary to registeractivities. Activity functions might include data transmission, housekeeping operations, onboard data acquisition and processing, and others.ActorSince PASEOS is fully-decentralised, each node of a PASEOS constellation shall run an instance of PASEOS modelling all the nodes of that constellation. The abstraction of a constellation node inside a PASEOS instance is a PASEOSactor.Constraint functionA constraint function is an asynchronous function that can be used by the PASEOS user to specify some constraints that shall be met during the execution of an activity.Custom PropertyUsers can define their own physical quantity to track parameters not natively simulated by PASEOS. This is described in detailaboveand in a dedicated example notebook on modelling total ionizing dose.GroundstationActorPASEOS actoremulating a ground station.Local actorThelocal actoris theactorwhose behaviour is modelled by the locally running PASEOS instance.Known actorsIn a PASEOS instance,known actorsare all the other actors that are known to thelocal actor.On-termination functionAn on-termination function is an asynchronous function that can be used by the PASEOS user to specify some operations to be executed on termination of the predefined PASEOS user's activity.SpacecraftActorPASEOSactoremulating a spacecraft or a satellite.Physical Model ParametersDescription of the physical model parameters and default values in PASEOS with indications on sensitivity of parameters and suggested ranges.NameDatatypeDescriptionDefaultSuggested RangeSensitivityBattery Level [Ws]floatCurrent battery level-> 0highMaximum Battery Level [Ws]floatMaximum battery level-> 0highCharging Rate [W]floatCharging rate of the battery-> 0highPower Device TypeenumType of power device. Can be either "SolarPanel" or "RTG"SolarPanel-mediumData Corruption Events [Hz]floatRate of single bit of data being corrupted, i.e. a Single Event Upset (SEU)->= 0lowRestart Events [Hz]floatRate of device restart being triggered->= 0mediumFailure Events [Hz]floatRate of complete device failure due to a Single Event Latch-Up (SEL)->= 0highMass [kg]floatActor's mass-> 0lowInitial Temperature [K]floatActor's initial temperature->= 0mediumSun AbsorptancefloatActor's absorptance of solar light-[0,1]highInfrared AbsorptancefloatActor's absportance of infrared light-[0,1]mediumSun-Facing Area [$m^2$]floatActor's area facing the sun->= 0highCentral Body-Facing Area [$m^2$]floatActor's area facing central body->= 0mediumEmissive Area [$m^2$]floatActor's area emitting (radiating) heat->= 0highThermal Capacity [$J / (kg * K)$]floatActor's thermal capacity->= 0lowBody Solar Irradiance [W]floatIrradiance from the sun1360>= 0mediumBody Surface Temperature [K]floatCentral body surface temperature288>= 0lowBody EmissivityfloatCentral body emissivity in infrared0.6[0,1]mediumBody ReflectancefloatCentral body reflectance of sunlight0.3[0,1]mediumHeat Conversion Ratio [-]floatConversion ratio for activities, 0 leads to know heat-up due to activity0.5[0,1]highContributingThePASEOSproject is open to contributions. To contribute, you can open anissueto report a bug or to request a new feature. If you prefer discussing new ideas and applications, you can contact us via email (please, refer toContact).
To contribute, please proceed as follow:Fork the ProjectCreate your Feature Branch (git checkout -b feature/AmazingFeature)Commit your Changes (git commit -m 'Add some AmazingFeature')Push to the Branch (git push origin feature/AmazingFeature)Open a Pull RequestLicenseDistributed under the GPL-3.0 License.ContactCreated by $\Phi$-lab@Sweden.Pablo Gómez - pablo.gomez at esa.int, pablo.gomez at ai.seGabriele Meoni - gabriele.meoni at esa.int, g.meoni at tudelft.nlJohan Östman - johan.ostman at ai.seVinutha Magal Shreenath - vinutha at ai.seReferenceIf you have usedPASEOS, please cite the following paper:@article{gomez23paseos,
  author = {Gómez, Pablo and Östman, Johan and Shreenath, Vinutha Magal and Meoni, Gabriele},
  title = {{PA}seos {S}imulates the {E}nvironment for {O}perating multiple {S}pacecraft},
  journal = {arXiv:2302.02659 [cs.DC]},
  year = {2023},
}