diff --git a/.bazelrc b/.bazelrc index 96296ed3..9371e114 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,2 +1,2 @@ -build --cxxopt=-std=c++14 +build --cxxopt=-std=c++14 --cxxopt=-O3 --cxxopt=-Werror --cxxopt=-Wall build --python_top=//:venv --incompatible_use_python_toolchains=false diff --git a/.github/workflows/psim.yml b/.github/workflows/psim.yml index a39afa38..931bbbf3 100644 --- a/.github/workflows/psim.yml +++ b/.github/workflows/psim.yml @@ -28,5 +28,5 @@ jobs: - name: Build and Test run: | bazel build //:gnc - bazel build //:psim + bazel build //python:psim bazel test //test/psim:ci diff --git a/BUILD b/BUILD.bazel similarity index 51% rename from BUILD rename to BUILD.bazel index 404d71dc..f10f1885 100644 --- a/BUILD +++ b/BUILD.bazel @@ -1,24 +1,7 @@ -load("@rules_python//python:defs.bzl", "py_runtime", "py_binary") load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_python//python:defs.bzl", "py_runtime", "py_binary") -load("//:tools/psim.bzl", "psim_autocode") - -# Build flight software's gnc code as a library. -cc_library( - name = "gnc", - srcs = glob([ - "src/gnc/**/*.hpp", "src/gnc/*.cpp", "src/gnc/**/*.inl", - "include/gnc/**/*.inl" - ]), - hdrs = glob([ - "include/gnc/**/*.hpp", "include/orb/*.h", "include/orb/*.hpp" - ]), - includes = ['include'], - copts = ['-Isrc'], - linkstatic = True, - visibility = ["//visibility:public"], - deps = ["@lin//:lin"], -) +load("//bazel:psim_build.bzl", "psim_autocoded_cc_library", "psim_cc_library", "psim_py_extension") # Let bazel know about our virtual environment. # @@ -34,42 +17,50 @@ py_runtime( visibility = ["//visibility:private"], ) -# Register the autocoder as an executable. -# -# This is later consumed by the autocoder command. See psim.bzl for more -# information. +# Register the autocoder as an executable. This is later consumed by the autocoder +# command. See psim.bzl for more information. py_binary( name = "autocoder", - srcs = ["tools/autocoder.py"], + srcs = ["//:tools/autocoder.py"], srcs_version = "PY3", visibility = ["//visibility:private"], ) -# Autocode the model interfaces -psim_autocode( - name = "autocode", - srcs = glob(["include/psim/**/*.yml"]), - includes = ["include"], - tool = "//:autocoder", - visibility = ["//visibility:private"], -) - -# Build all PSim models as a library -# -# This will be linked with executables later on. +# Builds the GNC flight software implementations to be linked in as a dependancy +# of PSim. cc_library( - name = "psim", + name = "gnc", srcs = glob([ - "src/psim/**/*.hpp", "src/psim/**/*.inl", "src/psim/**/*.cpp", - "include/psim/**/*.inl", "include/psim/**/*.yml.hpp", + "src/gnc/**/*.hpp", "src/gnc/*.cpp", "src/gnc/**/*.inl", + "include/gnc/**/*.inl" + ]), + hdrs = glob([ + "include/gnc/**/*.hpp", "include/orb/*.h", "include/orb/*.hpp" ]), - hdrs = glob( - ["include/psim/**/*.hpp"], - exclude = ["include/psim/**/*.yml.hpp"], - ), includes = ["include"], - copts = ["-Isrc"], + copts = ["-Isrc", "-fvisibility=hidden"], linkstatic = True, visibility = ["//visibility:public"], - deps = ["@lin//:lin", "//:gnc", "//:autocode"], + deps = ["@lin//:lin"], +) + +# Builds the core PSim infrastructure. +psim_cc_library( + name = "core", + deps = ["@lin//:lin"], + visibility = ["//visibility:public"], +) + +# Builds the PSim truth models +psim_autocoded_cc_library( + name = "truth", + deps = ["@lin//:lin", "//:gnc", "//:psim_core"], + visibility = ["//visibility:public"], +) + +# Builds all PSim models deemed "simulation" worthy. +psim_cc_library( + name = "simulations", + deps = ["//:psim_core", "//:psim_truth"], + visibility = ["//visibility:public"], ) diff --git a/README.md b/README.md index 1ae9f599..35d008f3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ two main modules: MATLAB code listed in `MATLAB/**` is deprecated and will be phased out by the new C++ implementation of PSim. -## Getting Setup +## Getting Started For GNC development, it's only necessary to setup the PlatformIO development environment. This requires setting up a local virtual environment as shown @@ -36,8 +36,21 @@ for the GNC code with: development!_** For PSim development, we need to have the virtual environment setup as shown -above _and_ setup Bazel. You should be able to install Bazel through your -system's package manager. +above, a couple extra Python bells and whistles you most likely already have, +and then Bazel. + +Besides your system install of Python and the virtual environment that will be +used to work with the PSim repository, you need to install the Python +development headers and `distutils` for the version of Python you'll be using. +The Bazel build system will require both of these to compile PSim into a Python +module. + +Generally the Python development headers and `distutils` can be installed via a +package manager. Note that Bazel will explicitly let you know if it can't find +either of these. + +As far as Bazel is concerned, you should be able to install it through your +package manager of choice as well. It's also recommended to install the Bazel build tools along with the VSCode extensions 'Bazel' by the 'Bazel Build Team'. With this extension plus @@ -46,7 +59,8 @@ configuring C/C++ intellisense according to fairly well with VSCode. To ensure everything is working as expected, you should be able to run the -following successfully: +following successfully (make sure you're in your virtual environment when +working with Bazel as well): bazel test //test/psim:all diff --git a/WORKSPACE b/WORKSPACE index c67ac7fa..d2d649ca 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,12 +1,34 @@ -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +workspace(name = "psim") + load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("//bazel:psim_workspace.bzl", "psim_configure") + +# Standard cc rules git_repository( + name = "rules_cc", + remote = "https://github.com/bazelbuild/rules_cc", + commit = "bea600c25d246a79398a1c1a73e9261aee35a6b1", +) + +# Standard Python rules +http_archive( name = "rules_python", - remote = "https://github.com/bazelbuild/rules_python.git", - commit = "3baa2660569a76898d0f520c73b299ea39b6374d", # (9-8-2020) + url = "https://github.com/bazelbuild/rules_python/releases/download/0.0.2/rules_python-0.0.2.tar.gz", + strip_prefix = "rules_python-0.0.2", + sha256 = "b5668cde8bb6e3515057ef465a35ad712214962f0b3a314e551204266c7be90c", ) +# Pybind11 +http_archive( + name = "pybind11", + build_file = "//bazel:pybind11.BUILD", + strip_prefix = "pybind11-2.5.0", + urls = ["https://github.com/pybind/pybind11/archive/v2.5.0.tar.gz"], +) + +# Googletest http_archive( name = "gtest", url = "https://github.com/google/googletest/archive/release-1.10.0.zip", @@ -14,14 +36,30 @@ http_archive( strip_prefix = "googletest-release-1.10.0", ) -# http_archive( -# name = "sofa", -# url = "https://github.com/pathfinder-for-autonomous-navigation/sofa/archive/v1.0.zip", -# sha256 = "a5660ff94270934c33b3fdaaf891620c9a454181dad7fb12378e66a5b73c205f", -# strip_prefix = "sofa-1.0", -# ) +# Sofa/IAU Coordinate Transformations +http_archive( + name = "sofa", + url = "https://github.com/pathfinder-for-autonomous-navigation/sofa/archive/v1.0.zip", + sha256 = "a5660ff94270934c33b3fdaaf891620c9a454181dad7fb12378e66a5b73c205f", + strip_prefix = "sofa-1.0", +) + +# std::variant alternative for use in C++ 14 +http_archive( + name = "variant", + build_file = "//bazel:variant.BUILD", + url = "https://github.com/mapbox/variant/archive/v1.2.0.zip", + sha256 = "48df02096b954ea35c447edc3e662e7f163a92e1de2ab5f226445d6001a4537d", + strip_prefix = "variant-1.2.0", +) +# lin local_repository( name = "lin", path = "lib/lin", ) + +# Grabs the systems Python headers in preparation for building with Pybind11 +psim_configure( + name = "psim_configuration" +) diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel new file mode 100644 index 00000000..e69de29b diff --git a/bazel/psim.bzl b/bazel/psim.bzl new file mode 100644 index 00000000..49c29247 --- /dev/null +++ b/bazel/psim.bzl @@ -0,0 +1,30 @@ +# +# MIT License +# +# Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +"""Collection of tools shared across PSim's Bazel rules. +""" + +def psim_fail(message): + """Outputs an error message and fails the build. Used throughout PSim's rules. + """ + fail("\033[0;31mPSim Error:\033[0m %s" % message) diff --git a/bazel/psim_build.bzl b/bazel/psim_build.bzl new file mode 100644 index 00000000..0f863689 --- /dev/null +++ b/bazel/psim_build.bzl @@ -0,0 +1,161 @@ +# +# MIT License +# +# Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +"""Custom rules for PSim. +""" + +load("@rules_cc//cc:defs.bzl", _cc_binary = "cc_binary", _cc_library = "cc_library", _cc_test = "cc_test") +load("@rules_python//python:defs.bzl", _py_library = "py_library") + + +def _psim_autocode_impl(ctx): + outs = [] + for yml in ctx.attr.ymls: + for file in yml.files.to_list(): + out = ctx.actions.declare_file(file.path + ".hpp") + outs.append(out) + + ctx.actions.run( + inputs = [file], + outputs = [out], + executable = ctx.executable.tool, + arguments = [file.path, out.path], + progress_message = "Autocoding '%s' into '%s'" % (file.path, out.path), + ) + + includes = [] + includes_base = outs[0].path[:-len(outs[0].short_path)] + for include in ctx.attr.includes: + includes.append(includes_base + include) + + return CcInfo( + compilation_context = cc_common.create_compilation_context( + headers = depset(outs), + includes = depset(includes), + ), + linking_context = None, + ) + + +_psim_autocode = rule( + implementation = _psim_autocode_impl, + output_to_genfiles = True, + attrs = { + "includes": attr.string_list(mandatory = True, allow_empty = False), + "ymls": attr.label_list(mandatory = True, allow_files = [".yml"]), + "tool": attr.label(mandatory = True, executable = True, cfg = "host"), + }, +) + + +def psim_autocoded_cc_library(name, deps = None, local_defines = None, visibility = None): + """Defines a PSim library with autocoded header files for models. + """ + _include_dir = "include/psim/" + name + + _psim_autocode( + name = "psim_" + name + "_autocoded", + includes = ["include"], + ymls = native.glob([_include_dir + "/**/*.yml"]), + tool = "//:autocoder", + visibility = ["//visibility:private"], + ) + + psim_cc_library( + name, + deps = deps + ["psim_" + name + "_autocoded"], + local_defines = local_defines, + visibility = visibility + ) + + +def psim_cc_library(name, deps = None, local_defines = None, visibility = None): + """Defines a PSim library without autocoded header files for models. + """ + _include_dir = "include/psim/" + name + _src_dir = "src/psim/" + name + + _cc_library( + name = "psim_" + name, + srcs = native.glob([ + _src_dir + "/**/*.hpp", _src_dir + "/**/*.inl", + _src_dir + "/**/*.cpp", _include_dir + "/**/*.inl", + ]), + hdrs = native.glob([_include_dir + "/**/*.hpp"]), + includes = ["include"], + copts = ["-Isrc", "-fvisibility=hidden"], + linkstatic = True, + deps = deps, + local_defines = local_defines, + visibility = visibility, + ) + + +def psim_cc_test(name, deps = None, tags = None): + """Defines a PSim CC test. + """ + _test_dir = name + + _cc_test( + name = name + "_test", + srcs = native.glob([ + _test_dir + "/**/*.hpp", _test_dir + "/**/*.inl", + _test_dir + "/**/*.cpp", + ]), + data = native.glob([_test_dir + "/**/*.txt"]), + deps = deps + ["@gtest//:gtest_main"], + tags = tags, + visibility = ["//visibility:public"], + ) + + +def psim_py_extension(name, srcs = None, hdrs = None, data = None, deps = None, local_defines = None, visibility = None): + """Compiles a PSim Python extension written in C++. + + This uses pybind11. Inspiration was taken from the code listed at the link + below and the pybind11 documentation: + - https://github.com/blais/oblique/blob/2ac7b3f82b4e3470da94a6b0417935b0eec3eec8/third_party/python/py_extension.bzl#L1 + - https://pybind11.readthedocs.io/en/stable/compiling.html + """ + _cc_binary( + name = name + ".so", + srcs = srcs, + hdrs = hdrs, + data = data, + copts = ["-Isrc", "-fvisibility=hidden"], + linkopts = select({ + "@pybind11//:osx": ["-undefined", "dynamic_lookup"], + "//conditions:default": ['-Wl,--export-dynamic-symbol=PyInit_%s' % name], + }), + linkshared = True, + linkstatic = True, + deps = deps + ["@pybind11//:pybind11"], + local_defines = local_defines, + visibility = ["//visibility:private"], + ) + + _py_library( + name = name, + data = [name + ".so"], + visibility = visibility, + ) diff --git a/bazel/psim_configuration.BUILD.tpl b/bazel/psim_configuration.BUILD.tpl new file mode 100644 index 00000000..86ae1eb4 --- /dev/null +++ b/bazel/psim_configuration.BUILD.tpl @@ -0,0 +1,8 @@ +load("@rules_cc//cc:defs.bzl", _cc_library = "cc_library") + +_cc_library( + name = "python", + hdrs = glob(["include/**/*.h"]), + includes = ["include"], + visibility = ["//visibility:public"], +) diff --git a/bazel/psim_workspace.bzl b/bazel/psim_workspace.bzl new file mode 100644 index 00000000..26a001ef --- /dev/null +++ b/bazel/psim_workspace.bzl @@ -0,0 +1,110 @@ +# +# MIT License +# +# Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +"""Custom repository rules for PSim. +""" + +load("//bazel:psim.bzl", _psim_fail = "psim_fail") + + +def _psim_get_python_bin(repository_ctx): + """Retrieves the Python binary that will be used by this repository. + + We are required here that Python 3 is used. + """ + python_bin = repository_ctx.which("python3") + if python_bin == None: + _psim_fail("Failed to detect a python binary. Ensure a 'python3'" + + "executable is in your path.") + + # TODO : Potentially add check here to ensure we are in a virtual environment. + return python_bin + + +def _psim_get_python_lib(repository_ctx, python_bin): + """Retrieves the Python path to the appropriate system site packages. + """ + result = repository_ctx.execute([ + python_bin, "-c", ( + "\n" + + "import site\n" + + "import sys\n" + + "\n" + + "site_packages = site.getsitepackages()\n" + + "\n" + + "if len(site_packages) == 1:\n" + + " print(site_packages[0])\n" + + "else:\n" + + " print('Expected a single system site package path', file=sys.stderr)\n" + + " print('but instead got: ', site_packages, file=sys.stderr)\n" + + "" + ) + ]) + if result.stderr: + _psim_fail("Failed to retrieve Python library path:\n%s" % result.stderr) + + return result.stdout.strip("\n") + + +def _psim_get_python_inc(repository_ctx, python_bin): + """Retrieves the include path to the core Python library. + + This requires distutilsl to be installed. + """ + result = repository_ctx.execute([ + python_bin, "-c", ( + "from distutils import sysconfig\n" + + "\n" + + "print(sysconfig.get_python_inc())\n" + + "" + ) + ]) + if result.stderr or not result.stdout: + _psim_fail("Failed to retrive Python include path. Ensure 'distutils' is installed.\n" + + result.stderr) + + return result.stdout.strip("\n") + + +def _psim_configure_impl(repository_ctx): + """Generates a new repository containing the Python development headers. + + Ensure the Python development packages is installed on your machine. + """ + python_bin = _psim_get_python_bin(repository_ctx) + python_inc = _psim_get_python_inc(repository_ctx, python_bin) + + if repository_ctx.name != "psim_configuration": + _psim_fail("The 'psim_configure' repository name must be 'psim_configuration'.") + + repository_ctx.symlink(python_inc, "include") + repository_ctx.template( + "BUILD", + Label("//bazel:psim_configuration.BUILD.tpl"), + { }, + ) + + +psim_configure = repository_rule( + implementation = _psim_configure_impl, +) diff --git a/bazel/pybind11.BUILD b/bazel/pybind11.BUILD new file mode 100644 index 00000000..6344481f --- /dev/null +++ b/bazel/pybind11.BUILD @@ -0,0 +1,23 @@ +load("@rules_cc//cc:defs.bzl", _cc_library = "cc_library") + +_cc_library( + name = "pybind11", + hdrs = glob( + ["include/pybind11/*.h", "include/pybind11/detail/*.h"], + exclude = ["include/pybind11/common.h"], + ), + copts = [ + "-fexceptions", + "-Xclang-only=-Wno-undefined-inline", + "-Xclang-only=-Wno-pragma-once-outside-header", + "-Xgcc-only=-Wno-error", + ], + includes = ["include"], + deps = ["@psim_configuration//:python"], + visibility = ["//visibility:public"], +) + +config_setting( + name = "osx", + constraint_values = ["@platforms//os:osx"], +) diff --git a/bazel/variant.BUILD b/bazel/variant.BUILD new file mode 100644 index 00000000..5afecfe8 --- /dev/null +++ b/bazel/variant.BUILD @@ -0,0 +1,8 @@ +load("@rules_cc//cc:defs.bzl", _cc_library = "cc_library") + +_cc_library( + name = "variant", + hdrs = glob(["include/mapbox/*.hpp"]), + includes = ["include"], + visibility = ["//visibility:public"], +) diff --git a/config/BUILD.bazel b/config/BUILD.bazel new file mode 100644 index 00000000..009e5ffd --- /dev/null +++ b/config/BUILD.bazel @@ -0,0 +1,12 @@ + +filegroup( + name = "parameters", + data = glob(["parameters/**/*.txt"]), + visibility = ["//visibility:public"], +) + +filegroup( + name = "plots", + data = glob(["plots/**/*.txt"]), + visibility = ["//visibility:public"], +) diff --git a/config/parameters/single_orbit.txt b/config/parameters/single_orbit.txt new file mode 100644 index 00000000..c37f27a8 --- /dev/null +++ b/config/parameters/single_orbit.txt @@ -0,0 +1,14 @@ + +# Setup time +truth.t.ns 0 +truth.dt.ns 100000000 + +# Satellite mass +truth.leader.m 5.0 + +# Orbital initial conditions +truth.leader.orbit.r 6.8538e6 0.0 0.0 +truth.leader.orbit.v 0.0 5.3952e3 5.3952e3 + +# Start with no impulse applied +truth.leader.orbit.J.eci 0.0 0.0 0.0 diff --git a/include/gnc/ode.hpp b/include/gnc/ode.hpp index a2842663..b4b82d1d 100644 --- a/include/gnc/ode.hpp +++ b/include/gnc/ode.hpp @@ -17,22 +17,6 @@ #ifndef GNC_ODE_HPP_ #define GNC_ODE_HPP_ -#define GNC_ODEX_SINGLE_TEMPLATE(odex, type) \ - template \ - void odex(type, type, type const *, type *, unsigned int, type *, \ - void (* const)(type, type const *, type *)) - -#define GNC_ODEX_SINGLE_EXTERN_TEMPLATE(odex, type) \ - extern GNC_ODEX_SINGLE_TEMPLATE(odex, type) - -#define GNC_ODEX_MULTI_TEMPLATE(odex, type) \ - template \ - void odex(type const *, unsigned int, type **, unsigned int, type *, \ - void (* const)(type, type const *, type *)) - -#define GNC_ODEX_MULTI_EXTERN_TEMPLATE(odex, type) \ - extern GNC_ODEX_MULTI_TEMPLATE(odex, type) - #define GNC_ODEXX_TEMPLATE(odexx, type) \ template \ int odexx(type, type, type const *, type *, unsigned int, type *, \ @@ -57,158 +41,6 @@ enum : int { ODE_ERR_BAD_INTERVAL = (0b1 << 2) }; -/** @fn ode1 - * @param[in] ti Initial conditions for the independant variable. - * @param[in] dt Integrator step for the independant variable. - * @param[in] yi Initial conditions for the dependant variables. - * @param[out] yf Final state of the system (dependant varaibles). - * @param[in] ne Number of dependant variables. - * @param[in] bf Buffer of length (ne). - * @param[in] f Dependant variable update function. - * Integrates the system from (ti, yi) to (ti + dt, yf) using a first order - * method. You may pass yi and yf as the same pointer if yi can be overwritten. - * NOTE: Template specializations are provided for double and float types. */ -template -void ode1(T ti, T dt, T const *yi, T *yf, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)); - -GNC_ODEX_SINGLE_EXTERN_TEMPLATE(ode1, float); -GNC_ODEX_SINGLE_EXTERN_TEMPLATE(ode1, double); - -/** @fn ode1 - * @param[in] t Array of independant variables states. - * @param[in] nt Length of the dependant variable array t. - * @param[inout] y Array of dependant variables states. - * @param[in] ne Number of dependant variables. - * @param[in] bf Buffer of length (ne). - * @param[in] f Dependant variable update function. - * Integrates the system using a first order method through the state sequence - * below given t[i] and y[0]: - * (t[0], y[0]) -> (t[1], y[1]) -> ... -> (t[nt - 1], y[nt - 1]) - * Passing y as a list of the same pointer is permissible if intermediate state - * information can be overwritten. - * NOTE: Template specializations are provided for double and float types. */ -template -void ode1(T const *t, unsigned int nt, T **y, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)); - -GNC_ODEX_MULTI_EXTERN_TEMPLATE(ode1, float); -GNC_ODEX_MULTI_EXTERN_TEMPLATE(ode1, double); - -/** @fn ode2 - * @param[in] ti Initial conditions for the independant variable. - * @param[in] dt Integrator step for the independant variable. - * @param[in] yi Initial conditions for the dependant variables. - * @param[out] yf Final state of the system (dependant varaibles). - * @param[in] ne Number of dependant variables. - * @param[in] bf Buffer of length (3 * ne). - * @param[in] f Dependant variable update function. - * Integrates the system from (ti, yi) to (ti + dt, yf) using a second order - * method. You may pass yi and yf as the same pointer if yi can be overwritten. - * NOTE: Template specializations are provided for double and float types. */ -template -void ode2(T ti, T dt, T const *yi, T *yf, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)); - -GNC_ODEX_SINGLE_EXTERN_TEMPLATE(ode2, float); -GNC_ODEX_SINGLE_EXTERN_TEMPLATE(ode2, double); - -/** @fn ode2 - * @param[in] t Array of independant variables states. - * @param[in] nt Length of the dependant variable array t. - * @param[inout] y Array of dependant variables states. - * @param[in] ne Number of dependant variables. - * @param[in] bf Buffer of length (3 * ne). - * @param[in] f Dependant variable update function. - * Integrates the system using a second order method through the state sequence - * below given t[i] and y[0]: - * (t[0], y[0]) -> (t[1], y[1]) -> ... -> (t[nt - 1], y[nt - 1]) - * Passing y as a list of the same pointer is permissible if intermediate state - * information can be overwritten. - * NOTE: Template specializations are provided for double and float types. */ -template -void ode2(T const *t, unsigned int nt, T **y, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)); - -GNC_ODEX_MULTI_EXTERN_TEMPLATE(ode2, float); -GNC_ODEX_MULTI_EXTERN_TEMPLATE(ode2, double); - -/** @fn ode3 - * @param[in] ti Initial conditions for the independant variable. - * @param[in] dt Integrator step for the independant variable. - * @param[in] yi Initial conditions for the dependant variables. - * @param[out] yf Final state of the system (dependant varaibles). - * @param[in] ne Number of dependant variables. - * @param[in] bf Buffer of length (4 * ne). - * @param[in] f Dependant variable update function. - * Integrates the system from (ti, yi) to (ti + dt, yf) using a third order - * method. You may pass yi and yf as the same pointer if yi can be overwritten. - * NOTE: Template specializations are provided for double and float types. */ -template -void ode3(T ti, T dt, T const *yi, T *yf, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)); - -GNC_ODEX_SINGLE_EXTERN_TEMPLATE(ode3, float); -GNC_ODEX_SINGLE_EXTERN_TEMPLATE(ode3, double); - -/** @fn ode3 - * @param[in] t Array of independant variables states. - * @param[in] nt Length of the dependant variable array t. - * @param[inout] y Array of dependant variables states. - * @param[in] ne Number of dependant variables. - * @param[in] bf Buffer of length (4 * ne). - * @param[in] f Dependant variable update function. - * Integrates the system using a third order method through the state sequence - * below given t[i] and y[0]: - * (t[0], y[0]) -> (t[1], y[1]) -> ... -> (t[nt - 1], y[nt - 1]) - * Passing y as a list of the same pointer is permissible if intermediate state - * information can be overwritten. - * NOTE: Template specializations are provided for double and float types. */ -template -void ode3(T const *t, unsigned int nt, T **y, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)); - -GNC_ODEX_MULTI_EXTERN_TEMPLATE(ode3, float); -GNC_ODEX_MULTI_EXTERN_TEMPLATE(ode3, double); - -/** @fn ode4 - * @param[in] ti Initial conditions for the independant variable. - * @param[in] dt Integrator step for the independant variable. - * @param[in] yi Initial conditions for the dependant variables. - * @param[out] yf Final state of the system (dependant varaibles). - * @param[in] ne Number of dependant variables. - * @param[in] bf Buffer of length (5 * ne). - * @param[in] f Dependant variable update function. - * Integrates the system from (ti, yi) to (ti + dt, yf) using a fourth order - * method. You may pass yi and yf as the same pointer if yi can be overwritten. - * NOTE: Template specializations are provided for double and float types. */ -template -void ode4(T ti, T dt, T const *yi, T *yf, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)); - -GNC_ODEX_SINGLE_EXTERN_TEMPLATE(ode4, float); -GNC_ODEX_SINGLE_EXTERN_TEMPLATE(ode4, double); - -/** @fn ode4 - * @param[in] t Array of independant variables states. - * @param[in] nt Length of the dependant variable array t. - * @param[inout] y Array of dependant variables states. - * @param[in] ne Number of dependant variables. - * @param[in] bf Buffer of length (5 * ne). - * @param[in] f Dependant variable update function. - * Integrates the system using a fourth order method through the state sequence - * below given t[i] and y[0]: - * (t[0], y[0]) -> (t[1], y[1]) -> ... -> (t[nt - 1], y[nt - 1]) - * Passing y as a list of the same pointer is permissible if intermediate state - * information can be overwritten. - * NOTE: Template specializations are provided for double and float types. */ -template -void ode4(T const *t, unsigned int nt, T **y, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)); - -GNC_ODEX_MULTI_EXTERN_TEMPLATE(ode4, float); -GNC_ODEX_MULTI_EXTERN_TEMPLATE(ode4, double); - /** @fn ode23 * @param[in] ti Initial conditions for the independant variable. * @param[in] tf Desired final state for the independant variable. diff --git a/include/gnc/ode1.hpp b/include/gnc/ode1.hpp new file mode 100644 index 00000000..46237266 --- /dev/null +++ b/include/gnc/ode1.hpp @@ -0,0 +1,76 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file gnc/ode1.hpp + * @author Kyle Krol + */ + +#ifndef GNC_ODE1_HPP_ +#define GNC_ODE1_HPP_ + +#include + +namespace gnc { + +/** @brief First order fixed step size integrator. + * + * @tparam T Fundamental data type. + * @tparam N Number of state parameters. + */ +template +class Ode1 { + private: + lin::Vector _k[1]; + + public: + /** @brief Step a differential equation forward in time by a single timestep. + * + * @param[in] ti Initial time. + * @param[in] dt Integrator timestep. + * @param[in] xi Initial state. + * @param[in] ptr Pointer to arbitrary data accesible in the update function. + * @param[in] dx Differential update function. + * + * Used for fixed timestep numerical integration. Implements the first order + * integration scheme known as Euler's method. + * + * Reference(s): + * - https://en.wikipedia.org/wiki/Euler_method + */ + lin::Vector operator()( + T ti, T dt, + lin::Vector const &xi, void *ptr, + lin::Vector (*dx)(T t, lin::Vector const &x, void *ptr)) { + // Scratch buffers + auto &k1 = _k[0]; + + k1 = dx(ti, xi, ptr); + + // Step forwad in time + return (xi + dt * k1).eval(); + } +}; +} // namespace gnc + +#endif diff --git a/include/gnc/ode2.hpp b/include/gnc/ode2.hpp new file mode 100644 index 00000000..70c4a7d2 --- /dev/null +++ b/include/gnc/ode2.hpp @@ -0,0 +1,85 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file gnc/ode2.hpp + * @author Kyle Krol + */ + +#ifndef GNC_ODE2_HPP_ +#define GNC_ODE2_HPP_ + +#include + +namespace gnc { + +/** @brief Second order fixed step size integrator. + * + * @tparam T Fundamental data type. + * @tparam N Number of state parameters. + */ +template +class Ode2 { + private: + lin::Vector _k[3]; + + public: + /** @brief Step a differential equation forward in time by a single timestep. + * + * @param[in] ti Initial time. + * @param[in] dt Integrator timestep. + * @param[in] xi Initial state. + * @param[in] ptr Pointer to arbitrary data accesible in the update function. + * @param[in] dx Differential update function. + * + * Used for fixed timestep numerical integration. Implements the second order + * integration scheme known as Heun's method. + * + * Reference(s): + * - https://en.wikipedia.org/wiki/List_of_Runge–Kutta_methods#Heun's_method + */ + lin::Vector operator()( + T ti, T dt, + lin::Vector const &xi, void *ptr, + lin::Vector (*dx)(T t, lin::Vector const &x, void *ptr)) { + // Table of integration constants + static constexpr T + b1 = 1.0 / 2.0, b2 = 1.0 / 2.0; + + // Scratch buffers + auto &ks = _k[0]; + auto &k1 = _k[1]; + auto &k2 = _k[2]; + + // Step forward + k1 = dx(ti, xi, ptr); + ks = xi + dt * k1; + k2 = dx(ti + dt, ks, ptr); + + // Step forward in time + return (xi + dt * (b1 * k1 + b2 * k2)).eval(); + } +}; +} // namespace gnc + +#endif diff --git a/include/gnc/ode3.hpp b/include/gnc/ode3.hpp new file mode 100644 index 00000000..b1120e29 --- /dev/null +++ b/include/gnc/ode3.hpp @@ -0,0 +1,89 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file gnc/ode3.hpp + * @author Kyle Krol + */ + +#ifndef GNC_ODE3_HPP_ +#define GNC_ODE3_HPP_ + +#include + +namespace gnc { + +/** @brief Third order fixed step size integrator. + * + * @tparam T Fundamental data type. + * @tparam N Number of state parameters. + */ +template +class Ode3 { + private: + lin::Vector _k[5]; + + public: + /** @brief Step a differential equation forward in time by a single timestep. + * + * @param[in] ti Initial time. + * @param[in] dt Integrator timestep. + * @param[in] xi Initial state. + * @param[in] ptr Pointer to arbitrary data accesible in the update function. + * @param[in] dx Differential update function. + * + * Used for fixed timestep numerical integration. Implements the third order + * integration scheme known as Ralston's method. + * + * Reference(s): + * - https://en.wikipedia.org/wiki/List_of_Runge–Kutta_methods#Ralston's_method + */ + lin::Vector operator()( + T ti, T dt, + lin::Vector const &xi, void *ptr, + lin::Vector (*dx)(T t, lin::Vector const &x, void *ptr)) { + // Table of values + constexpr static T + c2 = 1.0 / 2.0, a21 = 1.0 / 2.0, + c3 = 3.0 / 4.0, a22 = 3.0 / 4.0, + b1 = 2.0 / 9.0, b2 = 1.0 / 3.0, b3 = 4.0 / 9.0; + + // Scratch buffers + auto &ks = _k[0]; + auto &k1 = _k[1]; + auto &k2 = _k[2]; + auto &k3 = _k[3]; + + k1 = dx(ti, xi, ptr); + ks = xi + a21 * dt * k1; + k2 = dx(ti + c2 * dt, ks, ptr); + ks = xi + a22 * dt * k2; + k3 = dx(ti + c3 * dt, ks, ptr); + + // Step forward in time + return (xi + dt * (b1 * k1 + b2 * k2 + b3 * k3)).eval(); + } +}; +} // namespace gnc + +#endif diff --git a/include/gnc/ode4.hpp b/include/gnc/ode4.hpp new file mode 100644 index 00000000..34652bde --- /dev/null +++ b/include/gnc/ode4.hpp @@ -0,0 +1,93 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file gnc/ode4.hpp + * @author Kyle Krol + */ + +#ifndef GNC_ODE4_HPP_ +#define GNC_ODE4_HPP_ + +#include + +namespace gnc { + +/** @brief Fourth order fixed step size integrator. + * + * @tparam T Fundamental data type. + * @tparam N Number of state parameters. + */ +template +class Ode4 { + private: + lin::Vector _k[5]; + + public: + /** @brief Step a differential equation forward in time by a single timestep. + * + * @param[in] ti Initial time. + * @param[in] dt Integrator timestep. + * @param[in] xi Initial state. + * @param[in] ptr Pointer to arbitrary data accesible in the update function. + * @param[in] dx Differential update function. + * + * Used for fixed timestep numerical integration. Implements the classic + * fourth order Runge Kutta method. + * + * Reference(s): + * - https://en.wikipedia.org/wiki/List_of_Runge–Kutta_methods#Classic_fourth-order_method + */ + lin::Vector operator()( + T ti, T dt, + lin::Vector const &xi, void *ptr, + lin::Vector (*dx)(T t, lin::Vector const &x, void *ptr)) { + // Table of integration constants + static constexpr T + c2 = 1.0 / 2.0, a21 = 1.0 / 2.0, + c3 = 1.0 / 2.0, a32 = 1.0 / 2.0, + + b1 = 1.0 / 6.0, b2 = 1.0 / 3.0, b3 = 1.0 / 3.0, b4 = 1.0 / 6.0; + + // Scratch buffers + auto &ks = _k[0]; + auto &k1 = _k[1]; + auto &k2 = _k[2]; + auto &k3 = _k[3]; + auto &k4 = _k[4]; + + k1 = dx(ti, xi, ptr); + ks = xi + a21 * dt * k1; + k2 = dx(ti + c2 * dt, ks, ptr); + ks = xi + a32 * dt * k2; + k3 = dx(ti + c3 * dt, ks, ptr); + ks = xi + dt * k3; + k4 = dx(ti + dt, ks, ptr); + + // Step forward in time + return (xi + dt * (b1 * k1 + b2 * k2 + b3 * k3 + b4 * k4)).eval(); + } +}; +} // namespace gnc + +#endif diff --git a/include/psim/core/model.hpp b/include/psim/core/model.hpp index aeeff627..66392508 100644 --- a/include/psim/core/model.hpp +++ b/include/psim/core/model.hpp @@ -85,7 +85,7 @@ class Model { * error will be thrown. */ template - StateFieldWritable *get_field(State &state, std::string const &name) { + StateFieldWritable *get_writable_field(State &state, std::string const &name) { auto *field_ptr = dynamic_cast *>(state.get_writable(name)); if (!field_ptr) throw std::runtime_error("Invalid cast while getting a writable field: " + name); diff --git a/include/psim/core/parameter.hpp b/include/psim/core/parameter.hpp index a3541570..011b26c6 100644 --- a/include/psim/core/parameter.hpp +++ b/include/psim/core/parameter.hpp @@ -106,10 +106,12 @@ class Parameter : public ParameterBase { */ Parameter &operator=(T const &value) { _value = value; + return *this; } Parameter &operator=(T &&value) { _value = std::move(value); + return *this; } /** @} */ @@ -125,10 +127,12 @@ class Parameter : public ParameterBase { */ Parameter &operator=(ParameterBase const ¶m) { _value = param.get(); + return *this; } Parameter &operator=(ParameterBase &¶m) { _value = std::move(param.get()); + return *this; } /** @} */ diff --git a/include/psim/core/parameter_base.hpp b/include/psim/core/parameter_base.hpp index 71754888..afe90323 100644 --- a/include/psim/core/parameter_base.hpp +++ b/include/psim/core/parameter_base.hpp @@ -64,7 +64,7 @@ class ParameterBase { auto const *param_ptr = dynamic_cast const *>(this); if (!param_ptr) throw std::runtime_error("Invalid parameter type in call to 'get'."); - + return (T const &) *param_ptr; } diff --git a/include/psim/simulations/single_orbit.hpp b/include/psim/simulations/single_orbit.hpp new file mode 100644 index 00000000..1c2d2fea --- /dev/null +++ b/include/psim/simulations/single_orbit.hpp @@ -0,0 +1,49 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file psim/simulation/single_orbit.hpp + * @author Kyle Krol + */ + +#ifndef PSIM_SIMULATIONS_SINGLE_ORBIT_HPP_ +#define PSIM_SIMULATIONS_SINGLE_ORBIT_HPP_ + +#include +#include + +namespace psim { + +/** @brief Models a single satellite's orbital dynamics. All models are backed + * by flight software's GNC implementations if possible. + */ +class SingleOrbitGnc : public ModelList { + public: + SingleOrbitGnc() = delete; + virtual ~SingleOrbitGnc() = default; + + SingleOrbitGnc(Configuration const &config); +}; +} // namespace psim + +#endif diff --git a/include/psim/truth/earth.hpp b/include/psim/truth/earth.hpp new file mode 100644 index 00000000..39e10897 --- /dev/null +++ b/include/psim/truth/earth.hpp @@ -0,0 +1,57 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file psim/truth/earth.hpp + * @author Kyle Krol + */ + +#ifndef PSIM_TRUTH_EARTH_HPP_ +#define PSIM_TRUTH_EARTH_HPP_ + +#include + +namespace psim { + +/** @brief Ephemeris model implemented using the flight software GNC library. + * + * An updated model should be implemented as well that uses a more accurate + * model as our truth model. + */ +class EarthGnc : public Earth { + private: + typedef Earth Super; + + public: + using Super::Earth; + + EarthGnc() = delete; + virtual ~EarthGnc() = default; + + Vector4 prefix_earth_q_ecef_eci() const; + Vector4 prefix_earth_q_eci_ecef() const; + Vector3 prefix_earth_w_earth() const; +}; +} // namespace psim + +#endif diff --git a/include/psim/truth/earth.yml b/include/psim/truth/earth.yml new file mode 100644 index 00000000..c36dc440 --- /dev/null +++ b/include/psim/truth/earth.yml @@ -0,0 +1,30 @@ + +name: Earth +type: Model +comment: > + Responsible for providing ephemeris data related to the Earth. This includes + quaternions to facilitate the transformations between the ECEF and ECI + reference frames. + +args: + - prefix + +adds: + - name: "{prefix}.earth.q.eci_ecef" + type: Lazy Vector4 + comment: > + Quaternion rotating from ECEF to ECI. + - name: "{prefix}.earth.q.ecef_eci" + type: Lazy Vector4 + comment: > + Quaternion rotating from ECI to ECEF. + - name: "{prefix}.earth.w" + type: Lazy Vector3 + comment: > + Angular rate of Earth (i.e. also the angular rate of the ECEF frame). + +gets: + - name: "{prefix}.t.s" + type: Real + comment: > + Time sense the PAN epoch in seconds. diff --git a/include/psim/truth/environment.hpp b/include/psim/truth/environment.hpp new file mode 100644 index 00000000..5ac18057 --- /dev/null +++ b/include/psim/truth/environment.hpp @@ -0,0 +1,54 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file psim/truth/environment.hpp + * @author Kyle Krol + */ + +#ifndef PSIM_TRUTH_ENVIRONMENT_HPP_ +#define PSIM_TRUTH_ENVIRONMENT_HPP_ + +#include + +namespace psim { + +class EnvironmentGnc : public Environment { + private: + typedef Environment Super; + + public: + EnvironmentGnc() = delete; + virtual ~EnvironmentGnc() = default; + + /** @brief Set the frame argument to ECEF. + */ + EnvironmentGnc(Configuration const &config, std::string const &prefix, + std::string const &satellite); + + Vector3 prefix_satellite_environment_b() const; // Reported in ECEF + Vector3 prefix_satellite_environment_s() const; // Reported in ECI +}; +} // namespace psim + +#endif diff --git a/include/psim/truth/environment.yml b/include/psim/truth/environment.yml new file mode 100644 index 00000000..bec6e779 --- /dev/null +++ b/include/psim/truth/environment.yml @@ -0,0 +1,29 @@ + +name: Environment +type: Model +comment: > + Calculates environmental measurements seen by each spacecraft like the + magnetic field reading and sun vector. + +args: + - prefix + - satellite + - frame + +adds: + - name: "{prefix}.{satellite}.environment.b" + type: Lazy Vector3 + comment: > + The magnetic field at the point of the spacecraft. The frame this is + reported in is left up to the implementation. + - name: "{prefix}.{satellite}.environment.s" + type: Lazy Vector3 + comment: > + The unit vector point from the spacecraft to the sun. The frame this + is reported in is left up to the implementation. + +gets: + - name: "{prefix}.t.s" + type: Real + - name: "{prefix}.{satellite}.orbit.r.{frame}" + type: Vector3 diff --git a/include/psim/truth/orbit.hpp b/include/psim/truth/orbit.hpp new file mode 100644 index 00000000..e3b7896c --- /dev/null +++ b/include/psim/truth/orbit.hpp @@ -0,0 +1,58 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file psim/truth/orbit.hpp + * @author Kyle Krol + */ + +#ifndef PSIM_TRUTH_ORBIT_HPP_ +#define PSIM_TRUTH_ORBIT_HPP_ + +#include + +#include + +namespace psim { + +/** @brief Orbit propegator in ECI implemented using the GNC gravity model. + */ +class OrbitGncEci : public Orbit { + private: + typedef Orbit Super; + gnc::Ode4 ode; + + public: + OrbitGncEci() = delete; + virtual ~OrbitGncEci() = default; + + /** @brief Set the frame argument to ECI. + */ + OrbitGncEci(Configuration const &config, std::string const &prefix, + std::string const &satellite); + + virtual void step() override; +}; +} // namespace psim + +#endif diff --git a/include/psim/truth/orbit.yml b/include/psim/truth/orbit.yml new file mode 100644 index 00000000..f50ca0b8 --- /dev/null +++ b/include/psim/truth/orbit.yml @@ -0,0 +1,47 @@ + +name: Orbit +type: Model +comment: > + Generic interface for a point mass orbit propagator. The coordinate system + the model operates in is implementation dependant. This model should be + used for simulations that don't rely on attitude dynamics. + +args: + - prefix + - satellite + - frame + +params: + - name: "{prefix}.{satellite}.m" + type: Real + comment: > + Mass of the satellite in units of kilograms. + +adds: + - name: "{prefix}.{satellite}.orbit.r" + type: Initialized Vector3 + comment: > + Position of the satellite in units of meters. The coordinate system is + implementation dependant. + - name: "{prefix}.{satellite}.orbit.v" + type: Initialized Vector3 + comment: > + Velocity of the satellite in units of meters per second. The + coordinate system is implementation dependant. + - name: "{prefix}.{satellite}.orbit.J.{frame}" + type: Initialized Writable Vector3 + comment: > + Impulse applied to the satellite on a given timestep in units of + kilogram meters per second squared. The coordinate system is + implementation dependant. Note that this field is zeroed out on each + simulation step to avoid applying a continuous input. + +gets: + - name: "{prefix}.t.s" + type: Real + comment: > + Time sense the PAN epoch in seconds. + - name: "{prefix}.dt.s" + type: Real + comment: > + Timestep of the simulation in seconds. diff --git a/include/psim/truth/transform_direction.hpp b/include/psim/truth/transform_direction.hpp new file mode 100644 index 00000000..18d505f8 --- /dev/null +++ b/include/psim/truth/transform_direction.hpp @@ -0,0 +1,88 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file psim/truth/transform_direction.hpp + * @author Kyle Krol + */ + +#ifndef PSIM_TRUTH_TRANSFORM_DIRECTION_HPP_ +#define PSIM_TRUTH_TRANSFORM_DIRECTION_HPP_ + +#include + +namespace psim { + +/** @brief Direction transformer taking input in the body frame. + */ +class TransformDirectionBody : public TransformDirection { + private: + typedef TransformDirection Super; + + public: + using Super::TransformDirection; + + TransformDirectionBody() = delete; + virtual ~TransformDirectionBody() = default; + + Vector3 vector_body() const; + Vector3 vector_ecef() const; + Vector3 vector_eci() const; +}; + +/** @brief Direction transformer taking input in ECEF. + */ +class TransformDirectionEcef : public TransformDirection { + private: + typedef TransformDirection Super; + + public: + using Super::TransformDirection; + + TransformDirectionEcef() = delete; + virtual ~TransformDirectionEcef() = default; + + Vector3 vector_body() const; + Vector3 vector_ecef() const; + Vector3 vector_eci() const; +}; + +/** @brief Direction transformer taking input in ECI. + */ +class TransformDirectionEci : public TransformDirection { + private: + typedef TransformDirection Super; + + public: + using Super::TransformDirection; + + TransformDirectionEci() = delete; + virtual ~TransformDirectionEci() = default; + + Vector3 vector_body() const; + Vector3 vector_ecef() const; + Vector3 vector_eci() const; +}; +} // namespace psim + +#endif diff --git a/include/psim/truth/transform_direction.yml b/include/psim/truth/transform_direction.yml new file mode 100644 index 00000000..6139e8e2 --- /dev/null +++ b/include/psim/truth/transform_direction.yml @@ -0,0 +1,43 @@ + +name: TransformDirection +type: Model +comment: > + Reference frame independent interface for providing lazy transformations of + a vector in an implementation dependant frame to a vector in the body, ECEF, + and ECI frame. This is identical to the position transformer except it + includes the body frame as well. This is primarily intended for transforming + 'directional' vectors into the body frame to act as sensor measurements. + +args: + - prefix + - satellite + - vector + +adds: + - name: "{vector}.body" + type: Lazy Vector3 + comment: > + Input vector represented in the body frame. + - name: "{vector}.ecef" + type: Lazy Vector3 + comment: > + Input vector represented in the ECEF frame. + - name: "{vector}.eci" + type: Lazy Vector3 + comment: > + Input vector represented in the ECI frame. + +gets: + - name: "{vector}" + type: Vector3 + comment: > + Input vector. The frame of this vector is specified by the + implementation. + - name: "{prefix}.earth.q.eci_ecef" + type: Vector4 + - name: "{prefix}.earth.q.ecef_eci" + type: Vector4 + - name: "{prefix}.{satellite}.attitude.q.body_eci" + type: Vector4 + - name: "{prefix}.{satellite}.attitude.q.eci_body" + type: Vector4 diff --git a/include/psim/truth/transform_position.hpp b/include/psim/truth/transform_position.hpp new file mode 100644 index 00000000..1041f049 --- /dev/null +++ b/include/psim/truth/transform_position.hpp @@ -0,0 +1,69 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file psim/truth/transform_position.hpp + * @author Kyle Krol + */ + +#ifndef PSIM_TRUTH_TRANSFORM_POSITION_HPP_ +#define PSIM_TRUTH_TRANSFORM_POSITION_HPP_ + +#include + +namespace psim { + +/** @brief Position transformer taking a position in ECEF as input. + */ +class TransformPositionEcef : public TransformPosition { + private: + typedef TransformPosition Super; + + public: + using Super::TransformPosition; + + TransformPositionEcef() = delete; + virtual ~TransformPositionEcef() = default; + + Vector3 vector_ecef() const; + Vector3 vector_eci() const; +}; + +/** @brief Position transformer taking a position in ECI as input. + */ +class TransformPositionEci : public TransformPosition { + private: + typedef TransformPosition Super; + + public: + using Super::TransformPosition; + + TransformPositionEci() = delete; + virtual ~TransformPositionEci() = default; + + Vector3 vector_ecef() const; + Vector3 vector_eci() const; +}; +} // namespace psim + +#endif diff --git a/include/psim/truth/transform_position.yml b/include/psim/truth/transform_position.yml new file mode 100644 index 00000000..3bd128ce --- /dev/null +++ b/include/psim/truth/transform_position.yml @@ -0,0 +1,32 @@ + +name: TransformPosition +type: Model +comment: > + Reference frame independent interface for providing lazy transformations of + a position vector in an implementation dependant frame to position vectors + in the ECI and ECEF frames. + +args: + - prefix + - vector + +adds: + - name: "{vector}.ecef" + type: Lazy Vector3 + comment: > + Input position vector represented in the ECEF frame. + - name: "{vector}.eci" + type: Lazy Vector3 + comment: > + Input position vector represented in the ECI frame. + +gets: + - name: "{vector}" + type: Vector3 + comment: > + Input position vector. The frame of this vector is specified by the + implementation. + - name: "{prefix}.earth.q.eci_ecef" + type: Vector4 + - name: "{prefix}.earth.q.ecef_eci" + type: Vector4 diff --git a/include/psim/truth/transform_velocity.hpp b/include/psim/truth/transform_velocity.hpp new file mode 100644 index 00000000..28b2ccc2 --- /dev/null +++ b/include/psim/truth/transform_velocity.hpp @@ -0,0 +1,75 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file psim/truth/transform_position.hpp + * @author Kyle Krol + */ + +#ifndef PSIM_TRUTH_TRANSFORM_VELOCITY_HPP_ +#define PSIM_TRUTH_TRANSFORM_VELOCITY_HPP_ + +#include + +namespace psim { + +/** @brief Velocity transformer taking a velocity in ECEF as input. + */ +class TransformVelocityEcef : public TransformVelocity { + private: + typedef TransformVelocity Super; + + public: + TransformVelocityEcef() = delete; + virtual ~TransformVelocityEcef() = default; + + /** @brief Sets the frame arguments to ECEF. + */ + TransformVelocityEcef(Configuration const &config, std::string const &prefix, + std::string const &satellite, std::string const &vector); + + Vector3 vector_ecef() const; + Vector3 vector_eci() const; +}; + +/** @brief Velocity transformer taking a velocity in ECI as input. + */ +class TransformVelocityEci : public TransformVelocity { + private: + typedef TransformVelocity Super; + + public: + TransformVelocityEci() = delete; + virtual ~TransformVelocityEci() = default; + + /** @brief Sets the frame arguments to ECI. + */ + TransformVelocityEci(Configuration const &config, std::string const &prefix, + std::string const &satellite, std::string const &vector); + + Vector3 vector_ecef() const; + Vector3 vector_eci() const; +}; +} // namespace psim + +#endif diff --git a/include/psim/truth/transform_velocity.yml b/include/psim/truth/transform_velocity.yml new file mode 100644 index 00000000..3d3dd248 --- /dev/null +++ b/include/psim/truth/transform_velocity.yml @@ -0,0 +1,42 @@ + +name: TransformVelocity +type: Model +comment: > + Reference frame independent interface for providing lazy transformations of + a velocity vector in an implementation dependant frame to velocity vectors + in the ECI and ECEF frames. + +args: + - prefix + - satellite + - vector + - frame + +adds: + - name: "{vector}.ecef" + type: Lazy Vector3 + comment: > + Input velocity vector represented in the ECEF frame. + - name: "{vector}.eci" + type: Lazy Vector3 + comment: > + Input velocity vector represented in the ECI frame. + +gets: + - name: "{vector}" + type: Vector3 + comment: > + Input velocity vector. The frame of this vector is specified by the + implementation. + - name: "{prefix}.earth.q.eci_ecef" + type: Vector4 + - name: "{prefix}.earth.q.ecef_eci" + type: Vector4 + - name: "{prefix}.earth.w" + type: Vector3 + - name: "{prefix}.{satellite}.orbit.r.{frame}" + type: Vector3 + comment: > + The frame of the position vector needs to be explicitly specified here + because we need the position of the frame we're transforming from. + This implies this model is also dependant on a position transformer. diff --git a/lib/lin b/lib/lin index 1adce4f9..a1c79148 160000 --- a/lib/lin +++ b/lib/lin @@ -1 +1 @@ -Subproject commit 1adce4f99d326c6fc57f516caec4ae7f9589dc59 +Subproject commit a1c7914809de55f83c9f8d2ebf6ca89dd34690d8 diff --git a/python/BUILD.bazel b/python/BUILD.bazel new file mode 100644 index 00000000..27918cec --- /dev/null +++ b/python/BUILD.bazel @@ -0,0 +1,32 @@ +load("@rules_python//python:defs.bzl", "py_binary", "py_library") + +load("//bazel:psim_build.bzl", "psim_py_extension") + +# PSim in Python +psim_py_extension( + name = "_psim", + srcs = glob([ + "psim/_psim/**/*.hpp", "psim/_psim/**/*.inl", + "psim/_psim/**/*.cpp", + ]), + deps = [ + "@variant//:variant", "//:psim_core", "//:psim_simulations", + ], + visibility = ["//visibility:private"], +) + +py_library( + name = "psim", + srcs = glob(["psim/**/*.py"]), + data = ["//config:parameters", "//config:plots"], + deps = [":_psim"], + imports = ["."], + visibility = ["//visibility:public"], +) + +py_binary( + name = "single_orbit", + srcs = ["scripts/single_orbit.py"], + deps = ["//python:psim"], + visibility = ["//visibility:public"], +) diff --git a/python/psim/__init__.py b/python/psim/__init__.py new file mode 100644 index 00000000..353cc9d1 --- /dev/null +++ b/python/psim/__init__.py @@ -0,0 +1,59 @@ +"""Python wrappers and utilities for PSim simulations. +""" + +from _psim import ( + Configuration as _Configuration, + # Import C++ simulations + SingleOrbitGnc, +) + +# ASCII art from: +# http://patorjk.com/software/taag/#p=display&f=3D Diagonal&t=PSIM +_BANNER = """ +,-.----. ____ +\ / \ .--.--. ,---, ,' , `. +| : \ / / '. ,`--.' | ,-+-,.' _ | +| | .\ :| : /`. / | : : ,-+-. ; , || +. : |: |; | |--` : | ' ,--.'|' | ;| +| | \ :| : ;_ | : || | ,', | ': +| : . / \ \ `. ' ' ;| | / | | || +; | |`-' `----. \| | |' | : | : |, +| | ; __ \ \ |' : ;; . | ; |--' +: ' | / /`--' /| | '| : | | , +: : : '--'. / ' : || : ' |/ +| | : `--'---' ; |.' ; | |`-' +`---'.| '---' | ;/ + `---` '---' + +""" + + +class Simulation(object): + """Small wrapper for a PSim simulation. + + This is the main entrypoint for end users and allows them to override the + step function and add in there own actions accordingly. + """ + def __init__(self, sim, config, quiet = False): + super(Simulation, self).__init__() + + # Generate the simulation + self._simulation = sim(_Configuration(config)) + + if not quiet: + print(_BANNER) + + def __getitem__(self, name): + """Retrives a state field from the underlying simulation. + """ + return self._simulation[name] + + def __setitem__(self, name, value): + """Sets a state field in the underlying simulation. + """ + self._simulation[name] = value + + def step(self): + """Steps the underlying simulation forward in time. + """ + self._simulation.step() diff --git a/python/psim/_psim/configuration.cpp b/python/psim/_psim/configuration.cpp new file mode 100644 index 00000000..214e85eb --- /dev/null +++ b/python/psim/_psim/configuration.cpp @@ -0,0 +1,70 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file python/psim/_psim/configuration.hpp + * @author Kyle Krol + */ + +#include "pybind11.hpp" + +#include +#include +#include + +namespace py = pybind11; + +#include + +using PyVariant = mapbox::util::variant; + +static PyVariant py_visit(psim::ParameterBase const &field) { + // TODO : Update this to use double dispatch or something else that's better + { + auto const *ptr = dynamic_cast const *>(&field); + if (ptr) return ptr->get(); + } + { + auto const *ptr = dynamic_cast const *>(&field); + if (ptr) return ptr->get(); + } + { + auto const *ptr = dynamic_cast const *>(&field); + if (ptr) return ptr->get(); + } + { + auto const *ptr = dynamic_cast const *>(&field); + if (ptr) return ptr->get(); + } + { + auto const *ptr = dynamic_cast const *>(&field); + if (ptr) return ptr->get(); + } + throw std::runtime_error("Attempted to retrieve state field of an unsupported type."); +} + +void py_configuration(py::module &m) { + py::class_(m, "Configuration") + .def(py::init([](std::string const &file) { return psim::Configuration::make(file); })) + .def("__getitem__", [](psim::Configuration const &self, std::string const &name) { return py_visit(self[name]); }); +} diff --git a/python/psim/_psim/module.cpp b/python/psim/_psim/module.cpp new file mode 100644 index 00000000..19a5d890 --- /dev/null +++ b/python/psim/_psim/module.cpp @@ -0,0 +1,41 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file python/psim/_psim/module.cpp + * @author Kyle Krol + */ + +#include "pybind11.hpp" + +#include + +namespace py = pybind11; + +void py_configuration(py::module &m); +void py_simulation(py::module &m); + +PYBIND11_MODULE(_psim, m) { + py_configuration(m); + py_simulation(m); +} diff --git a/python/psim/_psim/pybind11.hpp b/python/psim/_psim/pybind11.hpp new file mode 100644 index 00000000..418dd3bb --- /dev/null +++ b/python/psim/_psim/pybind11.hpp @@ -0,0 +1,58 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file psim/_psim/pybind11.hpp + * @author Kyle Krol + */ + +#ifndef PSIM_PYTHON_VARIANT_HPP_ +#define PSIM_PYTHON_VARIANT_HPP_ + +#include +#include + +#include + +namespace pybind11 { +namespace detail { + +/* This code allows us to plug the variant type into pybind11. See the following + * link for more information: + * - https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html?highlight=stl + */ + +template +struct type_caster> : variant_caster> {}; + +template <> +struct visit_helper { + template + static auto call(Args &&...args) -> decltype(mapbox::util::apply_visitor(args...)) { + return mapbox::util::apply_visitor(args...); + } +}; +} // namespace detail +} // namespace pybind11 + +#endif diff --git a/python/psim/_psim/simulations.cpp b/python/psim/_psim/simulations.cpp new file mode 100644 index 00000000..6237eb21 --- /dev/null +++ b/python/psim/_psim/simulations.cpp @@ -0,0 +1,105 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file python/psim/_psim/simulations.hpp + * @author Kyle Krol + */ + +#include "pybind11.hpp" + +#include +#include +#include +#include +#include + +#include + +namespace py = pybind11; + +using PyVariant = mapbox::util::variant; + +static PyVariant py_visit(psim::StateFieldBase const &field) { + // TODO : Update this to use double dispatch or something else that's better + { + auto const *ptr = dynamic_cast const *>(&field); + if (ptr) return ptr->get(); + } + { + auto const *ptr = dynamic_cast const *>(&field); + if (ptr) return ptr->get(); + } + { + auto const *ptr = dynamic_cast const *>(&field); + if (ptr) return ptr->get(); + } + { + auto const *ptr = dynamic_cast const *>(&field); + if (ptr) return ptr->get(); + } + { + auto const *ptr = dynamic_cast const *>(&field); + if (ptr) return ptr->get(); + } + throw std::runtime_error("Attempted to retrieve state field of an unsupported type."); +} + +template +static void py_assign(psim::StateFieldWritableBase &field, T const &value) { + auto *ptr = dynamic_cast *>(&field); + if (!ptr) + std::runtime_error("Attempted to write to state field with incorrect type."); + *ptr = value; +} + +#define PY_SIMULATION(model) \ + py::class_(m, #model) \ + .def(py::init([](psim::Configuration const &config) { \ + return psim::Simulation::make(config); \ + })) \ + .def("__getitem__", [](psim::Simulation const &self, std::string const &name) { \ + auto const *ptr = self.get(name); \ + if (!ptr) \ + throw std::runtime_error("State field '" + name + "' does not exist."); \ + return py_visit(*ptr); \ + }) \ + .def("__setitem__", [](psim::Simulation &self, std::string const &name, PyVariant const &value) { \ + auto *ptr = self.get_writable(name); \ + if (!ptr) \ + throw std::runtime_error("Writable state field '" + name + "' does not exist."); \ + value.match( \ + [&ptr](psim::Real const &v) { py_assign(*ptr, v); }, \ + [&ptr](psim::Integer const &v) { py_assign(*ptr, v); }, \ + [&ptr](psim::Vector2 const &v) { py_assign(*ptr, v); }, \ + [&ptr](psim::Vector3 const &v) { py_assign(*ptr, v); }, \ + [&ptr](psim::Vector4 const &v) { py_assign(*ptr, v); } \ + ); \ + }) \ + .def("step", [](psim::Simulation &self) { \ + self.step(); \ + }) + +void py_simulation(py::module &m) { + PY_SIMULATION(SingleOrbitGnc); +} diff --git a/python/psim/sims.py b/python/psim/sims.py new file mode 100644 index 00000000..9d546525 --- /dev/null +++ b/python/psim/sims.py @@ -0,0 +1,6 @@ +"""Pulls in PSim simulations into a public module. +""" + +from _psim import ( + SingleOrbitGnc, +) diff --git a/python/scripts/single_orbit.py b/python/scripts/single_orbit.py new file mode 100644 index 00000000..76fedaf0 --- /dev/null +++ b/python/scripts/single_orbit.py @@ -0,0 +1,60 @@ +"""Example script showing how to run a single orbit simulation. +""" + +from matplotlib import pyplot as plt +from mpl_toolkits.mplot3d import Axes3D + +import lin +import psim + + +class Simulation(psim.Simulation): + + def __init__(self): + super(Simulation, self).__init__(psim.SingleOrbitGnc, "config/parameters/single_orbit.txt") + + self.ts = [] + self.rx = [] + self.ry = [] + self.rz = [] + + self.n = 0 + + def step(self): + super(Simulation, self).step() + + self.n = self.n + 1 + if self.n % 100 == 0: + r = self["truth.leader.orbit.r.eci"] + self.rx.append(r[0]) + self.ry.append(r[1]) + self.rz.append(r[2]) + + self.ts.append(self["truth.t.s"]) + + self.n = 0 + + +sim = Simulation() +steps = 3600.0 * 24.0 / sim['truth.dt.s'] # Simulate for a day +for _ in range(int(steps)): + sim.step() + +fig = plt.figure() +plt.plot(sim.rx, sim.ry, 'b.') +plt.xlabel('x (m)') +plt.ylabel('y (m)') +plt.title('Position Projected into the XY Plane') +fig.show() + +fig = plt.figure() +ax = fig.add_subplot(111, projection='3d') +ax.plot(sim.rx, sim.ry, sim.rz, 'b-') +fig.show() + +plt.figure() +plt.plot(sim.ts, sim.rz, 'b.') +plt.xlabel("t (s)") +plt.ylabel("z (m)") +plt.title('Z Position') +plt.show() diff --git a/requirements.txt b/requirements.txt index dee5447d..f1539169 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,13 @@ -argparse +matplotlib +numpy platformio pylint -numpy -matplotlib +pytest +pyyaml +./lib/lin +# Held over from orbit estimator days cppimport h5py dulwich ipykernel notebook -pyyaml \ No newline at end of file diff --git a/src/gnc/ode.cpp b/src/gnc/ode.cpp index f17d323e..24d9ab1a 100644 --- a/src/gnc/ode.cpp +++ b/src/gnc/ode.cpp @@ -9,150 +9,6 @@ namespace gnc { -template -static inline void odex(T const *t, unsigned int nt, T **y, unsigned int ne, - T *bf, void (*const f)(T, T const *, T *)) { - // Loop through timesteps - for (unsigned int i = 1; i < nt; i++) - S(t[i - 1], t[i] - t[i - 1], y[i - 1], y[i], ne, bf, f); -} - -// Reference: -// https://en.wikipedia.org/wiki/Euler_method -template -void ode1(T ti, T dt, T const *yi, T *yf, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)) { - // Setup scratch buffers - T *const k1 = bf; - - // Step forward - f(ti, yi, k1); - for (unsigned int i = 0; i < ne; i++) yf[i] = yi[i] + dt * k1[i]; -} - -GNC_ODEX_SINGLE_TEMPLATE(ode1, float); -GNC_ODEX_SINGLE_TEMPLATE(ode1, double); - -template -void ode1(T const *t, unsigned int nt, T **y, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)) { - odex(t, nt, y, ne, bf, f); -} - -GNC_ODEX_MULTI_TEMPLATE(ode1, float); -GNC_ODEX_MULTI_TEMPLATE(ode1, double); - -// Reference: -// https://en.wikipedia.org/wiki/List_of_Runge–Kutta_methods#Heun's_method -template -void ode2(T ti, T dt, T const *yi, T *yf, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)) { - // Table of values - constexpr static T b1 = 1.0 / 2.0, b2 = 1.0 / 2.0; - - // Setup scratch buffers - T *const k1 = bf; - T *const k2 = k1 + ne; - T *const ks = k2 + ne; - - // Step forward - f(ti, yi, k1); - for (unsigned int i = 0; i < ne; i++) ks[i] = yi[i] + dt * k1[i]; - f(ti + dt, ks, k2); - for (unsigned int i = 0; i < ne; i++) yf[i] = yi[i] + dt * (b1 * k1[i] + b2 * k2[i]); -} - -GNC_ODEX_SINGLE_TEMPLATE(ode2, float); -GNC_ODEX_SINGLE_TEMPLATE(ode2, double); - -template -void ode2(T const *t, unsigned int nt, T **y, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)) { - odex(t, nt, y, ne, bf, f); -} - -GNC_ODEX_MULTI_TEMPLATE(ode2, float); -GNC_ODEX_MULTI_TEMPLATE(ode2, double); - -// Reference: -// https://en.wikipedia.org/wiki/List_of_Runge–Kutta_methods#Ralston's_method -template -void ode3(T ti, T dt, T const *yi, T *yf, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)) { - // Table of values - constexpr static T a21 = 1.0 / 2.0, a22 = 3.0 / 4.0; - constexpr static T c2 = 1.0 / 2.0, c3 = 3.0 / 4.0; - constexpr static T b1 = 2.0 / 9.0, b2 = 1.0 / 3.0, b3 = 4.0 / 9.0; - - // Setup scratch buffers - T *const k1 = bf; - T *const k2 = k1 + ne; - T *const k3 = k2 + ne; - T *const ks = k3 + ne; - - // Step forward - f(ti, yi, k1); - for (unsigned int i = 0; i < ne; i++) ks[i] = yi[i] + a21 * dt * k1[i]; - f(ti + c2 * dt, ks, k2); - for (unsigned int i = 0; i < ne; i++) ks[i] = yi[i] + a22 * dt * k2[i]; - f(ti + c3 * dt, ks, k3); - for (unsigned int i = 0; i < ne; i++) yf[i] = yi[i] + dt * (b1 * k1[i] + b2 * k2[i] + b3 * k3[i]); -} - -GNC_ODEX_SINGLE_TEMPLATE(ode3, float); -GNC_ODEX_SINGLE_TEMPLATE(ode3, double); - -template -void ode3(T const *t, unsigned int nt, T **y, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)) { - odex(t, nt, y, ne, bf, f); -} - -GNC_ODEX_MULTI_TEMPLATE(ode3, float); -GNC_ODEX_MULTI_TEMPLATE(ode3, double); - -// Reference: -// https://en.wikipedia.org/wiki/List_of_Runge–Kutta_methods#Classic_fourth-order_method -template -void ode4(T ti, T dt, T const *yi, T *yf, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)) { - // Table of values - constexpr static T a21 = 1.0 / 2.0, a32 = 1.0 / 2.0; - constexpr static T c2 = 1.0 / 2.0, c3 = 1.0 / 2.0; - constexpr static T b1 = 1.0 / 6.0, b2 = 1.0 / 3.0, b3 = 1.0 / 3.0, b4 = 1.0 / 6.0; - - // Setup scratch buffers - T *const k1 = bf; - T *const k2 = k1 + ne; - T *const k3 = k2 + ne; - T *const k4 = k3 + ne; - T *const ks = k4 + ne; - - // Step forward - f(ti, yi, k1); - for (unsigned int i = 0; i < ne; i++) ks[i] = yi[i] + a21 * dt * k1[i]; - f(ti + c2 * dt, ks, k2); - for (unsigned int i = 0; i < ne; i++) ks[i] = yi[i] + a32 * dt * k2[i]; - f(ti + c3 * dt, ks, k3); - for (unsigned int i = 0; i < ne; i++) ks[i] = yi[i] + dt * k3[i]; - f(ti + dt, ks, k4); - for (unsigned int i = 0; i < ne; i++) - yf[i] = yi[i] + dt * (b1 * k1[i] + b2 * k2[i] + b3 * k3[i] + b4 * k4[i]); -} - -GNC_ODEX_SINGLE_TEMPLATE(ode4, float); -GNC_ODEX_SINGLE_TEMPLATE(ode4, double); - -template -void ode4(T const *t, unsigned int nt, T **y, unsigned int ne, T *bf, - void (*const f)(T, T const *, T *)) { - odex(t, nt, y, ne, bf, f); -} - -GNC_ODEX_MULTI_TEMPLATE(ode4, float); -GNC_ODEX_MULTI_TEMPLATE(ode4, double); - // Reference: // https://en.wikipedia.org/wiki/Bogacki–Shampine_method template diff --git a/src/psim/simulations/single_orbit.cpp b/src/psim/simulations/single_orbit.cpp new file mode 100644 index 00000000..868b1c3d --- /dev/null +++ b/src/psim/simulations/single_orbit.cpp @@ -0,0 +1,51 @@ +// +// MIT License +// +// Copyright (c) 2020 Pathfinder for Autonomous Navigation (PAN) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** @file psim/simulation/single_orbit.cpp + * @author Kyle Krol + */ + +#include + +#include +#include +#include +#include +#include +#include + +namespace psim { + +SingleOrbitGnc::SingleOrbitGnc(Configuration const &config) { + // Time and Earth ephemeris + add