From 1a1c42dd80c3f35be446c3c16d64aa86e46ca6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Fri, 23 Sep 2022 15:56:21 +0200 Subject: [PATCH 01/14] - Fixing init.py - Adding main simulation module as singleton - Added func to load cfg and set log level - Added cfg file --- resources/default_cfg.toml | 6 ++++++ romeos/__init__..py | 0 romeos/__init__.py | 18 ++++++++++++++++++ romeos/romeos.py | 25 +++++++++++++++++++++++++ romeos/utils/load_default_cfg.py | 16 ++++++++++++++++ romeos/utils/set_log_level.py | 19 +++++++++++++++++++ 6 files changed, 84 insertions(+) create mode 100644 resources/default_cfg.toml delete mode 100644 romeos/__init__..py create mode 100644 romeos/__init__.py create mode 100644 romeos/romeos.py create mode 100644 romeos/utils/load_default_cfg.py create mode 100644 romeos/utils/set_log_level.py diff --git a/resources/default_cfg.toml b/resources/default_cfg.toml new file mode 100644 index 00000000..b8c9fa2a --- /dev/null +++ b/resources/default_cfg.toml @@ -0,0 +1,6 @@ +[sim] +startDate = "0" # Start of the simulation, relevant for propagation with SGP4 / pykep, in MJD2000 + +[comms] + +[radiation] diff --git a/romeos/__init__..py b/romeos/__init__..py deleted file mode 100644 index e69de29b..00000000 diff --git a/romeos/__init__.py b/romeos/__init__.py new file mode 100644 index 00000000..353752bf --- /dev/null +++ b/romeos/__init__.py @@ -0,0 +1,18 @@ +from loguru import logger + +from .utils.set_log_level import set_log_level +from .romeos import ROMEOS + +set_log_level("DEBUG") + +logger.debug("Loaded module.") + + +def init_sim(): + logger.debug("Initializing simulation.") + sim = ROMEOS() + sim.init() + return sim + + +__all__ = ["init"] diff --git a/romeos/romeos.py b/romeos/romeos.py new file mode 100644 index 00000000..257154ad --- /dev/null +++ b/romeos/romeos.py @@ -0,0 +1,25 @@ +from dotmap import DotMap +from loguru import logger + +from .utils.load_default_cfg import load_default_cfg + + +class ROMEOS: + """This class serves as the main interface with the user. It is designed as a singleton to ensure we only have one instance running at any time.""" + + _cfg = None + + def __new__(self): + if not hasattr(self, "instance"): + self.instance = super(ROMEOS, self).__new__(self) + else: + logger.warning("Tried to create another instance of ROMEOS simulation.") + return self.instance + + def init(self): + logger.trace("Initializing ROMEOS") + self._cfg = load_default_cfg() + pass + + def get_cfg(self) -> DotMap: + return self._cfg diff --git a/romeos/utils/load_default_cfg.py b/romeos/utils/load_default_cfg.py new file mode 100644 index 00000000..24a714e0 --- /dev/null +++ b/romeos/utils/load_default_cfg.py @@ -0,0 +1,16 @@ +import os +import toml +from dotmap import DotMap +from loguru import logger + + +def load_default_cfg(): + """Loads the default toml config file from the cfg folder.""" + logger.debug("Loading default cfg...") + + path = os.path.join( + os.path.dirname(__file__) + "/../../resources/", "default_cfg.toml" + ) + with open(path) as cfg: + # dynamic=False inhibits automatic generation of non-existing keys + return DotMap(toml.load(cfg), _dynamic=False) diff --git a/romeos/utils/set_log_level.py b/romeos/utils/set_log_level.py new file mode 100644 index 00000000..0bc0d9a7 --- /dev/null +++ b/romeos/utils/set_log_level.py @@ -0,0 +1,19 @@ +from loguru import logger +import sys + + +def set_log_level(log_level: str): + """Set the log level for the logger. + + Args: + log_level (str): The log level to set. Options are 'TRACE','DEBUG', 'INFO', 'SUCCESS', 'WARNING', 'ERROR', 'CRITICAL'. + """ + logger.remove() + logger.add( + sys.stderr, + colorize=True, + level=log_level.upper(), + format="{time:HH:mm:ss}|ROMEOS-{level}| {message}", + filter="romeos", + ) + logger.debug(f"Setting LogLevel to {log_level.upper()}") From 2f0ffc9bbb5ecc147db1ad271486f235df1770bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Fri, 23 Sep 2022 16:12:30 +0200 Subject: [PATCH 02/14] Added simple test for init of the model --- romeos/tests/import_test.py | 2 ++ romeos/tests/init_test.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 romeos/tests/init_test.py diff --git a/romeos/tests/import_test.py b/romeos/tests/import_test.py index fea35cab..4482af74 100644 --- a/romeos/tests/import_test.py +++ b/romeos/tests/import_test.py @@ -1,3 +1,5 @@ +"""Trivial test to see if model import still succeeds.""" + import sys sys.path.append("../..") diff --git a/romeos/tests/init_test.py b/romeos/tests/init_test.py new file mode 100644 index 00000000..045c447d --- /dev/null +++ b/romeos/tests/init_test.py @@ -0,0 +1,16 @@ +"""Tests to see if the module can be initialized and set up as planned.""" + +import sys + +sys.path.append("../..") + + +def test_init(): + import romeos + + sim = romeos.init_sim() + cfg = sim.get_cfg() # noqa + + +if __name__ == "__main__": + test_init() From 8034a89ad11726ae7d05ff3fad63d75ed00aaba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Fri, 30 Sep 2022 15:04:43 +0200 Subject: [PATCH 03/14] - Started adding basic actors - Added more docs and logging --- romeos/__init__.py | 1 - romeos/actors/base_actor.py | 33 +++++++++++++++++++++++++++ romeos/actors/ground_station_actor.py | 11 +++++++++ romeos/actors/spacecraft_actor.py | 15 ++++++++++++ romeos/romeos.py | 14 ++++++++++-- 5 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 romeos/actors/base_actor.py create mode 100644 romeos/actors/ground_station_actor.py create mode 100644 romeos/actors/spacecraft_actor.py diff --git a/romeos/__init__.py b/romeos/__init__.py index 353752bf..271f412a 100644 --- a/romeos/__init__.py +++ b/romeos/__init__.py @@ -11,7 +11,6 @@ def init_sim(): logger.debug("Initializing simulation.") sim = ROMEOS() - sim.init() return sim diff --git a/romeos/actors/base_actor.py b/romeos/actors/base_actor.py new file mode 100644 index 00000000..73def71e --- /dev/null +++ b/romeos/actors/base_actor.py @@ -0,0 +1,33 @@ +from loguru import logger + +from abc import ABC + + +class BaseActor(ABC): + """This (abstract) class is the baseline implementation of an actor + (e.g. spacecraft, ground station) in the simulation. The abstraction allows + some actors to have e.g. limited power (spacecraft) and others not to. + """ + + # Orbital parameters of the actor, described as position, + # velocity vectors, ground stations are in "ground" orbit + _position = None + _velocity = None + + # Constraint for max bandwidth used when comms are available + _max_bandwidth_kbps = None + + def __init__(self) -> None: + logger.trace("Instantiating Actor.") + super().__init__() + + def is_in_line_of_sight(position): + """Determines whether a position is in line of sight of this actor + + Args: + position (np.array): vector of 3 coordinates in central body frame. + + Returns: + bool: true if in line-of-sight. + """ + raise NotImplementedError("Not yet implemented.") diff --git a/romeos/actors/ground_station_actor.py b/romeos/actors/ground_station_actor.py new file mode 100644 index 00000000..1575ebb5 --- /dev/null +++ b/romeos/actors/ground_station_actor.py @@ -0,0 +1,11 @@ +from loguru import logger + +from romeos.actors.base_actor import BaseActor + + +class GroundstationActor(BaseActor): + """This class models a groundstation actor.""" + + def __init__(self) -> None: + logger.trace("Instantiating GroundstationActor.") + super().__init__() diff --git a/romeos/actors/spacecraft_actor.py b/romeos/actors/spacecraft_actor.py new file mode 100644 index 00000000..53e4b0e8 --- /dev/null +++ b/romeos/actors/spacecraft_actor.py @@ -0,0 +1,15 @@ +from loguru import logger + +from romeos.actors.base_actor import BaseActor + + +class SpacecraftActor(BaseActor): + """This class models a spacecraft actor which in addition to pos,velocity also has additional constraints such as power/battery.""" + + # Power constraints + # TODO + _available_power = None + + def __init__(self) -> None: + logger.trace("Instantiating SpacecraftActor.") + super().__init__() diff --git a/romeos/romeos.py b/romeos/romeos.py index 257154ad..9967b1b7 100644 --- a/romeos/romeos.py +++ b/romeos/romeos.py @@ -7,19 +7,29 @@ class ROMEOS: """This class serves as the main interface with the user. It is designed as a singleton to ensure we only have one instance running at any time.""" + # Config file of the simulation + # Beyond the initial cfg we use it to store variables + # like the central body etc. _cfg = None def __new__(self): if not hasattr(self, "instance"): self.instance = super(ROMEOS, self).__new__(self) else: - logger.warning("Tried to create another instance of ROMEOS simulation.") + logger.warning( + "Tried to create another instance of ROMEOS simulation. Keeping original one..." + ) return self.instance - def init(self): + def __init__(self): logger.trace("Initializing ROMEOS") self._cfg = load_default_cfg() pass def get_cfg(self) -> DotMap: + """Returns the current cfg of the simulation + + Returns: + DotMap: cfg + """ return self._cfg From 10c680688bd64481b506e44d98b19a89546dac84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Fri, 30 Sep 2022 15:30:00 +0200 Subject: [PATCH 04/14] Renaming the project --- .github/workflows/run_tests.yml | 2 +- README.md | 4 ++-- docs/source/conf.py | 2 +- docs/source/index.rst | 4 ++-- docs/source/modules.rst | 2 +- environment.yml | 2 +- {romeos => paseos}/__init__.py | 4 ++-- {romeos => paseos}/actors/base_actor.py | 0 {romeos => paseos}/actors/ground_station_actor.py | 2 +- {romeos => paseos}/actors/spacecraft_actor.py | 2 +- romeos/romeos.py => paseos/paseos.py | 8 ++++---- {romeos => paseos}/tests/import_test.py | 2 +- {romeos => paseos}/tests/init_test.py | 4 ++-- {romeos => paseos}/utils/load_default_cfg.py | 0 {romeos => paseos}/utils/set_log_level.py | 4 ++-- 15 files changed, 21 insertions(+), 21 deletions(-) rename {romeos => paseos}/__init__.py (83%) rename {romeos => paseos}/actors/base_actor.py (100%) rename {romeos => paseos}/actors/ground_station_actor.py (83%) rename {romeos => paseos}/actors/spacecraft_actor.py (88%) rename romeos/romeos.py => paseos/paseos.py (81%) rename {romeos => paseos}/tests/import_test.py (83%) rename {romeos => paseos}/tests/init_test.py (82%) rename {romeos => paseos}/utils/load_default_cfg.py (100%) rename {romeos => paseos}/utils/set_log_level.py (84%) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 13965244..530be5d4 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -48,7 +48,7 @@ jobs: $CONDA/bin/conda env update --file environment.yml --name base - name: Test with pytest run: | - cd romeos/tests + cd paseos/tests conda install pytest $CONDA/bin/pip3 install pytest-error-for-skips $CONDA/bin/pytest -ra --error-for-skips diff --git a/README.md b/README.md index 6073e6bb..b98c3306 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# ROMEOS -ROMEOS - ROmeos Models the Environment for Operating Spacecraft swarms +# PASEOS +PASEOS - PAseos Simulates the Environment for Operating multiple Spacecraft diff --git a/docs/source/conf.py b/docs/source/conf.py index d1848447..917ea135 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,7 +21,7 @@ # -- Project information ----------------------------------------------------- -project = "ROMEOS" +project = "PASEOS" copyright = "2022" author = "Pablo Gómez, Gabriele Meoni, Johan Östman, Vinutha Magal Shreenath" diff --git a/docs/source/index.rst b/docs/source/index.rst index cfdcbaf7..dc75c8c5 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,6 +1,6 @@ -.. ROMEOS documentation master file +.. PASEOS documentation master file -Welcome to ROMEOS' documentation! +Welcome to PASEOS' documentation! ================================ .. toctree:: diff --git a/docs/source/modules.rst b/docs/source/modules.rst index 10b745c4..82d256d1 100644 --- a/docs/source/modules.rst +++ b/docs/source/modules.rst @@ -4,4 +4,4 @@ Overview of modules .. toctree:: :maxdepth: 4 - romeos + paseos diff --git a/environment.yml b/environment.yml index fff93ceb..503e585c 100644 --- a/environment.yml +++ b/environment.yml @@ -1,4 +1,4 @@ -name: romeos +name: paseos channels: - conda-forge dependencies: diff --git a/romeos/__init__.py b/paseos/__init__.py similarity index 83% rename from romeos/__init__.py rename to paseos/__init__.py index 271f412a..d2d16e48 100644 --- a/romeos/__init__.py +++ b/paseos/__init__.py @@ -1,7 +1,7 @@ from loguru import logger from .utils.set_log_level import set_log_level -from .romeos import ROMEOS +from .paseos import PASEOS set_log_level("DEBUG") @@ -10,7 +10,7 @@ def init_sim(): logger.debug("Initializing simulation.") - sim = ROMEOS() + sim = PASEOS() return sim diff --git a/romeos/actors/base_actor.py b/paseos/actors/base_actor.py similarity index 100% rename from romeos/actors/base_actor.py rename to paseos/actors/base_actor.py diff --git a/romeos/actors/ground_station_actor.py b/paseos/actors/ground_station_actor.py similarity index 83% rename from romeos/actors/ground_station_actor.py rename to paseos/actors/ground_station_actor.py index 1575ebb5..54630e2c 100644 --- a/romeos/actors/ground_station_actor.py +++ b/paseos/actors/ground_station_actor.py @@ -1,6 +1,6 @@ from loguru import logger -from romeos.actors.base_actor import BaseActor +from paseos.actors.base_actor import BaseActor class GroundstationActor(BaseActor): diff --git a/romeos/actors/spacecraft_actor.py b/paseos/actors/spacecraft_actor.py similarity index 88% rename from romeos/actors/spacecraft_actor.py rename to paseos/actors/spacecraft_actor.py index 53e4b0e8..796f6601 100644 --- a/romeos/actors/spacecraft_actor.py +++ b/paseos/actors/spacecraft_actor.py @@ -1,6 +1,6 @@ from loguru import logger -from romeos.actors.base_actor import BaseActor +from paseos.actors.base_actor import BaseActor class SpacecraftActor(BaseActor): diff --git a/romeos/romeos.py b/paseos/paseos.py similarity index 81% rename from romeos/romeos.py rename to paseos/paseos.py index 9967b1b7..c219f861 100644 --- a/romeos/romeos.py +++ b/paseos/paseos.py @@ -4,7 +4,7 @@ from .utils.load_default_cfg import load_default_cfg -class ROMEOS: +class PASEOS: """This class serves as the main interface with the user. It is designed as a singleton to ensure we only have one instance running at any time.""" # Config file of the simulation @@ -14,15 +14,15 @@ class ROMEOS: def __new__(self): if not hasattr(self, "instance"): - self.instance = super(ROMEOS, self).__new__(self) + self.instance = super(PASEOS, self).__new__(self) else: logger.warning( - "Tried to create another instance of ROMEOS simulation. Keeping original one..." + "Tried to create another instance of PASEOS simulation. Keeping original one..." ) return self.instance def __init__(self): - logger.trace("Initializing ROMEOS") + logger.trace("Initializing PASEOS") self._cfg = load_default_cfg() pass diff --git a/romeos/tests/import_test.py b/paseos/tests/import_test.py similarity index 83% rename from romeos/tests/import_test.py rename to paseos/tests/import_test.py index 4482af74..c9279c26 100644 --- a/romeos/tests/import_test.py +++ b/paseos/tests/import_test.py @@ -6,7 +6,7 @@ def test_import(): - import romeos # noqa: F401 + import paseos # noqa: F401 if __name__ == "__main__": diff --git a/romeos/tests/init_test.py b/paseos/tests/init_test.py similarity index 82% rename from romeos/tests/init_test.py rename to paseos/tests/init_test.py index 045c447d..ea5ac129 100644 --- a/romeos/tests/init_test.py +++ b/paseos/tests/init_test.py @@ -6,9 +6,9 @@ def test_init(): - import romeos + import paseos - sim = romeos.init_sim() + sim = paseos.init_sim() cfg = sim.get_cfg() # noqa diff --git a/romeos/utils/load_default_cfg.py b/paseos/utils/load_default_cfg.py similarity index 100% rename from romeos/utils/load_default_cfg.py rename to paseos/utils/load_default_cfg.py diff --git a/romeos/utils/set_log_level.py b/paseos/utils/set_log_level.py similarity index 84% rename from romeos/utils/set_log_level.py rename to paseos/utils/set_log_level.py index 0bc0d9a7..4b8c9aa2 100644 --- a/romeos/utils/set_log_level.py +++ b/paseos/utils/set_log_level.py @@ -13,7 +13,7 @@ def set_log_level(log_level: str): sys.stderr, colorize=True, level=log_level.upper(), - format="{time:HH:mm:ss}|ROMEOS-{level}| {message}", - filter="romeos", + format="{time:HH:mm:ss}|PASEOS-{level}| {message}", + filter="paseos", ) logger.debug(f"Setting LogLevel to {log_level.upper()}") From 1a8b3f90b2b1ea14d487e03095785d9c9aae80fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Thu, 6 Oct 2022 11:00:58 +0200 Subject: [PATCH 05/14] - Fixing flake8 config - Fixing long lines --- .github/workflows/run_tests.yml | 2 +- paseos/paseos.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 530be5d4..d8e29a56 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -40,7 +40,7 @@ jobs: run: | # stop the build if there are Python syntax errors or undefined names pip install flake8 - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --max-line-length=127 # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Install dependencies diff --git a/paseos/paseos.py b/paseos/paseos.py index c219f861..ba6c8b3a 100644 --- a/paseos/paseos.py +++ b/paseos/paseos.py @@ -5,7 +5,8 @@ class PASEOS: - """This class serves as the main interface with the user. It is designed as a singleton to ensure we only have one instance running at any time.""" + """This class serves as the main interface with the user. It is designed + as a singleton to ensure we only have one instance running at any time.""" # Config file of the simulation # Beyond the initial cfg we use it to store variables @@ -17,7 +18,7 @@ def __new__(self): self.instance = super(PASEOS, self).__new__(self) else: logger.warning( - "Tried to create another instance of PASEOS simulation. Keeping original one..." + "Tried to create another instance of PASEOS simulation.Keeping original one..." ) return self.instance From 5b3480c588603d282d7dd5a9422edb25fa01b126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Thu, 6 Oct 2022 11:01:40 +0200 Subject: [PATCH 06/14] Fixed missing package in conda env file --- environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/environment.yml b/environment.yml index 503e585c..4d9718b1 100644 --- a/environment.yml +++ b/environment.yml @@ -2,6 +2,7 @@ name: paseos channels: - conda-forge dependencies: +- dotmap - loguru - pytest - python From daa586d416888a3e8e8bf4c729eefa87d69d2789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Thu, 6 Oct 2022 13:58:01 +0200 Subject: [PATCH 07/14] Added pykep to requirements --- environment.yml | 1 + requirements.txt | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/environment.yml b/environment.yml index 4d9718b1..766f3d34 100644 --- a/environment.yml +++ b/environment.yml @@ -4,6 +4,7 @@ channels: dependencies: - dotmap - loguru +- pykep - pytest - python - sphinx diff --git a/requirements.txt b/requirements.txt index adc20d64..5d3addba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ dotmap loguru -tqdm -toml \ No newline at end of file +pykep +toml +tqdm \ No newline at end of file From fcb88047b0e74fba183b333b8c2637e3d4167ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Thu, 6 Oct 2022 16:02:09 +0200 Subject: [PATCH 08/14] Added orbital position computation for actors --- paseos/__init__.py | 4 ++- paseos/actors/base_actor.py | 47 ++++++++++++++++++++++----- paseos/actors/ground_station_actor.py | 16 +++++++-- paseos/actors/spacecraft_actor.py | 16 +++++++-- paseos/paseos.py | 41 +++++++++++++++++++++-- paseos/tests/init_test.py | 36 ++++++++++++++++++-- 6 files changed, 142 insertions(+), 18 deletions(-) diff --git a/paseos/__init__.py b/paseos/__init__.py index d2d16e48..4196757d 100644 --- a/paseos/__init__.py +++ b/paseos/__init__.py @@ -2,6 +2,8 @@ from .utils.set_log_level import set_log_level from .paseos import PASEOS +from .actors.spacecraft_actor import SpacecraftActor +from .actors.ground_station_actor import GroundstationActor set_log_level("DEBUG") @@ -14,4 +16,4 @@ def init_sim(): return sim -__all__ = ["init"] +__all__ = ["GroundstationActor", "SpacecraftActor"] diff --git a/paseos/actors/base_actor.py b/paseos/actors/base_actor.py index 73def71e..4d52a88f 100644 --- a/paseos/actors/base_actor.py +++ b/paseos/actors/base_actor.py @@ -1,25 +1,56 @@ from loguru import logger +import pykep as pk -from abc import ABC - -class BaseActor(ABC): +class BaseActor: """This (abstract) class is the baseline implementation of an actor (e.g. spacecraft, ground station) in the simulation. The abstraction allows some actors to have e.g. limited power (spacecraft) and others not to. """ - # Orbital parameters of the actor, described as position, - # velocity vectors, ground stations are in "ground" orbit - _position = None - _velocity = None + # Orbital parameters of the actor, stored in a pykep planet object + _orbital_parameters = None # Constraint for max bandwidth used when comms are available _max_bandwidth_kbps = None - def __init__(self) -> None: + def __init__( + self, name: str, position, velocity, epoch: pk.epoch, central_body: pk.planet + ) -> None: + """Constructor for a base actor + + Args: + name (str): Name of this actor + position (list of floats): [x,y,z] + velocity (list of floats): [vx,vy,vz] + epoch (pykep.epoch): Epoch at this pos / velocity + central_body (pk.planet): pykep central body + """ logger.trace("Instantiating Actor.") super().__init__() + self._orbital_parameters = pk.planet.keplerian( + epoch, + position, + velocity, + central_body.mu_self, + 1.0, + 1.0, + 1.0, + name, + ) + + def __str__(self): + return self._orbital_parameters.name + + def get_position_velocity(self, epoch: pk.epoch): + logger.trace( + "Computing " + + self._orbital_parameters.name + + " position / velocity at time " + + str(epoch.mjd2000) + + " (mjd2000)." + ) + return self._orbital_parameters.eph(epoch) def is_in_line_of_sight(position): """Determines whether a position is in line of sight of this actor diff --git a/paseos/actors/ground_station_actor.py b/paseos/actors/ground_station_actor.py index 54630e2c..362391e1 100644 --- a/paseos/actors/ground_station_actor.py +++ b/paseos/actors/ground_station_actor.py @@ -1,4 +1,5 @@ from loguru import logger +import pykep as pk from paseos.actors.base_actor import BaseActor @@ -6,6 +7,17 @@ class GroundstationActor(BaseActor): """This class models a groundstation actor.""" - def __init__(self) -> None: + def __init__( + self, name: str, position, velocity, epoch: pk.epoch, central_body: pk.planet + ) -> None: + """Constructor for a groundstation actor + + Args: + name (str): Name of this actor + position (list of floats): [x,y,z] + velocity (list of floats): [vx,vy,vz] + epoch (pykep.epoch): Epoch at this pos / velocity + central_body (pk.planet): pykep central body + """ logger.trace("Instantiating GroundstationActor.") - super().__init__() + super().__init__(name, position, velocity, epoch, central_body) diff --git a/paseos/actors/spacecraft_actor.py b/paseos/actors/spacecraft_actor.py index 796f6601..d31bd2cb 100644 --- a/paseos/actors/spacecraft_actor.py +++ b/paseos/actors/spacecraft_actor.py @@ -1,4 +1,5 @@ from loguru import logger +import pykep as pk from paseos.actors.base_actor import BaseActor @@ -10,6 +11,17 @@ class SpacecraftActor(BaseActor): # TODO _available_power = None - def __init__(self) -> None: + def __init__( + self, name: str, position, velocity, epoch: pk.epoch, central_body: pk.planet + ) -> None: + """Constructor for a spacecraft actor + + Args: + name (str): Name of this actor + position (list of floats): [x,y,z] + velocity (list of floats): [vx,vy,vz] + epoch (pykep.epoch): Epoch at this pos / velocity + central_body (pk.planet): pykep central body + """ logger.trace("Instantiating SpacecraftActor.") - super().__init__() + super().__init__(name, position, velocity, epoch, central_body) diff --git a/paseos/paseos.py b/paseos/paseos.py index ba6c8b3a..57cbe87c 100644 --- a/paseos/paseos.py +++ b/paseos/paseos.py @@ -1,5 +1,8 @@ from dotmap import DotMap from loguru import logger +import pykep as pk + +from paseos.actors.base_actor import BaseActor from .utils.load_default_cfg import load_default_cfg @@ -9,10 +12,11 @@ class PASEOS: as a singleton to ensure we only have one instance running at any time.""" # Config file of the simulation - # Beyond the initial cfg we use it to store variables - # like the central body etc. _cfg = None + # Stores the simulation state + _state = None + def __new__(self): if not hasattr(self, "instance"): self.instance = super(PASEOS, self).__new__(self) @@ -25,7 +29,38 @@ def __new__(self): def __init__(self): logger.trace("Initializing PASEOS") self._cfg = load_default_cfg() - pass + self._state = DotMap(_dynamic=False) + self._state.time = 0 + self._state.actors = [] + + def advance_time(self, dt: float): + """Advances the simulation by a specified amount of time + + Args: + dt (float): Time to advance in seconds + """ + logger.debug("Advancing time by " + str(dt) + " s.") + self._state.time += dt + + logger.debug("New time is: " + str(self._state.time) + " s.") + + def add_actor(self, actor: BaseActor): + """Adds an actor to the simulation. + + Args: + actor (BaseActor): Actor to add + """ + logger.debug("Adding actor:" + str(actor)) + self._state.actors.append(actor) + + def set_central_body(self, planet: pk.planet): + """Sets the central body of the simulation for the orbit simulation + + Args: + planet (pk.planet): The central body as a pykep planet + """ + logger.debug("Setting central body to " + planet) + self._state.central_body = planet def get_cfg(self) -> DotMap: """Returns the current cfg of the simulation diff --git a/paseos/tests/init_test.py b/paseos/tests/init_test.py index ea5ac129..774dbf7e 100644 --- a/paseos/tests/init_test.py +++ b/paseos/tests/init_test.py @@ -4,13 +4,45 @@ sys.path.append("../..") +import paseos +from paseos import SpacecraftActor +import pykep as pk +import numpy as np -def test_init(): - import paseos +def test_init(): sim = paseos.init_sim() cfg = sim.get_cfg() # noqa + print(cfg) + + +def test_adding_sat(): + earth = pk.planet.jpl_lp("earth") + actor = SpacecraftActor("sat1", [1000000, 0, 0], [0, 8000.0, 0], pk.epoch(0), earth) + sim = paseos.init_sim() + sim.add_actor(actor) + + # check initial positions + r, v = actor.get_position_velocity(pk.epoch(0)) + assert np.isclose(r[0], 1000000) + assert np.isclose(r[1], 0) + assert np.isclose(r[2], 0) + assert np.isclose(v[0], 0) + assert np.isclose(v[1], 8000.0) + assert np.isclose(v[2], 0) + + # check positions one second later + r, v = actor.get_position_velocity(pk.epoch(1 * pk.SEC2DAY)) + assert np.isclose(r[0], 999800.6897266058) + assert np.isclose(r[1], 7999.468463301808) + assert np.isclose(r[2], 0.0) + assert np.isclose(v[0], -398.64065398803876) + assert np.isclose(v[1], 7998.405250997527) + assert np.isclose(v[2], 0.0) + + # TODO add more checks if this was done correctly if __name__ == "__main__": test_init() + test_adding_sat() From 02dde4165e2851d3669285f9a142244572483b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Thu, 6 Oct 2022 16:07:20 +0200 Subject: [PATCH 09/14] Fixing flake problems --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index d8e29a56..065f091d 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -24,7 +24,7 @@ jobs: - name: Lint with flake8 run: | pip install flake8 - flake8 . --count --show-source --statistics + flake8 . --count --show-source --statistics --max-line-length=127 build: From dcb350ad556806316bb1aa2823cfec73ca20cde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Thu, 6 Oct 2022 16:11:19 +0200 Subject: [PATCH 10/14] Ignoring E402 in flake --- .github/workflows/run_tests.yml | 2 +- paseos/actors/spacecraft_actor.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 065f091d..c1c8d87a 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -24,7 +24,7 @@ jobs: - name: Lint with flake8 run: | pip install flake8 - flake8 . --count --show-source --statistics --max-line-length=127 + flake8 . --count --show-source --statistics --max-line-length=127 --ignore=E402 build: diff --git a/paseos/actors/spacecraft_actor.py b/paseos/actors/spacecraft_actor.py index d31bd2cb..c6e9df89 100644 --- a/paseos/actors/spacecraft_actor.py +++ b/paseos/actors/spacecraft_actor.py @@ -5,7 +5,8 @@ class SpacecraftActor(BaseActor): - """This class models a spacecraft actor which in addition to pos,velocity also has additional constraints such as power/battery.""" + """This class models a spacecraft actor which in addition to pos, + velocity also has additional constraints such as power/battery.""" # Power constraints # TODO From d2401b03d8240300c8b59ce462095eada1ea9ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Fri, 7 Oct 2022 16:36:35 +0200 Subject: [PATCH 11/14] - Added line of sight computation - Added test for line of sight - Added some additional checks --- environment.yml | 1 + paseos/actors/base_actor.py | 69 ++++++++++++++++++++++++++++-- paseos/paseos.py | 7 +++ paseos/tests/line_of_sight_test.py | 35 +++++++++++++++ requirements.txt | 1 + 5 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 paseos/tests/line_of_sight_test.py diff --git a/environment.yml b/environment.yml index 766f3d34..8652398f 100644 --- a/environment.yml +++ b/environment.yml @@ -7,6 +7,7 @@ dependencies: - pykep - pytest - python +- scikit-spatial - sphinx - sphinx_rtd_theme - toml diff --git a/paseos/actors/base_actor.py b/paseos/actors/base_actor.py index 4d52a88f..91c0474f 100644 --- a/paseos/actors/base_actor.py +++ b/paseos/actors/base_actor.py @@ -1,19 +1,30 @@ from loguru import logger import pykep as pk +from skspatial.objects import Line, Sphere -class BaseActor: +from abc import ABC + + +class BaseActor(ABC): """This (abstract) class is the baseline implementation of an actor (e.g. spacecraft, ground station) in the simulation. The abstraction allows some actors to have e.g. limited power (spacecraft) and others not to. """ + # Actor name, has to be unique + name = None + # Orbital parameters of the actor, stored in a pykep planet object _orbital_parameters = None # Constraint for max bandwidth used when comms are available _max_bandwidth_kbps = None + # Earth as a sphere (for now) + # TODO replace this in the future depending on central body + _central_body_sphere = Sphere([0, 0, 0], 6371000) + def __init__( self, name: str, position, velocity, epoch: pk.epoch, central_body: pk.planet ) -> None: @@ -28,6 +39,7 @@ def __init__( """ logger.trace("Instantiating Actor.") super().__init__() + self.name = name self._orbital_parameters = pk.planet.keplerian( epoch, position, @@ -52,13 +64,62 @@ def get_position_velocity(self, epoch: pk.epoch): ) return self._orbital_parameters.eph(epoch) - def is_in_line_of_sight(position): + def is_in_line_of_sight( + self, other_actor: "BaseActor", epoch: pk.epoch, plot=False + ): """Determines whether a position is in line of sight of this actor Args: - position (np.array): vector of 3 coordinates in central body frame. + other_actor (BaseActor): The actor to check line of sight with + epoch (pk,.epoch): Epoch at which to check the line of sight + plot (bool): Whether to plot a diagram illustrating the positions. Returns: bool: true if in line-of-sight. """ - raise NotImplementedError("Not yet implemented.") + logger.debug( + "Computing line of sight between actors: " + + str(self) + + " " + + str(other_actor) + ) + my_pos, _ = self.get_position_velocity(epoch) + other_actor_pos, _ = other_actor.get_position_velocity(epoch) + + logger.trace( + "Computed positions for actors are " + + str(my_pos) + + " and " + + str(other_actor_pos) + ) + line_between_actors = Line( + my_pos, + [ + other_actor_pos[0] - my_pos[0], + other_actor_pos[1] - my_pos[1], + other_actor_pos[2] - my_pos[2], + ], + ) + print(line_between_actors) + if plot: + from skspatial.plotting import plot_3d + + # Currently skspatial throws a ValueError if there is no intersection so we have to use this rather ugly way. + try: + p1, p2 = self._central_body_sphere.intersect_line(line_between_actors) + logger.trace("Intersections observed at " + str(p1) + " and " + str(p2)) + if plot: + plot_3d( + line_between_actors.plotter(t_1=0, t_2=1, c="k"), + self._central_body_sphere.plotter(alpha=0.2), + p1.plotter(c="r", s=100), + p2.plotter(c="r", s=100), + ) + except ValueError: + if plot: + plot_3d( + line_between_actors.plotter(t_1=0, t_2=1, c="k"), + self._central_body_sphere.plotter(alpha=0.2), + ) + return True + return False diff --git a/paseos/paseos.py b/paseos/paseos.py index 57cbe87c..7b754b61 100644 --- a/paseos/paseos.py +++ b/paseos/paseos.py @@ -51,6 +51,13 @@ def add_actor(self, actor: BaseActor): actor (BaseActor): Actor to add """ logger.debug("Adding actor:" + str(actor)) + # Check for duplicate actors by name + for existing_actor in self._state.actors: + if existing_actor.name == actor.name: + raise ValueError( + "Trying to add actor with already existing name: " + actor.name + ) + # Else add self._state.actors.append(actor) def set_central_body(self, planet: pk.planet): diff --git a/paseos/tests/line_of_sight_test.py b/paseos/tests/line_of_sight_test.py new file mode 100644 index 00000000..f3ff135a --- /dev/null +++ b/paseos/tests/line_of_sight_test.py @@ -0,0 +1,35 @@ +"""Tests to check line of sight computations.""" +import sys + +sys.path.append("../..") + +import paseos +from paseos import SpacecraftActor + +import pykep as pk + + +def test_los(): + # init simulation + sim = paseos.init_sim() + + # create satellites where sat1 and 2 are in sight of each other (as well as sat 1 and 3) + # but sat 2 and 3 are on opposite sides of the planet + earth = pk.planet.jpl_lp("earth") + sat1 = SpacecraftActor("sat1", [10000000, 0, 0], [0, 8000.0, 0], pk.epoch(0), earth) + sim.add_actor(sat1) + sat2 = SpacecraftActor("sat2", [0, 10000000, 0], [0, 0, 8000.0], pk.epoch(0), earth) + sim.add_actor(sat2) + sat3 = SpacecraftActor( + "sat3", [0, -10000000, 0], [0, 0, -8000.0], pk.epoch(0), earth + ) + sim.add_actor(sat3) + + # check that LOS is correct + assert sat1.is_in_line_of_sight(sat2, pk.epoch(0)) + assert sat1.is_in_line_of_sight(sat3, pk.epoch(0)) + assert not sat2.is_in_line_of_sight(sat3, pk.epoch(0)) + + +if __name__ == "__main__": + test_los() diff --git a/requirements.txt b/requirements.txt index 5d3addba..5d292ddb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ dotmap loguru pykep +scikit-spatial toml tqdm \ No newline at end of file From 35d342fdac57de9fa12fb87a9190a4deaf5c549e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Fri, 7 Oct 2022 16:40:50 +0200 Subject: [PATCH 12/14] Ignoring incompatibility between black and flake8 --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index c1c8d87a..dac37152 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -24,7 +24,7 @@ jobs: - name: Lint with flake8 run: | pip install flake8 - flake8 . --count --show-source --statistics --max-line-length=127 --ignore=E402 + flake8 . --count --show-source --statistics --max-line-length=127 --ignore=E402,W503 build: From 7c666fd0ae2d2258e38d99e7ce88f0df41714a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Mon, 10 Oct 2022 10:28:09 +0200 Subject: [PATCH 13/14] Added comments --- paseos/actors/ground_station_actor.py | 3 ++- paseos/tests/init_test.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/paseos/actors/ground_station_actor.py b/paseos/actors/ground_station_actor.py index 362391e1..bf81f2e6 100644 --- a/paseos/actors/ground_station_actor.py +++ b/paseos/actors/ground_station_actor.py @@ -10,7 +10,8 @@ class GroundstationActor(BaseActor): def __init__( self, name: str, position, velocity, epoch: pk.epoch, central_body: pk.planet ) -> None: - """Constructor for a groundstation actor + """Constructor for a groundstation actor. + Pos / velocity are relative to central body origin. Args: name (str): Name of this actor diff --git a/paseos/tests/init_test.py b/paseos/tests/init_test.py index 774dbf7e..23ce0747 100644 --- a/paseos/tests/init_test.py +++ b/paseos/tests/init_test.py @@ -23,6 +23,7 @@ def test_adding_sat(): sim.add_actor(actor) # check initial positions + # r - position vector, v - velocity vector r, v = actor.get_position_velocity(pk.epoch(0)) assert np.isclose(r[0], 1000000) assert np.isclose(r[1], 0) From 1211ad47d733b6886e2a7e361cf6401c081a0abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20G=C3=B3mez?= Date: Mon, 10 Oct 2022 10:31:20 +0200 Subject: [PATCH 14/14] Removed whitespace --- paseos/actors/ground_station_actor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paseos/actors/ground_station_actor.py b/paseos/actors/ground_station_actor.py index bf81f2e6..0441df93 100644 --- a/paseos/actors/ground_station_actor.py +++ b/paseos/actors/ground_station_actor.py @@ -10,7 +10,7 @@ class GroundstationActor(BaseActor): def __init__( self, name: str, position, velocity, epoch: pk.epoch, central_body: pk.planet ) -> None: - """Constructor for a groundstation actor. + """Constructor for a groundstation actor. Pos / velocity are relative to central body origin. Args: